mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 03:27:54 +00:00
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:
3
changelog/16598.txt
Normal file
3
changelog/16598.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:improvement
|
||||||
|
identity/oidc: Adds the `client_secret_post` token endpoint authentication method.
|
||||||
|
```
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user