mirror of
https://github.com/keycloak/keycloak.git
synced 2026-05-02 21:21:10 -05:00
Remove AuthorizationDetailsResponse and make AuthorizationDetailsJSONRepresentation as base of RAR processors
closes #45706 Signed-off-by: mposolda <mposolda@gmail.com>
This commit is contained in:
@@ -150,7 +150,7 @@ public class AccessToken extends IDToken {
|
||||
protected String scope;
|
||||
|
||||
@JsonProperty(OAuth2Constants.AUTHORIZATION_DETAILS)
|
||||
protected List<AuthorizationDetailsResponse> authorizationDetails;
|
||||
protected List<AuthorizationDetailsJSONRepresentation> authorizationDetails;
|
||||
|
||||
@JsonIgnore
|
||||
public Map<String, Access> getResourceAccess() {
|
||||
@@ -279,11 +279,11 @@ public class AccessToken extends IDToken {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public List<AuthorizationDetailsResponse> getAuthorizationDetails() {
|
||||
public List<AuthorizationDetailsJSONRepresentation> getAuthorizationDetails() {
|
||||
return authorizationDetails;
|
||||
}
|
||||
|
||||
public void setAuthorizationDetails(List<AuthorizationDetailsResponse> authorizationDetails) {
|
||||
public void setAuthorizationDetails(List<AuthorizationDetailsJSONRepresentation> authorizationDetails) {
|
||||
this.authorizationDetails = authorizationDetails;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public class AccessTokenResponse {
|
||||
protected String scope;
|
||||
|
||||
@JsonProperty(OAuth2Constants.AUTHORIZATION_DETAILS)
|
||||
protected List<AuthorizationDetailsResponse> authorizationDetails;
|
||||
protected List<AuthorizationDetailsJSONRepresentation> authorizationDetails;
|
||||
|
||||
@JsonProperty("error")
|
||||
protected String error;
|
||||
@@ -84,11 +84,11 @@ public class AccessTokenResponse {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public List<AuthorizationDetailsResponse> getAuthorizationDetails() {
|
||||
public List<AuthorizationDetailsJSONRepresentation> getAuthorizationDetails() {
|
||||
return authorizationDetails;
|
||||
}
|
||||
|
||||
public void setAuthorizationDetails(List<AuthorizationDetailsResponse> authorizationDetails) {
|
||||
public void setAuthorizationDetails(List<AuthorizationDetailsJSONRepresentation> authorizationDetails) {
|
||||
this.authorizationDetails = authorizationDetails;
|
||||
}
|
||||
|
||||
|
||||
+14
-1
@@ -22,15 +22,19 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.keycloak.util.AuthorizationDetailsParser;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* The JSON representation of a Rich Authorization Request's "authorization_details" object.
|
||||
* The "authorization_details" parameter is array of objects and this class represents single entry of that array. It is used as a base
|
||||
* for "authorization_details" for both requests and responses.
|
||||
*
|
||||
* @author <a href="mailto:dgozalob@redhat.com">Daniel Gozalo</a>
|
||||
* @see {@link <a href="https://datatracker.ietf.org/doc/html/rfc9396#section-2">Request parameter "authorization_details"</a>}
|
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc9396#section-2">Request parameter "authorization_details"</a>
|
||||
*/
|
||||
public class AuthorizationDetailsJSONRepresentation implements Serializable {
|
||||
|
||||
@@ -126,6 +130,15 @@ public class AuthorizationDetailsJSONRepresentation implements Serializable {
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clazz Subtype of {@link AuthorizationDetailsJSONRepresentation}, which will be returned by calling this method
|
||||
* @return this authorizationDetails content cast to the class specified by clazz parameter as long as parser corresponding to the type returned by {@link #getType}
|
||||
* is able to parse this authorizationDetails and convert it to that subtype
|
||||
*/
|
||||
public <T extends AuthorizationDetailsJSONRepresentation> T asSubtype(Class<T> clazz) {
|
||||
return AuthorizationDetailsParser.parseToSubtype(this, clazz);
|
||||
}
|
||||
|
||||
public String getScopeNameFromCustomData() {
|
||||
if (this.getType().equalsIgnoreCase(DYNAMIC_SCOPE_RAR_TYPE) || this.getType().equalsIgnoreCase(STATIC_SCOPE_RAR_TYPE)) {
|
||||
List<String> accessList = (List<String>) this.customData.get("access");
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.representations;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Generic response object for authorization details processing.
|
||||
* This class serves as a base for different types of authorization details responses
|
||||
* from various RAR (Rich Authorization Requests) implementations.
|
||||
*
|
||||
* @author <a href="mailto:Forkim.Akwichek@adorsys.com">Forkim Akwichek</a>
|
||||
*/
|
||||
public class AuthorizationDetailsResponse extends AuthorizationDetailsJSONRepresentation {
|
||||
|
||||
// Map of parsers for specific values of "type" claim of authorizationDetails
|
||||
private static final Map<String, AuthorizationDetailsResponseParser<?>> PARSERS = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Register new parser for specific type. This can be later used by {@link #asSubtype} method. Parsers are supposed to be
|
||||
* registered before authorizationDetails are being used by the application and before method {@link #asSubtype} is called for the first time.
|
||||
* Usually it is supposed to be registered at the startup of the application. If implementing Keycloak provider <em>AuthorizationDetailsProcessor</em>, it might
|
||||
* be good to register corresponding parser in the <em>AuthorizationDetailsProcessorFactory.init</em> method of your provider
|
||||
*
|
||||
* @param type
|
||||
* @param parser
|
||||
*/
|
||||
public static void registerParser(String type, AuthorizationDetailsResponseParser<?> parser) {
|
||||
PARSERS.put(type, parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clazz Subtype of {@link AuthorizationDetailsResponse}, which will be returned by calling this method
|
||||
* @return this authorizationDetails content cast to the class specified by clazz parameter as long as parser corresponding to the type returned by {@link #getType}
|
||||
* is able to parse this authorizationDetails and convert it to that subtype
|
||||
*/
|
||||
public <T extends AuthorizationDetailsResponse> T asSubtype(Class<T> clazz) {
|
||||
AuthorizationDetailsResponseParser<T> parser = (AuthorizationDetailsResponseParser<T>) PARSERS.get(getType());
|
||||
if (parser == null) {
|
||||
throw new IllegalArgumentException("Unsupported to parse response of type '" + getType() + "' to the type '" + clazz +
|
||||
"'. Please make sure that corresponding parser is registered.");
|
||||
}
|
||||
return parser.asSubtype(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parser, which is able to create specific subtype of {@link AuthorizationDetailsResponse} in performant way
|
||||
*/
|
||||
public interface AuthorizationDetailsResponseParser<T extends AuthorizationDetailsResponse> {
|
||||
|
||||
T asSubtype(AuthorizationDetailsResponse response);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.keycloak.util;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
|
||||
/**
|
||||
* Parser, which is able to create specific subtype of {@link AuthorizationDetailsJSONRepresentation} in performant way
|
||||
*/
|
||||
public interface AuthorizationDetailsParser {
|
||||
|
||||
<T extends AuthorizationDetailsJSONRepresentation> T asSubtype(AuthorizationDetailsJSONRepresentation authzDetail, Class<T> clazz);
|
||||
|
||||
|
||||
Map<String, AuthorizationDetailsParser> PARSERS = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Register new parser for specific type. This can be later used by {@link #asSubtype} method. Parsers are supposed to be
|
||||
* registered before authorizationDetails are being used by the application and before method {@link #asSubtype} is called for the first time.
|
||||
* Usually it is supposed to be registered at the startup of the application. If implementing Keycloak provider <em>AuthorizationDetailsProcessor</em>, it might
|
||||
* be good to register corresponding parser in the <em>AuthorizationDetailsProcessorFactory.init</em> method of your provider
|
||||
*
|
||||
* @param type Type as used in the "type" claim of "authorization_details" object entry
|
||||
* @param parser Parser for this type
|
||||
*/
|
||||
static void registerParser(String type, AuthorizationDetailsParser parser) {
|
||||
PARSERS.put(type, parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is not supposed to be called directly. Rather please make sure to use {@link #registerParser(String, AuthorizationDetailsParser)} and
|
||||
* then use {@link #asSubtype(AuthorizationDetailsJSONRepresentation, Class)} to call directly from the application
|
||||
*
|
||||
* @param authzDetail Authorization detail object to cast
|
||||
* @param clazz Subtype of {@link AuthorizationDetailsJSONRepresentation}, which will be returned by calling this method
|
||||
* @return given authzDetail passed in <em>authzDetail</em> parameter cast to the class specified by clazz parameter as long as parser corresponding to the type
|
||||
* returned by {@link AuthorizationDetailsJSONRepresentation#getType} is able to parse this authorizationDetails and convert it to that subtype
|
||||
*/
|
||||
static <T extends AuthorizationDetailsJSONRepresentation> T parseToSubtype(AuthorizationDetailsJSONRepresentation authzDetail, Class<T> clazz) {
|
||||
if (authzDetail.getType() == null) {
|
||||
throw new IllegalArgumentException("Used authzDetail entry does not have 'type' set. The used authzDetail entry was: " + authzDetail);
|
||||
}
|
||||
AuthorizationDetailsParser parser = PARSERS.get(authzDetail.getType());
|
||||
if (parser == null) {
|
||||
throw new IllegalArgumentException("Unsupported to parse response of type '" + authzDetail.getType() + "' to the class '" + clazz +
|
||||
"'. Please make sure that corresponding parser is registered.");
|
||||
}
|
||||
return parser.asSubtype(authzDetail, clazz);
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -22,7 +22,6 @@ import org.keycloak.models.ClientSessionContext;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
|
||||
/**
|
||||
* Provider interface for processing authorization_details parameter in OAuth2/OIDC authorization and token requests.
|
||||
@@ -33,7 +32,7 @@ import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
*
|
||||
* @author <a href="mailto:Forkim.Akwichek@adorsys.com">Forkim Akwichek</a>
|
||||
*/
|
||||
public interface AuthorizationDetailsProcessor<ADR extends AuthorizationDetailsResponse> extends Provider {
|
||||
public interface AuthorizationDetailsProcessor<ADR extends AuthorizationDetailsJSONRepresentation> extends Provider {
|
||||
|
||||
/**
|
||||
* Checks if this processor should be regarded as supported in the running context.
|
||||
@@ -47,7 +46,7 @@ public interface AuthorizationDetailsProcessor<ADR extends AuthorizationDetailsR
|
||||
String getSupportedType();
|
||||
|
||||
/**
|
||||
* @return supported Java type of {@link AuthorizationDetailsResponse} subclass, which this processor can create in the token response
|
||||
* @return supported Java type of {@link AuthorizationDetailsJSONRepresentation} subclass, which this processor can create in the token response
|
||||
*/
|
||||
Class<ADR> getSupportedResponseJavaType();
|
||||
|
||||
@@ -94,7 +93,7 @@ public interface AuthorizationDetailsProcessor<ADR extends AuthorizationDetailsR
|
||||
* @param authzDetailsResponse all the authorizationDetails. May contain also authorizationDetails entries, with different "type" than the type understandable by this processor
|
||||
* @return sublist of the list provided by "authDetailsResponse" parameter, which will contain just the authorizationDetails of the corresponding type of this processor.
|
||||
*/
|
||||
default List<ADR> getSupportedAuthorizationDetails(List<AuthorizationDetailsResponse> authzDetailsResponse) {
|
||||
default List<ADR> getSupportedAuthorizationDetails(List<AuthorizationDetailsJSONRepresentation> authzDetailsResponse) {
|
||||
if (authzDetailsResponse == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+6
-31
@@ -18,8 +18,7 @@ package org.keycloak.protocol.oid4vc.issuance;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.keycloak.protocol.oid4vc.model.ClaimsDescription;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.protocol.oid4vc.model.OID4VCAuthorizationDetail;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@@ -29,29 +28,13 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
*
|
||||
* @author <a href="mailto:Forkim.Akwichek@adorsys.com">Forkim Akwichek</a>
|
||||
*/
|
||||
public class OID4VCAuthorizationDetailResponse extends AuthorizationDetailsResponse {
|
||||
public class OID4VCAuthorizationDetailResponse extends OID4VCAuthorizationDetail {
|
||||
|
||||
public static final String CREDENTIAL_CONFIGURATION_ID = "credential_configuration_id";
|
||||
public static final String CREDENTIAL_IDENTIFIERS = "credential_identifiers";
|
||||
public static final String CLAIMS = "claims";
|
||||
|
||||
@JsonProperty(CREDENTIAL_CONFIGURATION_ID)
|
||||
private String credentialConfigurationId;
|
||||
|
||||
@JsonProperty(CREDENTIAL_IDENTIFIERS)
|
||||
private List<String> credentialIdentifiers;
|
||||
|
||||
@JsonProperty(CLAIMS)
|
||||
private List<ClaimsDescription> claims;
|
||||
|
||||
public String getCredentialConfigurationId() {
|
||||
return credentialConfigurationId;
|
||||
}
|
||||
|
||||
public void setCredentialConfigurationId(String credentialConfigurationId) {
|
||||
this.credentialConfigurationId = credentialConfigurationId;
|
||||
}
|
||||
|
||||
public List<String> getCredentialIdentifiers() {
|
||||
return credentialIdentifiers;
|
||||
}
|
||||
@@ -60,22 +43,14 @@ public class OID4VCAuthorizationDetailResponse extends AuthorizationDetailsRespo
|
||||
this.credentialIdentifiers = credentialIdentifiers;
|
||||
}
|
||||
|
||||
public List<ClaimsDescription> getClaims() {
|
||||
return claims;
|
||||
}
|
||||
|
||||
public void setClaims(List<ClaimsDescription> claims) {
|
||||
this.claims = claims;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OID4VCAuthorizationDetailsResponse{" +
|
||||
"type='" + getType() + '\'' +
|
||||
return "OID4VCAuthorizationDetailsResponse {" +
|
||||
" type='" + getType() + '\'' +
|
||||
", locations='" + getLocations() + '\'' +
|
||||
", credentialConfigurationId='" + credentialConfigurationId + '\'' +
|
||||
", credentialConfigurationId='" + getCredentialConfigurationId() + '\'' +
|
||||
", credentialIdentifiers=" + credentialIdentifiers +
|
||||
", claims=" + claims +
|
||||
", claims=" + getClaims() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
+41
-38
@@ -43,7 +43,7 @@ import org.keycloak.protocol.oidc.grants.PreAuthorizedCodeGrantTypeFactory;
|
||||
import org.keycloak.protocol.oidc.rar.AuthorizationDetailsProcessor;
|
||||
import org.keycloak.protocol.oidc.rar.InvalidAuthorizationDetailsException;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.util.AuthorizationDetailsParser;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
@@ -81,7 +81,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
||||
|
||||
@Override
|
||||
public OID4VCAuthorizationDetailResponse process(UserSessionModel userSession, ClientSessionContext clientSessionCtx, AuthorizationDetailsJSONRepresentation authzDetail) {
|
||||
OID4VCAuthorizationDetail detail = convertRequestType(authzDetail);
|
||||
OID4VCAuthorizationDetail detail = authzDetail.asSubtype(OID4VCAuthorizationDetail.class);
|
||||
Map<String, SupportedCredentialConfiguration> supportedCredentials = OID4VCIssuerWellKnownProvider.getSupportedCredentials(session);
|
||||
|
||||
// Retrieve authorization servers and issuer identifier for locations check
|
||||
@@ -264,7 +264,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
||||
String credentialConfigurationId = detail.getCredentialConfigurationId();
|
||||
|
||||
// Try to reuse identifier from authorizationDetailsResponse in client session context
|
||||
List<AuthorizationDetailsResponse> previousResponses = clientSessionCtx.getAttribute(AUTHORIZATION_DETAILS_RESPONSE, List.class);
|
||||
List<AuthorizationDetailsJSONRepresentation> previousResponses = clientSessionCtx.getAttribute(AUTHORIZATION_DETAILS_RESPONSE, List.class);
|
||||
List<OID4VCAuthorizationDetailResponse> oid4vcPreviousResponses = getSupportedAuthorizationDetails(previousResponses);
|
||||
List<String> credentialIdentifiers = oid4vcPreviousResponses != null && !oid4vcPreviousResponses.isEmpty()
|
||||
? oid4vcPreviousResponses.get(0).getCredentialIdentifiers()
|
||||
@@ -400,49 +400,52 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
||||
}
|
||||
|
||||
|
||||
public static class OID4VCAuthorizationDetailsParser implements AuthorizationDetailsResponse.AuthorizationDetailsResponseParser<OID4VCAuthorizationDetailResponse> {
|
||||
public static class OID4VCAuthorizationDetailsParser implements AuthorizationDetailsParser {
|
||||
|
||||
@Override
|
||||
public OID4VCAuthorizationDetailResponse asSubtype(AuthorizationDetailsResponse response) {
|
||||
if (response instanceof OID4VCAuthorizationDetailResponse) {
|
||||
return (OID4VCAuthorizationDetailResponse) response;
|
||||
public <T extends AuthorizationDetailsJSONRepresentation> T asSubtype(AuthorizationDetailsJSONRepresentation authzDetail, Class<T> clazz) {
|
||||
if (OID4VCAuthorizationDetail.class.equals(clazz)) {
|
||||
if (authzDetail instanceof OID4VCAuthorizationDetail) {
|
||||
return clazz.cast(authzDetail);
|
||||
} else {
|
||||
OID4VCAuthorizationDetail detail = new OID4VCAuthorizationDetail();
|
||||
fillFields(authzDetail, detail);
|
||||
return clazz.cast(detail);
|
||||
}
|
||||
} else if (OID4VCAuthorizationDetailResponse.class.equals(clazz)) {
|
||||
if (authzDetail instanceof OID4VCAuthorizationDetailResponse) {
|
||||
return clazz.cast(authzDetail);
|
||||
} else {
|
||||
OID4VCAuthorizationDetailResponse detail = new OID4VCAuthorizationDetailResponse();
|
||||
fillFields(authzDetail, detail);
|
||||
detail.setCredentialIdentifiers((List<String>) authzDetail.getCustomData().get(CREDENTIAL_IDENTIFIERS));
|
||||
return clazz.cast(detail);
|
||||
}
|
||||
} else {
|
||||
OID4VCAuthorizationDetailResponse detail = new OID4VCAuthorizationDetailResponse();
|
||||
detail.setType(response.getType());
|
||||
detail.setLocations(response.getLocations());
|
||||
detail.setCredentialConfigurationId((String) response.getCustomData().get(CREDENTIAL_CONFIGURATION_ID));
|
||||
detail.setClaims(parseClaims((List<Map>) response.getCustomData().get(CLAIMS)));
|
||||
detail.setCredentialIdentifiers((List<String>) response.getCustomData().get(CREDENTIAL_IDENTIFIERS));
|
||||
return detail;
|
||||
throw new IllegalArgumentException("Authorization details '" + authzDetail + "' is unsupported to be parsed to '" + clazz + "'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fillFields(AuthorizationDetailsJSONRepresentation inDetail, OID4VCAuthorizationDetail outDetail) {
|
||||
outDetail.setType(inDetail.getType());
|
||||
outDetail.setLocations(inDetail.getLocations());
|
||||
outDetail.setCredentialConfigurationId((String) inDetail.getCustomData().get(CREDENTIAL_CONFIGURATION_ID));
|
||||
outDetail.setClaims(parseClaims((List<Map>) inDetail.getCustomData().get(CLAIMS)));
|
||||
}
|
||||
|
||||
private static OID4VCAuthorizationDetail convertRequestType(AuthorizationDetailsJSONRepresentation request) {
|
||||
if (request instanceof OID4VCAuthorizationDetail) {
|
||||
return (OID4VCAuthorizationDetail) request;
|
||||
} else {
|
||||
OID4VCAuthorizationDetail detail = new OID4VCAuthorizationDetail();
|
||||
detail.setType(request.getType());
|
||||
detail.setLocations(request.getLocations());
|
||||
detail.setCredentialConfigurationId((String) request.getCustomData().get(CREDENTIAL_CONFIGURATION_ID));
|
||||
detail.setClaims(parseClaims((List<Map>) request.getCustomData().get(CLAIMS)));
|
||||
return detail;
|
||||
private static List<ClaimsDescription> parseClaims(List<Map> genericClaims) {
|
||||
if (genericClaims == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return genericClaims.stream()
|
||||
.map(claim -> {
|
||||
List<Object> path = (List<Object>) claim.get(PATH);
|
||||
Boolean mandatory = (Boolean) claim.get(MANDATORY);
|
||||
return new ClaimsDescription(path, mandatory);
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<ClaimsDescription> parseClaims(List<Map> genericClaims) {
|
||||
if (genericClaims == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return genericClaims.stream()
|
||||
.map(claim -> {
|
||||
List<Object> path = (List<Object>) claim.get(PATH);
|
||||
Boolean mandatory = (Boolean) claim.get(MANDATORY);
|
||||
return new ClaimsDescription(path, mandatory);
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.protocol.oid4vc.OID4VCEnvironmentProviderFactory;
|
||||
import org.keycloak.protocol.oidc.rar.AuthorizationDetailsProcessorFactory;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.util.AuthorizationDetailsParser;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
|
||||
@@ -41,7 +41,7 @@ public class OID4VCAuthorizationDetailsProcessorFactory implements Authorization
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
AuthorizationDetailsResponse.registerParser(OPENID_CREDENTIAL, new OID4VCAuthorizationDetailsProcessor.OID4VCAuthorizationDetailsParser());
|
||||
AuthorizationDetailsParser.registerParser(OPENID_CREDENTIAL, new OID4VCAuthorizationDetailsProcessor.OID4VCAuthorizationDetailsParser());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-5
@@ -112,7 +112,7 @@ import org.keycloak.protocol.oidc.grants.PreAuthorizedCodeGrantType;
|
||||
import org.keycloak.protocol.oidc.grants.PreAuthorizedCodeGrantTypeFactory;
|
||||
import org.keycloak.protocol.oidc.rar.AuthorizationDetailsProcessor;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.representations.dpop.DPoP;
|
||||
import org.keycloak.saml.processing.api.util.DeflateUtil;
|
||||
import org.keycloak.services.CorsErrorResponseException;
|
||||
@@ -298,8 +298,8 @@ public class OID4VCIssuerEndpoint {
|
||||
* the OpenId4VCI nonce-endpoint
|
||||
*
|
||||
* @return a short-lived c_nonce value that must be presented in key-bound proofs at the credential endpoint.
|
||||
* @see https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-16.html#name-nonce-endpoint
|
||||
* @see https://datatracker.ietf.org/doc/html/draft-demarco-nonce-endpoint#name-nonce-response
|
||||
* @see <a href="https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-16.html#name-nonce-endpoint">Nonce endpoint</a>
|
||||
* @see <a href="https://datatracker.ietf.org/doc/html/draft-demarco-nonce-endpoint#name-nonce-response">Nonce response</a>
|
||||
*/
|
||||
@POST
|
||||
@Produces({MediaType.APPLICATION_JSON})
|
||||
@@ -391,7 +391,7 @@ public class OID4VCIssuerEndpoint {
|
||||
* @param type The response type, which can be 'uri' or 'qr-code'
|
||||
* @param width The width of the QR code image
|
||||
* @param height The height of the QR code image
|
||||
* @see https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-offer-endpoint
|
||||
* @see <a href="https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-offer-endpoint">Credential offer endpoint</a>
|
||||
*/
|
||||
@GET
|
||||
@Produces({MediaType.APPLICATION_JSON, RESPONSE_TYPE_IMG_PNG})
|
||||
@@ -935,7 +935,7 @@ public class OID4VCIssuerEndpoint {
|
||||
}
|
||||
|
||||
private OID4VCAuthorizationDetailResponse getAuthorizationDetailFromToken(AccessToken accessToken) {
|
||||
List<AuthorizationDetailsResponse> tokenAuthDetails = accessToken.getAuthorizationDetails();
|
||||
List<AuthorizationDetailsJSONRepresentation> tokenAuthDetails = accessToken.getAuthorizationDetails();
|
||||
AuthorizationDetailsProcessor<OID4VCAuthorizationDetailResponse> oid4vcProcessor = session.getProvider(AuthorizationDetailsProcessor.class, OPENID_CREDENTIAL);
|
||||
List<OID4VCAuthorizationDetailResponse> oid4vcResponses = oid4vcProcessor.getSupportedAuthorizationDetails(tokenAuthDetails);
|
||||
return oid4vcResponses == null || oid4vcResponses.isEmpty() ? null : oid4vcResponses.get(0);
|
||||
|
||||
+8
-4
@@ -29,10 +29,14 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
*/
|
||||
public class OID4VCAuthorizationDetail extends AuthorizationDetailsJSONRepresentation {
|
||||
|
||||
@JsonProperty("credential_configuration_id")
|
||||
public static final String CREDENTIAL_CONFIGURATION_ID = "credential_configuration_id";
|
||||
public static final String CREDENTIAL_IDENTIFIERS = "credential_identifiers";
|
||||
public static final String CLAIMS = "claims";
|
||||
|
||||
@JsonProperty(CREDENTIAL_CONFIGURATION_ID)
|
||||
private String credentialConfigurationId;
|
||||
|
||||
@JsonProperty("claims")
|
||||
@JsonProperty(CLAIMS)
|
||||
private List<ClaimsDescription> claims;
|
||||
|
||||
public String getCredentialConfigurationId() {
|
||||
@@ -53,8 +57,8 @@ public class OID4VCAuthorizationDetail extends AuthorizationDetailsJSONRepresent
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OID4VCAuthorizationDetailsResponse{" +
|
||||
"type='" + getType() + '\'' +
|
||||
return "OID4VCAuthorizationDetail {" +
|
||||
" type='" + getType() + '\'' +
|
||||
", locations='" + getLocations() + '\'' +
|
||||
", credentialConfigurationId='" + credentialConfigurationId + '\'' +
|
||||
", claims=" + claims +
|
||||
|
||||
@@ -104,7 +104,6 @@ import org.keycloak.rar.AuthorizationRequestContext;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
import org.keycloak.representations.LogoutToken;
|
||||
@@ -355,7 +354,7 @@ public class TokenManager {
|
||||
validation.userSession, validation.clientSessionCtx).offlineToken( TokenUtil.TOKEN_TYPE_OFFLINE.equals(refreshToken.getType())).accessToken(validation.newToken);
|
||||
|
||||
// Copy authorization_details from refresh token to new access token and to acessTokenResponse (if present)
|
||||
List<AuthorizationDetailsResponse> authorizationDetails = refreshToken.getAuthorizationDetails();
|
||||
List<AuthorizationDetailsJSONRepresentation> authorizationDetails = refreshToken.getAuthorizationDetails();
|
||||
if (authorizationDetails != null) {
|
||||
validation.newToken.setAuthorizationDetails(authorizationDetails);
|
||||
validation.clientSessionCtx.setAttribute(AUTHORIZATION_DETAILS_RESPONSE, authorizationDetails);
|
||||
@@ -1404,7 +1403,7 @@ public class TokenManager {
|
||||
res.setScope(responseScope);
|
||||
event.detail(Details.SCOPE, responseScope);
|
||||
|
||||
List<AuthorizationDetailsResponse> authDetailsResponse = clientSessionCtx.getAttribute(AUTHORIZATION_DETAILS_RESPONSE, List.class);
|
||||
List<AuthorizationDetailsJSONRepresentation> authDetailsResponse = clientSessionCtx.getAttribute(AUTHORIZATION_DETAILS_RESPONSE, List.class);
|
||||
if (authDetailsResponse != null && !authDetailsResponse.isEmpty()) {
|
||||
res.setAuthorizationDetails(authDetailsResponse);
|
||||
}
|
||||
|
||||
+3
-3
@@ -39,7 +39,7 @@ import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.protocol.oidc.utils.OAuth2Code;
|
||||
import org.keycloak.protocol.oidc.utils.OAuth2CodeParser;
|
||||
import org.keycloak.protocol.oidc.utils.PkceUtils;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.services.CorsErrorResponseException;
|
||||
import org.keycloak.services.clientpolicy.ClientPolicyException;
|
||||
import org.keycloak.services.clientpolicy.context.TokenRequestContext;
|
||||
@@ -215,7 +215,7 @@ public class AuthorizationCodeGrantType extends OAuth2GrantTypeBase {
|
||||
clientSessionCtx.setAttribute(OIDCLoginProtocol.NONCE_PARAM, codeData.getNonce());
|
||||
|
||||
// Process authorization_details using provider discovery (if present in request)
|
||||
List<AuthorizationDetailsResponse> authorizationDetailsResponse = null;
|
||||
List<AuthorizationDetailsJSONRepresentation> authorizationDetailsResponse = null;
|
||||
if (formParams.getFirst(AUTHORIZATION_DETAILS) != null) {
|
||||
authorizationDetailsResponse = processAuthorizationDetails(userSession, clientSessionCtx);
|
||||
if (authorizationDetailsResponse != null && !authorizationDetailsResponse.isEmpty()) {
|
||||
@@ -252,7 +252,7 @@ public class AuthorizationCodeGrantType extends OAuth2GrantTypeBase {
|
||||
|
||||
return createTokenResponse(user, userSession, clientSessionCtx, scopeParam, true, s -> {
|
||||
// Add authorization_details to the access token and refresh token if they were processed
|
||||
List<AuthorizationDetailsResponse> authDetailsResponse = clientSessionCtx.getAttribute(AUTHORIZATION_DETAILS_RESPONSE, List.class);
|
||||
List<AuthorizationDetailsJSONRepresentation> authDetailsResponse = clientSessionCtx.getAttribute(AUTHORIZATION_DETAILS_RESPONSE, List.class);
|
||||
if (authDetailsResponse != null && !authDetailsResponse.isEmpty()) {
|
||||
s.getAccessToken().setAuthorizationDetails(authDetailsResponse);
|
||||
// Also add to refresh token if one is generated
|
||||
|
||||
@@ -56,7 +56,7 @@ import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
|
||||
import org.keycloak.rar.AuthorizationRequestContext;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.services.CorsErrorResponseException;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.clientpolicy.ClientPolicyContext;
|
||||
@@ -291,7 +291,7 @@ public abstract class OAuth2GrantTypeBase implements OAuth2GrantType {
|
||||
* @param authorizationDetailsResponse the processed authorization details response
|
||||
*/
|
||||
protected void afterAuthorizationDetailsProcessed(UserSessionModel userSession, ClientSessionContext clientSessionCtx,
|
||||
List<AuthorizationDetailsResponse> authorizationDetailsResponse) {
|
||||
List<AuthorizationDetailsJSONRepresentation> authorizationDetailsResponse) {
|
||||
// Default: do nothing
|
||||
// Subclasses or processors can override/extend this to perform post-processing
|
||||
}
|
||||
@@ -304,7 +304,7 @@ public abstract class OAuth2GrantTypeBase implements OAuth2GrantType {
|
||||
* @param clientSessionCtx the client session context
|
||||
* @return the authorization details response if processing was successful, null otherwise
|
||||
*/
|
||||
protected List<AuthorizationDetailsResponse> processAuthorizationDetails(UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||
protected List<AuthorizationDetailsJSONRepresentation> processAuthorizationDetails(UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||
String authorizationDetailsParam = formParams.getFirst(AUTHORIZATION_DETAILS);
|
||||
if (authorizationDetailsParam != null) {
|
||||
try {
|
||||
@@ -328,7 +328,7 @@ public abstract class OAuth2GrantTypeBase implements OAuth2GrantType {
|
||||
* @param clientSessionCtx the client session context
|
||||
* @return the authorization details response if generation was successful, null otherwise
|
||||
*/
|
||||
protected List<AuthorizationDetailsResponse> handleMissingAuthorizationDetails(UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||
protected List<AuthorizationDetailsJSONRepresentation> handleMissingAuthorizationDetails(UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||
try {
|
||||
return new AuthorizationDetailsProcessorManager().handleMissingAuthorizationDetails(session, userSession, clientSessionCtx);
|
||||
} catch (RuntimeException e) {
|
||||
@@ -348,7 +348,7 @@ public abstract class OAuth2GrantTypeBase implements OAuth2GrantType {
|
||||
* @param clientSessionCtx the client session context
|
||||
* @return the authorization details response if processing was successful, null otherwise
|
||||
*/
|
||||
protected List<AuthorizationDetailsResponse> processStoredAuthorizationDetails(UserSessionModel userSession, ClientSessionContext clientSessionCtx) throws CorsErrorResponseException {
|
||||
protected List<AuthorizationDetailsJSONRepresentation> processStoredAuthorizationDetails(UserSessionModel userSession, ClientSessionContext clientSessionCtx) throws CorsErrorResponseException {
|
||||
// Check if authorization_details was stored during authorization request (e.g., from PAR)
|
||||
String storedAuthDetails = clientSessionCtx.getClientSession().getNote(AUTHORIZATION_DETAILS);
|
||||
if (storedAuthDetails != null) {
|
||||
|
||||
+2
-2
@@ -39,7 +39,7 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.TokenManager.AccessTokenResponseBuilder;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.services.CorsErrorResponseException;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.utils.MediaType;
|
||||
@@ -139,7 +139,7 @@ public class PreAuthorizedCodeGrantType extends OAuth2GrantTypeBase {
|
||||
.user(userModel);
|
||||
|
||||
// Process authorization_details using provider discovery
|
||||
List<AuthorizationDetailsResponse> authorizationDetailsResponses = processAuthorizationDetails(userSession, sessionContext);
|
||||
List<AuthorizationDetailsJSONRepresentation> authorizationDetailsResponses = processAuthorizationDetails(userSession, sessionContext);
|
||||
LOGGER.debugf("Initial authorization_details processing result: %s", authorizationDetailsResponses);
|
||||
|
||||
// If no authorization_details were processed from the request, try to generate them from credential offer
|
||||
|
||||
+8
-9
@@ -12,7 +12,6 @@ import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
@@ -22,14 +21,14 @@ public class AuthorizationDetailsProcessorManager {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AuthorizationDetailsProcessorManager.class);
|
||||
|
||||
public List<AuthorizationDetailsResponse> processAuthorizationDetails(KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx,
|
||||
public List<AuthorizationDetailsJSONRepresentation> processAuthorizationDetails(KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx,
|
||||
String authorizationDetailsParam) throws InvalidAuthorizationDetailsException {
|
||||
return processAuthzDetailsImpl(session, authorizationDetailsParam,
|
||||
(processor, authzDetail) -> processor.process(userSession, clientSessionCtx, authzDetail));
|
||||
}
|
||||
|
||||
|
||||
public List<AuthorizationDetailsResponse> processStoredAuthorizationDetails(KeycloakSession session, UserSessionModel userSession,
|
||||
public List<AuthorizationDetailsJSONRepresentation> processStoredAuthorizationDetails(KeycloakSession session, UserSessionModel userSession,
|
||||
ClientSessionContext clientSessionCtx,
|
||||
String authorizationDetailsParam) throws InvalidAuthorizationDetailsException {
|
||||
return processAuthzDetailsImpl(session, authorizationDetailsParam,
|
||||
@@ -39,8 +38,8 @@ public class AuthorizationDetailsProcessorManager {
|
||||
}
|
||||
|
||||
|
||||
public List<AuthorizationDetailsResponse> handleMissingAuthorizationDetails(KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||
List<AuthorizationDetailsResponse> allAuthzDetails = new ArrayList<>();
|
||||
public List<AuthorizationDetailsJSONRepresentation> handleMissingAuthorizationDetails(KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||
List<AuthorizationDetailsJSONRepresentation> allAuthzDetails = new ArrayList<>();
|
||||
session.getKeycloakSessionFactory()
|
||||
.getProviderFactoriesStream(AuthorizationDetailsProcessor.class)
|
||||
.sorted((f1, f2) -> f2.order() - f1.order())
|
||||
@@ -52,13 +51,13 @@ public class AuthorizationDetailsProcessorManager {
|
||||
}
|
||||
|
||||
|
||||
private List<AuthorizationDetailsResponse> processAuthzDetailsImpl(KeycloakSession session, String authorizationDetailsParam,
|
||||
BiFunction<AuthorizationDetailsProcessor<?>, AuthorizationDetailsJSONRepresentation, AuthorizationDetailsResponse> function) throws InvalidAuthorizationDetailsException {
|
||||
private List<AuthorizationDetailsJSONRepresentation> processAuthzDetailsImpl(KeycloakSession session, String authorizationDetailsParam,
|
||||
BiFunction<AuthorizationDetailsProcessor<?>, AuthorizationDetailsJSONRepresentation, AuthorizationDetailsJSONRepresentation> function) throws InvalidAuthorizationDetailsException {
|
||||
if (authorizationDetailsParam == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<AuthorizationDetailsResponse> authzResponses = new ArrayList<>();
|
||||
List<AuthorizationDetailsJSONRepresentation> authzResponses = new ArrayList<>();
|
||||
|
||||
List<AuthorizationDetailsJSONRepresentation> authzDetails = parseAuthorizationDetails(authorizationDetailsParam);
|
||||
|
||||
@@ -76,7 +75,7 @@ public class AuthorizationDetailsProcessorManager {
|
||||
throw new InvalidAuthorizationDetailsException(errorDetails);
|
||||
}
|
||||
function.apply(processor, authzDetail);
|
||||
AuthorizationDetailsResponse response = function.apply(processor, authzDetail);
|
||||
AuthorizationDetailsJSONRepresentation response = function.apply(processor, authzDetail);
|
||||
if (response != null) {
|
||||
authzResponses.add(response);
|
||||
} else {
|
||||
|
||||
+8
-5
@@ -22,7 +22,8 @@ import java.util.List;
|
||||
|
||||
import org.keycloak.protocol.oid4vc.model.ClaimsDescription;
|
||||
import org.keycloak.protocol.oid4vc.model.OID4VCAuthorizationDetail;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.util.AuthorizationDetailsParser;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import org.junit.Assert;
|
||||
@@ -53,7 +54,7 @@ public class OID4VCAuthorizationDetailsProcessorTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
AuthorizationDetailsResponse.registerParser(OPENID_CREDENTIAL, new OID4VCAuthorizationDetailsProcessor.OID4VCAuthorizationDetailsParser());
|
||||
AuthorizationDetailsParser.registerParser(OPENID_CREDENTIAL, new OID4VCAuthorizationDetailsProcessor.OID4VCAuthorizationDetailsParser());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -505,7 +506,8 @@ public class OID4VCAuthorizationDetailsProcessorTest {
|
||||
|
||||
OID4VCAuthorizationDetail invalidDetail1 = createInvalidTypeAuthorizationDetail();
|
||||
|
||||
List<AuthorizationDetailsResponse> responses = List.of(
|
||||
// Convert to the "generic" types to be able to test parser
|
||||
List<AuthorizationDetailsJSONRepresentation> responses = List.of(
|
||||
convertToResponseType(validDetail1),
|
||||
convertToResponseType(validDetail2),
|
||||
convertToResponseType(invalidDetail1)
|
||||
@@ -519,8 +521,9 @@ public class OID4VCAuthorizationDetailsProcessorTest {
|
||||
assertValidClaims(authzResponses.get(1).getClaims());
|
||||
}
|
||||
|
||||
private AuthorizationDetailsResponse convertToResponseType(Object oid4vcDetails) throws IOException {
|
||||
return JsonSerialization.readValue(JsonSerialization.writeValueAsString(oid4vcDetails), AuthorizationDetailsResponse.class);
|
||||
|
||||
private AuthorizationDetailsJSONRepresentation convertToResponseType(OID4VCAuthorizationDetail oid4vcDetails) throws IOException {
|
||||
return JsonSerialization.readValue(JsonSerialization.writeValueAsString(oid4vcDetails), AuthorizationDetailsJSONRepresentation.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-5
@@ -7,7 +7,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
@@ -23,7 +23,7 @@ public class AccessTokenResponse extends AbstractHttpResponse {
|
||||
private String refreshToken;
|
||||
private String scope;
|
||||
private String sessionState;
|
||||
private List<AuthorizationDetailsResponse> authorizationDetails;
|
||||
private List<AuthorizationDetailsJSONRepresentation> authorizationDetails;
|
||||
|
||||
private Map<String, Object> otherClaims;
|
||||
|
||||
@@ -68,7 +68,7 @@ public class AccessTokenResponse extends AbstractHttpResponse {
|
||||
break;
|
||||
case OAuth2Constants.AUTHORIZATION_DETAILS:
|
||||
var valJson = JsonSerialization.valueAsString(entry.getValue());
|
||||
var arr = JsonSerialization.valueFromString(valJson, AuthorizationDetailsResponse[].class);
|
||||
var arr = JsonSerialization.valueFromString(valJson, AuthorizationDetailsJSONRepresentation[].class);
|
||||
authorizationDetails = Arrays.asList(arr);
|
||||
break;
|
||||
default:
|
||||
@@ -118,11 +118,11 @@ public class AccessTokenResponse extends AbstractHttpResponse {
|
||||
return otherClaims;
|
||||
}
|
||||
|
||||
public List<AuthorizationDetailsResponse> getAuthorizationDetails() {
|
||||
public List<AuthorizationDetailsJSONRepresentation> getAuthorizationDetails() {
|
||||
return authorizationDetails;
|
||||
}
|
||||
|
||||
public <ADR extends AuthorizationDetailsResponse> List<ADR> getAuthorizationDetails(Class<ADR> clazz) {
|
||||
public <ADR extends AuthorizationDetailsJSONRepresentation> List<ADR> getAuthorizationDetails(Class<ADR> clazz) {
|
||||
if (getAuthorizationDetails() == null) {
|
||||
return null;
|
||||
} else {
|
||||
|
||||
+2
-2
@@ -85,7 +85,6 @@ import org.keycloak.protocol.oid4vc.model.ProofTypesSupported;
|
||||
import org.keycloak.protocol.oid4vc.model.SupportedCredentialConfiguration;
|
||||
import org.keycloak.protocol.oid4vc.model.VerifiableCredential;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.AuthorizationDetailsResponse;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.ClientScopeRepresentation;
|
||||
import org.keycloak.representations.idm.ComponentExportRepresentation;
|
||||
@@ -100,6 +99,7 @@ import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenRequest;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||
import org.keycloak.testsuite.util.oauth.OAuthClient;
|
||||
import org.keycloak.util.AuthorizationDetailsParser;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
@@ -142,7 +142,7 @@ public abstract class OID4VCTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
AuthorizationDetailsResponse.registerParser(OPENID_CREDENTIAL, new OID4VCAuthorizationDetailsProcessor.OID4VCAuthorizationDetailsParser());
|
||||
AuthorizationDetailsParser.registerParser(OPENID_CREDENTIAL, new OID4VCAuthorizationDetailsProcessor.OID4VCAuthorizationDetailsParser());
|
||||
}
|
||||
|
||||
protected static CredentialSubject getCredentialSubject(Map<String, Object> claims) {
|
||||
|
||||
Reference in New Issue
Block a user