From 59831a8d5cd0ed1ab62af09ac15a1c85fddd6208 Mon Sep 17 00:00:00 2001 From: Austin Gebauer <34121980+austingebauer@users.noreply.github.com> Date: Mon, 8 Aug 2022 08:41:09 -0700 Subject: [PATCH] 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 --- changelog/16598.txt | 3 +++ vault/identity_store_oidc_provider.go | 16 ++++++++++----- vault/identity_store_oidc_provider_test.go | 20 +++++++++++++++++-- .../secret/identity/oidc-provider.mdx | 18 +++++++++++------ .../content/docs/concepts/oidc-provider.mdx | 2 +- .../docs/secrets/identity/oidc-provider.mdx | 3 ++- 6 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 changelog/16598.txt diff --git a/changelog/16598.txt b/changelog/16598.txt new file mode 100644 index 000000000..5b051b16b --- /dev/null +++ b/changelog/16598.txt @@ -0,0 +1,3 @@ +```release-note:improvement +identity/oidc: Adds the `client_secret_post` token endpoint authentication method. +``` diff --git a/vault/identity_store_oidc_provider.go b/vault/identity_store_oidc_provider.go index d8eac8643..319638f33 100644 --- a/vault/identity_store_oidc_provider.go +++ b/vault/identity_store_oidc_provider.go @@ -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 { diff --git a/vault/identity_store_oidc_provider_test.go b/vault/identity_store_oidc_provider_test.go index 1215121b1..8fd89c075 100644 --- a/vault/identity_store_oidc_provider_test.go +++ b/vault/identity_store_oidc_provider_test.go @@ -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, } diff --git a/website/content/api-docs/secret/identity/oidc-provider.mdx b/website/content/api-docs/secret/identity/oidc-provider.mdx index 3867a3e46..de4e873ff 100644 --- a/website/content/api-docs/secret/identity/oidc-provider.mdx +++ b/website/content/api-docs/secret/identity/oidc-provider.mdx @@ -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: )` - 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: )` - 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: )` - The secret of the requesting client. This + parameter is required for `confidential` clients using the `client_secret_post` client + authentication method. - `code_verifier` `(string: )` - 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: )` - An HTTP Basic authentication scheme header +- `Authorization: Basic` `(string: )` - 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 diff --git a/website/content/docs/concepts/oidc-provider.mdx b/website/content/docs/concepts/oidc-provider.mdx index c7c7a014a..8f8ec8fbf 100644 --- a/website/content/docs/concepts/oidc-provider.mdx +++ b/website/content/docs/concepts/oidc-provider.mdx @@ -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 diff --git a/website/content/docs/secrets/identity/oidc-provider.mdx b/website/content/docs/secrets/identity/oidc-provider.mdx index 05527ceba..2bf0cf5c7 100644 --- a/website/content/docs/secrets/identity/oidc-provider.mdx +++ b/website/content/docs/secrets/identity/oidc-provider.mdx @@ -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" ] } ```