@@ -14,6 +14,7 @@ import (
1414 "net/url"
1515 "slices"
1616 "strings"
17+ "time"
1718
1819 "code.gitea.io/gitea/models/db"
1920 "code.gitea.io/gitea/modules/container"
@@ -27,6 +28,11 @@ import (
2728 "xorm.io/xorm"
2829)
2930
31+ // Authorization codes should expire within 10 minutes per https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2
32+ const oauth2AuthorizationCodeValidity = 10 * time .Minute
33+
34+ var ErrOAuth2AuthorizationCodeInvalidated = errors .New ("oauth2 authorization code already invalidated" )
35+
3036// OAuth2Application represents an OAuth2 client (RFC 6749)
3137type OAuth2Application struct {
3238 ID int64 `xorm:"pk autoincr"`
@@ -386,6 +392,14 @@ func (code *OAuth2AuthorizationCode) TableName() string {
386392 return "oauth2_authorization_code"
387393}
388394
395+ // IsExpired reports whether the authorization code is expired.
396+ func (code * OAuth2AuthorizationCode ) IsExpired () bool {
397+ if code .ValidUntil .IsZero () {
398+ return true
399+ }
400+ return code .ValidUntil <= timeutil .TimeStampNow ()
401+ }
402+
389403// GenerateRedirectURI generates a redirect URI for a successful authorization request. State will be used if not empty.
390404func (code * OAuth2AuthorizationCode ) GenerateRedirectURI (state string ) (* url.URL , error ) {
391405 redirect , err := url .Parse (code .RedirectURI )
@@ -403,8 +417,14 @@ func (code *OAuth2AuthorizationCode) GenerateRedirectURI(state string) (*url.URL
403417
404418// Invalidate deletes the auth code from the database to invalidate this code
405419func (code * OAuth2AuthorizationCode ) Invalidate (ctx context.Context ) error {
406- _ , err := db .GetEngine (ctx ).ID (code .ID ).NoAutoCondition ().Delete (code )
407- return err
420+ affected , err := db .GetEngine (ctx ).ID (code .ID ).NoAutoCondition ().Delete (code )
421+ if err != nil {
422+ return err
423+ }
424+ if affected == 0 {
425+ return ErrOAuth2AuthorizationCodeInvalidated
426+ }
427+ return nil
408428}
409429
410430// ValidateCodeChallenge validates the given verifier against the saved code challenge. This is part of the PKCE implementation.
@@ -472,13 +492,15 @@ func (grant *OAuth2Grant) GenerateNewAuthorizationCode(ctx context.Context, redi
472492 // for code scanners to grab sensitive tokens.
473493 codeSecret := "gta_" + base32Lower .EncodeToString (rBytes )
474494
495+ validUntil := time .Now ().Add (oauth2AuthorizationCodeValidity )
475496 code = & OAuth2AuthorizationCode {
476497 Grant : grant ,
477498 GrantID : grant .ID ,
478499 RedirectURI : redirectURI ,
479500 Code : codeSecret ,
480501 CodeChallenge : codeChallenge ,
481502 CodeChallengeMethod : codeChallengeMethod ,
503+ ValidUntil : timeutil .TimeStamp (validUntil .Unix ()),
482504 }
483505 if err := db .Insert (ctx , code ); err != nil {
484506 return nil , err
0 commit comments