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 20d051c669
commit 0e4b329a47
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.", Description: "The code verifier associated with the authorization code.",
}, },
// For confidential clients, the client_id and client_secret are provided to // For confidential clients, the client_id and client_secret are provided to
// the token endpoint via the 'client_secret_basic' authentication method, which // the token endpoint via the 'client_secret_basic' or 'client_secret_post'
// uses the HTTP Basic authentication scheme. See the OIDC spec for details at: // authentication methods. See the OIDC spec for details at:
// https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication // https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
// For public clients, the client_id is required and a client_secret does // 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, Type: framework.TypeString,
Description: "The ID of the requesting client.", 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{ Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{ logical.UpdateOperation: &framework.PathOperation{
@@ -1483,6 +1487,7 @@ func (i *IdentityStore) pathOIDCProviderDiscovery(ctx context.Context, req *logi
// PKCE is required for auth method "none" // PKCE is required for auth method "none"
"none", "none",
"client_secret_basic", "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") 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) clientID, clientSecret, okBasicAuth := basicAuth(req)
if !okBasicAuth { if !okBasicAuth {
// client_secret_post - Check for client credentials in the request body
clientID = d.Get("client_id").(string) clientID = d.Get("client_id").(string)
if clientID == "" { if clientID == "" {
return tokenResponse(nil, ErrTokenInvalidRequest, "client_id parameter is required") return tokenResponse(nil, ErrTokenInvalidRequest, "client_id parameter is required")
} }
clientSecret = d.Get("client_secret").(string)
} }
client, err := i.clientByID(ctx, req.Storage, clientID) client, err := i.clientByID(ctx, req.Storage, clientID)
if err != nil { 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") return tokenResponse(nil, ErrTokenInvalidClient, "client failed to authenticate")
} }
// Authenticate the client using the client_secret_basic authentication method if it's a // Authenticate the client if it's a confidential client type.
// confidential client. The authentication method uses the HTTP Basic authentication scheme.
// Details at https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication // Details at https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication
if client.Type == confidential && if client.Type == confidential &&
subtle.ConstantTimeCompare([]byte(client.ClientSecret), []byte(clientSecret)) == 0 { 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), 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", name: "valid token request",
args: args{ args: args{
@@ -3613,7 +3629,7 @@ func TestOIDC_Path_OpenIDProviderConfig(t *testing.T) {
TokenEndpoint: basePath + "/token", TokenEndpoint: basePath + "/token",
UserinfoEndpoint: basePath + "/userinfo", UserinfoEndpoint: basePath + "/userinfo",
GrantTypes: []string{"authorization_code"}, GrantTypes: []string{"authorization_code"},
AuthMethods: []string{"none", "client_secret_basic"}, AuthMethods: []string{"none", "client_secret_basic", "client_secret_post"},
RequestParameter: false, RequestParameter: false,
RequestURIParameter: false, RequestURIParameter: false,
} }
@@ -3668,7 +3684,7 @@ func TestOIDC_Path_OpenIDProviderConfig(t *testing.T) {
TokenEndpoint: basePath + "/token", TokenEndpoint: basePath + "/token",
UserinfoEndpoint: basePath + "/userinfo", UserinfoEndpoint: basePath + "/userinfo",
GrantTypes: []string{"authorization_code"}, GrantTypes: []string{"authorization_code"},
AuthMethods: []string{"none", "client_secret_basic"}, AuthMethods: []string{"none", "client_secret_basic", "client_secret_post"},
RequestParameter: false, RequestParameter: false,
RequestURIParameter: false, RequestURIParameter: false,
} }

View File

@@ -288,7 +288,7 @@ This endpoint creates or updates a client.
- `confidential` - `confidential`
- Capable of maintaining the confidentiality of its credentials - Capable of maintaining the confidentiality of its credentials
- Has a client secret - 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)) - May use Proof Key for Code Exchange ([PKCE](https://datatracker.ietf.org/doc/html/rfc7636))
for the authorization code flow for the authorization code flow
- `public` - `public`
@@ -602,6 +602,7 @@ $ curl \
], ],
"token_endpoint_auth_methods_supported": [ "token_endpoint_auth_methods_supported": [
"client_secret_basic", "client_secret_basic",
"client_secret_post",
"none" "none"
]} ]}
``` ```
@@ -737,9 +738,13 @@ for an OIDC provider.
authorization request was sent. This must match the `redirect_uri` used when the authorization request was sent. This must match the `redirect_uri` used when the
original authorization code was generated. original authorization code was generated.
- `client_id` `(string: <required>)` - The ID of the requesting client. This parameter - `client_id` `(string: <optional>)` - The ID of the requesting client. This parameter
is only required for `public` clients which do not have a client secret. `confidential` is required for `public` clients which do not have a client secret or `confidential`
clients should not use this parameter. 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_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). `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 ### 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) 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 ### 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. during the authorization code flow.
Confidential clients must authenticate to the token endpoint using the 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 ##### 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": [ "token_endpoint_auth_methods_supported": [
"none", "none",
"client_secret_basic" "client_secret_basic",
"client_secret_post"
] ]
} }
``` ```