Skip to content

fix(oid4vc): account for realm optional client scopes in credential scope resolution - #51237

Open
aditya-cyberverse wants to merge 1 commit into
keycloak:mainfrom
aditya-cyberverse:fix/oid4vci-optional-scopes
Open

fix(oid4vc): account for realm optional client scopes in credential scope resolution#51237
aditya-cyberverse wants to merge 1 commit into
keycloak:mainfrom
aditya-cyberverse:fix/oid4vci-optional-scopes

Conversation

@aditya-cyberverse

Copy link
Copy Markdown

Closes #51187

Summary

Updates OID4VCI scope evaluation in CredentialScopeUtils, OID4VCAuthorizationCheckProvider, and CredentialClientPolicyExecutor to properly recognize and account for requested scopes configured as Realm Optional Client Scopes.

Copilot AI review requested due to automatic review settings July 28, 2026 12:15
@aditya-cyberverse
aditya-cyberverse requested a review from a team as a code owner July 28, 2026 12:15
@aditya-cyberverse
aditya-cyberverse force-pushed the fix/oid4vci-optional-scopes branch from fb9e9b4 to aa1cb8c Compare July 28, 2026 12:17

Copilot AI 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.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Updates OID4VCI credential scope resolution to also consider Realm Optional Client Scopes when evaluating requested scopes during authorization and client policy checks.

Changes:

  • Expanded scope resolution to union client default scopes, client optional scopes, and realm optional client scopes.
  • Updated authorization and client policy call sites to pass KeycloakSession into scope resolution.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
services/src/main/java/org/keycloak/protocol/oid4vc/utils/CredentialScopeUtils.java Expands scope lookup to include realm optional client scopes and changes API to require a session.
services/src/main/java/org/keycloak/protocol/oid4vc/issuance/OID4VCAuthorizationCheckProvider.java Updates authorization checker to use the new scope-resolution signature.
services/src/main/java/org/keycloak/protocol/oid4vc/clientpolicy/CredentialClientPolicyExecutor.java Updates client policy executor to use the new scope-resolution signature.

* Get the list of credential scopes associated by the given and requested by the given authorization request
*/
public static List<CredentialScopeModel> getCredentialScopesForAuthorization(ClientModel client, AuthorizationEndpointRequest request) {
public static List<CredentialScopeModel> getCredentialScopesForAuthorization(KeycloakSession session, ClientModel client, AuthorizationEndpointRequest request) {
Map<String, ClientScopeModel> clientScopes = client.getClientScopes(false);
// Get the list of requested credential scopes that are associated with this client.
// Include client default scopes, client optional scopes and realm optional client scopes.
RealmModel realm = session.getContext().getRealm();
Comment on lines +88 to +91
Map<String, ClientScopeModel> clientScopes = Stream.concat(
Stream.concat(client.getClientScopes(true).values().stream(), client.getClientScopes(false).values().stream()),
realm.getDefaultClientScopesStream(false))
.collect(Collectors.toMap(ClientScopeModel::getName, Function.identity(), (existing, replacement) -> existing));
Copilot AI review requested due to automatic review settings July 28, 2026 12:31

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

services/src/main/java/org/keycloak/protocol/oid4vc/utils/CredentialScopeUtils.java:90

  • The comment says this includes “realm optional client scopes”, but the call realm.getDefaultClientScopesStream(false) is easy to misread as “default scopes” (and the boolean flag is non-obvious). If the RealmModel API provides a dedicated optional-scope stream method, prefer that for clarity. Otherwise, consider adding a short clarifying comment about what the boolean flag represents to prevent future regressions/misuse.
        // Include client default scopes, client optional scopes and realm optional client scopes.
        RealmModel realm = session.getContext().getRealm();
        Map<String, ClientScopeModel> clientScopes = Stream.concat(
                        Stream.concat(client.getClientScopes(true).values().stream(), client.getClientScopes(false).values().stream()),
                        realm.getDefaultClientScopesStream(false))

services/src/main/java/org/keycloak/protocol/oid4vc/utils/CredentialScopeUtils.java:91

  • This silently drops duplicate client scope names by keeping the first occurrence. If duplicates can happen (e.g., misconfiguration across client vs realm scope sets), this can hide configuration issues and make debugging difficult. Consider either (a) explicitly documenting the precedence being enforced, or (b) failing fast/logging when a duplicate name is encountered so the configuration problem is visible.
                .collect(Collectors.toMap(ClientScopeModel::getName, Function.identity(), (existing, replacement) -> existing));

services/src/main/java/org/keycloak/protocol/oid4vc/utils/CredentialScopeUtils.java:78

  • This utility now depends on KeycloakSession solely to access the realm context. To reduce coupling and improve testability/reusability, consider passing RealmModel realm (or a precomputed stream/map of realm optional scopes) instead of the full session object, unless other session-derived data is expected to be needed here later.
    public static List<CredentialScopeModel> getCredentialScopesForAuthorization(KeycloakSession session, ClientModel client, AuthorizationEndpointRequest request) {

…cope resolution

Signed-off-by: aditya-cyberverse <aditya1107zagade@gmail.com>
@aditya-cyberverse
aditya-cyberverse force-pushed the fix/oid4vci-optional-scopes branch from aa1cb8c to 9b41d6e Compare July 28, 2026 13:45
Copilot AI review requested due to automatic review settings July 28, 2026 13:45

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

services/src/main/java/org/keycloak/protocol/oid4vc/utils/CredentialScopeUtils.java:103

  • The code/comment relies on the boolean semantics of realm.getDefaultClientScopesStream(false) to represent “realm optional client scopes”, which is non-obvious and easy to misuse. Prefer calling an explicit API if available (e.g., realm.getOptionalClientScopesStream()), or wrap the call in a clearly named helper method (e.g., getRealmOptionalClientScopes(realm)) to make intent unambiguous and reduce future regressions.
                        realm.getDefaultClientScopesStream(false)
                )
                .flatMap(Function.identity())
                .collect(Collectors.toMap(
                        ClientScopeModel::getName,
                        Function.identity(),
                        (existing, replacement) -> existing // Preserve client-level override priority
                ));

services/src/main/java/org/keycloak/protocol/oid4vc/utils/CredentialScopeUtils.java:75

  • The Javadoc opening delimiter indentation/formatting changed (the /** is now flush-left while surrounding code is indented). Consider reformatting to match the file’s existing style to keep diffs minimal and readability consistent.
/**

// Get the list of requested credential scopes that are associated with this client
//
List<CredentialScopeModel> credScopes = CredentialScopeUtils.getCredentialScopesForAuthorization(client, request);
List<CredentialScopeModel> credScopes = CredentialScopeUtils.getCredentialScopesForAuthorization(session, client, request);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[OID4VCI] Handling of realm optional client scopes

2 participants