8000 [OID4VCI] Add support for `authorization_code` grant by tdiesler · Pull Request #46385 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content

[OID4VCI] Add support for authorization_code grant - #46385

Closed
tdiesler wants to merge 5 commits into
keycloak:mainfrom
tdiesler:ghi46261
Closed

tdiesler wants to merge 5 commits into
keycloak:mainfrom
tdiesler:ghi46261

Conversation

@tdiesler
@tdiesler tdiesler commented Feb 16, 2026 •
Copy link
Copy Markdown
Contributor

closes #46261

This PR provides

  • support for authorization_code grant
  • initial support for issuer_state
  • fixes incorrect usage of credential_identifier
  • removes createOfferStateForAuthorizationCodeFlow
  • fixes authorization_details for scope only AuthorizationRequest
  • single entry point for credential offer creation

https://github.com/openid/OpenID4VC-HAIP/issues/60
https://github.com/italia/eid-wallet-it-docs/issues/172

@tdiesler
tdiesler force-pushed the ghi46261 branch 3 times, most recently from d03beea to 2f73e90 Compare February 25, 2026 22:03
@tdiesler tdiesler changed the title [OID4VCI] Add support for authorization_code grant and issuer_state [OID4VCI] Add support for authorization_code grant offers Feb 26, 2026
@keycloak-github-bot keycloak-github-bot Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unreported flaky test detected, please review

@keycloak-github-bot
Copy link
Copy Markdown

Unreported flaky test detected

If the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.forms.AuthenticatorSubflowsTest2#testSubflow2

Keycloak CI - Forms IT (chrome)

java.lang.AssertionError: Expected AppPage but was PushTheButton (https://localhost:8543/auth/realms/test/login-actions/authenticate?execution=e2f9bf66-02fe-4095-8bae-24125007af82&client_id=test-app&tab_id=Ubk4rMZIKWY&client_data=eyJydSI6Imh0dHBzOi8vbG9jYWxob3N0Ojg1NDMvYXV0aC9yZWFsbXMvbWFzdGVyL2FwcC9hdXRoIiwicnQiOiJjb2RlIn0)
	at org.junit.Assert.fail(Assert.java:89)
	at org.junit.Assert.assertTrue(Assert.java:42)
	at org.keycloak.testsuite.pages.AbstractPage.assertCurrent(AbstractPage.java:39)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
...

Report flaky test

@mposolda mposolda left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR is hard to review. It touches 45 files and touches multiple areas. Is it possible to rather divide into more PRs, which would touch single area and focus on single issue each?

As Support for authorization_code grant and changes related to credential-identifier could be rather handled independently. Also there is the conflict now as there was other PR in the meantime, which touched the area of fixes authorization_details for scope only AuthorizationRequest (however that other PR was more isolated and fixed just that instead of touching big amount of files with multiple changes).

Ideal would be also to not migrate tests in the same PR, which changes the "logic". Would be good to either migrate the test beforehand in the dedicated PR or keep the test in the old testsuite in case PR touches the "codebase" .

@tdiesler
tdiesler commented Mar 4, 2026 •
8000 Copy link
Copy Markdown
Contributor Author

I split up this PR into these independent areas ...

  1. Migrate OID4VCCredentialOfferMatrixTest - without any other functional change
  2. Migrate CredentialsOffer to multiple grant types - which is foundational and ripples into many files. Look at CredentialsOffer
  3. Remove deprecated constants used in the old testsuite - cosmetic. Was there too not hit the max 50 lines change wall
  4. Add support for authorization_code grant - the main topic of this PR
  5. Add CredentialOfferProvider as single entry for offer creation

CredentialOfferProvider as single entry for offer creation

Start with DefaultCredentialOfferProvider.createCredentialOffer(...), this is entry for credential offer creation. It creates an immutable CredentialOfferState that already has AuthorizationDetails attached to it.

The offer state can be found by ...

  • The nonce from the credential_offer_uri
  • The pre-auth_code grant (optionally)
  • It's unique id as recorded in the opaque issuer_state

The credential_idetifier is no longer a key that can be used to find the offer state.

Much of the offer validation logic, that was previously found in the endpoint has moved here. The endpoint becomes easier because it now delegates to this provider. The provider would also be the place to apply policies related to offer creation.

The authorization_code grant

A credentials offer can be created with authorization_code grant with the preAuthorized=false on the offer endpoint or directly through the CredentialOfferProvider. In which case a CredentialOfferState is created and it's unique id recorded in the IssuerState, which the Wallet receives as part of the credentials offer. The Wallet can then play back the issuer_state as part of the AuthorizationRequest. When processing authorization details as part of the AccessToken Request, we can find the associated CredentialOfferState and hence have access to it's already existing authorization details.

An authorization_code grant offer can also be targeted to a specific user. For details see the discussion or javadoc on CredentialOfferProvider.

By not providing issuer_state as part of the AuthorizationRequest, the Wallet can by-pass the credential offer process i.e. it can request a credential by scope alone. This is the moment to shine for vc.policy.offer.required - with this policy enabled (PR #46466), an AuthorizationRequest without issuer_details that references a valid CredentialsOffer will be rejected.

Other than the pre-authorized_code grant, an authorization_code grant can be anonymous (i.e. not bound to a specific user). Whoever has such an offer can fetch the associated credentials. This is a mid-way approach, between completely open (i.e. request by scope) and targeted (i.e. only for a specific user). Using this, the Issuer can still reserve the right to offer credential configurations before they can get accessed.

Processing authorization details

OID4VCAuthorizationDetailsProcessor.buildAuthorizationDetail() and handleMissingAuthorizationDetails()
become a lot easier because they can access the offer state (when there is one), otherwise the same authorization details creation logic is used as in the CredentialOfferProvider.

The Access token will always have authorization_details.

Credential Identifier

Is no longer a system generated UUID, instead it is the the value of the VC_IDENTIFIER attribute from the CredentialScopeModel

Request Credential Endpoint

Now has the notion of

  • authorized credential_configuration_id + credential_identifier
  • requested credential_configuration_id + credential_identifier
  • offered credential_configuration_ids

Checks are in place that only the credential that was (optionally) offered and was authorized can be requested.

tdiesler added 5 commits March 5, 2026 11:39
Signed-off-by: Thomas Diesler <tdiesler@ibm.com>
Signed-off-by: Thomas Diesler <tdiesler@ibm.com>
Signed-off-by: Thomas Diesler <tdiesler@ibm.com>
Signed-off-by: Thomas Diesler <tdiesler@ibm.com>
Signed-off-by: Thomas Diesler <tdiesler@ibm.com>
@mposolda mposolda left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tdiesler Thanks for splitting this into separate commits. Added review comments only to the first commit (Migrate OID4VCCredentialOfferMatrixTest). Can be good to extract that commit into separate PR and then handle other things as a follow-ups.

@mposolda
mposolda commented Mar 6, 2026
Copy link
Copy Markdown
Contributor

@tdiesler Thanks for the update! So is the plan to split this into dedicated PRs? So far, I've added the review only to the first commit (Migrating the OID4VCCredentialOfferMatrixTest). Feel free to send dedicated PR for that commit (once PR review comments addressed).

Some other comments below to your last comment:

The credential_idetifier is no longer a key that can be used to find the offer state.

It probably works and is better way than our current use of credential_identifier . At the same time, our current use of credential_identifier makes it possible to make sure that access-token used in the credential-request is really the same permission as the one returned from the corresponding token response . That is due the fact that currently access-token contains authorization_details claim with unique credential_identifier inside it, which can be correlated with the corresponding entry related to that credential_identifier in the credential-offer-storage. With this change, we in fact loose this correlation in some way (thinking if there are some corner case scenarios, which would make it possible that incorrect access-token would be sent in the credential-request). But we can possibly solve it by adding the custom claim into access-token, which would be related to the ID inside credential-offer.

By not providing issuer_state as part of the AuthorizationRequest, the Wallet can by-pass the credential offer process i.e. it can request a credential by scope alone. This is the moment to shine for vc.policy.offer.required - with this policy enabled (PR #46466), an AuthorizationRequest without issuer_details that references a valid CredentialsOffer will be rejected.

Just a note that we may need also something like "Allow user self-issued credential" - #45777 . The vc.policy.offer.required is likely useful, however user himself can create offer in theory with his access token only, which may not be desired behaviour. As in some cases, you may want that user can obtain credential just if it was approved by administrator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[OID4VCI] Add support for authorization_code grant

2 participants

0