Skip to content

Commit 22e23a2

Browse files
committed
oauth: allow client_credentials 2 legged oauth to suport ivory
1 parent e4885c4 commit 22e23a2

File tree

3 files changed

+50
-25
lines changed

3 files changed

+50
-25
lines changed

Diff for: internal/httpx/params.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ package httpx
33
import (
44
"errors"
55
"fmt"
6+
"mime"
67
"net/http"
78
"net/url"
8-
"strings"
99

1010
"github.com/go-json-experiment/json"
1111
"github.com/gorilla/schema"
@@ -25,7 +25,11 @@ func Params(r *http.Request, v interface{}) error {
2525
return Error(http.StatusBadRequest, err)
2626
}
2727
case "POST", "PUT":
28-
switch mediaType(r) {
28+
mt, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
29+
if err != nil {
30+
return Error(http.StatusBadRequest, err)
31+
}
32+
switch mt {
2933
case "application/json":
3034
if err := json.UnmarshalFull(r.Body, v); err != nil {
3135
return Error(http.StatusBadRequest, err)
@@ -61,8 +65,3 @@ func Params(r *http.Request, v interface{}) error {
6165
}
6266
return nil
6367
}
64-
65-
// mediaType returns the media type of the request.
66-
func mediaType(req *http.Request) string {
67-
return strings.Split(req.Header.Get("Content-Type"), ";")[0]
68-
}

Diff for: models/token.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
type Token struct {
1515
AccessToken string `gorm:"size:64;primaryKey;autoIncrement:false"`
1616
CreatedAt time.Time
17-
AccountID snowflake.ID
17+
AccountID *snowflake.ID
1818
Account *Account `gorm:"constraint:OnDelete:CASCADE;<-:false;"`
1919
ApplicationID snowflake.ID
2020
Application *Application `gorm:"constraint:OnDelete:CASCADE;<-:false;"`

Diff for: oauth/oauth.go

+43-17
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"errors"
55
"fmt"
66
"io"
7-
"log"
87

98
"net/http"
109

@@ -88,7 +87,7 @@ func AuthorizeCreate(env *activitypub.Env, w http.ResponseWriter, r *http.Reques
8887

8988
token := &models.Token{
9089
AccessToken: uuid.New().String(),
91-
AccountID: account.ID,
90+
AccountID: &account.ID,
9291
ApplicationID: app.ID,
9392
TokenType: models.TokenType("Bearer"),
9493
Scope: "read write follow push",
@@ -110,32 +109,59 @@ func TokenCreate(env *activitypub.Env, w http.ResponseWriter, r *http.Request) e
110109
ClientID string `json:"client_id" schema:"client_id,required"`
111110
ClientSecret string `json:"client_secret" schema:"client_secret,required"`
112111
GrantType string `json:"grant_type" schema:"grant_type,required"`
113-
Code string `json:"code" schema:"code,required"`
112+
Code string `json:"code" schema:"code"`
114113
RedirectURI string `json:"redirect_uri" schema:"redirect_uri,required"`
115-
Scope string `json:"-" schema:"scope"` // ignored
114+
Scope string `json:"scope" schema:"scope"`
116115
}
117116
if err := httpx.Params(r, &params); err != nil {
118117
return err
119118
}
120-
var token models.Token
121-
if err := env.DB.Where("authorization_code = ?", params.Code).First(&token).Error; err != nil {
122-
return httpx.Error(http.StatusUnauthorized, fmt.Errorf("token with code %s not found", params.Code))
123-
}
119+
124120
var app models.Application
125121
if err := env.DB.Where("client_id = ?", params.ClientID).First(&app).Error; err != nil {
126122
return httpx.Error(http.StatusBadRequest, fmt.Errorf("failed to find application: %w", err))
127123
}
124+
if app.ClientSecret != params.ClientSecret {
125+
return httpx.Error(http.StatusUnauthorized, fmt.Errorf("invalid client_secret"))
126+
}
128127

129-
if token.ApplicationID != app.ID {
130-
log.Println("client_id mismatch", token.ApplicationID, app.ID)
131-
return httpx.Error(http.StatusUnauthorized, fmt.Errorf("client_id mismatch"))
128+
switch params.GrantType {
129+
case "authorization_code":
130+
var token models.Token
131+
if err := env.DB.Where("authorization_code = ?", params.Code).First(&token).Error; err != nil {
132+
return httpx.Error(http.StatusUnauthorized, fmt.Errorf("token with code %s not found", params.Code))
133+
}
134+
if token.ApplicationID != app.ID {
135+
return httpx.Error(http.StatusUnauthorized, fmt.Errorf("client_id mismatch"))
136+
}
137+
return to.JSON(w, map[string]any{
138+
"access_token": token.AccessToken,
139+
"token_type": token.TokenType,
140+
"scope": token.Scope,
141+
"created_at": token.CreatedAt.Unix(),
142+
})
143+
case "refresh_token":
144+
return httpx.Error(http.StatusNotImplemented, fmt.Errorf("refresh_token grant type not implemented"))
145+
case "client_credentials":
146+
token := &models.Token{
147+
AccessToken: uuid.New().String(),
148+
ApplicationID: app.ID,
149+
TokenType: models.TokenType("Bearer"),
150+
Scope: params.Scope,
151+
AuthorizationCode: uuid.New().String(),
152+
}
153+
if err := env.DB.Create(token).Error; err != nil {
154+
return err
155+
}
156+
return to.JSON(w, map[string]any{
157+
"access_token": token.AccessToken,
158+
"token_type": token.TokenType,
159+
"scope": token.Scope,
160+
"created_at": token.CreatedAt.Unix(),
161+
})
162+
default:
163+
return httpx.Error(http.StatusBadRequest, fmt.Errorf("invalid grant_type"))
132164
}
133-
return to.JSON(w, map[string]any{
134-
"access_token": token.AccessToken,
135-
"token_type": token.TokenType,
136-
"scope": token.Scope,
137-
"created_at": token.CreatedAt.Unix(),
138-
})
139165
}
140166

141167
func TokenDestroy(env *activitypub.Env, w http.ResponseWriter, r *http.Request) error {

0 commit comments

Comments
 (0)