@ -13,6 +13,7 @@ import (
"strings"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth/sso"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
@ -93,6 +94,24 @@ func (err AccessTokenError) Error() string {
return fmt . Sprintf ( "%s: %s" , err . ErrorCode , err . ErrorDescription )
}
// BearerTokenErrorCode represents an error code specified in RFC 6750
type BearerTokenErrorCode string
const (
// BearerTokenErrorCodeInvalidRequest represents an error code specified in RFC 6750
BearerTokenErrorCodeInvalidRequest BearerTokenErrorCode = "invalid_request"
// BearerTokenErrorCodeInvalidToken represents an error code specified in RFC 6750
BearerTokenErrorCodeInvalidToken BearerTokenErrorCode = "invalid_token"
// BearerTokenErrorCodeInsufficientScope represents an error code specified in RFC 6750
BearerTokenErrorCodeInsufficientScope BearerTokenErrorCode = "insufficient_scope"
)
// BearerTokenError represents an error response specified in RFC 6750
type BearerTokenError struct {
ErrorCode BearerTokenErrorCode ` json:"error" form:"error" `
ErrorDescription string ` json:"error_description" `
}
// TokenType specifies the kind of token
type TokenType string
@ -193,6 +212,45 @@ func newAccessTokenResponse(grant *models.OAuth2Grant, clientSecret string) (*Ac
} , nil
}
type userInfoResponse struct {
Sub string ` json:"sub" `
Name string ` json:"name" `
Username string ` json:"preferred_username" `
Email string ` json:"email" `
Picture string ` json:"picture" `
}
// InfoOAuth manages request for userinfo endpoint
func InfoOAuth ( ctx * context . Context ) {
header := ctx . Req . Header . Get ( "Authorization" )
auths := strings . Fields ( header )
if len ( auths ) != 2 || auths [ 0 ] != "Bearer" {
ctx . HandleText ( http . StatusUnauthorized , "no valid auth token authorization" )
return
}
uid := sso . CheckOAuthAccessToken ( auths [ 1 ] )
if uid == 0 {
handleBearerTokenError ( ctx , BearerTokenError {
ErrorCode : BearerTokenErrorCodeInvalidToken ,
ErrorDescription : "Access token not assigned to any user" ,
} )
return
}
authUser , err := models . GetUserByID ( uid )
if err != nil {
ctx . ServerError ( "GetUserByID" , err )
return
}
response := & userInfoResponse {
Sub : fmt . Sprint ( authUser . ID ) ,
Name : authUser . FullName ,
Username : authUser . Name ,
Email : authUser . Email ,
Picture : authUser . AvatarLink ( ) ,
}
ctx . JSON ( http . StatusOK , response )
}
// AuthorizeOAuth manages authorize requests
func AuthorizeOAuth ( ctx * context . Context ) {
form := web . GetForm ( ctx ) . ( * forms . AuthorizationForm )
@ -571,3 +629,18 @@ func handleAuthorizeError(ctx *context.Context, authErr AuthorizeError, redirect
redirect . RawQuery = q . Encode ( )
ctx . Redirect ( redirect . String ( ) , 302 )
}
func handleBearerTokenError ( ctx * context . Context , beErr BearerTokenError ) {
ctx . Resp . Header ( ) . Set ( "WWW-Authenticate" , fmt . Sprintf ( "Bearer realm=\"\", error=\"%s\", error_description=\"%s\"" , beErr . ErrorCode , beErr . ErrorDescription ) )
switch beErr . ErrorCode {
case BearerTokenErrorCodeInvalidRequest :
ctx . JSON ( http . StatusBadRequest , beErr )
case BearerTokenErrorCodeInvalidToken :
ctx . JSON ( http . StatusUnauthorized , beErr )
case BearerTokenErrorCodeInsufficientScope :
ctx . JSON ( http . StatusForbidden , beErr )
default :
log . Error ( "Invalid BearerTokenErrorCode: %v" , beErr . ErrorCode )
ctx . ServerError ( "Unhandled BearerTokenError" , fmt . Errorf ( "BearerTokenError: error=\"%v\", error_description=\"%v\"" , beErr . ErrorCode , beErr . ErrorDescription ) )
}
}