mirror of
https://github.com/keycloak/keycloak.git
synced 2026-02-18 18:37:54 -05:00
[OID4VCI] Simplify OID4VCAuthorizationDetail handling
Signed-off-by: Thomas Diesler <tdiesler@ibm.com>
This commit is contained in:
parent
bd703eb767
commit
44e7cf2da9
26 changed files with 295 additions and 248 deletions
|
|
@ -174,7 +174,4 @@ public interface OAuth2Constants {
|
|||
String DPOP_JWT_HEADER_TYPE = "dpop+jwt";
|
||||
String ALGS_ATTRIBUTE = "algs";
|
||||
|
||||
// OID4VCI - https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html
|
||||
String OPENID_CREDENTIAL = "openid_credential";
|
||||
String CREDENTIAL_IDENTIFIERS = "credential_identifiers";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,11 @@ public class OID4VCConstants {
|
|||
public static final String RESPONSE_TYPE_IMG_PNG = "image/png";
|
||||
public static final String CREDENTIAL_OFFER_URI_CODE_SCOPE = "credential-offer";
|
||||
|
||||
// OID4VCI - https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html
|
||||
public static final String OPENID_CREDENTIAL = "openid_credential";
|
||||
public static final String CREDENTIAL_IDENTIFIERS = "credential_identifiers";
|
||||
public static final String CREDENTIAL_CONFIGURATION_ID = "credential_configuration_id";
|
||||
|
||||
private OID4VCConstants() {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,71 +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.protocol.oid4vc.issuance;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.keycloak.protocol.oid4vc.model.OID4VCAuthorizationDetail;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* OID4VCI-specific authorization details response that extends the generic response
|
||||
* with OID4VCI-specific fields like credential_identifiers.
|
||||
*
|
||||
* @author <a href="mailto:Forkim.Akwichek@adorsys.com">Forkim Akwichek</a>
|
||||
*/
|
||||
public class OID4VCAuthorizationDetailResponse extends OID4VCAuthorizationDetail {
|
||||
|
||||
public static final String CREDENTIAL_IDENTIFIERS = "credential_identifiers";
|
||||
|
||||
@JsonProperty(CREDENTIAL_IDENTIFIERS)
|
||||
private List<String> credentialIdentifiers;
|
||||
|
||||
public List<String> getCredentialIdentifiers() {
|
||||
return credentialIdentifiers;
|
||||
}
|
||||
|
||||
public void setCredentialIdentifiers(List<String> credentialIdentifiers) {
|
||||
this.credentialIdentifiers = credentialIdentifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OID4VCAuthorizationDetailsResponse {" +
|
||||
" type='" + getType() + '\'' +
|
||||
", locations='" + getLocations() + '\'' +
|
||||
", credentialConfigurationId='" + getCredentialConfigurationId() + '\'' +
|
||||
", credentialIdentifiers=" + credentialIdentifiers +
|
||||
", claims=" + getClaims() +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
OID4VCAuthorizationDetailResponse that = (OID4VCAuthorizationDetailResponse) o;
|
||||
return Objects.equals(credentialIdentifiers, that.credentialIdentifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), credentialIdentifiers);
|
||||
}
|
||||
}
|
||||
|
|
@ -48,15 +48,15 @@ import org.keycloak.util.JsonSerialization;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.CREDENTIAL_CONFIGURATION_ID;
|
||||
import static org.keycloak.OID4VCConstants.CREDENTIAL_IDENTIFIERS;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.models.Constants.AUTHORIZATION_DETAILS_RESPONSE;
|
||||
import static org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse.CLAIMS;
|
||||
import static org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse.CREDENTIAL_CONFIGURATION_ID;
|
||||
import static org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse.CREDENTIAL_IDENTIFIERS;
|
||||
import static org.keycloak.protocol.oid4vc.model.ClaimsDescription.MANDATORY;
|
||||
import static org.keycloak.protocol.oid4vc.model.ClaimsDescription.PATH;
|
||||
import static org.keycloak.protocol.oid4vc.model.OID4VCAuthorizationDetail.CLAIMS;
|
||||
|
||||
public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetailsProcessor<OID4VCAuthorizationDetailResponse> {
|
||||
public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetailsProcessor<OID4VCAuthorizationDetail> {
|
||||
private static final Logger logger = Logger.getLogger(OID4VCAuthorizationDetailsProcessor.class);
|
||||
private final KeycloakSession session;
|
||||
|
||||
|
|
@ -75,12 +75,12 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
}
|
||||
|
||||
@Override
|
||||
public Class<OID4VCAuthorizationDetailResponse> getSupportedResponseJavaType() {
|
||||
return OID4VCAuthorizationDetailResponse.class;
|
||||
public Class<OID4VCAuthorizationDetail> getSupportedResponseJavaType() {
|
||||
return OID4VCAuthorizationDetail.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OID4VCAuthorizationDetailResponse process(UserSessionModel userSession, ClientSessionContext clientSessionCtx, AuthorizationDetailsJSONRepresentation authzDetail) {
|
||||
public OID4VCAuthorizationDetail process(UserSessionModel userSession, ClientSessionContext clientSessionCtx, AuthorizationDetailsJSONRepresentation authzDetail) {
|
||||
OID4VCAuthorizationDetail detail = authzDetail.asSubtype(OID4VCAuthorizationDetail.class);
|
||||
Map<String, SupportedCredentialConfiguration> supportedCredentials = OID4VCIssuerWellKnownProvider.getSupportedCredentials(session);
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
String issuerIdentifier = OID4VCIssuerWellKnownProvider.getIssuer(session.getContext());
|
||||
|
||||
validateAuthorizationDetail(detail, supportedCredentials, authorizationServers, issuerIdentifier);
|
||||
OID4VCAuthorizationDetailResponse responseDetail = buildAuthorizationDetailResponse(detail, userSession, clientSessionCtx);
|
||||
OID4VCAuthorizationDetail responseDetail = buildAuthorizationDetail(detail, userSession, clientSessionCtx);
|
||||
|
||||
// For authorization code flow, create CredentialOfferState if credential identifiers are present
|
||||
// This allows credential requests with credential_identifier to find the associated offer state
|
||||
|
|
@ -104,7 +104,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
* Processes all OID4VC authorization details to support multiple credential requests.
|
||||
*/
|
||||
private void createOfferStateForAuthorizationCodeFlow(UserSessionModel userSession, ClientSessionContext clientSessionCtx,
|
||||
OID4VCAuthorizationDetailResponse oid4vcDetail) {
|
||||
OID4VCAuthorizationDetail oid4vcDetail) {
|
||||
AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
|
||||
ClientModel client = clientSession != null ? clientSession.getClient() : null;
|
||||
UserModel user = userSession != null ? userSession.getUser() : null;
|
||||
|
|
@ -171,6 +171,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
|
||||
String type = detail.getType();
|
||||
String credentialConfigurationId = detail.getCredentialConfigurationId();
|
||||
List<String> credentialIdentifiers = detail.getCredentialIdentifiers();
|
||||
List<ClaimsDescription> claims = detail.getClaims();
|
||||
|
||||
// Validate type first
|
||||
|
|
@ -188,6 +189,12 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
}
|
||||
}
|
||||
|
||||
// credential_identifiers not allowed
|
||||
if (credentialIdentifiers != null && !credentialIdentifiers.isEmpty()) {
|
||||
logger.warnf("Property credential_identifiers not allowed in authorization_details");
|
||||
throw getInvalidRequestException("credential_identifiers not allowed");
|
||||
}
|
||||
|
||||
// credential_configuration_id is REQUIRED
|
||||
if (credentialConfigurationId == null) {
|
||||
logger.warnf("Missing credential_configuration_id in authorization_details");
|
||||
|
|
@ -260,12 +267,12 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
}
|
||||
}
|
||||
|
||||
private OID4VCAuthorizationDetailResponse buildAuthorizationDetailResponse(OID4VCAuthorizationDetail detail, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||
private OID4VCAuthorizationDetail buildAuthorizationDetail(OID4VCAuthorizationDetail detail, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||
String credentialConfigurationId = detail.getCredentialConfigurationId();
|
||||
|
||||
// Try to reuse identifier from authorizationDetailsResponse in client session context
|
||||
List<AuthorizationDetailsJSONRepresentation> previousResponses = clientSessionCtx.getAttribute(AUTHORIZATION_DETAILS_RESPONSE, List.class);
|
||||
List<OID4VCAuthorizationDetailResponse> oid4vcPreviousResponses = getSupportedAuthorizationDetails(previousResponses);
|
||||
List<OID4VCAuthorizationDetail> oid4vcPreviousResponses = getSupportedAuthorizationDetails(previousResponses);
|
||||
List<String> credentialIdentifiers = oid4vcPreviousResponses != null && !oid4vcPreviousResponses.isEmpty()
|
||||
? oid4vcPreviousResponses.get(0).getCredentialIdentifiers()
|
||||
: null;
|
||||
|
|
@ -275,7 +282,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
credentialIdentifiers.add(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
OID4VCAuthorizationDetailResponse responseDetail = new OID4VCAuthorizationDetailResponse();
|
||||
OID4VCAuthorizationDetail responseDetail = new OID4VCAuthorizationDetail();
|
||||
responseDetail.setType(OPENID_CREDENTIAL);
|
||||
responseDetail.setCredentialConfigurationId(credentialConfigurationId);
|
||||
responseDetail.setCredentialIdentifiers(credentialIdentifiers);
|
||||
|
|
@ -292,7 +299,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
* @param clientSession the client session that contains the credential offer information
|
||||
* @return the authorization details response if generation was successful, null otherwise
|
||||
*/
|
||||
private List<OID4VCAuthorizationDetailResponse> generateAuthorizationDetailsFromCredentialOffer(AuthenticatedClientSessionModel clientSession) {
|
||||
private List<OID4VCAuthorizationDetail> generateAuthorizationDetailsFromCredentialOffer(AuthenticatedClientSessionModel clientSession) {
|
||||
logger.debug("Processing authorization_details from credential offer");
|
||||
|
||||
// Get supported credentials
|
||||
|
|
@ -311,7 +318,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
}
|
||||
|
||||
// Generate authorization_details for each credential configuration
|
||||
List<OID4VCAuthorizationDetailResponse> authorizationDetailsList = new ArrayList<>();
|
||||
List<OID4VCAuthorizationDetail> authorizationDetailsList = new ArrayList<>();
|
||||
|
||||
for (String credentialConfigurationId : credentialConfigurationIds) {
|
||||
SupportedCredentialConfiguration config = supportedCredentials.get(credentialConfigurationId);
|
||||
|
|
@ -324,7 +331,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
logger.debugf("Generated credential identifier '%s' for configuration '%s'",
|
||||
credentialIdentifier, credentialConfigurationId);
|
||||
|
||||
OID4VCAuthorizationDetailResponse authDetail = new OID4VCAuthorizationDetailResponse();
|
||||
OID4VCAuthorizationDetail authDetail = new OID4VCAuthorizationDetail();
|
||||
authDetail.setType(OPENID_CREDENTIAL);
|
||||
authDetail.setCredentialConfigurationId(credentialConfigurationId);
|
||||
authDetail.setCredentialIdentifiers(List.of(credentialIdentifier));
|
||||
|
|
@ -363,7 +370,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<OID4VCAuthorizationDetailResponse> handleMissingAuthorizationDetails(UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||
public List<OID4VCAuthorizationDetail> handleMissingAuthorizationDetails(UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
|
||||
// Only generate authorization_details from credential offer if:
|
||||
// 1. No authorization_details were processed yet, AND
|
||||
// 2. There's a credential offer note in the client session (indicating this is a credential offer flow)
|
||||
|
|
@ -377,7 +384,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
}
|
||||
|
||||
@Override
|
||||
public OID4VCAuthorizationDetailResponse processStoredAuthorizationDetails(UserSessionModel userSession, ClientSessionContext clientSessionCtx, AuthorizationDetailsJSONRepresentation storedAuthDetails)
|
||||
public OID4VCAuthorizationDetail processStoredAuthorizationDetails(UserSessionModel userSession, ClientSessionContext clientSessionCtx, AuthorizationDetailsJSONRepresentation storedAuthDetails)
|
||||
throws InvalidAuthorizationDetailsException {
|
||||
if (storedAuthDetails == null) {
|
||||
return null;
|
||||
|
|
@ -412,15 +419,6 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
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 {
|
||||
throw new IllegalArgumentException("Authorization details '" + authzDetail + "' is unsupported to be parsed to '" + clazz + "'.");
|
||||
}
|
||||
|
|
@ -430,6 +428,7 @@ public class OID4VCAuthorizationDetailsProcessor implements AuthorizationDetails
|
|||
outDetail.setType(inDetail.getType());
|
||||
outDetail.setLocations(inDetail.getLocations());
|
||||
outDetail.setCredentialConfigurationId((String) inDetail.getCustomData().get(CREDENTIAL_CONFIGURATION_ID));
|
||||
outDetail.setCredentialIdentifiers((List<String>) inDetail.getCustomData().get(CREDENTIAL_IDENTIFIERS));
|
||||
outDetail.setClaims(parseClaims((List<Map>) inDetail.getCustomData().get(CLAIMS)));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import org.keycloak.protocol.oid4vc.OID4VCEnvironmentProviderFactory;
|
|||
import org.keycloak.protocol.oidc.rar.AuthorizationDetailsProcessorFactory;
|
||||
import org.keycloak.util.AuthorizationDetailsParser;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
/**
|
||||
* Factory for creating OID4VCI-specific authorization details processors.
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ import org.keycloak.protocol.oid4vc.model.ErrorResponse;
|
|||
import org.keycloak.protocol.oid4vc.model.ErrorType;
|
||||
import org.keycloak.protocol.oid4vc.model.JwtProof;
|
||||
import org.keycloak.protocol.oid4vc.model.NonceResponse;
|
||||
import org.keycloak.protocol.oid4vc.model.OID4VCAuthorizationDetail;
|
||||
import org.keycloak.protocol.oid4vc.model.OfferUriType;
|
||||
import org.keycloak.protocol.oid4vc.model.PreAuthorizedCode;
|
||||
import org.keycloak.protocol.oid4vc.model.PreAuthorizedGrant;
|
||||
|
|
@ -136,7 +137,7 @@ import org.apache.http.client.methods.HttpOptions;
|
|||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.constants.OID4VCIConstants.CREDENTIAL_OFFER_CREATE;
|
||||
import static org.keycloak.constants.OID4VCIConstants.OID4VC_PROTOCOL;
|
||||
import static org.keycloak.protocol.oid4vc.model.ErrorType.INVALID_CREDENTIAL_OFFER_REQUEST;
|
||||
|
|
@ -795,9 +796,9 @@ public class OID4VCIssuerEndpoint {
|
|||
}
|
||||
|
||||
CredentialScopeModel requestedCredential;
|
||||
OID4VCAuthorizationDetailResponse authDetails;
|
||||
CredentialOfferState offerState = null;
|
||||
CredentialOfferStorage offerStorage = null;
|
||||
OID4VCAuthorizationDetail authDetails;
|
||||
|
||||
// When the CredentialRequest contains a credential identifier the caller must have gone through the
|
||||
// CredentialOffer process or otherwise have set up a valid CredentialOfferState
|
||||
|
|
@ -831,7 +832,7 @@ public class OID4VCIssuerEndpoint {
|
|||
|
||||
// Validate that authorization_details from the token matches the offer state
|
||||
// This ensures the correct access token is being used for the credential request
|
||||
OID4VCAuthorizationDetailResponse tokenAuthDetails = getAuthorizationDetailFromToken(accessToken);
|
||||
OID4VCAuthorizationDetail tokenAuthDetails = getAuthorizationDetailFromToken(accessToken);
|
||||
if (tokenAuthDetails != null && !tokenAuthDetails.equals(authDetails)) {
|
||||
var errorMessage = "Authorization details in access token do not match the credential offer state. " +
|
||||
"The access token may not be the one issued for this credential offer.";
|
||||
|
|
@ -954,10 +955,10 @@ public class OID4VCIssuerEndpoint {
|
|||
return response;
|
||||
}
|
||||
|
||||
private OID4VCAuthorizationDetailResponse getAuthorizationDetailFromToken(AccessToken accessToken) {
|
||||
private OID4VCAuthorizationDetail getAuthorizationDetailFromToken(AccessToken accessToken) {
|
||||
List<AuthorizationDetailsJSONRepresentation> tokenAuthDetails = accessToken.getAuthorizationDetails();
|
||||
AuthorizationDetailsProcessor<OID4VCAuthorizationDetailResponse> oid4vcProcessor = session.getProvider(AuthorizationDetailsProcessor.class, OPENID_CREDENTIAL);
|
||||
List<OID4VCAuthorizationDetailResponse> oid4vcResponses = oid4vcProcessor.getSupportedAuthorizationDetails(tokenAuthDetails);
|
||||
AuthorizationDetailsProcessor<OID4VCAuthorizationDetail> oid4vcProcessor = session.getProvider(AuthorizationDetailsProcessor.class, OPENID_CREDENTIAL);
|
||||
List<OID4VCAuthorizationDetail> oid4vcResponses = oid4vcProcessor.getSupportedAuthorizationDetails(tokenAuthDetails);
|
||||
return oid4vcResponses == null || oid4vcResponses.isEmpty() ? null : oid4vcResponses.get(0);
|
||||
}
|
||||
|
||||
|
|
@ -1442,7 +1443,7 @@ public class OID4VCIssuerEndpoint {
|
|||
*/
|
||||
private Object getCredential(AuthenticationManager.AuthResult authResult,
|
||||
SupportedCredentialConfiguration credentialConfig,
|
||||
OID4VCAuthorizationDetailResponse authDetail,
|
||||
OID4VCAuthorizationDetail authDetail,
|
||||
CredentialRequest credentialRequestVO,
|
||||
EventBuilder eventBuilder
|
||||
) {
|
||||
|
|
@ -1536,7 +1537,7 @@ public class OID4VCIssuerEndpoint {
|
|||
|
||||
// builds the unsigned credential by applying all protocol mappers.
|
||||
private VCIssuanceContext getVCToSign(List<OID4VCMapper> protocolMappers, SupportedCredentialConfiguration credentialConfig,
|
||||
AuthenticationManager.AuthResult authResult, OID4VCAuthorizationDetailResponse authDetail, CredentialRequest credentialRequestVO,
|
||||
AuthenticationManager.AuthResult authResult, OID4VCAuthorizationDetail authDetail, CredentialRequest credentialRequestVO,
|
||||
CredentialScopeModel credentialScopeModel, EventBuilder eventBuilder) {
|
||||
|
||||
// Compute issuance date and apply correlation-mitigation according to realm configuration
|
||||
|
|
@ -1650,7 +1651,7 @@ public class OID4VCIssuerEndpoint {
|
|||
* @throws BadRequestException if mandatory requested claims are missing
|
||||
*/
|
||||
private void validateRequestedClaimsArePresent(Map<String, Object> allClaims, SupportedCredentialConfiguration credentialConfig,
|
||||
UserModel user, OID4VCAuthorizationDetailResponse authzDetail, String scope, EventBuilder eventBuilder) {
|
||||
UserModel user, OID4VCAuthorizationDetail authzDetail, String scope, EventBuilder eventBuilder) {
|
||||
// Protocol mappers from configuration
|
||||
Map<List<Object>, ClaimsDescription> claimsConfig = credentialConfig.getCredentialMetadata().getClaims()
|
||||
.stream()
|
||||
|
|
@ -1696,7 +1697,7 @@ public class OID4VCIssuerEndpoint {
|
|||
}
|
||||
|
||||
|
||||
private List<ClaimsDescription> getClaimsFromAuthzDetails(String scope, UserModel user, OID4VCAuthorizationDetailResponse authzDetail) {
|
||||
private List<ClaimsDescription> getClaimsFromAuthzDetails(String scope, UserModel user, OID4VCAuthorizationDetail authzDetail) {
|
||||
List<ClaimsDescription> storedClaims = authzDetail == null ? null : authzDetail.getClaims();
|
||||
if (storedClaims == null || storedClaims.isEmpty()) {
|
||||
String username = user.getUsername();
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import java.util.Optional;
|
|||
import org.keycloak.common.util.Base64Url;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialsOffer;
|
||||
import org.keycloak.protocol.oid4vc.model.OID4VCAuthorizationDetail;
|
||||
import org.keycloak.protocol.oid4vc.model.PreAuthorizedCode;
|
||||
import org.keycloak.protocol.oid4vc.model.PreAuthorizedGrant;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
|
@ -41,7 +41,7 @@ public interface CredentialOfferStorage extends Provider {
|
|||
private String userId;
|
||||
private String nonce;
|
||||
private int expiration;
|
||||
private OID4VCAuthorizationDetailResponse authorizationDetails;
|
||||
private OID4VCAuthorizationDetail authorizationDetails;
|
||||
|
||||
public CredentialOfferState(CredentialsOffer credOffer, String clientId, String userId, int expiration) {
|
||||
this.credentialsOffer = credOffer;
|
||||
|
|
@ -88,11 +88,11 @@ public interface CredentialOfferStorage extends Provider {
|
|||
return expiration;
|
||||
}
|
||||
|
||||
public OID4VCAuthorizationDetailResponse getAuthorizationDetails() {
|
||||
public OID4VCAuthorizationDetail getAuthorizationDetails() {
|
||||
return authorizationDetails;
|
||||
}
|
||||
|
||||
public void setAuthorizationDetails(OID4VCAuthorizationDetailResponse authorizationDetails) {
|
||||
public void setAuthorizationDetails(OID4VCAuthorizationDetail authorizationDetails) {
|
||||
this.authorizationDetails = authorizationDetails;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,23 +20,36 @@ import java.util.List;
|
|||
import java.util.Objects;
|
||||
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import static org.keycloak.OID4VCConstants.CREDENTIAL_CONFIGURATION_ID;
|
||||
import static org.keycloak.OID4VCConstants.CREDENTIAL_IDENTIFIERS;
|
||||
|
||||
/**
|
||||
* Represents an authorization_details object in the Token Request as per OID4VCI.
|
||||
*
|
||||
*
|
||||
* @author <a href="mailto:Forkim.Akwichek@adorsys.com">Forkim Akwichek</a>
|
||||
*/
|
||||
public class OID4VCAuthorizationDetail extends AuthorizationDetailsJSONRepresentation {
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* The 'credential_identifiers' property is populated by the Issuer in the AccessToken Response
|
||||
* <p/>
|
||||
* Identifying Credentials Being Issued Throughout the Issuance Flow
|
||||
* https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#section-3.3.4
|
||||
* <p/>
|
||||
* The property should not be used in Authorization or AccessToken requests.
|
||||
*/
|
||||
@JsonProperty(CREDENTIAL_IDENTIFIERS)
|
||||
private List<String> credentialIdentifiers;
|
||||
|
||||
@JsonProperty(CLAIMS)
|
||||
private List<ClaimsDescription> claims;
|
||||
|
||||
|
|
@ -48,6 +61,14 @@ public class OID4VCAuthorizationDetail extends AuthorizationDetailsJSONRepresent
|
|||
this.credentialConfigurationId = credentialConfigurationId;
|
||||
}
|
||||
|
||||
public List<String> getCredentialIdentifiers() {
|
||||
return credentialIdentifiers;
|
||||
}
|
||||
|
||||
public void setCredentialIdentifiers(List<String> credentialIdentifiers) {
|
||||
this.credentialIdentifiers = credentialIdentifiers;
|
||||
}
|
||||
|
||||
public List<ClaimsDescription> getClaims() {
|
||||
return claims;
|
||||
}
|
||||
|
|
@ -58,12 +79,7 @@ public class OID4VCAuthorizationDetail extends AuthorizationDetailsJSONRepresent
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OID4VCAuthorizationDetail {" +
|
||||
" type='" + getType() + '\'' +
|
||||
", locations='" + getLocations() + '\'' +
|
||||
", credentialConfigurationId='" + credentialConfigurationId + '\'' +
|
||||
", claims=" + claims +
|
||||
'}';
|
||||
return JsonSerialization.valueAsString(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ import org.keycloak.models.ClientSessionContext;
|
|||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerWellKnownProvider;
|
||||
import org.keycloak.protocol.oid4vc.issuance.credentialoffer.CredentialOfferStorage;
|
||||
import org.keycloak.protocol.oid4vc.model.OID4VCAuthorizationDetail;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.TokenManager.AccessTokenResponseBuilder;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
|
|
@ -171,7 +171,7 @@ public class PreAuthorizedCodeGrantType extends OAuth2GrantTypeBase {
|
|||
}
|
||||
|
||||
// Add authorization_details to the OfferState and otherClaims
|
||||
var authDetails = (OID4VCAuthorizationDetailResponse) authorizationDetailsResponses.get(0);
|
||||
var authDetails = (OID4VCAuthorizationDetail) authorizationDetailsResponses.get(0);
|
||||
offerState.setAuthorizationDetails(authDetails);
|
||||
offerStorage.replaceOfferState(session, offerState);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ import org.junit.Assert;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.CREDENTIAL_IDENTIFIERS;
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.CREDENTIAL_IDENTIFIERS;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
|
@ -135,17 +135,19 @@ public class OID4VCAuthorizationDetailsProcessorTest {
|
|||
assertNotNull("Locations should not be null", authDetail.getLocations());
|
||||
assertEquals("Should have exactly one location", 1, authDetail.getLocations().size());
|
||||
assertEquals("Location should match issuer", "https://test-issuer.com", authDetail.getLocations().get(0));
|
||||
assertNull("Has no credential_identifier", authDetail.getCredentialIdentifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that an AuthorizationDetail has valid structure
|
||||
*/
|
||||
private void assertValidAuthorizationDetailResponse(OID4VCAuthorizationDetailResponse authDetail) {
|
||||
private void assertValidAuthorizationDetailResponse(OID4VCAuthorizationDetail authDetail) {
|
||||
assertEquals("Type should be openid_credential", OPENID_CREDENTIAL, authDetail.getType());
|
||||
assertEquals("Credential configuration ID should be set", "test-config-id", authDetail.getCredentialConfigurationId());
|
||||
assertNotNull("Locations should not be null", authDetail.getLocations());
|
||||
assertEquals("Should have exactly one location", 1, authDetail.getLocations().size());
|
||||
assertEquals("Location should match issuer", "https://test-issuer.com", authDetail.getLocations().get(0));
|
||||
assertNotNull("Has credential_identifier", authDetail.getCredentialIdentifiers());
|
||||
assertEquals(1, authDetail.getCredentialIdentifiers().size());
|
||||
assertEquals("test-identifier-123", authDetail.getCredentialIdentifiers().get(0));
|
||||
}
|
||||
|
|
@ -391,7 +393,7 @@ public class OID4VCAuthorizationDetailsProcessorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testBuildAuthorizationDetailResponseLogic() {
|
||||
public void testBuildAuthorizationDetailLogic() {
|
||||
// Test the response structure that would be built
|
||||
String expectedCredentialConfigurationId = "test-config-id";
|
||||
List<String> expectedCredentialIdentifiers = List.of("test-identifier-123");
|
||||
|
|
@ -403,7 +405,7 @@ public class OID4VCAuthorizationDetailsProcessorTest {
|
|||
authDetail.setCustomData(CREDENTIAL_IDENTIFIERS, expectedCredentialIdentifiers);
|
||||
authDetail.setClaims(expectedClaims);
|
||||
|
||||
// Verify the data structure that buildAuthorizationDetailResponse() would process
|
||||
// Verify the data structure that buildAuthorizationDetail() would process
|
||||
assertValidAuthorizationDetail(authDetail);
|
||||
assertNotNull("Claims should not be null", authDetail.getClaims());
|
||||
assertEquals("Should have exactly one claim", 1, authDetail.getClaims().size());
|
||||
|
|
@ -512,7 +514,7 @@ public class OID4VCAuthorizationDetailsProcessorTest {
|
|||
convertToResponseType(validDetail2),
|
||||
convertToResponseType(invalidDetail1)
|
||||
);
|
||||
List<OID4VCAuthorizationDetailResponse> authzResponses = new OID4VCAuthorizationDetailsProcessor(null).getSupportedAuthorizationDetails(responses);
|
||||
List<OID4VCAuthorizationDetail> authzResponses = new OID4VCAuthorizationDetailsProcessor(null).getSupportedAuthorizationDetails(responses);
|
||||
|
||||
Assert.assertEquals(2, authzResponses.size());
|
||||
assertValidAuthorizationDetailResponse(authzResponses.get(0));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.testsuite.util.oauth;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -33,12 +32,8 @@ public abstract class AbstractUrlBuilder {
|
|||
}
|
||||
|
||||
protected void parameter(String name, Object value) {
|
||||
try {
|
||||
String encoded = URLEncoder.encode(JsonSerialization.writeValueAsString(value), StandardCharsets.UTF_8);
|
||||
parameter(name, encoded);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String encoded = URLEncoder.encode(JsonSerialization.valueAsString(value), StandardCharsets.UTF_8);
|
||||
parameter(name, encoded);
|
||||
}
|
||||
|
||||
public String build() {
|
||||
|
|
|
|||
|
|
@ -2,17 +2,15 @@ package org.keycloak.testsuite.util.oauth;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.model.OID4VCAuthorizationDetail;
|
||||
import org.keycloak.representations.AuthorizationDetailsJSONRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
|
||||
public class AccessTokenResponse extends AbstractHttpResponse {
|
||||
|
|
@ -126,7 +124,17 @@ public class AccessTokenResponse extends AbstractHttpResponse {
|
|||
return authorizationDetails;
|
||||
}
|
||||
|
||||
public <ADR extends AuthorizationDetailsJSONRepresentation> List<ADR> getAuthorizationDetails(Class<ADR> clazz) {
|
||||
/**
|
||||
* Get authorization details as OID4VC-specific response objects.
|
||||
* This is useful when you need to access OID4VC-specific fields like credential_identifiers.
|
||||
*
|
||||
* @return a list of authorization details, or an empty list if none are present.
|
||||
*/
|
||||
public List<OID4VCAuthorizationDetail> getOid4vcAuthorizationDetails() {
|
||||
return getAuthorizationDetails(OID4VCAuthorizationDetail.class);
|
||||
}
|
||||
|
||||
private <ADR extends AuthorizationDetailsJSONRepresentation> List<ADR> getAuthorizationDetails(Class<ADR> clazz) {
|
||||
if (authorizationDetails == null) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -134,30 +142,4 @@ public class AccessTokenResponse extends AbstractHttpResponse {
|
|||
.map(authzResponse -> authzResponse.asSubtype(clazz))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get authorization details as OID4VC-specific response objects.
|
||||
* This is useful when you need to access OID4VC-specific fields like credential_identifiers.
|
||||
*
|
||||
* @return a list of authorization details, or an empty list if none are present.
|
||||
* @throws RuntimeException if there's an error parsing the JSON response
|
||||
*/
|
||||
public List<OID4VCAuthorizationDetailResponse> getOid4vcAuthorizationDetails() {
|
||||
if (responseJson == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Object authDetailsObj = responseJson.get(OAuth2Constants.AUTHORIZATION_DETAILS);
|
||||
if (authDetailsObj == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return JsonSerialization.readValue(
|
||||
JsonSerialization.writeValueAsString(authDetailsObj),
|
||||
new TypeReference<List<OID4VCAuthorizationDetailResponse>>() {
|
||||
}
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to parse authorization_details from token response", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package org.keycloak.testsuite.util.oauth;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
|
|
@ -21,6 +23,12 @@ public class LoginUrlBuilder extends AbstractUrlBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public LoginUrlBuilder scope(String... scopes) {
|
||||
String joinedScopes = String.join(" ", Arrays.asList(scopes));
|
||||
parameter(OAuth2Constants.SCOPE, joinedScopes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoginUrlBuilder state(String state) {
|
||||
parameter(OIDCLoginProtocol.STATE_PARAM, state);
|
||||
return this;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import org.keycloak.OAuth2Constants;
|
|||
import org.keycloak.TokenVerifier;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialIssuer;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialOfferURI;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialRequest;
|
||||
|
|
@ -54,8 +53,8 @@ import org.apache.directory.api.util.Strings;
|
|||
import org.apache.http.HttpStatus;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OAuth2Constants.SCOPE_OPENID;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.constants.OID4VCIConstants.CREDENTIAL_OFFER_CREATE;
|
||||
import static org.keycloak.protocol.oid4vc.model.ErrorType.INVALID_CREDENTIAL_OFFER_REQUEST;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.findUserByUsernameId;
|
||||
|
|
@ -122,7 +121,7 @@ public class OID4VCICredentialOfferMatrixTest extends OID4VCIssuerEndpointTest {
|
|||
public void testCredentialWithoutOffer() throws Exception {
|
||||
var ctx = new TestContext(false, null, appUsername);
|
||||
|
||||
OID4VCAuthorizationDetailResponse authDetail = new OID4VCAuthorizationDetailResponse();
|
||||
OID4VCAuthorizationDetail authDetail = new OID4VCAuthorizationDetail();
|
||||
authDetail.setType(OPENID_CREDENTIAL);
|
||||
authDetail.setCredentialConfigurationId(credConfigId);
|
||||
authDetail.setLocations(List.of(ctx.issuerMetadata.getCredentialIssuer()));
|
||||
|
|
@ -138,14 +137,14 @@ public class OID4VCICredentialOfferMatrixTest extends OID4VCIssuerEndpointTest {
|
|||
|
||||
// When authorization_details are sent in token request, they are returned in token response with credential_identifiers
|
||||
// The credential request MUST use credential_identifier (not credential_configuration_id)
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = JsonSerialization.readValue(
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = JsonSerialization.readValue(
|
||||
JsonSerialization.writeValueAsString(tokenAuthDetails),
|
||||
new TypeReference<>() {}
|
||||
);
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
|
||||
OID4VCAuthorizationDetailResponse authDetailResponse = authDetailsResponse.get(0);
|
||||
OID4VCAuthorizationDetail authDetailResponse = authDetailsResponse.get(0);
|
||||
List<String> credentialIdentifiers = authDetailResponse.getCredentialIdentifiers();
|
||||
assertNotNull("credential_identifiers should be present", credentialIdentifiers);
|
||||
assertFalse("credential_identifiers should not be empty", credentialIdentifiers.isEmpty());
|
||||
|
|
@ -273,14 +272,14 @@ public class OID4VCICredentialOfferMatrixTest extends OID4VCIssuerEndpointTest {
|
|||
// 4. does not reflect anything from the credential offer
|
||||
//
|
||||
AccessTokenResponse accessToken = getPreAuthorizedAccessTokenResponse(ctx, credOffer);
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = accessToken.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = accessToken.getOid4vcAuthorizationDetails();
|
||||
if (authDetailsResponse == null || authDetailsResponse.isEmpty()) {
|
||||
throw new IllegalStateException("No authorization_details in token response");
|
||||
}
|
||||
if (authDetailsResponse.size() > 1) {
|
||||
throw new IllegalStateException("Multiple authorization_details in token response");
|
||||
}
|
||||
OID4VCAuthorizationDetailResponse authDetailResponse = authDetailsResponse.get(0);
|
||||
OID4VCAuthorizationDetail authDetailResponse = authDetailsResponse.get(0);
|
||||
|
||||
// Get the credential and verify
|
||||
//
|
||||
|
|
@ -340,9 +339,9 @@ public class OID4VCICredentialOfferMatrixTest extends OID4VCIssuerEndpointTest {
|
|||
}
|
||||
}
|
||||
|
||||
private List<OID4VCAuthorizationDetailResponse> extractAuthorizationDetails(AccessTokenResponse tokenResponse) {
|
||||
private List<OID4VCAuthorizationDetail> extractAuthorizationDetails(AccessTokenResponse tokenResponse) {
|
||||
// First check if already populated in token response
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
if (authDetailsResponse != null && !authDetailsResponse.isEmpty()) {
|
||||
return authDetailsResponse;
|
||||
}
|
||||
|
|
@ -422,7 +421,7 @@ public class OID4VCICredentialOfferMatrixTest extends OID4VCIssuerEndpointTest {
|
|||
return accessTokenResponse;
|
||||
}
|
||||
|
||||
private CredentialResponse getCredentialByAuthDetail(String accessToken, OID4VCAuthorizationDetailResponse authDetail) throws Exception {
|
||||
private CredentialResponse getCredentialByAuthDetail(String accessToken, OID4VCAuthorizationDetail authDetail) throws Exception {
|
||||
var credentialRequest = new CredentialRequest();
|
||||
if (authDetail.getCredentialIdentifiers() != null) {
|
||||
credentialRequest.setCredentialIdentifier(authDetail.getCredentialIdentifiers().get(0));
|
||||
|
|
@ -439,7 +438,7 @@ public class OID4VCICredentialOfferMatrixTest extends OID4VCIssuerEndpointTest {
|
|||
var credentialRequest = new CredentialRequest();
|
||||
|
||||
// Extract authorization_details (from token response or JWT)
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = extractAuthorizationDetails(tokenResponse);
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = extractAuthorizationDetails(tokenResponse);
|
||||
|
||||
if (authDetailsResponse != null && !authDetailsResponse.isEmpty()) {
|
||||
// If authorization_details are present, credential_identifier is required
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import org.keycloak.jose.jws.JWSBuilder;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.oid4vci.CredentialScopeModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint;
|
||||
import org.keycloak.protocol.oid4vc.issuance.VCIssuanceContext;
|
||||
import org.keycloak.protocol.oid4vc.issuance.VCIssuerException;
|
||||
|
|
@ -40,7 +39,7 @@ import org.keycloak.util.JsonSerialization;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
|
@ -154,7 +153,7 @@ public class OID4VCAttestationProofTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import org.keycloak.events.Errors;
|
|||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.oid4vci.CredentialScopeModel;
|
||||
import org.keycloak.models.oid4vci.Oid4vcProtocolMapperModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.model.ClaimsDescription;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialIssuer;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialRequest;
|
||||
|
|
@ -66,7 +65,7 @@ import org.hamcrest.Matchers;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
|
@ -252,7 +251,7 @@ public abstract class OID4VCAuthorizationCodeFlowTestBase extends OID4VCIssuerEn
|
|||
|
||||
// Extract values from refreshed token for credential request
|
||||
String accessToken = tokenResponseRef.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetails = tokenResponseRef.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetails = tokenResponseRef.getOid4vcAuthorizationDetails();
|
||||
|
||||
String credentialIdentifier = null;
|
||||
if (authDetails != null && !authDetails.isEmpty()) {
|
||||
|
|
@ -991,11 +990,11 @@ public abstract class OID4VCAuthorizationCodeFlowTestBase extends OID4VCIssuerEn
|
|||
// Test successful token response. Returns "Credential identifier" of the VC credential
|
||||
private String assertTokenResponse(AccessTokenResponse tokenResponse) throws Exception {
|
||||
// Extract authorization_details from token response
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertEquals(1, authDetailsResponse.size());
|
||||
|
||||
OID4VCAuthorizationDetailResponse authDetailResponse = authDetailsResponse.get(0);
|
||||
OID4VCAuthorizationDetail authDetailResponse = authDetailsResponse.get(0);
|
||||
assertNotNull("Credential identifiers should be present", authDetailResponse.getCredentialIdentifiers());
|
||||
assertEquals(1, authDetailResponse.getCredentialIdentifiers().size());
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import java.util.List;
|
|||
import java.util.UUID;
|
||||
|
||||
import org.keycloak.models.oid4vci.CredentialScopeModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.model.ClaimsDescription;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialIssuer;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialResponse;
|
||||
|
|
@ -39,7 +38,7 @@ import org.keycloak.testsuite.util.oauth.oid4vc.Oid4vcCredentialResponse;
|
|||
import org.apache.http.HttpStatus;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
|
@ -153,11 +152,11 @@ public class OID4VCAuthorizationCodeFlowWithPARTest extends OID4VCIssuerEndpoint
|
|||
assertEquals(HttpStatus.SC_OK, tokenResponse.getStatusCode());
|
||||
|
||||
// Step 4: Verify authorization_details is present in token response
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertEquals("Should have exactly one authorization detail", 1, authDetailsResponse.size());
|
||||
|
||||
OID4VCAuthorizationDetailResponse authDetailResponse = authDetailsResponse.get(0);
|
||||
OID4VCAuthorizationDetail authDetailResponse = authDetailsResponse.get(0);
|
||||
assertEquals("Type should be openid_credential", OPENID_CREDENTIAL, authDetailResponse.getType());
|
||||
assertEquals("Credential configuration ID should match", credentialConfigurationId, authDetailResponse.getCredentialConfigurationId());
|
||||
|
||||
|
|
@ -288,7 +287,7 @@ public class OID4VCAuthorizationCodeFlowWithPARTest extends OID4VCIssuerEndpoint
|
|||
assertEquals(HttpStatus.SC_OK, tokenResponse.getStatusCode());
|
||||
|
||||
// Step 4: Verify NO authorization_details in token response (since none was in PAR request)
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertTrue("authorization_details should NOT be present in the response when not used in PAR request",
|
||||
authDetailsResponse == null || authDetailsResponse.isEmpty());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package org.keycloak.testsuite.oid4vc.issuance.signing;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
|
@ -28,7 +30,6 @@ import org.keycloak.events.Details;
|
|||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.oid4vci.CredentialScopeModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.model.ClaimsDescription;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialIssuer;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialOfferURI;
|
||||
|
|
@ -41,6 +42,7 @@ import org.keycloak.representations.idm.ClientScopeRepresentation;
|
|||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.util.ClientManager;
|
||||
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
|
||||
import org.keycloak.testsuite.util.oauth.AuthorizationEndpointResponse;
|
||||
import org.keycloak.testsuite.util.oauth.OAuthClient;
|
||||
import org.keycloak.testsuite.util.oauth.OpenIDProviderConfigurationResponse;
|
||||
import org.keycloak.testsuite.util.oauth.oid4vc.CredentialIssuerMetadataResponse;
|
||||
|
|
@ -57,7 +59,7 @@ import org.junit.Before;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
|
@ -167,6 +169,126 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
return ctx;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorizationCodeFlowWithAuthorizationDetails() throws Exception {
|
||||
|
||||
Oid4vcTestContext ctx = new Oid4vcTestContext();
|
||||
|
||||
// Get issuer metadata
|
||||
CredentialIssuerMetadataResponse issuerMetadataResponse = oauth.oid4vc().issuerMetadataRequest().send();
|
||||
assertEquals(HttpStatus.SC_OK, issuerMetadataResponse.getStatusCode());
|
||||
ctx.credentialIssuer = issuerMetadataResponse.getMetadata();
|
||||
|
||||
// Get credential_configuration_id
|
||||
ClientScopeRepresentation credClientScope = getCredentialClientScope();
|
||||
String credConfigId = credClientScope.getAttributes().get(CredentialScopeModel.CONFIGURATION_ID);
|
||||
|
||||
// Build authorization_details
|
||||
OID4VCAuthorizationDetail authDetail = new OID4VCAuthorizationDetail();
|
||||
authDetail.setType(OPENID_CREDENTIAL);
|
||||
authDetail.setCredentialConfigurationId(credConfigId);
|
||||
authDetail.setLocations(List.of(ctx.credentialIssuer.getCredentialIssuer()));
|
||||
|
||||
String authDetailsJson = JsonSerialization.valueAsString(List.of(authDetail));
|
||||
String authDetailsEncoded = URLEncoder.encode(authDetailsJson, Charset.defaultCharset());
|
||||
|
||||
// [TODO #44320] Requires Credential scope in AuthorizationRequest although already given in AuthorizationDetails
|
||||
AuthorizationEndpointResponse authEndpointResponse = oauth.loginForm()
|
||||
.scope(credClientScope.getName())
|
||||
.param("authorization_details", authDetailsEncoded)
|
||||
.doLogin("john","password");
|
||||
|
||||
String authCode = authEndpointResponse.getCode();
|
||||
assertNotNull("No authorization code", authCode);
|
||||
|
||||
AccessTokenResponse tokenResponse = oauth.accessTokenRequest(authCode)
|
||||
.authorizationDetails(List.of(authDetail))
|
||||
.send();
|
||||
assertEquals(HttpStatus.SC_OK, tokenResponse.getStatusCode());
|
||||
|
||||
String accessToken = tokenResponse.getAccessToken();
|
||||
|
||||
String credentialIdentifier;
|
||||
String credentialConfigurationId;
|
||||
OID4VCAuthorizationDetail authDetailResponse;
|
||||
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertEquals("Should have authorization_details for each credential configuration in the offer",
|
||||
1, authDetailsResponse.size());
|
||||
|
||||
// Use the first authorization detail for credential request
|
||||
authDetailResponse = authDetailsResponse.get(0);
|
||||
assertNotNull("Credential identifiers should be present", authDetailResponse.getCredentialIdentifiers());
|
||||
assertEquals(1, authDetailResponse.getCredentialIdentifiers().size());
|
||||
|
||||
credentialIdentifier = authDetailResponse.getCredentialIdentifiers().get(0);
|
||||
assertNotNull("Credential identifier should not be null", credentialIdentifier);
|
||||
|
||||
credentialConfigurationId = authDetailResponse.getCredentialConfigurationId();
|
||||
assertNotNull("Credential configuration id should not be null", credentialConfigurationId);
|
||||
|
||||
Oid4vcCredentialResponse credentialResponse = oauth.oid4vc().credentialRequest()
|
||||
.credentialIdentifier(credentialIdentifier)
|
||||
.bearerToken(accessToken)
|
||||
.send();
|
||||
|
||||
// Parse the credential response
|
||||
CredentialResponse parsedResponse = credentialResponse.getCredentialResponse();
|
||||
assertNotNull("Credential response should not be null", parsedResponse);
|
||||
assertNotNull("Credentials should be present", parsedResponse.getCredentials());
|
||||
assertEquals("Should have exactly one credential", 1, parsedResponse.getCredentials().size());
|
||||
|
||||
// Step 3: Verify that the issued credential structure is valid
|
||||
CredentialResponse.Credential credentialWrapper = parsedResponse.getCredentials().get(0);
|
||||
assertNotNull("Credential wrapper should not be null", credentialWrapper);
|
||||
|
||||
// The credential is stored as Object, so we need to cast it
|
||||
Object credentialObj = credentialWrapper.getCredential();
|
||||
assertNotNull("Credential object should not be null", credentialObj);
|
||||
|
||||
// Verify the credential structure based on format
|
||||
verifyCredentialStructure(credentialObj);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorizationCodeFlowWithCredentialIdentifier() throws Exception {
|
||||
|
||||
Oid4vcTestContext ctx = new Oid4vcTestContext();
|
||||
|
||||
// Get issuer metadata
|
||||
CredentialIssuerMetadataResponse issuerMetadataResponse = oauth.oid4vc().issuerMetadataRequest().send();
|
||||
assertEquals(HttpStatus.SC_OK, issuerMetadataResponse.getStatusCode());
|
||||
ctx.credentialIssuer = issuerMetadataResponse.getMetadata();
|
||||
|
||||
// Get credential_configuration_id
|
||||
ClientScopeRepresentation credClientScope = getCredentialClientScope();
|
||||
|
||||
// Build authorization_details
|
||||
OID4VCAuthorizationDetail authDetail = new OID4VCAuthorizationDetail();
|
||||
authDetail.setType(OPENID_CREDENTIAL);
|
||||
authDetail.setCredentialIdentifiers(List.of("credential_identifiers_not_allowed_here"));
|
||||
authDetail.setLocations(List.of(ctx.credentialIssuer.getCredentialIssuer()));
|
||||
|
||||
String authDetailsJson = JsonSerialization.valueAsString(List.of(authDetail));
|
||||
String authDetailsEncoded = URLEncoder.encode(authDetailsJson, Charset.defaultCharset());
|
||||
|
||||
// [TODO #44320] Requires Credential scope in AuthorizationRequest although already given in AuthorizationDetails
|
||||
AuthorizationEndpointResponse authEndpointResponse = oauth.loginForm()
|
||||
.scope(credClientScope.getName())
|
||||
.param("authorization_details", authDetailsEncoded)
|
||||
.doLogin("john","password");
|
||||
|
||||
String authCode = authEndpointResponse.getCode();
|
||||
assertNotNull("No authorization code", authCode);
|
||||
|
||||
AccessTokenResponse tokenResponse = oauth.accessTokenRequest(authCode)
|
||||
.authorizationDetails(List.of(authDetail))
|
||||
.send();
|
||||
assertEquals(HttpStatus.SC_BAD_REQUEST, tokenResponse.getStatusCode());
|
||||
assertTrue(tokenResponse.getErrorDescription().contains("credential_identifiers not allowed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPreAuthorizedCodeWithAuthorizationDetailsCredentialConfigurationId() throws Exception {
|
||||
String token = getBearerToken(oauth, client, getCredentialClientScope().getName());
|
||||
|
|
@ -187,10 +309,10 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
.send();
|
||||
|
||||
assertEquals(HttpStatus.SC_OK, tokenResponse.getStatusCode());
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertEquals(1, authDetailsResponse.size());
|
||||
OID4VCAuthorizationDetailResponse authDetailResponse = authDetailsResponse.get(0);
|
||||
OID4VCAuthorizationDetail authDetailResponse = authDetailsResponse.get(0);
|
||||
assertEquals(OPENID_CREDENTIAL, authDetailResponse.getType());
|
||||
assertEquals(getCredentialClientScope().getAttributes().get(CredentialScopeModel.CONFIGURATION_ID), authDetailResponse.getCredentialConfigurationId());
|
||||
assertNotNull(authDetailResponse.getCredentialIdentifiers());
|
||||
|
|
@ -241,10 +363,10 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
.send();
|
||||
|
||||
assertEquals(HttpStatus.SC_OK, tokenResponse.getStatusCode());
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertEquals(1, authDetailsResponse.size());
|
||||
OID4VCAuthorizationDetailResponse authDetailResponse = authDetailsResponse.get(0);
|
||||
OID4VCAuthorizationDetail authDetailResponse = authDetailsResponse.get(0);
|
||||
assertEquals(OPENID_CREDENTIAL, authDetailResponse.getType());
|
||||
assertEquals(getCredentialClientScope().getAttributes().get(CredentialScopeModel.CONFIGURATION_ID), authDetailResponse.getCredentialConfigurationId());
|
||||
assertNotNull(authDetailResponse.getClaims());
|
||||
|
|
@ -364,7 +486,7 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
} else {
|
||||
// If it succeeds, verify the response structure
|
||||
assertEquals(HttpStatus.SC_OK, statusCode);
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertEquals(1, authDetailsResponse.size());
|
||||
}
|
||||
|
|
@ -462,7 +584,7 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
|
||||
assertEquals(HttpStatus.SC_OK, tokenResponse.getStatusCode());
|
||||
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertEquals("Should have authorization_details for each credential configuration in the offer",
|
||||
ctx.credentialsOffer.getCredentialConfigurationIds().size(), authDetailsResponse.size());
|
||||
|
|
@ -470,7 +592,7 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
// Verify each credential configuration from the offer has corresponding authorization_details
|
||||
for (int i = 0; i < ctx.credentialsOffer.getCredentialConfigurationIds().size(); i++) {
|
||||
String expectedConfigId = ctx.credentialsOffer.getCredentialConfigurationIds().get(i);
|
||||
OID4VCAuthorizationDetailResponse authDetailResponse = authDetailsResponse.get(i);
|
||||
OID4VCAuthorizationDetail authDetailResponse = authDetailsResponse.get(i);
|
||||
|
||||
assertEquals(OPENID_CREDENTIAL, authDetailResponse.getType());
|
||||
assertEquals("Credential configuration ID should match the one from the offer",
|
||||
|
|
@ -490,7 +612,7 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCompleteFlowWithCredentialOfferBasedAuthorizationDetails() throws Exception {
|
||||
public void testPreAuthorizedFlowWithCredentialOfferBasedAuthorizationDetails() throws Exception {
|
||||
String token = getBearerToken(oauth, client, getCredentialClientScope().getName());
|
||||
|
||||
Oid4vcTestContext ctx = prepareOid4vcTestContext(token);
|
||||
|
|
@ -504,11 +626,11 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
|
||||
String credentialIdentifier;
|
||||
String credentialConfigurationId;
|
||||
OID4VCAuthorizationDetailResponse authDetailResponse;
|
||||
OID4VCAuthorizationDetail authDetailResponse;
|
||||
|
||||
assertEquals(HttpStatus.SC_OK, tokenResponse.getStatusCode());
|
||||
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertEquals("Should have authorization_details for each credential configuration in the offer",
|
||||
ctx.credentialsOffer.getCredentialConfigurationIds().size(), authDetailsResponse.size());
|
||||
|
|
@ -589,7 +711,7 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
String preAuthorizedToken = accessTokenResponse.getAccessToken();
|
||||
assertNotNull("Access token should be present", preAuthorizedToken);
|
||||
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = accessTokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = accessTokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
|
||||
|
|
@ -674,7 +796,7 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
|
||||
assertEquals(HttpStatus.SC_OK, tokenResponse.getStatusCode());
|
||||
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
|
||||
// Verify that we have authorization_details for each credential configuration in the offer
|
||||
|
|
@ -683,7 +805,7 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
|
||||
// Verify each authorization detail
|
||||
for (int i = 0; i < authDetailsResponse.size(); i++) {
|
||||
OID4VCAuthorizationDetailResponse authDetail = authDetailsResponse.get(i);
|
||||
OID4VCAuthorizationDetail authDetail = authDetailsResponse.get(i);
|
||||
String expectedConfigId = ctx.credentialsOffer.getCredentialConfigurationIds().get(i);
|
||||
|
||||
// Verify structure
|
||||
|
|
@ -750,10 +872,10 @@ public abstract class OID4VCAuthorizationDetailsFlowTestBase extends OID4VCIssue
|
|||
|
||||
String credentialIdentifier;
|
||||
String credentialConfigurationId;
|
||||
OID4VCAuthorizationDetailResponse authDetailResponse;
|
||||
OID4VCAuthorizationDetail authDetailResponse;
|
||||
|
||||
assertEquals(HttpStatus.SC_OK, tokenResponse.getStatusCode());
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertEquals(1, authDetailsResponse.size());
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ import org.keycloak.testsuite.util.oauth.OAuthClient;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ import org.keycloak.jose.jwk.JWKParser;
|
|||
import org.keycloak.models.KeyManager;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.oid4vci.CredentialScopeModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialIssuer;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialRequest;
|
||||
|
|
@ -59,7 +58,7 @@ import org.apache.http.HttpStatus;
|
|||
import org.jboss.logging.Logger;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.jose.jwe.JWEConstants.A256GCM;
|
||||
import static org.keycloak.protocol.oid4vc.model.ErrorType.INVALID_ENCRYPTION_PARAMETERS;
|
||||
import static org.keycloak.utils.MediaType.APPLICATION_JWT;
|
||||
|
|
@ -92,7 +91,7 @@ public class OID4VCIssuerEndpointEncryptionTest extends OID4VCIssuerEndpointTest
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
|
@ -201,7 +200,7 @@ public class OID4VCIssuerEndpointEncryptionTest extends OID4VCIssuerEndpointTest
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
|
@ -281,7 +280,7 @@ public class OID4VCIssuerEndpointEncryptionTest extends OID4VCIssuerEndpointTest
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
|
@ -547,7 +546,7 @@ public class OID4VCIssuerEndpointEncryptionTest extends OID4VCIssuerEndpointTest
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.oid4vci.CredentialScopeModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.JWTVCIssuerWellKnownProviderFactory;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint;
|
||||
import org.keycloak.protocol.oid4vc.issuance.TimeProvider;
|
||||
import org.keycloak.protocol.oid4vc.issuance.credentialbuilder.CredentialBuilder;
|
||||
|
|
@ -84,6 +83,7 @@ import org.keycloak.protocol.oid4vc.model.CredentialIssuer;
|
|||
import org.keycloak.protocol.oid4vc.model.CredentialRequest;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialResponse;
|
||||
import org.keycloak.protocol.oid4vc.model.DisplayObject;
|
||||
import org.keycloak.protocol.oid4vc.model.OID4VCAuthorizationDetail;
|
||||
import org.keycloak.protocol.oid4vc.model.SupportedCredentialConfiguration;
|
||||
import org.keycloak.protocol.oid4vc.model.VerifiableCredential;
|
||||
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
|
||||
|
|
@ -523,9 +523,9 @@ public abstract class OID4VCIssuerEndpointTest extends OID4VCTest {
|
|||
JsonWebToken jwt = new JWSInput(token).readJsonContent(JsonWebToken.class);
|
||||
Object authDetails = jwt.getOtherClaims().get(OAuth2Constants.AUTHORIZATION_DETAILS);
|
||||
if (authDetails != null) {
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = JsonSerialization.readValue(
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = JsonSerialization.readValue(
|
||||
JsonSerialization.writeValueAsString(authDetails),
|
||||
new TypeReference<List<OID4VCAuthorizationDetailResponse>>() {
|
||||
new TypeReference<List<OID4VCAuthorizationDetail>>() {
|
||||
}
|
||||
);
|
||||
if (!authDetailsResponse.isEmpty() &&
|
||||
|
|
@ -790,14 +790,14 @@ public abstract class OID4VCIssuerEndpointTest extends OID4VCTest {
|
|||
}
|
||||
}
|
||||
|
||||
protected List<OID4VCAuthorizationDetailResponse> parseAuthorizationDetails(String responseBody) throws IOException {
|
||||
protected List<OID4VCAuthorizationDetail> parseAuthorizationDetails(String responseBody) throws IOException {
|
||||
Map<String, Object> responseMap = JsonSerialization.readValue(responseBody, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
Object authDetailsObj = responseMap.get("authorization_details");
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsObj);
|
||||
return JsonSerialization.readValue(
|
||||
JsonSerialization.writeValueAsString(authDetailsObj),
|
||||
new TypeReference<List<OID4VCAuthorizationDetailResponse>>() {
|
||||
new TypeReference<List<OID4VCAuthorizationDetail>>() {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ import org.keycloak.common.VerificationException;
|
|||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.oid4vci.CredentialScopeModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerWellKnownProvider;
|
||||
import org.keycloak.protocol.oid4vc.issuance.credentialoffer.CredentialOfferStorage;
|
||||
|
|
@ -75,8 +74,8 @@ import org.apache.http.HttpStatus;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.CREDENTIAL_SUBJECT;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
|
@ -316,7 +315,7 @@ public class OID4VCJWTIssuerEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
try {
|
||||
|
|
@ -374,7 +373,7 @@ public class OID4VCJWTIssuerEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
|
@ -501,7 +500,7 @@ public class OID4VCJWTIssuerEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
assertNotNull("Access token should be present", theToken);
|
||||
|
||||
// Extract credential_identifier from authorization_details in token response
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = accessTokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = accessTokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
|
@ -644,7 +643,7 @@ public class OID4VCJWTIssuerEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
String cNonce = getCNonce();
|
||||
|
|
@ -952,7 +951,7 @@ public class OID4VCJWTIssuerEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
|
@ -999,7 +998,7 @@ public class OID4VCJWTIssuerEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
testingClient.server(TEST_REALM_NAME).run(session -> {
|
||||
|
|
@ -1084,7 +1083,7 @@ public class OID4VCJWTIssuerEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
testingClient.server(TEST_REALM_NAME).run(session -> {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import org.keycloak.models.ClientScopeModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.oid4vci.CredentialScopeModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerWellKnownProvider;
|
||||
import org.keycloak.protocol.oid4vc.issuance.credentialbuilder.JwtCredentialBuilder;
|
||||
|
|
@ -70,8 +69,8 @@ import org.apache.http.HttpStatus;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_SUBJECT_ID;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
|
|
@ -102,7 +101,7 @@ public class OID4VCSdJwtIssuingEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
final String clientScopeString = toJsonString(sdJwtTypeCredentialClientScope);
|
||||
|
|
@ -130,7 +129,7 @@ public class OID4VCSdJwtIssuingEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
final String clientScopeString = toJsonString(sdJwtTypeCredentialClientScope);
|
||||
|
|
@ -163,7 +162,7 @@ public class OID4VCSdJwtIssuingEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
final String clientScopeString = toJsonString(sdJwtTypeCredentialClientScope);
|
||||
|
|
@ -200,7 +199,7 @@ public class OID4VCSdJwtIssuingEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
final String clientScopeString = toJsonString(sdJwtTypeCredentialClientScope);
|
||||
|
|
@ -245,7 +244,7 @@ public class OID4VCSdJwtIssuingEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
final String clientScopeString = toJsonString(sdJwtTypeCredentialClientScope);
|
||||
|
|
@ -289,7 +288,7 @@ public class OID4VCSdJwtIssuingEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
final String clientScopeString = toJsonString(sdJwtTypeCredentialClientScope);
|
||||
|
|
@ -424,7 +423,7 @@ public class OID4VCSdJwtIssuingEndpointTest extends OID4VCIssuerEndpointTest {
|
|||
assertNotNull("Access token should be present", theToken);
|
||||
|
||||
// Extract credential_identifier from authorization_details in token response
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = accessTokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = accessTokenResponse.getOid4vcAuthorizationDetails();
|
||||
assertNotNull("authorization_details should be present in the response", authDetailsResponse);
|
||||
assertFalse("authorization_details should not be empty", authDetailsResponse.isEmpty());
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ import org.jboss.logging.Logger;
|
|||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.CLAIM_NAME_SUBJECT_ID;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.testsuite.oid4vc.issuance.signing.OID4VCIssuerEndpointTest.TIME_PROVIDER;
|
||||
import static org.keycloak.testsuite.oid4vc.issuance.signing.OID4VCSdJwtIssuingEndpointTest.getCredentialIssuer;
|
||||
import static org.keycloak.testsuite.oid4vc.issuance.signing.OID4VCSdJwtIssuingEndpointTest.getJtiGeneratedIdMapper;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import org.keycloak.TokenVerifier;
|
|||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.constants.OID4VCIConstants;
|
||||
import org.keycloak.models.oid4vci.CredentialScopeModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint;
|
||||
import org.keycloak.protocol.oid4vc.issuance.mappers.OID4VCIssuedAtTimeClaimMapper;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialIssuer;
|
||||
|
|
@ -44,7 +43,7 @@ import org.keycloak.util.JsonSerialization;
|
|||
import org.apache.http.HttpStatus;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
|
@ -76,7 +75,7 @@ public class OID4VCTimeNormalizationSdJwtTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
final String clientScopeString = toJsonString(sdJwtTypeCredentialClientScope);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import jakarta.ws.rs.core.Response;
|
|||
import org.keycloak.TokenVerifier;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.models.oid4vci.CredentialScopeModel;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCAuthorizationDetailResponse;
|
||||
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerEndpoint;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialIssuer;
|
||||
import org.keycloak.protocol.oid4vc.model.CredentialRequest;
|
||||
|
|
@ -40,7 +39,7 @@ import org.keycloak.util.JsonSerialization;
|
|||
import org.apache.http.HttpStatus;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.keycloak.OAuth2Constants.OPENID_CREDENTIAL;
|
||||
import static org.keycloak.OID4VCConstants.OPENID_CREDENTIAL;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
|
@ -72,7 +71,7 @@ public class OID4VCTimeNormalizationTest extends OID4VCIssuerEndpointTest {
|
|||
String authCode = getAuthorizationCode(oauth, client, "john", scopeName);
|
||||
org.keycloak.testsuite.util.oauth.AccessTokenResponse tokenResponse = getBearerToken(oauth, authCode, authDetail);
|
||||
String token = tokenResponse.getAccessToken();
|
||||
List<OID4VCAuthorizationDetailResponse> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
List<OID4VCAuthorizationDetail> authDetailsResponse = tokenResponse.getOid4vcAuthorizationDetails();
|
||||
String credentialIdentifier = authDetailsResponse.get(0).getCredentialIdentifiers().get(0);
|
||||
|
||||
testingClient.server(TEST_REALM_NAME).run(session -> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue