M db.go => db.go +2 -2
@@ 237,8 237,8 @@ func (db *DB) RevokeAccessTokens(ctx context.Context, clientID ID[*Client], user
func (db *DB) CreateAuthCode(ctx context.Context, code *AuthCode) error {
return db.db.QueryRowContext(ctx, `
- INSERT INTO AuthCode(hash, created_at, user, client, scope)
- VALUES (:hash, :created_at, :user, :client, :scope)
+ INSERT INTO AuthCode(hash, created_at, user, client, scope, redirect_uri)
+ VALUES (:hash, :created_at, :user, :client, :scope, :redirect_uri)
RETURNING id
`, entityArgs(code)...).Scan(&code.ID)
}
M entity.go => entity.go +19 -22
@@ 231,37 231,34 @@ type AuthorizedClient struct {
}
type AuthCode struct {
- ID ID[*AuthCode]
- Hash []byte
- CreatedAt time.Time
- User ID[*User]
- Client ID[*Client]
- Scope string
+ ID ID[*AuthCode]
+ Hash []byte
+ CreatedAt time.Time
+ User ID[*User]
+ Client ID[*Client]
+ Scope string
+ RedirectURI string
}
-func NewAuthCode(user ID[*User], client ID[*Client], scope string) (code *AuthCode, secret string, err error) {
+func (code *AuthCode) Generate() (secret string, err error) {
secret, hash, err := generateSecret()
if err != nil {
- return nil, "", fmt.Errorf("failed to generate authentication code secret: %v", err)
- }
- code = &AuthCode{
- Hash: hash,
- CreatedAt: time.Now(),
- User: user,
- Client: client,
- Scope: scope,
+ return "", fmt.Errorf("failed to generate authentication code secret: %v", err)
}
- return code, secret, nil
+ code.Hash = hash
+ code.CreatedAt = time.Now()
+ return secret, nil
}
func (code *AuthCode) columns() map[string]interface{} {
return map[string]interface{}{
- "id": &code.ID,
- "hash": &code.Hash,
- "created_at": &code.CreatedAt,
- "user": &code.User,
- "client": &code.Client,
- "scope": (*nullString)(&code.Scope),
+ "id": &code.ID,
+ "hash": &code.Hash,
+ "created_at": &code.CreatedAt,
+ "user": &code.User,
+ "client": &code.Client,
+ "scope": (*nullString)(&code.Scope),
+ "redirect_uri": (*nullString)(&code.RedirectURI),
}
}
M oauth2.go => oauth2.go +16 -4
@@ 158,13 158,19 @@ func authorize(w http.ResponseWriter, req *http.Request) {
return
}
- authCode, secret, err := NewAuthCode(loginToken.User, client.ID, scope)
+ authCode := AuthCode{
+ User: loginToken.User,
+ Client: client.ID,
+ Scope: scope,
+ RedirectURI: rawRedirectURI,
+ }
+ secret, err := authCode.Generate()
if err != nil {
httpError(w, fmt.Errorf("failed to generate authentication code: %v", err))
return
}
- if err := db.CreateAuthCode(ctx, authCode); err != nil {
+ if err := db.CreateAuthCode(ctx, &authCode); err != nil {
httpError(w, fmt.Errorf("failed to create authentication code: %v", err))
return
}
@@ 195,6 201,7 @@ func exchangeToken(w http.ResponseWriter, req *http.Request) {
clientID := values.Get("client_id")
grantType := oauth2.GrantType(values.Get("grant_type"))
scope := values.Get("scope")
+ redirectURI := values.Get("redirect_uri")
authClientID, clientSecret, _ := req.BasicAuth()
if clientID == "" && authClientID == "" {
@@ 263,8 270,13 @@ func exchangeToken(w http.ResponseWriter, req *http.Request) {
})
return
}
-
- // TODO: check redirect_uri
+ if redirectURI != authCode.RedirectURI {
+ oauthError(w, &oauth2.Error{
+ Code: oauth2.ErrorCodeAccessDenied,
+ Description: "Invalid redirect URI",
+ })
+ return
+ }
token, secret, err := NewAccessTokenFromAuthCode(authCode)
if err != nil {
M schema.sql => schema.sql +1 -0
@@ 36,6 36,7 @@ CREATE TABLE AuthCode (
created_at datetime NOT NULL,
user INTEGER NOT NULL,
client INTEGER NOT NULL,
+ redirect_uri TEXT,
scope TEXT,
FOREIGN KEY(user) REFERENCES User(id),
FOREIGN KEY(client) REFERENCES Client(id)