Parse OAuth Authorization header when request omits client secret (#21351)

This fixes error "unauthorized_client: invalid client secret" when
client includes secret in Authorization header rather than request body.
OAuth spec permits both.

Sanity validation that client id and client secret in request are
consistent with Authorization header.

Improve error descriptions. Error codes remain the same.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
M Hickford 2022-10-07 03:53:49 +01:00 committed by GitHub
parent f09f73d784
commit 34f509eb7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 21 additions and 2 deletions

View File

@ -588,7 +588,8 @@ func OIDCKeys(ctx *context.Context) {
// AccessTokenOAuth manages all access token requests by the client // AccessTokenOAuth manages all access token requests by the client
func AccessTokenOAuth(ctx *context.Context) { func AccessTokenOAuth(ctx *context.Context) {
form := *web.GetForm(ctx).(*forms.AccessTokenForm) form := *web.GetForm(ctx).(*forms.AccessTokenForm)
if form.ClientID == "" { // if there is no ClientID or ClientSecret in the request body, fill these fields by the Authorization header and ensure the provided field matches the Authorization header
if form.ClientID == "" || form.ClientSecret == "" {
authHeader := ctx.Req.Header.Get("Authorization") authHeader := ctx.Req.Header.Get("Authorization")
authContent := strings.SplitN(authHeader, " ", 2) authContent := strings.SplitN(authHeader, " ", 2)
if len(authContent) == 2 && authContent[0] == "Basic" { if len(authContent) == 2 && authContent[0] == "Basic" {
@ -608,7 +609,21 @@ func AccessTokenOAuth(ctx *context.Context) {
}) })
return return
} }
if form.ClientID != "" && form.ClientID != pair[0] {
handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeInvalidRequest,
ErrorDescription: "client_id in request body inconsistent with Authorization header",
})
return
}
form.ClientID = pair[0] form.ClientID = pair[0]
if form.ClientSecret != "" && form.ClientSecret != pair[1] {
handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeInvalidRequest,
ErrorDescription: "client_secret in request body inconsistent with Authorization header",
})
return
}
form.ClientSecret = pair[1] form.ClientSecret = pair[1]
} }
} }
@ -686,9 +701,13 @@ func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, s
return return
} }
if !app.ValidateClientSecret([]byte(form.ClientSecret)) { if !app.ValidateClientSecret([]byte(form.ClientSecret)) {
errorDescription := "invalid client secret"
if form.ClientSecret == "" {
errorDescription = "invalid empty client secret"
}
handleAccessTokenError(ctx, AccessTokenError{ handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeUnauthorizedClient, ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
ErrorDescription: "invalid client secret", ErrorDescription: errorDescription,
}) })
return return
} }