identity/oidc: adds client_secret_post token endpoint authentication method (#16598)

* identity/oidc: adds client_secret_post token endpoint authentication method

* fix test

* adds changelog
This commit is contained in:
Austin Gebauer 2022-08-08 08:41:09 -07:00 committed by GitHub
parent b7365df464
commit 59831a8d5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 47 additions and 15 deletions

3
changelog/16598.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
identity/oidc: Adds the `client_secret_post` token endpoint authentication method.
```

View File

@ -510,8 +510,8 @@ func oidcProviderPaths(i *IdentityStore) []*framework.Path {
Description: "The code verifier associated with the authorization code.",
},
// For confidential clients, the client_id and client_secret are provided to
// the token endpoint via the 'client_secret_basic' authentication method, which
// uses the HTTP Basic authentication scheme. See the OIDC spec for details at:
// the token endpoint via the 'client_secret_basic' or 'client_secret_post'
// authentication methods. See the OIDC spec for details at:
// https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
// For public clients, the client_id is required and a client_secret does
@ -522,6 +522,10 @@ func oidcProviderPaths(i *IdentityStore) []*framework.Path {
Type: framework.TypeString,
Description: "The ID of the requesting client.",
},
"client_secret": {
Type: framework.TypeString,
Description: "The secret of the requesting client.",
},
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
@ -1483,6 +1487,7 @@ func (i *IdentityStore) pathOIDCProviderDiscovery(ctx context.Context, req *logi
// PKCE is required for auth method "none"
"none",
"client_secret_basic",
"client_secret_post",
},
}
@ -1856,13 +1861,15 @@ func (i *IdentityStore) pathOIDCToken(ctx context.Context, req *logical.Request,
return tokenResponse(nil, ErrTokenInvalidRequest, "provider not found")
}
// Get the client ID
// client_secret_basic - Check for client credentials in the Authorization header
clientID, clientSecret, okBasicAuth := basicAuth(req)
if !okBasicAuth {
// client_secret_post - Check for client credentials in the request body
clientID = d.Get("client_id").(string)
if clientID == "" {
return tokenResponse(nil, ErrTokenInvalidRequest, "client_id parameter is required")
}
clientSecret = d.Get("client_secret").(string)
}
client, err := i.clientByID(ctx, req.Storage, clientID)
if err != nil {
@ -1873,8 +1880,7 @@ func (i *IdentityStore) pathOIDCToken(ctx context.Context, req *logical.Request,
return tokenResponse(nil, ErrTokenInvalidClient, "client failed to authenticate")
}
// Authenticate the client using the client_secret_basic authentication method if it's a
// confidential client. The authentication method uses the HTTP Basic authentication scheme.
// Authenticate the client if it's a confidential client type.
// Details at https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
if client.Type == confidential &&
subtle.ConstantTimeCompare([]byte(client.ClientSecret), []byte(clientSecret)) == 0 {

View File

@ -433,6 +433,22 @@ func TestOIDC_Path_OIDC_Token(t *testing.T) {
tokenReq: testTokenReq(s, "", clientID, clientSecret),
},
},
{
name: "valid token request with client_secret_post client authentication method",
args: args{
clientReq: testClientReq(s),
providerReq: testProviderReq(s, clientID),
assignmentReq: testAssignmentReq(s, entityID, groupID),
authorizeReq: testAuthorizeReq(s, clientID),
tokenReq: func() *logical.Request {
req := testTokenReq(s, "", clientID, clientSecret)
req.Headers = nil
req.Data["client_id"] = clientID
req.Data["client_secret"] = clientSecret
return req
}(),
},
},
{
name: "valid token request",
args: args{
@ -3613,7 +3629,7 @@ func TestOIDC_Path_OpenIDProviderConfig(t *testing.T) {
TokenEndpoint: basePath + "/token",
UserinfoEndpoint: basePath + "/userinfo",
GrantTypes: []string{"authorization_code"},
AuthMethods: []string{"none", "client_secret_basic"},
AuthMethods: []string{"none", "client_secret_basic", "client_secret_post"},
RequestParameter: false,
RequestURIParameter: false,
}
@ -3668,7 +3684,7 @@ func TestOIDC_Path_OpenIDProviderConfig(t *testing.T) {
TokenEndpoint: basePath + "/token",
UserinfoEndpoint: basePath + "/userinfo",
GrantTypes: []string{"authorization_code"},
AuthMethods: []string{"none", "client_secret_basic"},
AuthMethods: []string{"none", "client_secret_basic", "client_secret_post"},
RequestParameter: false,
RequestURIParameter: false,
}

View File

@ -288,7 +288,7 @@ This endpoint creates or updates a client.
- `confidential`
- Capable of maintaining the confidentiality of its credentials
- Has a client secret
- Uses the `client_secret_basic` [client authentication method](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
- Uses the `client_secret_basic` or `client_secret_post` [client authentication method](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
- May use Proof Key for Code Exchange ([PKCE](https://datatracker.ietf.org/doc/html/rfc7636))
for the authorization code flow
- `public`
@ -602,6 +602,7 @@ $ curl \
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post",
"none"
]}
```
@ -737,9 +738,13 @@ for an OIDC provider.
authorization request was sent. This must match the `redirect_uri` used when the
original authorization code was generated.
- `client_id` `(string: <required>)` - The ID of the requesting client. This parameter
is only required for `public` clients which do not have a client secret. `confidential`
clients should not use this parameter.
- `client_id` `(string: <optional>)` - The ID of the requesting client. This parameter
is required for `public` clients which do not have a client secret or `confidential`
clients using the `client_secret_post` client authentication method.
- `client_secret` `(string: <optional>)` - The secret of the requesting client. This
parameter is required for `confidential` clients using the `client_secret_post` client
authentication method.
- `code_verifier` `(string: <optional>)` - The code verifier associated with the given
`code`. Required for authorization codes that were granted using [PKCE](https://datatracker.ietf.org/doc/html/rfc7636).
@ -747,9 +752,10 @@ for an OIDC provider.
### Headers
- `Authorization: Basic` `(string: <required>)` - An HTTP Basic authentication scheme header
- `Authorization: Basic` `(string: <optional>)` - An HTTP Basic authentication scheme header
including the `client_id` and `client_secret` as described in the [client_secret_basic](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication)
authentication method. This header is only required for `confidential` clients.
authentication method. This header is only required for `confidential` clients using
the `client_secret_basic` client authentication method.
### Sample Request

View File

@ -138,7 +138,7 @@ Confidential clients may use Proof Key for Code Exchange ([PKCE](https://datatra
during the authorization code flow.
Confidential clients must authenticate to the token endpoint using the
`client_secret_basic` [client authentication method](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication).
`client_secret_basic` or `client_secret_post` [client authentication method](https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication).
##### Public

View File

@ -125,7 +125,8 @@ Any Vault auth method may be used within the OIDC flow. For simplicity, enable t
],
"token_endpoint_auth_methods_supported": [
"none",
"client_secret_basic"
"client_secret_basic",
"client_secret_post"
]
}
```