mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +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
	 Austin Gebauer
					Austin Gebauer