feat(authserver): add OAuth 2.0 client_credentials grant support for M2M clients#3962
Draft
RobertWi wants to merge 8 commits intostacklok:mainfrom
Draft
feat(authserver): add OAuth 2.0 client_credentials grant support for M2M clients#3962RobertWi wants to merge 8 commits intostacklok:mainfrom
RobertWi wants to merge 8 commits intostacklok:mainfrom
Conversation
added 8 commits
March 2, 2026 14:04
…rovider Add compose.OAuth2ClientCredentialsGrantFactory to the fosite composition, enabling the token endpoint to accept client_credentials grant requests. This is the foundational change — subsequent commits will update DCR validation, token session handling, and discovery metadata. Ref: MCP spec 2025-03-26 recommends client_credentials for M2M clients.
…tials Update DCR validation to accept client_credentials grant type and confidential client semantics: - redirect_uris optional for client_credentials-only clients - token_endpoint_auth_method allows client_secret_basic/client_secret_post - authorization_code no longer required when client_credentials is present - refresh_token alone is still rejected (must accompany a primary grant) Backward compatible: existing public client registrations are unaffected.
When a client registers with client_credentials grant type, the DCR handler now generates a random secret, creates a confidential fosite client with bcrypt-hashed secret, and returns the plaintext secret in the registration response (RFC 7591 Section 3.2.1). Public client registrations are unaffected.
For client_credentials grants, fosite uses the placeholder session directly (no stored authorize session exists). Populate the session's subject and client_id claims with the authenticated client ID so the resulting JWT has a meaningful 'sub' claim for downstream authorization. The Cedar authorizer extracts Client:: principal from the 'sub' claim, so this is required for M2M authorization to work.
Update OAuth AS metadata and OIDC discovery endpoints to advertise: - grant_types_supported: add client_credentials - token_endpoint_auth_methods_supported: add client_secret_basic, client_secret_post Per RFC 8414, this tells clients the server supports M2M authentication.
Update existing tests that expected client_credentials rejection to verify acceptance. Add new test cases covering: - client_secret_basic and client_secret_post auth methods - redirect_uris handling for confidential clients - rejection of auth_method=none for confidential clients - refresh_token-only and implicit grant type rejection
- Wire OAuth2ClientCredentialsGrantFactory into test setup - Register confidential test client with bcrypt-hashed secret - Replace unsupported grant type test (was client_credentials, now implicit) - Add success, wrong-secret, and resource-parameter tests for client_credentials - Update discovery assertions to include client_credentials and auth methods
Add integration tests covering: - Basic client_credentials flow with JWT claim validation (sub=clientID) - RFC 8707 resource parameter for audience binding - Wrong secret rejection (401 invalid_client) - Verify no refresh token issued for client_credentials Uses setupTestServer with new withConfidentialClient() option that registers a bcrypt-hashed confidential client.
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.
Summary
Add
client_credentialsgrant type support to ToolHive's built-in OAuth authorization server, enabling machine-to-machine (M2M) authentication as recommended by the MCP specification (2025-03-26).Motivation
The MCP spec explicitly recommends
client_credentialsfor non-human clients:PR #3425 correctly blocked
client_credentialsin DCR because the server couldn't fulfill it at the time. This PR implements the capability, then updates the allowlist.Changes
Production code (5 files, ~100 lines)
server_impl.go: WireOAuth2ClientCredentialsGrantFactoryinto fosite composition (1 line)registration/dcr.go: Support confidential client registration —redirect_urisoptional forclient_credentials-only clients,client_secret_basic/client_secret_postauth methods accepted, secret generationhandlers/dcr.go: Generate and returnclient_secretfor confidential clients in DCR responsehandlers/token.go: Populate session subject with client ID forclient_credentialsgrants so JWT has meaningfulsubclaimhandlers/discovery.go: Advertiseclient_credentialsgrant andclient_secret_basic/client_secret_postin OAuth AS + OIDC discovery metadataStorage / Authorization
No changes needed. The existing storage interface already satisfies fosite's requirements for
client_credentials. Cedar authorization naturally handlesClient::principals derived from thesubclaim.Tests (5 files, ~340 lines)
client_credentialsrejection cases to success, added 6 new confidential client test casesclient_credentialstests (success, wrong secret, resource parameter)client_credentials+ auth method assertionsTest Evidence
Full test suite with race detector — all 10 authserver packages pass:
New tests added by this PR — all pass:
go vet ./pkg/authserver/...— clean, no findings.Backward Compatibility
Fully backward compatible. Existing public client registrations and
authorization_codeflows are unaffected:defaultGrantTypesfor empty DCR requests remain["authorization_code", "refresh_token"]elsebranch)client_credentialsrejection were updated)Diff Stats