feat(oauth2): add client credentials flow with opt-in config flag#4583
Merged
nabokihms merged 18 commits intodexidp:masterfrom Mar 3, 2026
Merged
feat(oauth2): add client credentials flow with opt-in config flag#4583nabokihms merged 18 commits intodexidp:masterfrom
nabokihms merged 18 commits intodexidp:masterfrom
Conversation
370999f to
fab39be
Compare
nabokihms
requested changes
Feb 25, 2026
Member
nabokihms
left a comment
There was a problem hiding this comment.
Hello! There are some points to resolve before merging.
nabokihms
requested changes
Feb 25, 2026
0befb94 to
0b64c08
Compare
matzegebbe
commented
Feb 25, 2026
Implement the OAuth2 client_credentials grant type for machine-to-machine authentication. The grant is gated behind a new clientCredentialsEnabled config flag (defaults to false), following the same pattern as passwordConnector for the password grant. Closes dexidp#3660 Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
The gating happens via allSupportedGrants in server.go, not via the allowed list. Without client_credentials in the defaults, the intersection filter always excluded it even with the flag enabled. This matches how the password grant works: present in defaults but only activated when the corresponding config flag is set. Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
…onfig flag Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
…lient_credentials Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
The __client_credentials connector ID is no longer used since the client_credentials grant now uses an empty connector ID. Remove the __ prefix validation from CreateConnector and its associated test. Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
…ials Use an empty connector ID instead of __client_credentials to avoid requiring reserved ID validation. Read the nonce parameter from the token request and forward it to newAccessToken and newIDToken. Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
Add DEX_CLIENT_CREDENTIAL_GRANT_ENABLED_BY_DEFAULT feature flag (default false) so client_credentials is not advertised by default. Users can still explicitly enable it via oauth2.grantTypes config. The flag will be flipped to true in a future release before removal. Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
…lag is enabled The serve command sets a default grantTypes list when none is configured, which meant AllowedGrantTypes was never empty and the feature flag check in server.go was bypassed. Append client_credentials to the default list when DEX_CLIENT_CREDENTIAL_GRANT_ENABLED_BY_DEFAULT is true. Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
The feature flag check in the else branch of server.go is dead code since serve.go always sets a default AllowedGrantTypes list. Move the gate entirely to cmd/dex/serve.go and remove the unused featureflags import. Restore server_test.go to match server.go behavior directly. Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
Signed-off-by: Mathias Gebbe <mathias.gebbe@gmail.com>
40f46d7 to
2dcd9b9
Compare
Signed-off-by: Maksim Nabokikh <maksim.nabokikh@flant.com>
nabokihms
reviewed
Feb 26, 2026
Signed-off-by: Maksim Nabokikh <max.nabokih@gmail.com>
nabokihms
approved these changes
Feb 26, 2026
Member
There was a problem hiding this comment.
@sagikazarmark, since the feature is opt-in and not enabled by default, I see no problem merging this. If you have no objections, I will merge the PR.
@matzegebbe thank you for the PR.
Signed-off-by: Maksim Nabokikh <maksim.nabokikh@flant.com>
This was referenced Mar 24, 2026
This was referenced Mar 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Add OAuth2
client_credentialsgrant type support, gated behind an opt-inclientCredentialsEnabledconfig flag.What this PR does / why we need it
Dex has no built-in support for the
client_credentialsgrant, which is the standard OAuth2 flow (RFC 6749 Section 4.4) for service-to-service authentication where no end-user is involved. This has been a long-standing community request.This PR implements the grant and gates it behind a new
clientCredentialsEnabledconfig flag that defaults tofalse, following the same pattern used bypasswordConnectorfor thepasswordgrant. This ensures no behavior change for existing deployments.Grant behavior:
openid,email,profile,groupsscopesopenidscope is requestedoffline_accessandfederated:idscopes (no refresh tokens for M2M)aud= client ID,sub= encoded client ID + connector ID,name/preferred_username= client name (withprofilescope)Configuration:
Security considerations:
offline_accessscope is rejected, limiting token lifetimesubclaim is the client ID, not a userTesting:
go build ./...compiles cleanlygo test ./server/... -run TestHandleClientCredentials— 7 cases passgo test ./server/... -run TestServerSupportedGrants— 6 cases passgo test ./server/... -run TestHandleDiscovery— passesgo test ./server/...— full server suite passesclientCredentialsEnabled: true, request token viacurlManual verification:
examples/config-dev.yaml:Closes #3660
The gating mechanism works the same way as the
passwordgrant:client_credentialsis included in the default allowed grant types list, but only gets added toallSupportedGrants(and thus passes the intersection filter innewServer()) whenClientCredentialsEnabledistrue. Without the flag, the grant type is silently filtered out.