mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
[OID4VCI] Add mapper for mapping unmanaged attributes (#44828)
closes #44780 Signed-off-by: Pascal Knüppel <pascal.knueppel@governikus.de>
This commit is contained in:
parent
548a89c823
commit
b2778a6792
@ -849,7 +849,7 @@ public class OID4VCIssuerEndpoint {
|
||||
return credentialRequest;
|
||||
} catch (JsonProcessingException e) {
|
||||
String errorMessage = "Failed to parse JSON request: " + e.getMessage();
|
||||
LOGGER.errorf(e, "JSON parsing failed. Request payload length: %d",
|
||||
LOGGER.errorf(e, "JSON parsing failed. Request payload length: %d",
|
||||
requestPayload != null ? requestPayload.length() : 0);
|
||||
throw new BadRequestException(getErrorResponse(INVALID_CREDENTIAL_REQUEST, errorMessage));
|
||||
}
|
||||
@ -1367,8 +1367,7 @@ public class OID4VCIssuerEndpoint {
|
||||
.setType(List.of(credentialConfig.getScope()));
|
||||
|
||||
Map<String, Object> subjectClaims = new HashMap<>();
|
||||
protocolMappers
|
||||
.forEach(mapper -> mapper.setClaimsForSubject(subjectClaims, authResult.session()));
|
||||
protocolMappers.forEach(mapper -> mapper.setClaim(subjectClaims, authResult.session()));
|
||||
|
||||
// Validate that requested claims from authorization_details are present
|
||||
validateRequestedClaimsArePresent(subjectClaims, authResult.session(), credentialConfig.getScope());
|
||||
@ -1376,8 +1375,7 @@ public class OID4VCIssuerEndpoint {
|
||||
// Include all available claims
|
||||
subjectClaims.forEach((key, value) -> vc.getCredentialSubject().setClaims(key, value));
|
||||
|
||||
protocolMappers
|
||||
.forEach(mapper -> mapper.setClaimsForCredential(vc, authResult.session()));
|
||||
protocolMappers.forEach(mapper -> mapper.setClaim(vc, authResult.session()));
|
||||
|
||||
LOGGER.debugf("The credential to sign is: %s", vc);
|
||||
|
||||
|
||||
@ -74,8 +74,8 @@ public class OID4VCContextMapper extends OID4VCMapper {
|
||||
return List.of(TYPE_KEY);
|
||||
}
|
||||
|
||||
public void setClaimsForCredential(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
public void setClaim(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
// remove duplicates
|
||||
Set<String> contexts = new HashSet<>();
|
||||
if (verifiableCredential.getContext() != null) {
|
||||
@ -86,7 +86,7 @@ public class OID4VCContextMapper extends OID4VCMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClaimsForSubject(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
public void setClaim(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
// nothing to do for the mapper.
|
||||
}
|
||||
|
||||
|
||||
@ -77,13 +77,13 @@ public class OID4VCGeneratedIdMapper extends OID4VCMapper {
|
||||
return ListUtils.union(getAttributePrefix(), List.of(property));
|
||||
}
|
||||
|
||||
public void setClaimsForCredential(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
public void setClaim(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
// nothing to do for the mapper.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClaimsForSubject(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
public void setClaim(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
// Assign a generated ID
|
||||
List<String> attributePath = getMetadataAttributePath();
|
||||
String propertyName = attributePath.get(attributePath.size() - 1);
|
||||
|
||||
@ -106,8 +106,8 @@ public class OID4VCIssuedAtTimeClaimMapper extends OID4VCMapper {
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
public void setClaimsForCredential(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
public void setClaim(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
// Set the value
|
||||
List<String> attributePath = getMetadataAttributePath();
|
||||
String propertyName = attributePath.get(attributePath.size() - 1);
|
||||
@ -142,7 +142,7 @@ public class OID4VCIssuedAtTimeClaimMapper extends OID4VCMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClaimsForSubject(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
public void setClaim(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
// NoOp
|
||||
}
|
||||
|
||||
|
||||
@ -149,13 +149,13 @@ public abstract class OID4VCMapper implements ProtocolMapper, OID4VCEnvironmentP
|
||||
/**
|
||||
* Set the claims to credential, like f.e. the context
|
||||
*/
|
||||
public abstract void setClaimsForCredential(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel);
|
||||
public abstract void setClaim(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel);
|
||||
|
||||
/**
|
||||
* Set the claims to the credential subject.
|
||||
*/
|
||||
public abstract void setClaimsForSubject(Map<String, Object> claims,
|
||||
UserSessionModel userSessionModel);
|
||||
public abstract void setClaim(Map<String, Object> claims,
|
||||
UserSessionModel userSessionModel);
|
||||
|
||||
}
|
||||
|
||||
@ -61,13 +61,13 @@ public class OID4VCStaticClaimMapper extends OID4VCMapper {
|
||||
return CONFIG_PROPERTIES;
|
||||
}
|
||||
|
||||
public void setClaimsForCredential(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
public void setClaim(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
// nothing to do for the mapper.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClaimsForSubject(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
public void setClaim(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
List<String> attributePath = getMetadataAttributePath();
|
||||
String propertyName = attributePath.get(attributePath.size() - 1);
|
||||
String staticValue = mapperModel.getConfig().get(STATIC_CLAIM_KEY);
|
||||
|
||||
@ -75,13 +75,13 @@ public class OID4VCSubjectIdMapper extends OID4VCMapper {
|
||||
return mapperModel;
|
||||
}
|
||||
|
||||
public void setClaimsForCredential(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
public void setClaim(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
// nothing to do for the mapper.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClaimsForSubject(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
public void setClaim(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
List<String> attributePath = getMetadataAttributePath();
|
||||
String propertyName = attributePath.get(attributePath.size() - 1);
|
||||
claims.put(propertyName,
|
||||
|
||||
@ -143,14 +143,14 @@ public class OID4VCTargetRoleMapper extends OID4VCMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClaimsForCredential(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
public void setClaim(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
// nothing to do for the mapper.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClaimsForSubject(Map<String, Object> claims,
|
||||
UserSessionModel userSessionModel) {
|
||||
public void setClaim(Map<String, Object> claims,
|
||||
UserSessionModel userSessionModel) {
|
||||
List<String> attributePath = getMetadataAttributePath();
|
||||
String propertyName = attributePath.get(attributePath.size() - 1);
|
||||
String client = mapperModel.getConfig().get(CLIENT_CONFIG_KEY);
|
||||
|
||||
@ -73,8 +73,8 @@ public class OID4VCTypeMapper extends OID4VCMapper {
|
||||
return List.of("type");
|
||||
}
|
||||
|
||||
public void setClaimsForCredential(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
public void setClaim(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
// remove duplicates
|
||||
Set<String> types = new HashSet<>();
|
||||
if (verifiableCredential.getType() != null) {
|
||||
@ -85,7 +85,7 @@ public class OID4VCTypeMapper extends OID4VCMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClaimsForSubject(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
public void setClaim(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
// nothing to do for the mapper.
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||
* 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");
|
||||
@ -27,16 +27,17 @@ import java.util.Optional;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.ProtocolMapper;
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.protocol.oid4vc.OID4VCLoginProtocolFactory;
|
||||
import org.keycloak.protocol.oid4vc.model.VerifiableCredential;
|
||||
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
/**
|
||||
* Allows to add user attributes to the credential subject
|
||||
* Allows adding user properties to the credential subject
|
||||
*
|
||||
* @author <a href="https://github.com/wistefan">Stefan Wiedemann</a>
|
||||
*/
|
||||
@ -50,20 +51,16 @@ public class OID4VCUserAttributeMapper extends OID4VCMapper {
|
||||
static {
|
||||
ProviderConfigProperty subjectPropertyNameConfig = new ProviderConfigProperty();
|
||||
subjectPropertyNameConfig.setName(CLAIM_NAME);
|
||||
subjectPropertyNameConfig.setLabel("Claim Name");
|
||||
subjectPropertyNameConfig.setHelpText("The name of the claim added to the credential subject that is extracted " +
|
||||
"from the user attributes.");
|
||||
subjectPropertyNameConfig.setLabel(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_LABEL);
|
||||
subjectPropertyNameConfig.setHelpText(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME_TOOLTIP);
|
||||
subjectPropertyNameConfig.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
CONFIG_PROPERTIES.add(subjectPropertyNameConfig);
|
||||
|
||||
ProviderConfigProperty userAttributeConfig = new ProviderConfigProperty();
|
||||
userAttributeConfig.setName(USER_ATTRIBUTE_KEY);
|
||||
userAttributeConfig.setLabel("User attribute");
|
||||
userAttributeConfig.setHelpText("The user attribute to be added to the credential subject.");
|
||||
userAttributeConfig.setType(ProviderConfigProperty.LIST_TYPE);
|
||||
userAttributeConfig.setOptions(
|
||||
List.of(UserModel.USERNAME, UserModel.LOCALE, UserModel.FIRST_NAME, UserModel.LAST_NAME,
|
||||
UserModel.DISABLED_REASON, UserModel.EMAIL, UserModel.EMAIL_VERIFIED));
|
||||
userAttributeConfig.setLabel(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_LABEL);
|
||||
userAttributeConfig.setHelpText(ProtocolMapperUtils.USER_MODEL_ATTRIBUTE_HELP_TEXT);
|
||||
userAttributeConfig.setType(ProviderConfigProperty.USER_PROFILE_ATTRIBUTE_LIST_TYPE);
|
||||
CONFIG_PROPERTIES.add(userAttributeConfig);
|
||||
|
||||
ProviderConfigProperty aggregateAttributesConfig = new ProviderConfigProperty();
|
||||
@ -79,13 +76,13 @@ public class OID4VCUserAttributeMapper extends OID4VCMapper {
|
||||
return CONFIG_PROPERTIES;
|
||||
}
|
||||
|
||||
public void setClaimsForCredential(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
public void setClaim(VerifiableCredential verifiableCredential,
|
||||
UserSessionModel userSessionModel) {
|
||||
// nothing to do for the mapper.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClaimsForSubject(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
public void setClaim(Map<String, Object> claims, UserSessionModel userSessionModel) {
|
||||
List<String> attributePath = getMetadataAttributePath();
|
||||
String propertyName = attributePath.get(attributePath.size() - 1);
|
||||
String userAttribute = mapperModel.getConfig().get(USER_ATTRIBUTE_KEY);
|
||||
@ -102,7 +99,7 @@ public class OID4VCUserAttributeMapper extends OID4VCMapper {
|
||||
|
||||
public static ProtocolMapperModel create(String mapperName, String userAttribute, String propertyName,
|
||||
boolean aggregateAttributes) {
|
||||
var mapperModel = new ProtocolMapperModel();
|
||||
ProtocolMapperModel mapperModel = new ProtocolMapperModel();
|
||||
mapperModel.setName(mapperName);
|
||||
Map<String, String> configMap = new HashMap<>();
|
||||
configMap.put(CLAIM_NAME, propertyName);
|
||||
@ -121,7 +118,7 @@ public class OID4VCUserAttributeMapper extends OID4VCMapper {
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Maps user attributes to credential subject properties.";
|
||||
return "Maps user attributes or properties to credential claims.";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -64,7 +64,7 @@ public class OID4VCTargetRoleMapperTest extends OID4VCTest {
|
||||
AppAuthManager.BearerTokenAuthenticator authenticator = new AppAuthManager.BearerTokenAuthenticator(session);
|
||||
authenticator.setTokenString(token);
|
||||
UserSessionModel userSessionModel = authenticator.authenticate().session();
|
||||
roleMapper.setClaimsForSubject(claimsMap, userSessionModel);
|
||||
roleMapper.setClaim(claimsMap, userSessionModel);
|
||||
assertTrue("The roles should be included as a claim.", claimsMap.containsKey("roles"));
|
||||
if (claimsMap.get("roles") instanceof HashSet roles) {
|
||||
List<Role> rolesList = roles.stream().map(ro -> new ObjectMapper().convertValue(ro, Role.class)).toList();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user