|
|
|
@ -96,24 +96,6 @@ 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 |
|
|
|
|
|
|
|
|
@ -253,32 +235,53 @@ type userInfoResponse struct { |
|
|
|
|
|
|
|
|
|
// 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") |
|
|
|
|
if ctx.User == nil || ctx.Data["AuthedMethod"] != (&auth.OAuth2{}).Name() { |
|
|
|
|
ctx.Resp.Header().Set("WWW-Authenticate", `Bearer realm=""`) |
|
|
|
|
ctx.HandleText(http.StatusUnauthorized, "no valid authorization") |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
uid := auth.CheckOAuthAccessToken(auths[1]) |
|
|
|
|
if uid == 0 { |
|
|
|
|
handleBearerTokenError(ctx, BearerTokenError{ |
|
|
|
|
ErrorCode: BearerTokenErrorCodeInvalidToken, |
|
|
|
|
ErrorDescription: "Access token not assigned to any user", |
|
|
|
|
}) |
|
|
|
|
return |
|
|
|
|
response := &userInfoResponse{ |
|
|
|
|
Sub: fmt.Sprint(ctx.User.ID), |
|
|
|
|
Name: ctx.User.FullName, |
|
|
|
|
Username: ctx.User.Name, |
|
|
|
|
Email: ctx.User.Email, |
|
|
|
|
Picture: ctx.User.AvatarLink(), |
|
|
|
|
} |
|
|
|
|
authUser, err := models.GetUserByID(uid) |
|
|
|
|
if err != nil { |
|
|
|
|
ctx.ServerError("GetUserByID", err) |
|
|
|
|
ctx.JSON(http.StatusOK, response) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// IntrospectOAuth introspects an oauth token
|
|
|
|
|
func IntrospectOAuth(ctx *context.Context) { |
|
|
|
|
if ctx.User == nil { |
|
|
|
|
ctx.Resp.Header().Set("WWW-Authenticate", `Bearer realm=""`) |
|
|
|
|
ctx.HandleText(http.StatusUnauthorized, "no valid authorization") |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
response := &userInfoResponse{ |
|
|
|
|
Sub: fmt.Sprint(authUser.ID), |
|
|
|
|
Name: authUser.FullName, |
|
|
|
|
Username: authUser.Name, |
|
|
|
|
Email: authUser.Email, |
|
|
|
|
Picture: authUser.AvatarLink(), |
|
|
|
|
|
|
|
|
|
var response struct { |
|
|
|
|
Active bool `json:"active"` |
|
|
|
|
Scope string `json:"scope,omitempty"` |
|
|
|
|
jwt.StandardClaims |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
form := web.GetForm(ctx).(*forms.IntrospectTokenForm) |
|
|
|
|
token, err := oauth2.ParseToken(form.Token) |
|
|
|
|
if err == nil { |
|
|
|
|
if token.Valid() == nil { |
|
|
|
|
grant, err := models.GetOAuth2GrantByID(token.GrantID) |
|
|
|
|
if err == nil && grant != nil { |
|
|
|
|
app, err := models.GetOAuth2ApplicationByID(grant.ApplicationID) |
|
|
|
|
if err == nil && app != nil { |
|
|
|
|
response.Active = true |
|
|
|
|
response.Scope = grant.Scope |
|
|
|
|
response.Issuer = setting.AppURL |
|
|
|
|
response.Audience = app.ClientID |
|
|
|
|
response.Subject = fmt.Sprint(grant.UserID) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ctx.JSON(http.StatusOK, response) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -697,18 +700,3 @@ 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)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|