@@ -13,6 +13,7 @@ import (
1313 "strings"
1414
1515 "code.gitea.io/gitea/models"
16+ "code.gitea.io/gitea/modules/auth/sso"
1617 "code.gitea.io/gitea/modules/base"
1718 "code.gitea.io/gitea/modules/context"
1819 "code.gitea.io/gitea/modules/log"
@@ -93,6 +94,24 @@ func (err AccessTokenError) Error() string {
9394 return fmt .Sprintf ("%s: %s" , err .ErrorCode , err .ErrorDescription )
9495}
9596
97+ // BearerTokenErrorCode represents an error code specified in RFC 6750
98+ type BearerTokenErrorCode string
99+
100+ const (
101+ // BearerTokenErrorCodeInvalidRequest represents an error code specified in RFC 6750
102+ BearerTokenErrorCodeInvalidRequest BearerTokenErrorCode = "invalid_request"
103+ // BearerTokenErrorCodeInvalidToken represents an error code specified in RFC 6750
104+ BearerTokenErrorCodeInvalidToken BearerTokenErrorCode = "invalid_token"
105+ // BearerTokenErrorCodeInsufficientScope represents an error code specified in RFC 6750
106+ BearerTokenErrorCodeInsufficientScope BearerTokenErrorCode = "insufficient_scope"
107+ )
108+
109+ // BearerTokenError represents an error response specified in RFC 6750
110+ type BearerTokenError struct {
111+ ErrorCode BearerTokenErrorCode `json:"error" form:"error"`
112+ ErrorDescription string `json:"error_description"`
113+ }
114+
96115// TokenType specifies the kind of token
97116type TokenType string
98117
@@ -193,6 +212,45 @@ func newAccessTokenResponse(grant *models.OAuth2Grant, clientSecret string) (*Ac
193212 }, nil
194213}
195214
215+ type userInfoResponse struct {
216+ Sub string `json:"sub"`
217+ Name string `json:"name"`
218+ Username string `json:"preferred_username"`
219+ Email string `json:"email"`
220+ Picture string `json:"picture"`
221+ }
222+
223+ // InfoOAuth manages request for userinfo endpoint
224+ func InfoOAuth (ctx * context.Context ) {
225+ header := ctx .Req .Header .Get ("Authorization" )
226+ auths := strings .Fields (header )
227+ if len (auths ) != 2 || auths [0 ] != "Bearer" {
228+ ctx .HandleText (http .StatusUnauthorized , "no valid auth token authorization" )
229+ return
230+ }
231+ uid := sso .CheckOAuthAccessToken (auths [1 ])
232+ if uid == 0 {
233+ handleBearerTokenError (ctx , BearerTokenError {
234+ ErrorCode : BearerTokenErrorCodeInvalidToken ,
235+ ErrorDescription : "Access token not assigned to any user" ,
236+ })
237+ return
238+ }
239+ authUser , err := models .GetUserByID (uid )
240+ if err != nil {
241+ ctx .ServerError ("GetUserByID" , err )
242+ return
243+ }
244+ response := & userInfoResponse {
245+ Sub : fmt .Sprint (authUser .ID ),
246+ Name : authUser .FullName ,
247+ Username : authUser .Name ,
248+ Email : authUser .Email ,
249+ Picture : authUser .AvatarLink (),
250+ }
251+ ctx .JSON (http .StatusOK , response )
252+ }
253+
196254// AuthorizeOAuth manages authorize requests
197255func AuthorizeOAuth (ctx * context.Context ) {
198256 form := web .GetForm (ctx ).(* forms.AuthorizationForm )
@@ -571,3 +629,18 @@ func handleAuthorizeError(ctx *context.Context, authErr AuthorizeError, redirect
571629 redirect .RawQuery = q .Encode ()
572630 ctx .Redirect (redirect .String (), 302 )
573631}
632+
633+ func handleBearerTokenError (ctx * context.Context , beErr BearerTokenError ) {
634+ ctx .Resp .Header ().Set ("WWW-Authenticate" , fmt .Sprintf ("Bearer realm=\" \" , error=\" %s\" , error_description=\" %s\" " , beErr .ErrorCode , beErr .ErrorDescription ))
635+ switch beErr .ErrorCode {
636+ case BearerTokenErrorCodeInvalidRequest :
637+ ctx .JSON (http .StatusBadRequest , beErr )
638+ case BearerTokenErrorCodeInvalidToken :
639+ ctx .JSON (http .StatusUnauthorized , beErr )
640+ case BearerTokenErrorCodeInsufficientScope :
641+ ctx .JSON (http .StatusForbidden , beErr )
642+ default :
643+ log .Error ("Invalid BearerTokenErrorCode: %v" , beErr .ErrorCode )
644+ ctx .ServerError ("Unhandled BearerTokenError" , fmt .Errorf ("BearerTokenError: error=\" %v\" , error_description=\" %v\" " , beErr .ErrorCode , beErr .ErrorDescription ))
645+ }
646+ }
0 commit comments