python-sdk icon indicating copy to clipboard operation
python-sdk copied to clipboard

Support client_credentials flow with JWT and Basic auth

Open pcarleton opened this issue 1 month ago • 0 comments

Summary

Implements SEP-1046 OAuth client_credentials flow support with simplified providers for machine-to-machine authentication.

New OAuth Providers

  • ClientCredentialsOAuthProvider: For client_credentials grant with client_id + client_secret

    • Supports client_secret_basic (default) and client_secret_post auth methods
    • Sets client_info directly, bypassing dynamic client registration
  • PrivateKeyJWTOAuthProvider: For client_credentials grant with private_key_jwt authentication (RFC 7523 Section 2.2)

    • Takes an assertion_provider callback that receives the audience (authorization server's issuer identifier per RFC 7523bis) and returns a JWT
    • Designed for workload identity federation (GCP, AWS IAM, Azure AD)
  • SignedJWTParameters: Helper class for SDK-signed JWT assertions

    • create_assertion_provider() returns a callback for use with PrivateKeyJWTOAuthProvider
  • static_assertion_provider(): Helper for pre-built JWTs that don't need the audience parameter

Deprecation

RFC7523OAuthClientProvider is now deprecated with a DeprecationWarning.

The original implementation incorrectly used RFC 7523 Section 2.1 (jwt-bearer authorization grant where the JWT itself is the authorization) instead of the intended Section 2.2 (private_key_jwt client authentication with grant_type=client_credentials).

Use ClientCredentialsOAuthProvider or PrivateKeyJWTOAuthProvider instead.

Example Usage

# Simple client credentials with client_id + secret
provider = ClientCredentialsOAuthProvider(
    server_url="https://api.example.com",
    storage=my_token_storage,
    client_id="my-client-id",
    client_secret="my-client-secret",
)

# Private key JWT with workload identity federation
async def get_workload_identity_token(audience: str) -> str:
    return await fetch_token_from_identity_provider(audience=audience)

provider = PrivateKeyJWTOAuthProvider(
    server_url="https://api.example.com",
    storage=my_token_storage,
    client_id="my-client-id",
    assertion_provider=get_workload_identity_token,
)

# Private key JWT with SDK-signed assertions
jwt_params = SignedJWTParameters(
    issuer="my-client-id",
    subject="my-client-id",
    signing_key=private_key_pem,
)
provider = PrivateKeyJWTOAuthProvider(
    server_url="https://api.example.com",
    storage=my_token_storage,
    client_id="my-client-id",
    assertion_provider=jwt_params.create_assertion_provider(),
)

Testing

  • Unit tests with 100% coverage for new providers
  • Conformance tests pending

pcarleton avatar Nov 24 '25 22:11 pcarleton