Repository navigation
Replies: 2 comments 3 replies
|
Thanks for raising this one. There is an existing GitHub issue here #54099 opened recently. Would that be the same? If yes, please up-vote that issue, and connect with that author. |
3 replies
|
One more thing: Please try to find existing issues and discussions related to the topic. Keycloak's OAuth SIG https://www.keycloak.org/community might be a possible place to discuss this, and get a set of people interested. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Title: Support the SAML 2.0 Bearer Assertion Grant (RFC 7522) as the SAML counterpart of the JWT Authorization Grant
Summary
I'd like to propose, and contribute, support for the SAML 2.0 Bearer Assertion Grant (RFC 7522) in Keycloak. A client that already holds a SAML 2.0 assertion issued by a trusted identity provider could exchange it at the token endpoint for Keycloak access, refresh and ID tokens, with no browser redirect:
As RFC 7522 allows, client authentication is optional: confidential clients authenticate as usual, and public clients (mobile apps, SPAs) can use the grant without a client secret when an administrator explicitly allows it (see Public clients).
Keycloak 26.5 added the JWT Authorization Grant (RFC 7523) as the recommended alternative to external-to-internal token exchange V1. This proposal is its SAML counterpart: it reuses the same architecture and configuration model, for identity providers that only speak SAML.
We have run a working implementation of this as a custom provider against Okta and other SAML identity providers, and recently migrated it to Keycloak 26.8.1. I'd like to turn it into an upstream contribution that fits Keycloak's design, and would value the maintainers' guidance before opening a PR.
Motivation
Many organizations, especially in finance, healthcare and the enterprise, federate identity only through SAML 2.0 (ADFS, Okta SAML apps, PingFederate, Shibboleth, in-house IdPs). In these setups the following is common:
Today Keycloak has no built-in way to do this with a SAML assertion:
ExchangeExternalToken; the SAML IdP doesn't.subject_issuerand accepts access tokens only.So every SAML-only deployment that needs this has to write and maintain a custom grant or token-exchange provider, including SAML signature validation, decryption, condition checks and replay handling. That is security-sensitive code that belongs in Keycloak's SAML stack.
RFC 7522 is the standard answer to exactly this problem, and the companion of RFC 7523, which Keycloak already implements.
Proposed design
I propose to mirror the JWT Authorization Grant as closely as possible, so administrators get a consistent model and most of the infrastructure is reused.
Request
urn:ietf:params:oauth:grant-type:saml2-bearer(RFC 7522 §2.1), with the assertion in theassertionform parameter, base64url-encoded.client_idand may use the grant only when it is explicitly enabled for them (see below). This is a deliberate difference from the JWT Authorization Grant, which is confidential-only. The main use case is native and mobile apps that receive a SAML assertion and can't hold a client secret.saml2-bearer-grant, preview first).Trust configuration (SAML identity provider)
A new section on the SAML identity provider, analogous to the JWT Authorization Grant section on the OIDC identity provider:
The IdP's existing SAML settings are honoured, exactly as
SAMLEndpointdoes for browser logins:idpEntityId, signing certificates /validateSignature,wantAssertionsSigned,wantAssertionsEncrypted,principalType/principalAttribute, NameID policy andallowedClockSkew.Client configuration
Public clients
Without client authentication, the signed assertion is the only proof behind the request. Anyone who obtains a valid assertion could present it with the
client_idof any public client that allows the grant. To keep this safe, the following apply when the client is public (some are optional for confidential clients):client_idto appear in the assertion'sAudienceRestriction(orSubjectConfirmationData/@Recipient), so an assertion issued for one app can't be used by another.Validation (RFC 7522 §3)
AlternativeLookupProvider.AudienceRestrictionmust contain a value identifying Keycloak (the realm issuer and/or the IdP's configured SP entity ID).NameID(or the principal configured on the IdP).SubjectConfirmationwithMethod="urn:oasis:names:tc:SAML:2.0:cm:bearer", whoseSubjectConfirmationDatahas aNotOnOrAfterin the future and aRecipientequal to the token endpoint URL.ConditionsNotBefore/NotOnOrAfter, with clock skew, plus the configured maximum lifetime.EncryptedAssertionis decrypted with the realm's encryption keys (SAMLDecryptionKeysLocator), and enforced whenwantAssertionsEncryptedis on.IDis recorded withSingleUseObjectProvideruntilNotOnOrAfter, and a reused ID is rejected unless reuse is allowed. RFC 7522 §3 recommends this.Errors return
invalid_grantper RFC 7522 §3.1, with details in events rather than in the response.User resolution
Like the JWT Authorization Grant, by default the user must already exist and be linked to the IdP: look up by federated identity (IdP alias + principal), and reject disabled users or users with pending required actions. That keeps the grant non-interactive and predictable.
Tokens, sessions, policies and events
The same as the JWT Authorization Grant: create the user session and client session, check consents, issue tokens through
TokenManager, and record IdP notes on the session. Fire a client-policy context so administrators can restrict or extend the grant with client policies, and emit login events with the IdP alias, issuer and assertion ID.Implementation sketch
Saml2BearerGrantType/Saml2BearerGrantTypeFactoryinorg.keycloak.protocol.oidc.grants, next toJWTAuthorizationGrantType.SAMLIdentityProvider(the counterpart ofJWTAuthorizationGrantProvider) that validates the assertion with the existing SAML core utilities (AssertionUtil,XMLSignatureUtil,SAMLDecryptionKeysLocator,ConditionsValidator,DestinationValidator) and returns aBrokeredIdentityContext.What our current implementation does
Our provider is built as a token-exchange provider (
subject_token_type=urn:ietf:params:oauth:token-type:saml2), because the dedicated grant type and the JWT grant's infrastructure didn't exist when we started. It:Issuer, and validates signatures, conditions, audience, bearer subject confirmation andRecipient;Assertionas well as a fullResponse, plain or withEncryptedAssertion;SAMLEndpointdoes;BrokeredIdentityContext, so attributes behave as they do for a browser SAML login;For upstream I'd align it with the design above: the dedicated
saml2-bearergrant type, the JWT-grant configuration model, the existing-user requirement and replay protection, which our version doesn't have yet.Open questions for maintainers
client_idbinding, DPoP), or would you prefer to start confidential-only, like the JWT Authorization Grant, and add public clients in a second step?subject_token_type=urn:ietf:params:oauth:token-type:saml2also be accepted in standard token exchange, or is the grant enough?Assertion. In practice IdPs often hand out a whole signedResponsewith anEncryptedAssertion. Should Keycloak accept aResponsetoo, or strictly anAssertion/EncryptedAssertion?SAMLIdentityProvider(reusingSAMLEndpointlogic), or in a shared SAML assertion validator that both the broker endpoint and the grant use?Contribution
I'm happy to do the full implementation, tests, admin console changes and documentation, and to adjust the design to your feedback before writing the PR. If this direction looks acceptable, I'll open a GitHub issue to track it and link it here.
References
urn:ietf:params:oauth:token-type:saml2): https://datatracker.ietf.org/doc/html/rfc8693All reactions