Enhancing Light Weight Token(#22148)

Closes #21183
This commit is contained in:
shigeyuki kabano 2023-08-01 18:52:03 +09:00 committed by Marek Posolda
parent c51060ae34
commit 6112b25648
50 changed files with 1091 additions and 304 deletions

View file

@ -40,6 +40,9 @@ import java.util.Map;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class JsonWebToken implements Serializable, Token { public class JsonWebToken implements Serializable, Token {
public static final String AZP = "azp";
public static final String SUBJECT = "sub";
@JsonProperty("jti") @JsonProperty("jti")
protected String id; protected String id;
@ -53,11 +56,11 @@ public class JsonWebToken implements Serializable, Token {
@JsonSerialize(using = StringOrArraySerializer.class) @JsonSerialize(using = StringOrArraySerializer.class)
@JsonDeserialize(using = StringOrArrayDeserializer.class) @JsonDeserialize(using = StringOrArrayDeserializer.class)
protected String[] audience; protected String[] audience;
@JsonProperty("sub") @JsonProperty(SUBJECT)
protected String subject; protected String subject;
@JsonProperty("typ") @JsonProperty("typ")
protected String type; protected String type;
@JsonProperty("azp") @JsonProperty(AZP)
public String issuedFor; public String issuedFor;
protected Map<String, Object> otherClaims = new HashMap<>(); protected Map<String, Object> otherClaims = new HashMap<>();
@ -184,7 +187,7 @@ public class JsonWebToken implements Serializable, Token {
this.iat = iat; this.iat = iat;
return this; return this;
} }
/** /**
* @deprecated int will overflow with values after 2038. Use {@link #iat(Long)} ()} instead. * @deprecated int will overflow with values after 2038. Use {@link #iat(Long)} ()} instead.
*/ */

View file

@ -3096,6 +3096,10 @@
"label": "Add to userinfo", "label": "Add to userinfo",
"tooltip": "Should the claim be added to the userinfo?" "tooltip": "Should the claim be added to the userinfo?"
}, },
"includeInIntrospection": {
"label": "Add to token introspection",
"tooltip": "Should the claim be added to the token introspection?"
},
"sectorIdentifierUri": { "sectorIdentifierUri": {
"label": "Sector Identifier URI", "label": "Sector Identifier URI",
"tooltip": "Providers that use pairwise sub values and support Dynamic Client Registration SHOULD use the sector_identifier_uri parameter. It provides a way for a group of websites under common administrative control to have consistent pairwise sub values independent of the individual domain names. It also provides a way for Clients to change redirect_uri domains without having to reregister all their users." "tooltip": "Providers that use pairwise sub values and support Dynamic Client Registration SHOULD use the sector_identifier_uri parameter. It provides a way for a group of websites under common administrative control to have consistent pairwise sub values independent of the individual domain names. It also provides a way for Clients to change redirect_uri domains without having to reregister all their users."

View file

@ -315,6 +315,7 @@ public class UserSessionAdapter implements UserSessionModel {
update(task); update(task);
} }
@Override
public SessionPersistenceState getPersistenceState() { public SessionPersistenceState getPersistenceState() {
return persistenceState; return persistenceState;
} }

View file

@ -232,6 +232,11 @@ public class PersistentUserSessionAdapter implements OfflineUserSessionModel {
return State.valueOf(state); return State.valueOf(state);
} }
@Override
public SessionPersistenceState getPersistenceState() {
return SessionPersistenceState.PERSISTENT;
}
@Override @Override
public void setState(State state) { public void setState(State state) {
String stateStr = state==null ? null : state.toString(); String stateStr = state==null ? null : state.toString();

View file

@ -23,7 +23,6 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelIllegalStateException; import org.keycloak.models.ModelIllegalStateException;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.models.map.common.TimeAdapter; import org.keycloak.models.map.common.TimeAdapter;
@ -289,4 +288,9 @@ public class MapUserSessionAdapter extends AbstractUserSessionModel {
public int hashCode() { public int hashCode() {
return getId().hashCode(); return getId().hashCode();
} }
@Override
public SessionPersistenceState getPersistenceState() {
return entity.getPersistenceState();
}
} }

View file

@ -83,7 +83,7 @@ public interface UserSessionModel {
/** /**
* Returns map where key is ID of the client (its UUID) and value is ID respective {@link AuthenticatedClientSessionModel} object. * Returns map where key is ID of the client (its UUID) and value is ID respective {@link AuthenticatedClientSessionModel} object.
* @return * @return
*/ */
Map<String, AuthenticatedClientSessionModel> getAuthenticatedClientSessions(); Map<String, AuthenticatedClientSessionModel> getAuthenticatedClientSessions();
/** /**
@ -135,6 +135,8 @@ public interface UserSessionModel {
} }
} }
SessionPersistenceState getPersistenceState();
/** /**
* Flag used when creating user session * Flag used when creating user session
*/ */

View file

@ -24,6 +24,12 @@ import org.keycloak.TokenVerifier;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.crypto.SignatureProvider; import org.keycloak.crypto.SignatureProvider;
import org.keycloak.crypto.SignatureVerifierContext; import org.keycloak.crypto.SignatureVerifierContext;
import org.keycloak.events.Details;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.ImpersonationSessionNote; import org.keycloak.models.ImpersonationSessionNote;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
@ -31,6 +37,8 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.services.Urls; import org.keycloak.services.Urls;
import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.services.util.UserSessionUtil;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
@ -55,6 +63,7 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
public Response introspect(String token) { public Response introspect(String token) {
try { try {
AccessToken accessToken = verifyAccessToken(token); AccessToken accessToken = verifyAccessToken(token);
accessToken = transformAccessToken(accessToken);
ObjectNode tokenMetadata; ObjectNode tokenMetadata;
if (accessToken != null) { if (accessToken != null) {
@ -106,6 +115,57 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
} }
} }
private AccessToken transformAccessToken(AccessToken token) {
if (token == null) {
return null;
}
ClientModel client = realm.getClientByClientId(token.getIssuedFor());
EventBuilder event = new EventBuilder(realm, session, session.getContext().getConnection())
.event(EventType.INTROSPECT_TOKEN)
.detail(Details.AUTH_METHOD, Details.VALIDATE_ACCESS_TOKEN);
UserSessionModel userSession;
try {
userSession = UserSessionUtil.findValidSession(session, realm, token, event, client);
} catch (Exception e) {
logger.debugf("Can not get user session: %s", e.getMessage());
// Backwards compatibility
return token;
}
if (userSession.getUser() == null) {
logger.debugf("User not found");
// Backwards compatibility
return token;
}
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
ClientSessionContext clientSessionCtx = DefaultClientSessionContext.fromClientSessionScopeParameter(clientSession, session);
AccessToken smallToken = getAccessTokenFromStoredData(token, userSession);
return tokenManager.transformIntrospectionAccessToken(session, smallToken, userSession, clientSessionCtx);
}
private AccessToken getAccessTokenFromStoredData(AccessToken token, UserSessionModel userSession) {
// Copy just "basic" claims from the initial token. The same like filled in TokenManager.initToken. The rest should be possibly added by protocol mappers (only if configured for introspection response)
AccessToken newToken = new AccessToken();
newToken.id(token.getId());
newToken.type(token.getType());
newToken.subject(token.getSubject() != null ? token.getSubject() : userSession.getUser().getId());
newToken.iat(token.getIat());
newToken.exp(token.getExp());
newToken.issuedFor(token.getIssuedFor());
newToken.issuer(token.getIssuer());
newToken.setNonce(token.getNonce());
newToken.setScope(token.getScope());
newToken.setAuth_time(token.getAuth_time());
newToken.setSessionState(token.getSessionState());
// In the case of a refresh token, aud is a basic claim.
newToken.audience(token.getAudience());
// The cnf is not a claim controlled by the protocol mapper.
newToken.setConfirmation(token.getConfirmation());
return newToken;
}
protected AccessToken verifyAccessToken(String token) { protected AccessToken verifyAccessToken(String token) {
AccessToken accessToken; AccessToken accessToken;

View file

@ -133,29 +133,29 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
private Map<String, ProtocolMapperModel> builtins = new HashMap<>(); private Map<String, ProtocolMapperModel> builtins = new HashMap<>();
void initBuiltIns() { void initBuiltIns() {
ProtocolMapperModel model; ProtocolMapperModel model;
model = UserAttributeMapper.createClaimMapper(USERNAME, model = UserAttributeMapper.createClaimMapper(USERNAME,
"username", "username",
"preferred_username", String.class.getSimpleName(), "preferred_username", String.class.getSimpleName(),
true, true); true, true, true);
builtins.put(USERNAME, model); builtins.put(USERNAME, model);
model = UserAttributeMapper.createClaimMapper(EMAIL, model = UserAttributeMapper.createClaimMapper(EMAIL,
"email", "email",
"email", "String", "email", "String",
true, true); true, true, true);
builtins.put(EMAIL, model); builtins.put(EMAIL, model);
model = UserAttributeMapper.createClaimMapper(GIVEN_NAME, model = UserAttributeMapper.createClaimMapper(GIVEN_NAME,
"firstName", "firstName",
"given_name", "String", "given_name", "String",
true, true); true, true, true);
builtins.put(GIVEN_NAME, model); builtins.put(GIVEN_NAME, model);
model = UserAttributeMapper.createClaimMapper(FAMILY_NAME, model = UserAttributeMapper.createClaimMapper(FAMILY_NAME,
"lastName", "lastName",
"family_name", "String", "family_name", "String",
true, true); true, true, true);
builtins.put(FAMILY_NAME, model); builtins.put(FAMILY_NAME, model);
createUserAttributeMapper(MIDDLE_NAME, "middleName", IDToken.MIDDLE_NAME, "String"); createUserAttributeMapper(MIDDLE_NAME, "middleName", IDToken.MIDDLE_NAME, "String");
@ -175,10 +175,10 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
model = UserPropertyMapper.createClaimMapper(EMAIL_VERIFIED, model = UserPropertyMapper.createClaimMapper(EMAIL_VERIFIED,
"emailVerified", "emailVerified",
"email_verified", "boolean", "email_verified", "boolean",
true, true); true, true, true);
builtins.put(EMAIL_VERIFIED, model); builtins.put(EMAIL_VERIFIED, model);
ProtocolMapperModel fullName = FullNameMapper.create(FULL_NAME, true, true, true); ProtocolMapperModel fullName = FullNameMapper.create(FULL_NAME, true, true, true, true);
builtins.put(FULL_NAME, fullName); builtins.put(FULL_NAME, fullName);
ProtocolMapperModel address = AddressMapper.createAddressMapper(); ProtocolMapperModel address = AddressMapper.createAddressMapper();
@ -187,19 +187,19 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, KerberosConstants.GSS_DELEGATION_CREDENTIAL,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String", KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
true, false); true, false, true);
builtins.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, model); builtins.put(KerberosConstants.GSS_DELEGATION_CREDENTIAL, model);
model = UserRealmRoleMappingMapper.create(null, REALM_ROLES, "realm_access.roles", true, false, true); model = UserRealmRoleMappingMapper.create(null, REALM_ROLES, "realm_access.roles", true, false, true, true);
builtins.put(REALM_ROLES, model); builtins.put(REALM_ROLES, model);
model = UserClientRoleMappingMapper.create(null, null, CLIENT_ROLES, "resource_access.${client_id}.roles", true, false, true); model = UserClientRoleMappingMapper.create(null, null, CLIENT_ROLES, "resource_access.${client_id}.roles", true, false, true, true);
builtins.put(CLIENT_ROLES, model); builtins.put(CLIENT_ROLES, model);
model = AudienceResolveProtocolMapper.createClaimMapper(AUDIENCE_RESOLVE); model = AudienceResolveProtocolMapper.createClaimMapper(AUDIENCE_RESOLVE, true, true);
builtins.put(AUDIENCE_RESOLVE, model); builtins.put(AUDIENCE_RESOLVE, model);
model = AllowedWebOriginsProtocolMapper.createClaimMapper(ALLOWED_WEB_ORIGINS); model = AllowedWebOriginsProtocolMapper.createClaimMapper(ALLOWED_WEB_ORIGINS, true, true);
builtins.put(ALLOWED_WEB_ORIGINS, model); builtins.put(ALLOWED_WEB_ORIGINS, model);
builtins.put(IMPERSONATOR_ID.getDisplayName(), UserSessionNoteMapper.createUserSessionNoteMapper(IMPERSONATOR_ID)); builtins.put(IMPERSONATOR_ID.getDisplayName(), UserSessionNoteMapper.createUserSessionNoteMapper(IMPERSONATOR_ID));
@ -207,14 +207,14 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
model = UserAttributeMapper.createClaimMapper(UPN, "username", model = UserAttributeMapper.createClaimMapper(UPN, "username",
"upn", "String", "upn", "String",
true, true); true, true, true);
builtins.put(UPN, model); builtins.put(UPN, model);
model = UserRealmRoleMappingMapper.create(null, GROUPS, GROUPS, true, true, true); model = UserRealmRoleMappingMapper.create(null, GROUPS, GROUPS, true, true, true, true);
builtins.put(GROUPS, model); builtins.put(GROUPS, model);
if (Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) { if (Profile.isFeatureEnabled(Profile.Feature.STEP_UP_AUTHENTICATION)) {
model = AcrProtocolMapper.create(ACR, true, true); model = AcrProtocolMapper.create(ACR, true, true, true);
builtins.put(ACR, model); builtins.put(ACR, model);
} }
} }
@ -223,7 +223,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
ProtocolMapperModel model = UserAttributeMapper.createClaimMapper(name, ProtocolMapperModel model = UserAttributeMapper.createClaimMapper(name,
attrName, attrName,
claimName, type, claimName, type,
true, true, false); true, true, true, false);
builtins.put(name, model); builtins.put(name, model);
} }

View file

@ -61,6 +61,7 @@ import org.keycloak.models.utils.SessionExpirationUtils;
import org.keycloak.models.utils.RoleUtils; import org.keycloak.models.utils.RoleUtils;
import org.keycloak.protocol.ProtocolMapper; import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils; import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.mappers.TokenIntrospectionTokenMapper;
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper; import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper;
import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenResponseMapper; import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenResponseMapper;
import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper; import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper;
@ -785,6 +786,17 @@ public class TokenManager {
}); });
} }
public AccessToken transformIntrospectionAccessToken(KeycloakSession session, AccessToken token,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
return ProtocolMapperUtils.getSortedProtocolMappers(session, clientSessionCtx, mapper -> mapper.getValue() instanceof TokenIntrospectionTokenMapper)
.collect(new TokenCollector<AccessToken>(token) {
@Override
protected AccessToken applyMapper(AccessToken token, Map.Entry<ProtocolMapperModel, ProtocolMapper> mapper) {
return ((TokenIntrospectionTokenMapper) mapper.getValue()).transformIntrospectionToken(token, mapper.getKey(), session, userSession, clientSessionCtx);
}
});
}
public Map<String, Object> generateUserInfoClaims(AccessToken userInfo, UserModel userModel) { public Map<String, Object> generateUserInfoClaims(AccessToken userInfo, UserModel userModel) {
Map<String, Object> claims = new HashMap<>(); Map<String, Object> claims = new HashMap<>();
claims.put("sub", userInfo.getSubject() == null? userModel.getId() : userInfo.getSubject()); claims.put("sub", userInfo.getSubject() == null? userModel.getId() : userInfo.getSubject());

View file

@ -24,7 +24,6 @@ import org.keycloak.TokenVerifier;
import org.keycloak.common.ClientConnection; import org.keycloak.common.ClientConnection;
import org.keycloak.common.Profile; import org.keycloak.common.Profile;
import org.keycloak.common.VerificationException; import org.keycloak.common.VerificationException;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.crypto.ContentEncryptionProvider; import org.keycloak.crypto.ContentEncryptionProvider;
import org.keycloak.crypto.CekManagementProvider; import org.keycloak.crypto.CekManagementProvider;
import org.keycloak.crypto.KeyWrapper; import org.keycloak.crypto.KeyWrapper;
@ -48,7 +47,6 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.TokenManager;
@ -59,15 +57,11 @@ import org.keycloak.services.Urls;
import org.keycloak.services.clientpolicy.ClientPolicyException; import org.keycloak.services.clientpolicy.ClientPolicyException;
import org.keycloak.services.clientpolicy.context.UserInfoRequestContext; import org.keycloak.services.clientpolicy.context.UserInfoRequestContext;
import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.UserSessionCrossDCManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.services.resources.Cors; import org.keycloak.services.resources.Cors;
import org.keycloak.services.util.DPoPUtil; import org.keycloak.services.util.DPoPUtil;
import org.keycloak.services.util.DefaultClientSessionContext; import org.keycloak.services.util.DefaultClientSessionContext;
import org.keycloak.services.util.MtlsHoKTokenUtil; import org.keycloak.services.util.MtlsHoKTokenUtil;
import org.keycloak.sessions.AuthenticationSessionModel; import org.keycloak.services.util.UserSessionUtil;
import org.keycloak.sessions.RootAuthenticationSessionModel;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import org.keycloak.util.TokenUtil; import org.keycloak.util.TokenUtil;
import org.keycloak.utils.MediaType; import org.keycloak.utils.MediaType;
@ -145,15 +139,15 @@ public class UserInfoEndpoint {
authorization(accessToken); authorization(accessToken);
try { try {
String contentType = headers.getHeaderString(HttpHeaders.CONTENT_TYPE); String contentType = headers.getHeaderString(HttpHeaders.CONTENT_TYPE);
jakarta.ws.rs.core.MediaType mediaType = jakarta.ws.rs.core.MediaType.valueOf(contentType); jakarta.ws.rs.core.MediaType mediaType = jakarta.ws.rs.core.MediaType.valueOf(contentType);
if (jakarta.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(mediaType)) { if (jakarta.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE.isCompatible(mediaType)) {
MultivaluedMap<String, String> formParams = request.getDecodedFormParameters(); MultivaluedMap<String, String> formParams = request.getDecodedFormParameters();
checkAccessTokenDuplicated(formParams); checkAccessTokenDuplicated(formParams);
accessToken = formParams.getFirst(OAuth2Constants.ACCESS_TOKEN); accessToken = formParams.getFirst(OAuth2Constants.ACCESS_TOKEN);
authorization(accessToken); authorization(accessToken);
} }
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// not application/x-www-form-urlencoded, ignore // not application/x-www-form-urlencoded, ignore
@ -229,7 +223,7 @@ public class UserInfoEndpoint {
throw error.invalidToken("Client disabled"); throw error.invalidToken("Client disabled");
} }
UserSessionModel userSession = findValidSession(token, event, clientModel); UserSessionModel userSession = UserSessionUtil.findValidSession(session, realm, token, event, clientModel);
UserModel userModel = userSession.getUser(); UserModel userModel = userSession.getUser();
if (userModel == null) { if (userModel == null) {
@ -350,73 +344,6 @@ public class UserInfoEndpoint {
return encryptedToken; return encryptedToken;
} }
private UserSessionModel createTransientSessionForClient(AccessToken token, ClientModel client) {
// create a transient session
UserModel user = TokenManager.lookupUserFromStatelessToken(session, realm, token);
if (user == null) {
throw error.invalidToken("User not found");
}
UserSessionModel userSession = new UserSessionManager(session).createUserSession(KeycloakModelUtils.generateId(), realm, user, user.getUsername(), clientConnection.getRemoteAddr(),
ServiceAccountConstants.CLIENT_AUTH, false, null, null, UserSessionModel.SessionPersistenceState.TRANSIENT);
// attach an auth session for the client
RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(realm);
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
authSession.setAuthenticatedUser(userSession.getUser());
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
AuthenticationManager.setClientScopesInSession(authSession);
TokenManager.attachAuthenticationSession(session, userSession, authSession);
return userSession;
}
private UserSessionModel findValidSession(AccessToken token, EventBuilder event, ClientModel client) {
if (token.getSessionState() == null) {
return createTransientSessionForClient(token, client);
}
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
UserSessionModel offlineUserSession = null;
if (AuthenticationManager.isSessionValid(realm, userSession)) {
checkTokenIssuedAt(token, userSession, event, client);
event.session(userSession);
return userSession;
} else {
offlineUserSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId());
if (AuthenticationManager.isOfflineSessionValid(realm, offlineUserSession)) {
checkTokenIssuedAt(token, offlineUserSession, event, client);
event.session(offlineUserSession);
return offlineUserSession;
}
}
if (userSession == null && offlineUserSession == null) {
event.error(Errors.USER_SESSION_NOT_FOUND);
throw error.invalidToken("User session not found or doesn't have client attached on it");
}
if (userSession != null) {
event.session(userSession);
} else {
event.session(offlineUserSession);
}
event.error(Errors.SESSION_EXPIRED);
throw error.invalidToken("Session expired");
}
private void checkTokenIssuedAt(AccessToken token, UserSessionModel userSession, EventBuilder event, ClientModel client) {
if (token.isIssuedBeforeSessionStart(userSession.getStarted())) {
event.error(Errors.INVALID_TOKEN);
throw error.invalidToken("Stale token");
}
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (token.isIssuedBeforeSessionStart(clientSession.getStarted())) {
event.error(Errors.INVALID_TOKEN);
throw error.invalidToken("Stale token");
}
}
private void checkAccessTokenDuplicated(MultivaluedMap<String, String> formParams) { private void checkAccessTokenDuplicated(MultivaluedMap<String, String> formParams) {
// If access_token is not provided, error is thrown in issueUserInfo(). // If access_token is not provided, error is thrown in issueUserInfo().
// Only checks duplication of access token parameter in this function. // Only checks duplication of access token parameter in this function.

View file

@ -106,6 +106,17 @@ public abstract class AbstractOIDCProtocolMapper implements ProtocolMapper {
return accessTokenResponse; return accessTokenResponse;
} }
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
if (!OIDCAttributeMapperHelper.includeInIntrospection(mappingModel)){
return token;
}
setClaim(token, mappingModel, userSession, session, clientSessionCtx);
return token;
}
/** /**
* Intended to be overridden in {@link ProtocolMapper} implementations to add claims to an token. * Intended to be overridden in {@link ProtocolMapper} implementations to add claims to an token.
* @param token * @param token

View file

@ -14,6 +14,7 @@ import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken; import org.keycloak.representations.IDToken;
import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -22,7 +23,7 @@ import java.util.List;
* *
* @author <a href="mailto:martin.hardselius@gmail.com">Martin Hardselius</a> * @author <a href="mailto:martin.hardselius@gmail.com">Martin Hardselius</a>
*/ */
public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
public static final String PROVIDER_ID_SUFFIX = "-pairwise-sub-mapper"; public static final String PROVIDER_ID_SUFFIX = "-pairwise-sub-mapper";
public abstract String getIdPrefix(); public abstract String getIdPrefix();
@ -75,6 +76,12 @@ public abstract class AbstractPairwiseSubMapper extends AbstractOIDCProtocolMapp
return token; return token;
} }
@Override
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
setAccessTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSessionCtx.getClientSession().getClient(), mappingModel), userSession.getUser().getId()));
return token;
}
@Override @Override
public AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) { public AccessToken transformUserInfoToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
setUserInfoTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSessionCtx.getClientSession().getClient(), mappingModel), userSession.getUser().getId())); setUserInfoTokenSubject(token, generateSub(mappingModel, getSectorIdentifier(clientSessionCtx.getClientSession().getClient(), mappingModel), userSession.getUser().getId()));

View file

@ -38,7 +38,7 @@ import java.util.stream.Collectors;
* *
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a> * @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
*/ */
abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { abstract class AbstractUserRoleMappingMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
@Override @Override
public int getPriority() { public int getPriority() {

View file

@ -42,7 +42,7 @@ import org.keycloak.services.managers.AuthenticationManager;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, EnvironmentDependentProviderFactory { public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, TokenIntrospectionTokenMapper, EnvironmentDependentProviderFactory {
private static final Logger logger = Logger.getLogger(AcrProtocolMapper.class); private static final Logger logger = Logger.getLogger(AcrProtocolMapper.class);
@ -87,7 +87,7 @@ public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OID
token.setAcr(acr); token.setAcr(acr);
} }
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken) { public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID); mapper.setProtocolMapper(PROVIDER_ID);
@ -95,6 +95,7 @@ public class AcrProtocolMapper extends AbstractOIDCProtocolMapper implements OID
Map<String, String> config = new HashMap<>(); Map<String, String> config = new HashMap<>();
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }

View file

@ -35,7 +35,7 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
@ -69,10 +69,10 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
public static final String PROVIDER_ID = "oidc-address-mapper"; public static final String PROVIDER_ID = "oidc-address-mapper";
public static ProtocolMapperModel createAddressMapper() { public static ProtocolMapperModel createAddressMapper() {
return createAddressMapper(true, true, true); return createAddressMapper(true, true, true, true);
} }
public static ProtocolMapperModel createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo) { public static ProtocolMapperModel createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo, boolean introspectionEndpoint) {
Map<String, String> config; Map<String, String> config;
ProtocolMapperModel address = new ProtocolMapperModel(); ProtocolMapperModel address = new ProtocolMapperModel();
address.setName("address"); address.setName("address");
@ -82,6 +82,7 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, Boolean.toString(accessToken)); config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, Boolean.toString(accessToken));
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, Boolean.toString(idToken)); config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, Boolean.toString(idToken));
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.toString(userInfo)); config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.toString(userInfo));
config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, Boolean.toString(introspectionEndpoint));
config.put(getModelPropertyName(STREET), STREET); config.put(getModelPropertyName(STREET), STREET);
config.put(getModelPropertyName(AddressClaimSet.LOCALITY), AddressClaimSet.LOCALITY); config.put(getModelPropertyName(AddressClaimSet.LOCALITY), AddressClaimSet.LOCALITY);

View file

@ -19,7 +19,9 @@ package org.keycloak.protocol.oidc.mappers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
@ -32,18 +34,24 @@ import org.keycloak.protocol.oidc.utils.WebOriginsUtils;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION;
/** /**
* Protocol mapper to add allowed web origins to the access token to the 'allowed-origins' claim * Protocol mapper to add allowed web origins to the access token to the 'allowed-origins' claim
* *
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class AllowedWebOriginsProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { public class AllowedWebOriginsProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
public static final String PROVIDER_ID = "oidc-allowed-origins-mapper"; public static final String PROVIDER_ID = "oidc-allowed-origins-mapper";
static {
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AllowedWebOriginsProtocolMapper.class);
}
public List<ProviderConfigProperty> getConfigProperties() { public List<ProviderConfigProperty> getConfigProperties() {
return configProperties; return configProperties;
@ -72,23 +80,72 @@ public class AllowedWebOriginsProtocolMapper extends AbstractOIDCProtocolMapper
@Override @Override
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) { UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
if (!includeInAccessToken(mappingModel)){
return token;
}
setWebOrigin(token, session, clientSessionCtx);
return token;
}
private boolean includeInAccessToken(ProtocolMapperModel mappingModel) {
String includeInAccessToken = mappingModel.getConfig().get(INCLUDE_IN_ACCESS_TOKEN);
// Backwards compatibility
if (includeInAccessToken == null) {
return true;
}
return "true".equals(includeInAccessToken);
}
@Override
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
if (!includeInIntrospection(mappingModel)) {
return token;
}
setWebOrigin(token, session, clientSessionCtx);
return token;
}
private boolean includeInIntrospection(ProtocolMapperModel mappingModel) {
String includeInIntrospection = mappingModel.getConfig().get(INCLUDE_IN_INTROSPECTION);
// Backwards compatibility
if (includeInIntrospection == null) {
return true;
}
return "true".equals(includeInIntrospection);
}
private void setWebOrigin(AccessToken token, KeycloakSession session, ClientSessionContext clientSessionCtx) {
ClientModel client = clientSessionCtx.getClientSession().getClient(); ClientModel client = clientSessionCtx.getClientSession().getClient();
Set<String> allowedOrigins = client.getWebOrigins(); Set<String> allowedOrigins = client.getWebOrigins();
if (allowedOrigins != null && !allowedOrigins.isEmpty()) { if (allowedOrigins != null && !allowedOrigins.isEmpty()) {
token.setAllowedOrigins(WebOriginsUtils.resolveValidWebOrigins(session, client)); token.setAllowedOrigins(WebOriginsUtils.resolveValidWebOrigins(session, client));
} }
return token;
} }
public static ProtocolMapperModel createClaimMapper(String name, boolean accessToken, boolean introspectionEndpoint) {
public static ProtocolMapperModel createClaimMapper(String name) {
ProtocolMapperModel mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID); mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
mapper.setConfig(Collections.emptyMap()); Map<String, String> config = new HashMap<>();
if (accessToken) {
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
} else {
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
}
if (introspectionEndpoint) {
config.put(INCLUDE_IN_INTROSPECTION, "true");
} else {
config.put(INCLUDE_IN_INTROSPECTION, "false");
}
mapper.setConfig(config);
return mapper; return mapper;
} }

View file

@ -33,7 +33,7 @@ import org.keycloak.representations.IDToken;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper { public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -115,7 +115,7 @@ public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implement
public static ProtocolMapperModel createClaimMapper(String name, public static ProtocolMapperModel createClaimMapper(String name,
String includedClientAudience, String includedClientAudience,
String includedCustomAudience, String includedCustomAudience,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID); mapper.setProtocolMapper(PROVIDER_ID);
@ -131,6 +131,7 @@ public class AudienceProtocolMapper extends AbstractOIDCProtocolMapper implement
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }

View file

@ -19,6 +19,7 @@ package org.keycloak.protocol.oidc.mappers;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -32,16 +33,21 @@ import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.utils.RoleResolveUtil; import org.keycloak.utils.RoleResolveUtil;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION;
/** /**
* Protocol mapper, which adds all client_ids of "allowed" clients to the audience field of the token. Allowed client means the client * Protocol mapper, which adds all client_ids of "allowed" clients to the audience field of the token. Allowed client means the client
* for which user has at least one client role * for which user has at least one client role
* *
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper { public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AudienceResolveProtocolMapper.class);
}
public static final String PROVIDER_ID = "oidc-audience-resolve-mapper"; public static final String PROVIDER_ID = "oidc-audience-resolve-mapper";
@ -79,6 +85,46 @@ public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper im
@Override @Override
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session, public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) { UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
if (!includeInAccessToken(mappingModel)){
return token;
}
setAudience(token, clientSessionCtx, session);
return token;
}
private boolean includeInAccessToken(ProtocolMapperModel mappingModel) {
String includeInAccessToken = mappingModel.getConfig().get(INCLUDE_IN_ACCESS_TOKEN);
// Backwards compatibility
if (includeInAccessToken == null) {
return true;
}
return "true".equals(includeInAccessToken);
}
@Override
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
if (!includeInIntrospection(mappingModel)) {
return token;
}
setAudience(token, clientSessionCtx, session);
return token;
}
private boolean includeInIntrospection(ProtocolMapperModel mappingModel) {
String includeInIntrospection = mappingModel.getConfig().get(INCLUDE_IN_INTROSPECTION);
// Backwards compatibility
if (includeInIntrospection == null) {
return true;
}
return "true".equals(includeInIntrospection);
}
private void setAudience(AccessToken token, ClientSessionContext clientSessionCtx, KeycloakSession session) {
String clientId = clientSessionCtx.getClientSession().getClient().getClientId(); String clientId = clientSessionCtx.getClientSession().getClient().getClientId();
for (Map.Entry<String, AccessToken.Access> entry : RoleResolveUtil.getAllResolvedClientRoles(session, clientSessionCtx).entrySet()) { for (Map.Entry<String, AccessToken.Access> entry : RoleResolveUtil.getAllResolvedClientRoles(session, clientSessionCtx).entrySet()) {
@ -92,16 +138,25 @@ public class AudienceResolveProtocolMapper extends AbstractOIDCProtocolMapper im
token.addAudience(entry.getKey()); token.addAudience(entry.getKey());
} }
} }
return token;
} }
public static ProtocolMapperModel createClaimMapper(String name) { public static ProtocolMapperModel createClaimMapper(String name, boolean accessToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID); mapper.setProtocolMapper(PROVIDER_ID);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
mapper.setConfig(Collections.emptyMap()); Map<String, String> config = new HashMap<>();
if (accessToken) {
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
} else {
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
}
if (introspectionEndpoint) {
config.put(INCLUDE_IN_INTROSPECTION, "true");
} else {
config.put(INCLUDE_IN_INTROSPECTION, "false");
}
mapper.setConfig(config);
return mapper; return mapper;
} }
} }

View file

@ -110,16 +110,16 @@ public class ClaimsParameterTokenMapper extends AbstractOIDCProtocolMapper imple
fullNameMapper.setClaim(token, mappingModel, userSession); fullNameMapper.setClaim(token, mappingModel, userSession);
} else if (i.equals(IDToken.GIVEN_NAME)) { } else if (i.equals(IDToken.GIVEN_NAME)) {
UserAttributeMapper userPropertyMapper = new UserAttributeMapper(); UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested firstName", "firstName", IDToken.GIVEN_NAME, "String", false, true), userSession); userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested firstName", "firstName", IDToken.GIVEN_NAME, "String", false, true, false), userSession);
} else if (i.equals(IDToken.FAMILY_NAME)) { } else if (i.equals(IDToken.FAMILY_NAME)) {
UserAttributeMapper userPropertyMapper = new UserAttributeMapper(); UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested lastName", "lastName", IDToken.FAMILY_NAME, "String", false, true), userSession); userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested lastName", "lastName", IDToken.FAMILY_NAME, "String", false, true, false), userSession);
} else if (i.equals(IDToken.PREFERRED_USERNAME)) { } else if (i.equals(IDToken.PREFERRED_USERNAME)) {
UserAttributeMapper userPropertyMapper = new UserAttributeMapper(); UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested username", "username", IDToken.PREFERRED_USERNAME, "String", false, true), userSession); userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested username", "username", IDToken.PREFERRED_USERNAME, "String", false, true, false), userSession);
} else if (i.equals(IDToken.EMAIL)) { } else if (i.equals(IDToken.EMAIL)) {
UserAttributeMapper userPropertyMapper = new UserAttributeMapper(); UserAttributeMapper userPropertyMapper = new UserAttributeMapper();
userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested email", "email", IDToken.EMAIL, "String", false, true), userSession); userPropertyMapper.setClaim(token, UserAttributeMapper.createClaimMapper("requested email", "email", IDToken.EMAIL, "String", false, true, false), userSession);
} }
}); });
} }

View file

@ -123,7 +123,7 @@ public class ClaimsParameterWithValueIdTokenMapper extends AbstractOIDCProtocolM
} }
HardcodedClaim hardcodedClaimMapper = new HardcodedClaim(); HardcodedClaim hardcodedClaimMapper = new HardcodedClaim();
hardcodedClaimMapper.setClaim(token, HardcodedClaim.create("hard", claimName, claim, "String", false, true), userSession); hardcodedClaimMapper.setClaim(token, HardcodedClaim.create("hard", claimName, claim, "String", false, true, false), userSession);
} }
public static ProtocolMapperModel createMapper(String name, String attributeValue, boolean idToken) { public static ProtocolMapperModel createMapper(String name, String attributeValue, boolean idToken) {

View file

@ -37,7 +37,7 @@ import java.util.Optional;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -83,7 +83,7 @@ public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
} }
} }
public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean userInfo) { public static ProtocolMapperModel create(String name, boolean accessToken, boolean idToken, boolean userInfo, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID); mapper.setProtocolMapper(PROVIDER_ID);
@ -92,6 +92,7 @@ public class FullNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }

View file

@ -39,7 +39,7 @@ import java.util.stream.Collectors;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
@ -105,9 +105,9 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
} }
public static ProtocolMapperModel create(String name, public static ProtocolMapperModel create(String name,
String tokenClaimName, String tokenClaimName,
boolean consentRequired, String consentText, boolean consentRequired, String consentText,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID); mapper.setProtocolMapper(PROVIDER_ID);
@ -116,8 +116,9 @@ public class GroupMembershipMapper extends AbstractOIDCProtocolMapper implements
config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName); config.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, tokenClaimName);
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }

View file

@ -38,7 +38,7 @@ import java.util.Map;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper,
OIDCAccessTokenResponseMapper { OIDCAccessTokenResponseMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
@ -94,7 +94,7 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
@Override @Override
protected void setClaim(AccessTokenResponse accessTokenResponse, ProtocolMapperModel mappingModel, UserSessionModel userSession, protected void setClaim(AccessTokenResponse accessTokenResponse, ProtocolMapperModel mappingModel, UserSessionModel userSession,
KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) { KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
String attributeValue = mappingModel.getConfig().get(CLAIM_VALUE); String attributeValue = mappingModel.getConfig().get(CLAIM_VALUE);
if (attributeValue == null) return; if (attributeValue == null) return;
@ -102,9 +102,9 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
} }
public static ProtocolMapperModel create(String name, public static ProtocolMapperModel create(String name,
String hardcodedName, String hardcodedName,
String hardcodedValue, String claimType, String hardcodedValue, String claimType,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID); mapper.setProtocolMapper(PROVIDER_ID);
@ -115,6 +115,7 @@ public class HardcodedClaim extends AbstractOIDCProtocolMapper implements OIDCAc
config.put(OIDCAttributeMapperHelper.JSON_TYPE, claimType); config.put(OIDCAttributeMapperHelper.JSON_TYPE, claimType);
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }

View file

@ -40,7 +40,7 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper { public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -104,6 +104,14 @@ public class HardcodedRole extends AbstractOIDCProtocolMapper implements OIDCAcc
return token; return token;
} }
@Override
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
// the mapper is always executed and then other role mappers decide if the claims are really set to the token
setClaim(token, mappingModel, userSession, session, clientSessionCtx);
return token;
}
@Override @Override
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session, protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session,
ClientSessionContext clientSessionCtx) { ClientSessionContext clientSessionCtx) {

View file

@ -31,10 +31,17 @@ import org.keycloak.representations.IDToken;
import org.keycloak.services.ServicesLogger; import org.keycloak.services.ServicesLogger;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import java.util.*; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
@ -60,6 +67,10 @@ public class OIDCAttributeMapperHelper {
public static final String INCLUDE_IN_USERINFO_LABEL = "includeInUserInfo.label"; public static final String INCLUDE_IN_USERINFO_LABEL = "includeInUserInfo.label";
public static final String INCLUDE_IN_USERINFO_HELP_TEXT = "includeInUserInfo.tooltip"; public static final String INCLUDE_IN_USERINFO_HELP_TEXT = "includeInUserInfo.tooltip";
public static final String INCLUDE_IN_INTROSPECTION = "introspection.token.claim";
public static final String INCLUDE_IN_INTROSPECTION_LABEL = "includeInIntrospection.label";
public static final String INCLUDE_IN_INTROSPECTION_HELP_TEXT = "includeInIntrospection.tooltip";
private static final Logger logger = Logger.getLogger(OIDCAttributeMapperHelper.class); private static final Logger logger = Logger.getLogger(OIDCAttributeMapperHelper.class);
/** /**
@ -230,7 +241,7 @@ public class OIDCAttributeMapperHelper {
if (attributeValue instanceof String) return Boolean.valueOf((String) attributeValue); if (attributeValue instanceof String) return Boolean.valueOf((String) attributeValue);
return null; return null;
} }
private static JsonNode getJsonNode(Object attributeValue) { private static JsonNode getJsonNode(Object attributeValue) {
if (attributeValue instanceof JsonNode){ if (attributeValue instanceof JsonNode){
return (JsonNode) attributeValue; return (JsonNode) attributeValue;
@ -259,7 +270,7 @@ public class OIDCAttributeMapperHelper {
} }
private static <T> void mapClaim(T token, ProtocolMapperModel mappingModel, Object attributeValue, private static <T> void mapClaim(T token, ProtocolMapperModel mappingModel, Object attributeValue,
Map<String, PropertySetter<T>> setters, Map<String, Object> jsonObject) { Map<String, PropertySetter<T>> setters, Map<String, Object> jsonObject) {
attributeValue = mapAttributeValue(mappingModel, attributeValue); attributeValue = mapAttributeValue(mappingModel, attributeValue);
if (attributeValue == null) { if (attributeValue == null) {
return; return;
@ -317,16 +328,16 @@ public class OIDCAttributeMapperHelper {
public static ProtocolMapperModel createClaimMapper(String name, public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute, String userAttribute,
String tokenClaimName, String claimType, String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, boolean accessToken, boolean idToken, boolean introspectionEndpoint,
String mapperId) { String mapperId) {
return createClaimMapper(name, userAttribute,tokenClaimName, claimType, accessToken, idToken, true, mapperId); return createClaimMapper(name, userAttribute, tokenClaimName, claimType, accessToken, idToken, true, introspectionEndpoint, mapperId);
} }
public static ProtocolMapperModel createClaimMapper(String name, public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute, String userAttribute,
String tokenClaimName, String claimType, String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, boolean userinfo, boolean accessToken, boolean idToken, boolean userinfo, boolean introspectionEndpoint,
String mapperId) { String mapperId) {
ProtocolMapperModel mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(mapperId); mapper.setProtocolMapper(mapperId);
@ -338,6 +349,7 @@ public class OIDCAttributeMapperHelper {
if (accessToken) config.put(INCLUDE_IN_ACCESS_TOKEN, "true"); if (accessToken) config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(INCLUDE_IN_ID_TOKEN, "true"); if (idToken) config.put(INCLUDE_IN_ID_TOKEN, "true");
if (userinfo) config.put(INCLUDE_IN_USERINFO, "true"); if (userinfo) config.put(INCLUDE_IN_USERINFO, "true");
if (introspectionEndpoint) config.put(INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }
@ -369,6 +381,17 @@ public class OIDCAttributeMapperHelper {
return "true".equals(includeInUserInfo); return "true".equals(includeInUserInfo);
} }
public static boolean includeInIntrospection(ProtocolMapperModel mappingModel) {
String includeInIntrospection = mappingModel.getConfig().get(INCLUDE_IN_INTROSPECTION);
// Backwards compatibility
if (includeInIntrospection == null && includeInAccessToken(mappingModel)) {
return true;
}
return "true".equals(includeInIntrospection);
}
public static void addAttributeConfig(List<ProviderConfigProperty> configProperties, Class<? extends ProtocolMapper> protocolMapperClass) { public static void addAttributeConfig(List<ProviderConfigProperty> configProperties, Class<? extends ProtocolMapper> protocolMapperClass) {
addTokenClaimNameConfig(configProperties); addTokenClaimNameConfig(configProperties);
addJsonTypeConfig(configProperties); addJsonTypeConfig(configProperties);
@ -441,5 +464,15 @@ public class OIDCAttributeMapperHelper {
property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_RESPONSE_HELP_TEXT); property.setHelpText(INCLUDE_IN_ACCESS_TOKEN_RESPONSE_HELP_TEXT);
configProperties.add(property); configProperties.add(property);
} }
if (TokenIntrospectionTokenMapper.class.isAssignableFrom(protocolMapperClass)) {
ProviderConfigProperty property = new ProviderConfigProperty();
property.setName(INCLUDE_IN_INTROSPECTION);
property.setLabel(INCLUDE_IN_INTROSPECTION_LABEL);
property.setType(ProviderConfigProperty.BOOLEAN_TYPE);
property.setDefaultValue("true");
property.setHelpText(INCLUDE_IN_INTROSPECTION_HELP_TEXT);
configProperties.add(property);
}
} }
} }

View file

@ -40,7 +40,7 @@ import java.util.Map;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper { public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -111,6 +111,14 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
return token; return token;
} }
@Override
public AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx) {
// the mapper is always executed and then other role mappers decide if the claims are really set to the token
setClaim(token, mappingModel, userSession, session, clientSessionCtx);
return token;
}
@Override @Override
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session, protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession session,
ClientSessionContext clientSessionCtx) { ClientSessionContext clientSessionCtx) {

View file

@ -46,7 +46,7 @@ import java.util.List;
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a> * @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
*/ */
public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper,
OIDCAccessTokenResponseMapper, EnvironmentDependentProviderFactory { OIDCAccessTokenResponseMapper, TokenIntrospectionTokenMapper, EnvironmentDependentProviderFactory {
public static final String PROVIDER_ID = "oidc-script-based-protocol-mapper"; public static final String PROVIDER_ID = "oidc-script-based-protocol-mapper";
@ -197,14 +197,14 @@ public class ScriptBasedOIDCProtocolMapper extends AbstractOIDCProtocolMapper im
public static ProtocolMapperModel create(String name, public static ProtocolMapperModel create(String name,
String userAttribute, String userAttribute,
String tokenClaimName, String claimType, String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, String script, boolean multiValued) { boolean accessToken, boolean idToken, boolean introspectionEndpoint, String script, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute, ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
tokenClaimName, claimType, tokenClaimName, claimType,
accessToken, idToken, accessToken, idToken, introspectionEndpoint,
script); script);
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued)); mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
return mapper; return mapper;
} }
} }

View file

@ -0,0 +1,12 @@
package org.keycloak.protocol.oidc.mappers;
import org.keycloak.models.ClientSessionContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.AccessToken;
public interface TokenIntrospectionTokenMapper {
AccessToken transformIntrospectionToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,
UserSessionModel userSession, ClientSessionContext clientSessionCtx);
}

View file

@ -37,7 +37,7 @@ import java.util.List;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
@ -106,19 +106,19 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O
public static ProtocolMapperModel createClaimMapper(String name, public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute, String userAttribute,
String tokenClaimName, String claimType, String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, boolean multivalued) { boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multivalued) {
return createClaimMapper(name, userAttribute, tokenClaimName, claimType, return createClaimMapper(name, userAttribute, tokenClaimName, claimType,
accessToken, idToken, multivalued, false); accessToken, idToken, introspectionEndpoint, multivalued, false);
} }
public static ProtocolMapperModel createClaimMapper(String name, public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute, String userAttribute,
String tokenClaimName, String claimType, String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, boolean accessToken, boolean idToken, boolean introspectionEndpoint,
boolean multivalued, boolean aggregateAttrs) { boolean multivalued, boolean aggregateAttrs) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute, ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
tokenClaimName, claimType, tokenClaimName, claimType,
accessToken, idToken, accessToken, idToken, introspectionEndpoint,
PROVIDER_ID); PROVIDER_ID);
if (multivalued) { if (multivalued) {
@ -134,8 +134,8 @@ public class UserAttributeMapper extends AbstractOIDCProtocolMapper implements O
public static ProtocolMapperModel createClaimMapper(String name, public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute, String userAttribute,
String tokenClaimName, String claimType, String tokenClaimName, String claimType,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return createClaimMapper(name, userAttribute, tokenClaimName, claimType, return createClaimMapper(name, userAttribute, tokenClaimName, claimType,
accessToken, idToken, false, false); accessToken, idToken, introspectionEndpoint, false, false);
} }
} }

View file

@ -135,18 +135,18 @@ public class UserClientRoleMappingMapper extends AbstractUserRoleMappingMapper {
public static ProtocolMapperModel create(String clientId, String clientRolePrefix, public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
String name, String name,
String tokenClaimName, String tokenClaimName,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, false); return create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, false);
} }
public static ProtocolMapperModel create(String clientId, String clientRolePrefix, public static ProtocolMapperModel create(String clientId, String clientRolePrefix,
String name, String name,
String tokenClaimName, String tokenClaimName,
boolean accessToken, boolean idToken, boolean multiValued) { boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo", ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
tokenClaimName, "String", tokenClaimName, "String",
accessToken, idToken, false, accessToken, idToken, false, introspectionEndpoint,
PROVIDER_ID); PROVIDER_ID);
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued)); mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));

View file

@ -35,7 +35,7 @@ import java.util.List;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static { static {
@ -89,10 +89,10 @@ public class UserPropertyMapper extends AbstractOIDCProtocolMapper implements OI
public static ProtocolMapperModel createClaimMapper(String name, public static ProtocolMapperModel createClaimMapper(String name,
String userAttribute, String userAttribute,
String tokenClaimName, String claimType, String tokenClaimName, String claimType,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute, return OIDCAttributeMapperHelper.createClaimMapper(name, userAttribute,
tokenClaimName, claimType, tokenClaimName, claimType,
accessToken, idToken, accessToken, idToken, introspectionEndpoint,
PROVIDER_ID); PROVIDER_ID);
} }
} }

View file

@ -100,18 +100,18 @@ public class UserRealmRoleMappingMapper extends AbstractUserRoleMappingMapper {
public static ProtocolMapperModel create(String realmRolePrefix, public static ProtocolMapperModel create(String realmRolePrefix,
String name, String name,
String tokenClaimName, boolean accessToken, boolean idToken) { String tokenClaimName, boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, false); return create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, false);
} }
public static ProtocolMapperModel create(String realmRolePrefix, public static ProtocolMapperModel create(String realmRolePrefix,
String name, String name,
String tokenClaimName, boolean accessToken, boolean idToken, boolean multiValued) { String tokenClaimName, boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo", ProtocolMapperModel mapper = OIDCAttributeMapperHelper.createClaimMapper(name, "foo",
tokenClaimName, "String", tokenClaimName, "String",
accessToken, idToken, false, accessToken, idToken, false, introspectionEndpoint,
PROVIDER_ID); PROVIDER_ID);
mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued)); mapper.getConfig().put(ProtocolMapperUtils.MULTIVALUED, String.valueOf(multiValued));
mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX, realmRolePrefix); mapper.getConfig().put(ProtocolMapperUtils.USER_MODEL_REALM_ROLE_MAPPING_ROLE_PREFIX, realmRolePrefix);

View file

@ -38,7 +38,7 @@ import java.util.Map;
* *
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/ */
public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, OIDCAccessTokenResponseMapper, UserInfoTokenMapper { public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, OIDCAccessTokenResponseMapper, UserInfoTokenMapper, TokenIntrospectionTokenMapper {
private static final List<ProviderConfigProperty> configProperties = new ArrayList<>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<>();
@ -90,7 +90,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
@Override @Override
protected void setClaim(AccessTokenResponse accessTokenResponse, ProtocolMapperModel mappingModel, UserSessionModel userSession, protected void setClaim(AccessTokenResponse accessTokenResponse, ProtocolMapperModel mappingModel, UserSessionModel userSession,
KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) { KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) {
String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE); String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE);
String noteValue = userSession.getNote(noteName); String noteValue = userSession.getNote(noteName);
@ -101,14 +101,14 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
public static ProtocolMapperModel createClaimMapper(String name, public static ProtocolMapperModel createClaimMapper(String name,
String userSessionNote, String userSessionNote,
String tokenClaimName, String jsonType, String tokenClaimName, String jsonType,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return createClaimMapper(name, userSessionNote, tokenClaimName, jsonType, accessToken, idToken, false); return createClaimMapper(name, userSessionNote, tokenClaimName, jsonType, accessToken, idToken, false, introspectionEndpoint);
} }
public static ProtocolMapperModel createClaimMapper(String name, public static ProtocolMapperModel createClaimMapper(String name,
String userSessionNote, String userSessionNote,
String tokenClaimName, String jsonType, String tokenClaimName, String jsonType,
boolean accessToken, boolean idToken, boolean userInfo) { boolean accessToken, boolean idToken, boolean userInfo, boolean introspectionEndpoint) {
ProtocolMapperModel mapper = new ProtocolMapperModel(); ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name); mapper.setName(name);
mapper.setProtocolMapper(PROVIDER_ID); mapper.setProtocolMapper(PROVIDER_ID);
@ -120,6 +120,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true"); if (accessToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true"); if (idToken) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true"); if (userInfo) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, "true");
if (introspectionEndpoint) config.put(OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION, "true");
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }
@ -135,7 +136,7 @@ public class UserSessionNoteMapper extends AbstractOIDCProtocolMapper implements
userSessionNoteDescriptor.toString(), userSessionNoteDescriptor.toString(),
userSessionNoteDescriptor.getTokenClaim(), userSessionNoteDescriptor.getTokenClaim(),
"String", "String",
true, true true, true, true
); );
} }

View file

@ -170,7 +170,7 @@ public class ClientManager {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER, ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ID_PROTOCOL_MAPPER,
ServiceAccountConstants.CLIENT_ID, ServiceAccountConstants.CLIENT_ID,
ServiceAccountConstants.CLIENT_ID, "String", ServiceAccountConstants.CLIENT_ID, "String",
true, true); true, true, true);
client.addProtocolMapper(protocolMapper); client.addProtocolMapper(protocolMapper);
} }
@ -180,7 +180,7 @@ public class ClientManager {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_HOST_PROTOCOL_MAPPER, ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_HOST_PROTOCOL_MAPPER,
ServiceAccountConstants.CLIENT_HOST, ServiceAccountConstants.CLIENT_HOST,
ServiceAccountConstants.CLIENT_HOST, "String", ServiceAccountConstants.CLIENT_HOST, "String",
true, true); true, true, true);
client.addProtocolMapper(protocolMapper); client.addProtocolMapper(protocolMapper);
} }
@ -189,7 +189,7 @@ public class ClientManager {
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ADDRESS_PROTOCOL_MAPPER, ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(ServiceAccountConstants.CLIENT_ADDRESS_PROTOCOL_MAPPER,
ServiceAccountConstants.CLIENT_ADDRESS, ServiceAccountConstants.CLIENT_ADDRESS,
ServiceAccountConstants.CLIENT_ADDRESS, "String", ServiceAccountConstants.CLIENT_ADDRESS, "String",
true, true); true, true, true);
client.addProtocolMapper(protocolMapper); client.addProtocolMapper(protocolMapper);
} }
} }

View file

@ -0,0 +1,97 @@
package org.keycloak.services.util;
import org.keycloak.common.ClientConnection;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.AuthenticatedClientSessionModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.Urls;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.UserSessionCrossDCManager;
import org.keycloak.services.managers.UserSessionManager;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel;
import org.keycloak.utils.OAuth2Error;
public class UserSessionUtil {
public static UserSessionModel findValidSession(KeycloakSession session, RealmModel realm, AccessToken token, EventBuilder event, ClientModel client) {
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
if (token.getSessionState() == null) {
return createTransientSessionForClient(session, realm, token, client);
}
UserSessionModel userSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), false, client.getId());
UserSessionModel offlineUserSession = null;
if (AuthenticationManager.isSessionValid(realm, userSession)) {
checkTokenIssuedAt(realm, token, userSession, event, client);
event.session(userSession);
return userSession;
} else {
offlineUserSession = new UserSessionCrossDCManager(session).getUserSessionWithClient(realm, token.getSessionState(), true, client.getId());
if (AuthenticationManager.isOfflineSessionValid(realm, offlineUserSession)) {
checkTokenIssuedAt(realm, token, offlineUserSession, event, client);
event.session(offlineUserSession);
return offlineUserSession;
}
}
if (userSession == null && offlineUserSession == null) {
event.error(Errors.USER_SESSION_NOT_FOUND);
throw error.invalidToken("User session not found or doesn't have client attached on it");
}
if (userSession != null) {
event.session(userSession);
} else {
event.session(offlineUserSession);
}
event.error(Errors.SESSION_EXPIRED);
throw error.invalidToken("Session expired");
}
private static UserSessionModel createTransientSessionForClient(KeycloakSession session, RealmModel realm, AccessToken token, ClientModel client) {
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
// create a transient session
UserModel user = TokenManager.lookupUserFromStatelessToken(session, realm, token);
if (user == null) {
throw error.invalidToken("User not found");
}
ClientConnection clientConnection = session.getContext().getConnection();
UserSessionModel userSession = new UserSessionManager(session).createUserSession(KeycloakModelUtils.generateId(), realm, user, user.getUsername(), clientConnection.getRemoteAddr(),
ServiceAccountConstants.CLIENT_AUTH, false, null, null, UserSessionModel.SessionPersistenceState.TRANSIENT);
// attach an auth session for the client
RootAuthenticationSessionModel rootAuthSession = session.authenticationSessions().createRootAuthenticationSession(realm);
AuthenticationSessionModel authSession = rootAuthSession.createAuthenticationSession(client);
authSession.setAuthenticatedUser(userSession.getUser());
authSession.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
authSession.setClientNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(session.getContext().getUri().getBaseUri(), realm.getName()));
AuthenticationManager.setClientScopesInSession(authSession);
TokenManager.attachAuthenticationSession(session, userSession, authSession);
return userSession;
}
private static void checkTokenIssuedAt(RealmModel realm, AccessToken token, UserSessionModel userSession, EventBuilder event, ClientModel client) {
OAuth2Error error = new OAuth2Error().json(false).realm(realm);
if (token.isIssuedBeforeSessionStart(userSession.getStarted())) {
event.error(Errors.INVALID_TOKEN);
throw error.invalidToken("Stale token");
}
AuthenticatedClientSessionModel clientSession = userSession.getAuthenticatedClientSessionByClient(client.getId());
if (token.isIssuedBeforeSessionStart(clientSession.getStarted())) {
event.error(Errors.INVALID_TOKEN);
throw error.invalidToken("Stale token");
}
}
}

View file

@ -817,7 +817,7 @@ public class TestingResourceProvider implements RealmResourceProvider {
clientScopeModel.setIncludeInTokenScope(true); clientScopeModel.setIncludeInTokenScope(true);
// Add audience protocol mapper // Add audience protocol mapper
ProtocolMapperModel audienceMapper = AudienceProtocolMapper.createClaimMapper("Audience for " + clientId, clientId, null,true, false); ProtocolMapperModel audienceMapper = AudienceProtocolMapper.createClaimMapper("Audience for " + clientId, clientId, null,true, false, true );
clientScopeModel.addProtocolMapper(audienceMapper); clientScopeModel.addProtocolMapper(audienceMapper);
return clientScopeModel.getId(); return clientScopeModel.getId();

View file

@ -21,12 +21,12 @@ public class KcOidcBrokerSubMatchIntrospectionTest extends AbstractBrokerTest {
@Override @Override
public List<ClientRepresentation> createConsumerClients() { public List<ClientRepresentation> createConsumerClients() {
List<ClientRepresentation> clients = new ArrayList<>(super.createConsumerClients()); List<ClientRepresentation> clients = new ArrayList<>(super.createConsumerClients());
clients.add(ClientBuilder.create().clientId("consumer-client") clients.add(ClientBuilder.create().clientId("consumer-client")
.publicClient() .publicClient()
.redirectUris(getConsumerRoot() + "/auth/realms/master/app/auth/*") .redirectUris(getConsumerRoot() + "/auth/realms/master/app/auth/*")
.publicClient().build()); .publicClient().build());
return clients; return clients;
} }
@ -36,14 +36,14 @@ public class KcOidcBrokerSubMatchIntrospectionTest extends AbstractBrokerTest {
List<ProtocolMapperRepresentation> mappers = new ArrayList<>(); List<ProtocolMapperRepresentation> mappers = new ArrayList<>();
ProtocolMapperRepresentation hardcodedClaim = createHardcodedClaim("sub-override", "sub", "overriden", ProtocolMapperRepresentation hardcodedClaim = createHardcodedClaim("sub-override", "sub", "overriden",
"String", false, false); "String", false, false, false);
hardcodedClaim.getConfig().put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.TRUE.toString()); hardcodedClaim.getConfig().put(OIDCAttributeMapperHelper.INCLUDE_IN_USERINFO, Boolean.TRUE.toString());
mappers.add(hardcodedClaim); mappers.add(hardcodedClaim);
clients.get(0).setProtocolMappers(mappers); clients.get(0).setProtocolMappers(mappers);
return clients; return clients;
} }
}; };

View file

@ -100,7 +100,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
IdentityProviderMapperRepresentation attrMapper2 = new IdentityProviderMapperRepresentation(); IdentityProviderMapperRepresentation attrMapper2 = new IdentityProviderMapperRepresentation();
attrMapper2.setName("user-role-mapper"); attrMapper2.setName("user-role-mapper");
attrMapper2.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID); attrMapper2.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
attrMapper2.setConfig(ImmutableMap.<String,String>builder() attrMapper2.setConfig(ImmutableMap.<String, String>builder()
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString()) .put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
.put("external.role", ROLE_USER) .put("external.role", ROLE_USER)
.put("role", ROLE_USER) .put("role", ROLE_USER)
@ -114,7 +114,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
IdentityProviderMapperRepresentation friendlyManagerMapper = new IdentityProviderMapperRepresentation(); IdentityProviderMapperRepresentation friendlyManagerMapper = new IdentityProviderMapperRepresentation();
friendlyManagerMapper.setName("friendly-manager-role-mapper"); friendlyManagerMapper.setName("friendly-manager-role-mapper");
friendlyManagerMapper.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID); friendlyManagerMapper.setIdentityProviderMapper(ExternalKeycloakRoleToRoleMapper.PROVIDER_ID);
friendlyManagerMapper.setConfig(ImmutableMap.<String,String>builder() friendlyManagerMapper.setConfig(ImmutableMap.<String, String>builder()
.put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString()) .put(IdentityProviderMapperModel.SYNC_MODE, syncMode.toString())
.put("external.role", ROLE_FRIENDLY_MANAGER) .put("external.role", ROLE_FRIENDLY_MANAGER)
.put("role", ROLE_FRIENDLY_MANAGER) .put("role", ROLE_FRIENDLY_MANAGER)
@ -197,7 +197,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
logInWithBroker(bc); logInWithBroker(bc);
waitForPage(driver, loginIsDenied? "We are sorry..." : "update account information", false); waitForPage(driver, loginIsDenied ? "We are sorry..." : "update account information", false);
if (loginIsDenied) { if (loginIsDenied) {
return; return;
} }
@ -243,7 +243,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
ClientRepresentation brokerApp = clients.findByClientId("brokerapp").get(0); ClientRepresentation brokerApp = clients.findByClientId("brokerapp").get(0);
IdentityProviderResource identityProviderResource = getIdentityProviderResource(); IdentityProviderResource identityProviderResource = getIdentityProviderResource();
clients.get(brokerApp.getId()).getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true)).close(); clients.get(brokerApp.getId()).getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true, true)).close();
IdentityProviderMapperRepresentation hardCodedSessionNoteMapper = new IdentityProviderMapperRepresentation(); IdentityProviderMapperRepresentation hardCodedSessionNoteMapper = new IdentityProviderMapperRepresentation();
@ -445,7 +445,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
RealmResource realm = adminClient.realm(bc.providerRealmName()); RealmResource realm = adminClient.realm(bc.providerRealmName());
ClientRepresentation rep = realm.clients().findByClientId(BrokerTestConstants.CLIENT_ID).get(0); ClientRepresentation rep = realm.clients().findByClientId(BrokerTestConstants.CLIENT_ID).get(0);
ClientResource clientResource = realm.clients().get(rep.getId()); ClientResource clientResource = realm.clients().get(rep.getId());
ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "azp", "invalid-azp", ProviderConfigProperty.STRING_TYPE, true, true); ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "azp", "invalid-azp", ProviderConfigProperty.STRING_TYPE, true, true, true);
clientResource.getProtocolMappers().createMapper(hardCodedAzp); clientResource.getProtocolMappers().createMapper(hardCodedAzp);
log.debug("Logging in"); log.debug("Logging in");
@ -469,7 +469,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
RealmResource realm = adminClient.realm(bc.providerRealmName()); RealmResource realm = adminClient.realm(bc.providerRealmName());
ClientRepresentation rep = realm.clients().findByClientId(BrokerTestConstants.CLIENT_ID).get(0); ClientRepresentation rep = realm.clients().findByClientId(BrokerTestConstants.CLIENT_ID).get(0);
ClientResource clientResource = realm.clients().get(rep.getId()); ClientResource clientResource = realm.clients().get(rep.getId());
ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "aud", "invalid-aud", ProviderConfigProperty.LIST_TYPE, true, true); ProtocolMapperRepresentation hardCodedAzp = createHardcodedClaim("hard", "aud", "invalid-aud", ProviderConfigProperty.LIST_TYPE, true, true, true);
clientResource.getProtocolMappers().createMapper(hardCodedAzp); clientResource.getProtocolMappers().createMapper(hardCodedAzp);
log.debug("Logging in"); log.debug("Logging in");
@ -564,7 +564,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
loginFetchingUserFromUserEndpoint(true); loginFetchingUserFromUserEndpoint(true);
Assert.assertEquals("The ID token issued by the identity provider does not match the configured essential claim. Please contact your administrator.", Assert.assertEquals("The ID token issued by the identity provider does not match the configured essential claim. Please contact your administrator.",
loginPage.getInstruction()); loginPage.getInstruction());
List<UserRepresentation> users = realmsResouce().realm(bc.consumerRealmName()).users().search(bc.getUserLogin()); List<UserRepresentation> users = realmsResouce().realm(bc.consumerRealmName()).users().search(bc.getUserLogin());
@ -572,8 +572,8 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
} }
protected void postInitializeUser(UserRepresentation user) { protected void postInitializeUser(UserRepresentation user) {
user.setAttributes(ImmutableMap.<String, List<String>> builder() user.setAttributes(ImmutableMap.<String, List<String>>builder()
.put(USER_ATTRIBUTE_NAME, ImmutableList.<String> builder().add(USER_ATTRIBUTE_VALUE).build()) .put(USER_ATTRIBUTE_NAME, ImmutableList.<String>builder().add(USER_ATTRIBUTE_VALUE).build())
.build()); .build());
} }
@ -585,8 +585,8 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
assertThat(claimFilterValue, Matchers.notNullValue()); assertThat(claimFilterValue, Matchers.notNullValue());
if (idProvider.getConfig().getOrDefault(IdentityProviderModel.FILTERED_BY_CLAIMS, "false").equals(Boolean.toString(filteredByClaim)) && if (idProvider.getConfig().getOrDefault(IdentityProviderModel.FILTERED_BY_CLAIMS, "false").equals(Boolean.toString(filteredByClaim)) &&
idProvider.getConfig().getOrDefault(IdentityProviderModel.CLAIM_FILTER_NAME, "").equals(claimFilterName) && idProvider.getConfig().getOrDefault(IdentityProviderModel.CLAIM_FILTER_NAME, "").equals(claimFilterName) &&
idProvider.getConfig().getOrDefault(IdentityProviderModel.CLAIM_FILTER_VALUE, "").equals(claimFilterValue) idProvider.getConfig().getOrDefault(IdentityProviderModel.CLAIM_FILTER_VALUE, "").equals(claimFilterValue)
) { ) {
return; return;
} }
@ -722,7 +722,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
} }
private void updateIdPSyncMode(IdentityProviderRepresentation idProvider, IdentityProviderResource idProviderResource, private void updateIdPSyncMode(IdentityProviderRepresentation idProvider, IdentityProviderResource idProviderResource,
IdentityProviderSyncMode syncMode, boolean trustEmail) { IdentityProviderSyncMode syncMode, boolean trustEmail) {
assertThat(idProvider, Matchers.notNullValue()); assertThat(idProvider, Matchers.notNullValue());
assertThat(idProviderResource, Matchers.notNullValue()); assertThat(idProviderResource, Matchers.notNullValue());
assertThat(syncMode, Matchers.notNullValue()); assertThat(syncMode, Matchers.notNullValue());
@ -755,6 +755,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
} }
private static final CustomKcOidcBrokerConfiguration BROKER_CONFIG_INSTANCE = new CustomKcOidcBrokerConfiguration(); private static final CustomKcOidcBrokerConfiguration BROKER_CONFIG_INSTANCE = new CustomKcOidcBrokerConfiguration();
static class CustomKcOidcBrokerConfiguration extends KcOidcBrokerConfiguration { static class CustomKcOidcBrokerConfiguration extends KcOidcBrokerConfiguration {
@Override @Override
@ -766,7 +767,7 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
userAttrMapper.setName(USER_ATTRIBUTE_NAME); userAttrMapper.setName(USER_ATTRIBUTE_NAME);
userAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); userAttrMapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
userAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID); userAttrMapper.setProtocolMapper(UserAttributeMapper.PROVIDER_ID);
Map<String, String> userAttrMapperConfig = userAttrMapper.getConfig(); Map<String, String> userAttrMapperConfig = userAttrMapper.getConfig();
userAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, USER_ATTRIBUTE_NAME); userAttrMapperConfig.put(ProtocolMapperUtils.USER_ATTRIBUTE, USER_ATTRIBUTE_NAME);
userAttrMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, USER_ATTRIBUTE_NAME); userAttrMapperConfig.put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, USER_ATTRIBUTE_NAME);
@ -781,6 +782,6 @@ public final class KcOidcBrokerTest extends AbstractAdvancedBrokerTest {
client.setProtocolMappers(mappers); client.setProtocolMappers(mappers);
return clients; return clients;
} }
} }
} }

View file

@ -50,18 +50,16 @@ import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation; import org.keycloak.representations.idm.authorization.ClientPolicyRepresentation;
import org.keycloak.representations.idm.authorization.DecisionStrategy;
import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement; import org.keycloak.services.resources.admin.permissions.AdminPermissionManagement;
import org.keycloak.services.resources.admin.permissions.AdminPermissions; import org.keycloak.services.resources.admin.permissions.AdminPermissions;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.arquillian.annotation.EnableFeatures; import org.keycloak.testsuite.arquillian.annotation.EnableFeatures;
import org.keycloak.testsuite.util.AdminClientUtil; import org.keycloak.testsuite.util.AdminClientUtil;
import org.keycloak.testsuite.util.ClientBuilder;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.util.BasicAuthHelper; import org.keycloak.util.BasicAuthHelper;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@EnableFeatures({ @EnableFeature(Profile.Feature.TOKEN_EXCHANGE), @EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ) }) @EnableFeatures({@EnableFeature(Profile.Feature.TOKEN_EXCHANGE), @EnableFeature(Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ)})
public final class KcOidcBrokerTokenExchangeTest extends AbstractInitializedBaseBrokerTest { public final class KcOidcBrokerTokenExchangeTest extends AbstractInitializedBaseBrokerTest {
@Override @Override
@ -77,7 +75,7 @@ public final class KcOidcBrokerTokenExchangeTest extends AbstractInitializedBase
brokerApp.setDirectAccessGrantsEnabled(true); brokerApp.setDirectAccessGrantsEnabled(true);
ClientResource brokerAppResource = providerRealm.clients().get(brokerApp.getId()); ClientResource brokerAppResource = providerRealm.clients().get(brokerApp.getId());
brokerAppResource.update(brokerApp); brokerAppResource.update(brokerApp);
brokerAppResource.getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true)).close(); brokerAppResource.getProtocolMappers().createMapper(createHardcodedClaim("hard-coded", "hard-coded", "hard-coded", "String", true, true, true)).close();
IdentityProviderMapperRepresentation hardCodedSessionNoteMapper = new IdentityProviderMapperRepresentation(); IdentityProviderMapperRepresentation hardCodedSessionNoteMapper = new IdentityProviderMapperRepresentation();
hardCodedSessionNoteMapper.setName("hard-coded"); hardCodedSessionNoteMapper.setName("hard-coded");

View file

@ -47,6 +47,7 @@ import org.keycloak.testsuite.util.AccountHelper;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.TestAppHelper; import org.keycloak.testsuite.util.TestAppHelper;
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId; import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
/** /**
@ -180,7 +181,7 @@ public abstract class AbstractKerberosSingleRealmTest extends AbstractKerberosTe
ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME, ProtocolMapperModel protocolMapper = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, KerberosConstants.GSS_DELEGATION_CREDENTIAL,
KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String", KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
true, false, true); true, false, true, true);
ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper); ProtocolMapperRepresentation protocolMapperRep = ModelToRepresentation.toRepresentation(protocolMapper);
ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app"); ClientResource clientResource = findClientByClientId(testRealmResource(), "kerberos-app");
Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep); Response response = clientResource.getProtocolMappers().createMapper(protocolMapperRep);

View file

@ -105,8 +105,8 @@ public class LDAPMultipleAttributesTest extends AbstractLDAPTest {
ldapClient.addRedirectUri("/ldap-portal"); ldapClient.addRedirectUri("/ldap-portal");
ldapClient.addRedirectUri("/ldap-portal/*"); ldapClient.addRedirectUri("/ldap-portal/*");
ldapClient.setManagementUrl("/ldap-portal"); ldapClient.setManagementUrl("/ldap-portal");
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("postalCode", "postal_code", "postal_code", "String", true, true, true)); ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("postalCode", "postal_code", "postal_code", "String", true, true, true, true));
ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("street", "street", "street", "String", true, true, false)); ldapClient.addProtocolMapper(UserAttributeMapper.createClaimMapper("street", "street", "street", "String", true, true, true, false));
ldapClient.addScopeMapping(appRealm.getRole("user")); ldapClient.addScopeMapping(appRealm.getRole("user"));
ldapClient.setSecret("password"); ldapClient.setSecret("password");
}); });
@ -237,7 +237,6 @@ public class LDAPMultipleAttributesTest extends AbstractLDAPTest {
} }
} }

View file

@ -819,7 +819,7 @@ public class AccessTokenTest extends AbstractKeycloakTest {
String clientScopeId = ApiUtil.getCreatedId(response); String clientScopeId = ApiUtil.getCreatedId(response);
response.close(); response.close();
ClientScopeResource clientScopeResource = adminClient.proxy(ClientScopeResource.class, scopeUri); ClientScopeResource clientScopeResource = adminClient.proxy(ClientScopeResource.class, scopeUri);
ProtocolMapperModel hard = HardcodedClaim.create("hard", "hard", "coded", "String", true, true); ProtocolMapperModel hard = HardcodedClaim.create("hard", "hard", "coded", "String", true, true, true);
ProtocolMapperRepresentation mapper = ModelToRepresentation.toRepresentation(hard); ProtocolMapperRepresentation mapper = ModelToRepresentation.toRepresentation(hard);
response = clientScopeResource.getProtocolMappers().createMapper(mapper); response = clientScopeResource.getProtocolMappers().createMapper(mapper);
assertEquals(201, response.getStatus()); assertEquals(201, response.getStatus());

View file

@ -175,7 +175,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
directPublic.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL); directPublic.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
directPublic.setFullScopeAllowed(false); directPublic.setFullScopeAllowed(false);
directPublic.addRedirectUri("*"); directPublic.addRedirectUri("*");
directPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false)); directPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false, true));
ClientModel directUntrustedPublic = realm.addClient("direct-public-untrusted"); ClientModel directUntrustedPublic = realm.addClient("direct-public-untrusted");
directUntrustedPublic.setClientId("direct-public-untrusted"); directUntrustedPublic.setClientId("direct-public-untrusted");
@ -186,7 +186,7 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
directUntrustedPublic.setFullScopeAllowed(false); directUntrustedPublic.setFullScopeAllowed(false);
directUntrustedPublic.addRedirectUri("*"); directUntrustedPublic.addRedirectUri("*");
directUntrustedPublic.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+"); directUntrustedPublic.setAttribute(OIDCConfigAttributes.POST_LOGOUT_REDIRECT_URIS, "+");
directUntrustedPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false)); directUntrustedPublic.addProtocolMapper(AudienceProtocolMapper.createClaimMapper("client-exchanger-audience", clientExchanger.getClientId(), null, true, false, true));
ClientModel directNoSecret = realm.addClient("direct-no-secret"); ClientModel directNoSecret = realm.addClient("direct-no-secret");
directNoSecret.setClientId("direct-no-secret"); directNoSecret.setClientId("direct-no-secret");
@ -538,16 +538,16 @@ public class ClientTokenExchangeTest extends AbstractKeycloakTest {
} }
try (Response response = exchangeUrl.request() try (Response response = exchangeUrl.request()
.header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader("client-exchanger", "secret")) .header(HttpHeaders.AUTHORIZATION, BasicAuthHelper.createHeader("client-exchanger", "secret"))
.post(Entity.form( .post(Entity.form(
new Form() new Form()
.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE) .param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.TOKEN_EXCHANGE_GRANT_TYPE)
.param(OAuth2Constants.SUBJECT_TOKEN, accessToken) .param(OAuth2Constants.SUBJECT_TOKEN, accessToken)
.param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE) .param(OAuth2Constants.SUBJECT_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE)
.param(OAuth2Constants.REQUESTED_SUBJECT, "impersonated-user") .param(OAuth2Constants.REQUESTED_SUBJECT, "impersonated-user")
.param(OAuth2Constants.AUDIENCE, "target") .param(OAuth2Constants.AUDIENCE, "target")
))) { ))) {
org.junit.Assert.assertEquals(200, response.getStatus()); org.junit.Assert.assertEquals(200, response.getStatus());
AccessTokenResponse accessTokenResponse = response.readEntity(AccessTokenResponse.class); AccessTokenResponse accessTokenResponse = response.readEntity(AccessTokenResponse.class);
String exchangedTokenString = accessTokenResponse.getToken(); String exchangedTokenString = accessTokenResponse.getToken();

View file

@ -401,7 +401,7 @@ public class OAuthGrantTest extends AbstractKeycloakTest {
String fooScopeId = ApiUtil.getCreatedId(response); String fooScopeId = ApiUtil.getCreatedId(response);
response.close(); response.close();
ProtocolMapperRepresentation protocolMapper = ProtocolMapperUtil.createAddressMapper(true, true, true); ProtocolMapperRepresentation protocolMapper = ProtocolMapperUtil.createAddressMapper(true, true, true, true);
response = appRealm.clientScopes().get(fooScopeId).getProtocolMappers().createMapper(protocolMapper); response = appRealm.clientScopes().get(fooScopeId).getProtocolMappers().createMapper(protocolMapper);
response.close(); response.close();

View file

@ -153,11 +153,11 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
reconnectAdminClient(); reconnectAdminClient();
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app"); ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, "script-scripts/test-script-mapper1.js", false)).close(); app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, true,"script-scripts/test-script-mapper1.js", false)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, "script-scripts/test-script-mapper2.js", true)).close(); app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, true, "script-scripts/test-script-mapper2.js", true)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3","computed-json-via-script", "computed-json-via-script", "JSON", true, true, "script-scripts/test-script-mapper3.js", false)).close(); app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3","computed-json-via-script", "computed-json-via-script", "JSON", true, true, true, "script-scripts/test-script-mapper3.js", false)).close();
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, "script-scripts/test-bad-script-mapper3.js", false)); Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, true, "script-scripts/test-bad-script-mapper3.js", false));
assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR)); assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR));
response.close(); response.close();
} }
@ -199,31 +199,31 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app"); ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
ProtocolMapperRepresentation mapper = createAddressMapper(true, true, true); ProtocolMapperRepresentation mapper = createAddressMapper(true, true, true, true);
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.REGION), "region_some"); mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.REGION), "region_some");
mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.COUNTRY), "country_some"); mapper.getConfig().put(AddressMapper.getModelPropertyName(AddressClaimSet.COUNTRY), "country_some");
mapper.getConfig().remove(AddressMapper.getModelPropertyName(AddressClaimSet.POSTAL_CODE)); // Even if we remove protocolMapper config property, it should still default to postal_code mapper.getConfig().remove(AddressMapper.getModelPropertyName(AddressClaimSet.POSTAL_CODE)); // Even if we remove protocolMapper config property, it should still default to postal_code
app.getProtocolMappers().createMapper(mapper).close(); app.getProtocolMappers().createMapper(mapper).close();
ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", true, true); ProtocolMapperRepresentation hard = createHardcodedClaim("hard", "hard", "coded", "String", true, true, true);
app.getProtocolMappers().createMapper(hard).close(); app.getProtocolMappers().createMapper(hard).close();
app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", true, true)).close(); app.getProtocolMappers().createMapper(createHardcodedClaim("hard-nested", "nested.hard", "coded-nested", "String", true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, true, true)).close(); app.getProtocolMappers().createMapper(createClaimMapper("custom phone", "phone", "home_phone", "String", true, true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, true, true)).close(); app.getProtocolMappers().createMapper(createClaimMapper("nested phone", "phone", "home.phone", "String", true, true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("dotted phone", "phone", "home\\.phone", "String", true, true, true)).close(); app.getProtocolMappers().createMapper(createClaimMapper("dotted phone", "phone", "home\\.phone", "String", true, true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, true, true)).close(); app.getProtocolMappers().createMapper(createClaimMapper("departments", "departments", "department", "String", true, true, true, true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("firstDepartment", "departments", "firstDepartment", "String", true, true, false)).close(); app.getProtocolMappers().createMapper(createClaimMapper("firstDepartment", "departments", "firstDepartment", "String", true, true, true,false)).close();
app.getProtocolMappers().createMapper(createHardcodedRole("hard-realm", "hardcoded")).close(); app.getProtocolMappers().createMapper(createHardcodedRole("hard-realm", "hardcoded")).close();
app.getProtocolMappers().createMapper(createHardcodedRole("hard-app", "app.hardcoded")).close(); app.getProtocolMappers().createMapper(createHardcodedRole("hard-app", "app.hardcoded")).close();
app.getProtocolMappers().createMapper(createRoleNameMapper("rename-app-role", "test-app.customer-user", "realm-user")).close(); app.getProtocolMappers().createMapper(createRoleNameMapper("rename-app-role", "test-app.customer-user", "realm-user")).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, "'hello_' + user.username", false)).close(); app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper1","computed-via-script", "computed-via-script", "String", true, true, true, "'hello_' + user.username", false)).close();
app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, "new java.util.ArrayList(['A','B'])", true)).close(); app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper2","multiValued-via-script", "multiValued-via-script", "String", true, true, true, "new java.util.ArrayList(['A','B'])", true)).close();
app.getProtocolMappers().createMapper(createClaimMapper("json-attribute-mapper", "json-attribute", "claim-from-json-attribute", app.getProtocolMappers().createMapper(createClaimMapper("json-attribute-mapper", "json-attribute", "claim-from-json-attribute",
"JSON", true, true, false)).close(); "JSON", true, true, true, false)).close();
app.getProtocolMappers().createMapper(createClaimMapper("json-attribute-mapper-multi", "json-attribute-multi", "claim-from-json-attribute-multi", app.getProtocolMappers().createMapper(createClaimMapper("json-attribute-mapper-multi", "json-attribute-multi", "claim-from-json-attribute-multi",
"JSON", true, true, true)).close(); "JSON", true, true, true, true)).close();
Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, "func_tion foo(){ return 'fail';} foo()", false)); Response response = app.getProtocolMappers().createMapper(createScriptMapper("test-script-mapper3", "syntax-error-script", "syntax-error-script", "String", true, true, true, "func_tion foo(){ return 'fail';} foo()", false));
assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR)); assertThat(response.getStatusInfo().getFamily(), is(Response.Status.Family.CLIENT_ERROR));
response.close(); response.close();
} }
@ -360,10 +360,10 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create a user attr mapping for some claims that exist as properties in the tokens // create a user attr mapping for some claims that exist as properties in the tokens
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app"); ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
app.getProtocolMappers().createMapper(createClaimMapper("userid-as-sub", "userid", "sub", "String", true, true, false)).close(); app.getProtocolMappers().createMapper(createClaimMapper("userid-as-sub", "userid", "sub", "String", true, true, true,false)).close();
app.getProtocolMappers().createMapper(createClaimMapper("useraud", "useraud", "aud", "String", true, true, true)).close(); app.getProtocolMappers().createMapper(createClaimMapper("useraud", "useraud", "aud", "String", true, true, true, true)).close();
app.getProtocolMappers().createMapper(createHardcodedClaim("website-hardcoded", "website", "http://localhost", "String", true, true)).close(); app.getProtocolMappers().createMapper(createHardcodedClaim("website-hardcoded", "website", "http://localhost", "String", true, true, true)).close();
app.getProtocolMappers().createMapper(createHardcodedClaim("iat-hardcoded", "iat", "123", "long", true, false)).close(); app.getProtocolMappers().createMapper(createHardcodedClaim("iat-hardcoded", "iat", "123", "long", true, false, true)).close();
// login // login
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password"); OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password");
@ -432,6 +432,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
"claim-name", "claim-name",
String.class.getSimpleName(), String.class.getSimpleName(),
true, true,
true,
true true
)))) { )))) {
mapperId = getCreatedId(response); mapperId = getCreatedId(response);
@ -481,6 +482,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
"claim-name", "claim-name",
String.class.getSimpleName(), String.class.getSimpleName(),
true, true,
true,
true true
)))) { )))) {
mapperId = getCreatedId(response); mapperId = getCreatedId(response);
@ -519,8 +521,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.update(user); userResource.update(user);
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app"); ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
app.getProtocolMappers().createMapper(createClaimMapper("empty", "empty", "empty", "String", true, true, false)).close(); app.getProtocolMappers().createMapper(createClaimMapper("empty", "empty", "empty", "String", true, true, true,false)).close();
app.getProtocolMappers().createMapper(createClaimMapper("null", "null", "null", "String", true, true, false)).close(); app.getProtocolMappers().createMapper(createClaimMapper("null", "null", "null", "String", true, true, true,false)).close();
} }
{ {
@ -569,8 +571,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
public void testUserRoleToAttributeMappers() throws Exception { public void testUserRoleToAttributeMappers() throws Exception {
// Add mapper for realm roles // Add mapper for realm roles
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true); ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper("test-app", null, "Client roles mapper", "roles-custom.test-app", true, true); ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper("test-app", null, "Client roles mapper", "roles-custom.test-app", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper)); protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -792,7 +794,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
public void testRoleMapperWithRoleInheritedFromMoreGroups() throws Exception { public void testRoleMapperWithRoleInheritedFromMoreGroups() throws Exception {
// Create client-mapper // Create client-mapper
String clientId = "test-app"; String clientId = "test-app";
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app", true, true); ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers(); ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(clientMapper)); protocolMappers.createMapper(Arrays.asList(clientMapper));
@ -825,8 +827,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
public void testUserGroupRoleToAttributeMappers() throws Exception { public void testUserGroupRoleToAttributeMappers() throws Exception {
// Add mapper for realm roles // Add mapper for realm roles
String clientId = "test-app"; String clientId = "test-app";
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true); ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, "ta.", "Client roles mapper", "roles-custom.test-app", true, true); ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, "ta.", "Client roles mapper", "roles-custom.test-app", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers(); ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper)); protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -861,8 +863,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
@Test @Test
public void testUserGroupRoleToAttributeMappersNotScopedOtherApp() throws Exception { public void testUserGroupRoleToAttributeMappersNotScopedOtherApp() throws Exception {
String clientId = "test-app-authz"; String clientId = "test-app-authz";
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true); ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom." + clientId, true, true); ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom." + clientId, true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers(); ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper)); protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -901,8 +903,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
@Test @Test
public void testUserGroupRoleToAttributeMappersScoped() throws Exception { public void testUserGroupRoleToAttributeMappersScoped() throws Exception {
String clientId = "test-app-scope"; String clientId = "test-app-scope";
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true); ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app-scope", true, true); ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(clientId, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers(); ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper)); protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -935,8 +937,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
@Test @Test
public void testUserGroupRoleToAttributeMappersScopedClientNotSet() throws Exception { public void testUserGroupRoleToAttributeMappersScopedClientNotSet() throws Exception {
String clientId = "test-app-scope"; String clientId = "test-app-scope";
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true); ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true); ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, true);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers(); ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper)); protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -971,8 +973,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
@Test @Test
public void testSingleValuedRoleMapping() throws Exception { public void testSingleValuedRoleMapping() throws Exception {
String clientId = "test-app-scope"; String clientId = "test-app-scope";
ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, false); ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true,false);
ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, false); ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(null, null, "Client roles mapper", "roles-custom.test-app-scope", true, true, true, false);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers(); ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), clientId).getProtocolMappers();
protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper)); protocolMappers.createMapper(Arrays.asList(realmMapper, clientMapper));
@ -1008,8 +1010,8 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
final String diffClient = "test-app"; final String diffClient = "test-app";
final String realmName = "test"; final String realmName = "test";
final ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true); final ProtocolMapperRepresentation realmMapper = ProtocolMapperUtil.createUserRealmRoleMappingMapper("pref.", "Realm roles mapper", "roles-custom.realm", true, true, true);
final ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(diffClient, null, "Client roles mapper", "roles-custom.test-app", true, true); final ProtocolMapperRepresentation clientMapper = ProtocolMapperUtil.createUserClientRoleMappingMapper(diffClient, null, "Client roles mapper", "roles-custom.test-app", true, true, true);
try (ClientAttributeUpdater cau = ClientAttributeUpdater.forClient(adminClient, realmName, clientId).setDirectAccessGrantsEnabled(true); try (ClientAttributeUpdater cau = ClientAttributeUpdater.forClient(adminClient, realmName, clientId).setDirectAccessGrantsEnabled(true);
ProtocolMappersUpdater protocolMappers = new ProtocolMappersUpdater(cau.getResource().getProtocolMappers())) { ProtocolMappersUpdater protocolMappers = new ProtocolMappersUpdater(cau.getResource().getProtocolMappers())) {
@ -1057,7 +1059,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.joinGroup(group1.getId()); userResource.joinGroup(group1.getId());
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true,false, false)).close();
try { try {
// test it // test it
@ -1097,7 +1099,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.joinGroup(group1.getId()); userResource.joinGroup(group1.getId());
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
try { try {
// test it // test it
@ -1138,7 +1140,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.joinGroup(group1.getId()); userResource.joinGroup(group1.getId());
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, true)).close();
try { try {
// test it // test it
@ -1177,7 +1179,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
userResource.joinGroup(group1.getId()); userResource.joinGroup(group1.getId());
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
try { try {
// test it // test it
@ -1212,7 +1214,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
try { try {
// test it // test it
@ -1248,7 +1250,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, true)).close();
try { try {
// test it // test it
@ -1291,7 +1293,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
try { try {
// test it // test it
@ -1336,7 +1338,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
try { try {
// test it // test it
@ -1383,7 +1385,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, true)).close();
try { try {
// test it // test it
@ -1434,7 +1436,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
try { try {
// test it // test it
@ -1483,7 +1485,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, false, false)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false, false)).close();
try { try {
// test it // test it
@ -1532,7 +1534,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, false)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true, false)).close();
try { try {
// test it // test it
@ -1586,7 +1588,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
// create the attribute mapper // create the attribute mapper
ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers(); ProtocolMappersResource protocolMappers = findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true, true)).close(); protocolMappers.createMapper(createClaimMapper("group-value", "group-value", "group-value", "String", true, true, true,true, true)).close();
try { try {
// test it // test it
@ -1776,7 +1778,7 @@ public class OIDCProtocolMappersTest extends AbstractKeycloakTest {
put(ClientScopeModel.DYNAMIC_SCOPE_REGEXP, "dyn-scope-with-mapper:*"); put(ClientScopeModel.DYNAMIC_SCOPE_REGEXP, "dyn-scope-with-mapper:*");
}}); }});
// create the attribute mapper // create the attribute mapper
ProtocolMapperRepresentation protocolMapperRepresentation = createHardcodedClaim("dynamic-scope-hardcoded-mapper", "hardcoded-foo", "hardcoded-bar", "String", true, true); ProtocolMapperRepresentation protocolMapperRepresentation = createHardcodedClaim("dynamic-scope-hardcoded-mapper", "hardcoded-foo", "hardcoded-bar", "String", true, true, true);
scopeRep.setProtocolMappers(Collections.singletonList(protocolMapperRepresentation)); scopeRep.setProtocolMappers(Collections.singletonList(protocolMapperRepresentation));
try (Response resp = adminClient.realm("test").clientScopes().create(scopeRep)) { try (Response resp = adminClient.realm("test").clientScopes().create(scopeRep)) {

View file

@ -119,7 +119,7 @@ public class AudienceTest extends AbstractOIDCScopeTest {
public void testAudienceProtocolMapperWithClientAudience() throws Exception { public void testAudienceProtocolMapperWithClientAudience() throws Exception {
// Add audience protocol mapper to the clientScope "audience-scope" // Add audience protocol mapper to the clientScope "audience-scope"
ProtocolMapperRepresentation audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper", "service-client", ProtocolMapperRepresentation audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper", "service-client",
null, true, false); null, true, false, true);
ClientScopeResource clientScope = ApiUtil.findClientScopeByName(testRealm(), "audience-scope"); ClientScopeResource clientScope = ApiUtil.findClientScopeByName(testRealm(), "audience-scope");
Response resp = clientScope.getProtocolMappers().createMapper(audienceMapper); Response resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
String mapperId = ApiUtil.getCreatedId(resp); String mapperId = ApiUtil.getCreatedId(resp);
@ -131,7 +131,7 @@ public class AudienceTest extends AbstractOIDCScopeTest {
EventRepresentation loginEvent = events.expectLogin() EventRepresentation loginEvent = events.expectLogin()
.user(userId) .user(userId)
.assertEvent(); .assertEvent();
Tokens tokens = sendTokenRequest(loginEvent, userId,"openid profile email audience-scope", "test-app"); Tokens tokens = sendTokenRequest(loginEvent, userId, "openid profile email audience-scope", "test-app");
assertAudiences(tokens.accessToken, "service-client"); assertAudiences(tokens.accessToken, "service-client");
assertAudiences(tokens.idToken, "test-app"); assertAudiences(tokens.idToken, "test-app");
@ -145,14 +145,14 @@ public class AudienceTest extends AbstractOIDCScopeTest {
public void testAudienceProtocolMapperWithCustomAudience() throws Exception { public void testAudienceProtocolMapperWithCustomAudience() throws Exception {
// Add audience protocol mapper to the clientScope "audience-scope" // Add audience protocol mapper to the clientScope "audience-scope"
ProtocolMapperRepresentation audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper 1", null, ProtocolMapperRepresentation audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper 1", null,
"http://host/service/ctx1", true, false); "http://host/service/ctx1", true, false, true);
ClientScopeResource clientScope = ApiUtil.findClientScopeByName(testRealm(), "audience-scope"); ClientScopeResource clientScope = ApiUtil.findClientScopeByName(testRealm(), "audience-scope");
Response resp = clientScope.getProtocolMappers().createMapper(audienceMapper); Response resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
String mapper1Id = ApiUtil.getCreatedId(resp); String mapper1Id = ApiUtil.getCreatedId(resp);
resp.close(); resp.close();
audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper 2", null, audienceMapper = ProtocolMapperUtil.createAudienceMapper("audience mapper 2", null,
"http://host/service/ctx2", true, true); "http://host/service/ctx2", true, true, true);
resp = clientScope.getProtocolMappers().createMapper(audienceMapper); resp = clientScope.getProtocolMappers().createMapper(audienceMapper);
String mapper2Id = ApiUtil.getCreatedId(resp); String mapper2Id = ApiUtil.getCreatedId(resp);
resp.close(); resp.close();
@ -163,7 +163,7 @@ public class AudienceTest extends AbstractOIDCScopeTest {
EventRepresentation loginEvent = events.expectLogin() EventRepresentation loginEvent = events.expectLogin()
.user(userId) .user(userId)
.assertEvent(); .assertEvent();
Tokens tokens = sendTokenRequest(loginEvent, userId,"openid profile email audience-scope", "test-app"); Tokens tokens = sendTokenRequest(loginEvent, userId, "openid profile email audience-scope", "test-app");
assertAudiences(tokens.accessToken, "http://host/service/ctx1", "http://host/service/ctx2"); assertAudiences(tokens.accessToken, "http://host/service/ctx1", "http://host/service/ctx2");
assertAudiences(tokens.idToken, "test-app", "http://host/service/ctx2"); assertAudiences(tokens.idToken, "test-app", "http://host/service/ctx2");

View file

@ -0,0 +1,472 @@
package org.keycloak.testsuite.oidc;
import jakarta.ws.rs.NotFoundException;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.admin.client.resource.ClientScopeResource;
import org.keycloak.admin.client.resource.ProtocolMappersResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.Profile;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.mappers.AudienceProtocolMapper;
import org.keycloak.protocol.oidc.mappers.GroupMembershipMapper;
import org.keycloak.protocol.oidc.mappers.HardcodedClaim;
import org.keycloak.protocol.oidc.mappers.HardcodedRole;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.protocol.oidc.mappers.RoleNameMapper;
import org.keycloak.protocol.oidc.mappers.SHA256PairwiseSubMapper;
import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
import org.keycloak.testsuite.admin.ApiUtil;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.util.ClientManager;
import org.keycloak.testsuite.util.KeycloakModelUtils;
import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.ProtocolMapperUtil;
import org.keycloak.util.JsonSerialization;
import org.wildfly.common.Assert;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.keycloak.protocol.ProtocolMapperUtils.USER_SESSION_NOTE;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ACR;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ACR_SCOPE;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ADDRESS;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ALLOWED_WEB_ORIGINS;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.AUDIENCE_RESOLVE;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.CLIENT_ROLES;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.EMAIL;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.EMAIL_VERIFIED;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.FAMILY_NAME;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.FULL_NAME;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.GIVEN_NAME;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.PROFILE_CLAIM;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.REALM_ROLES;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.ROLES_SCOPE;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.USERNAME;
import static org.keycloak.protocol.oidc.OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE;
import static org.keycloak.protocol.oidc.mappers.AbstractPairwiseSubMapper.PROVIDER_ID_SUFFIX;
import static org.keycloak.protocol.oidc.mappers.AudienceProtocolMapper.INCLUDED_CLIENT_AUDIENCE;
import static org.keycloak.protocol.oidc.mappers.HardcodedClaim.CLAIM_VALUE;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN;
import static org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper.INCLUDE_IN_INTROSPECTION;
import static org.keycloak.protocol.oidc.mappers.PairwiseSubMapperHelper.PAIRWISE_SUB_ALGORITHM_SALT;
import static org.keycloak.protocol.oidc.mappers.RoleNameMapper.NEW_ROLE_NAME;
import static org.keycloak.protocol.oidc.mappers.RoleNameMapper.ROLE_CONFIG;
import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
@EnableFeature(value = Profile.Feature.TOKEN_EXCHANGE, skipRestart = true)
public class LightWeightAccessTokenTest extends AbstractKeycloakTest {
@Before
public void clientConfiguration() {
ClientManager.realm(adminClient.realm("test")).clientId("test-app").directAccessGrant(true).setServiceAccountsEnabled(true);
ClientManager.realm(adminClient.realm("test")).clientId("resource-server").directAccessGrant(true);
}
@Override
public void addTestRealms(List<RealmRepresentation> testRealms) {
RealmRepresentation realm = loadJson(getClass().getResourceAsStream("/testrealm.json"), RealmRepresentation.class);
UserRepresentation user = findUser(realm, "test-user@localhost");
Map<String, List<String>> attributes = new HashMap<>(){{
put("street", Arrays.asList("1 My Street"));
put("locality", Arrays.asList("Cardiff"));
put("region", Arrays.asList("Cardiff"));
put("postal_code", Arrays.asList("CF104RA"));
}};
user.setAttributes(attributes);
user.setGroups(Arrays.asList("/topGroup/level2group"));
ClientRepresentation confApp = KeycloakModelUtils.createClient(realm, "resource-server");
confApp.setSecret("password");
confApp.setServiceAccountsEnabled(Boolean.TRUE);
testRealms.add(realm);
}
private UserRepresentation findUser(RealmRepresentation testRealm, String userName) {
for (UserRepresentation user : testRealm.getUsers()) {
if (user.getUsername().equals(userName)) return user;
}
return null;
}
@Test
public void accessTokenFalseIntrospectionTrueTest() throws IOException {
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, true);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId("test-app");
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), true, false);
oauth.clientId("resource-server");
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
System.out.println("tokenResponse:" + tokenResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, false);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
@Test
public void accessTokenTrueIntrospectionFalseTest() throws IOException {
ProtocolMappersResource protocolMappers = setProtocolMappers(true, false, true);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId("test-app");
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), true, true);
oauth.clientId("resource-server");
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
System.out.println("tokenResponse:" + tokenResponse);
// Most of the claims should not be included in introspectionResponse as introspectionMapper was disabled
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, false, false);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
@Test
public void accessTokenTrueIntrospectionTrueTest() throws IOException {
ProtocolMappersResource protocolMappers = setProtocolMappers(true, true, true);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId("test-app");
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), true, true);
oauth.clientId("resource-server");
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
System.out.println("tokenResponse:" + tokenResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, false);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
@Test
public void offlineTokenTest() throws IOException {
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, true);
try {
oauth.nonce("123456");
oauth.scope("openid address offline_access");
oauth.clientId("test-app");
TokenResponseContext ctx = browserLogin("password", "test-user@localhost", "password");
OAuthClient.AccessTokenResponse response = ctx.tokenResponse;
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
System.out.println("idtoken:" + response.getIdToken());
assertAccessToken(oauth.verifyToken(accessToken), true, false);
oauth.clientId("resource-server");
removeSession(ctx.userSessionId);
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
System.out.println("tokenResponse:" + tokenResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, false);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
private void removeSession(final String sessionId) {
testingClient.testing().removeExpired("test");
try {
testingClient.testing().removeUserSession("test", sessionId);
} catch (NotFoundException nfe) {
// Ignore
}
}
@Test
public void clientCredentialTest() throws Exception {
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, false);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId("test-app");
OAuthClient.AccessTokenResponse response = oauth.doClientCredentialsGrantAccessTokenRequest("password");
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), false, false);
oauth.clientId("resource-server");
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", accessToken);
System.out.println("tokenResponse:" + tokenResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), false);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
@Test
public void exchangeTest() throws Exception {
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, true);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId("test-app");
OAuthClient.AccessTokenResponse response = browserLogin("password", "test-user@localhost", "password").tokenResponse;
String accessToken = response.getAccessToken();
System.out.println("accessToken:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), true, false);
response = oauth.doTokenExchange(TEST, accessToken, null, "test-app", "password");
String exchangedTokenString = response.getAccessToken();
System.out.println("exchangedTokenString:" + exchangedTokenString);
oauth.clientId("resource-server");
String tokenResponse = oauth.introspectAccessTokenWithClientCredential("resource-server", "password", exchangedTokenString);
System.out.println("tokenResponse:" + tokenResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(tokenResponse, AccessToken.class), true, true, true);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
private void assertMapperClaims(AccessToken token, boolean isAddMapperResponseFlag, boolean isAuthCodeFlow) {
if (isAddMapperResponseFlag) {
if (isAuthCodeFlow) {
Assert.assertNotNull(token.getName());
Assert.assertNotNull(token.getGivenName());
Assert.assertNotNull(token.getFamilyName());
Assert.assertNotNull(token.getAddress());
Assert.assertNotNull(token.getEmail());
Assert.assertNotNull(token.getOtherClaims().get("user-session-note"));
Assert.assertNotNull(token.getOtherClaims().get("test-claim"));
Assert.assertNotNull(token.getOtherClaims().get("group-name"));
}
Assert.assertNotNull(token.getAudience());
Assert.assertNotNull(token.getAcr());
Assert.assertNotNull(token.getAllowedOrigins());
Assert.assertNotNull(token.getRealmAccess());
Assert.assertNotNull(token.getResourceAccess());
Assert.assertNotNull(token.getEmailVerified());
Assert.assertNotNull(token.getPreferredUsername());
} else {
if (isAuthCodeFlow) {
Assert.assertTrue(token.getName() == null);
Assert.assertTrue(token.getGivenName() == null);
Assert.assertTrue(token.getFamilyName() == null);
Assert.assertTrue(token.getAddress() == null);
Assert.assertTrue(token.getEmail() == null);
Assert.assertTrue(token.getOtherClaims().get("user-session-note") == null);
Assert.assertTrue(token.getOtherClaims().get("test-claim") == null);
Assert.assertTrue(token.getOtherClaims().get("group-name") == null);
}
Assert.assertTrue(token.getAcr() == null);
Assert.assertTrue(token.getAllowedOrigins() == null);
Assert.assertTrue(token.getRealmAccess() == null);
Assert.assertTrue(token.getResourceAccess().isEmpty());
Assert.assertTrue(token.getEmailVerified() == null);
Assert.assertTrue(token.getPreferredUsername() == null);
}
}
private void assertInitClaims(AccessToken token, boolean isAuthCodeFlow) {
Assert.assertNotNull(token.getExp());
Assert.assertNotNull(token.getIat());
Assert.assertNotNull(token.getId());
Assert.assertNotNull(token.getType());
if (isAuthCodeFlow) {
Assert.assertNotNull(token.getSessionId());
Assert.assertNotNull(token.getAuth_time());
} else {
Assert.assertTrue(token.getSessionId() == null);
Assert.assertTrue(token.getAuth_time() == null);
}
Assert.assertNotNull(token.getIssuedFor());
Assert.assertNotNull(token.getScope());
Assert.assertNotNull(token.getIssuer());
}
private void assertIntrospectClaims(AccessToken token) {
Assert.assertNotNull(token.getOtherClaims().get("client_id"));
Assert.assertNotNull(token.getOtherClaims().get("active"));
Assert.assertNotNull(token.getOtherClaims().get("token_type"));
}
private void assertNonce(AccessToken token, boolean isAuthCodeFlow, boolean exchangeToken) {
if (isAuthCodeFlow && !exchangeToken) {
Assert.assertNotNull(token.getNonce());
} else {
Assert.assertTrue(token.getNonce() == null);
}
}
private void assertAccessToken(AccessToken token, boolean isAuthCodeFlow, boolean isAddToAccessToken) {
assertNonce(token, isAuthCodeFlow, false);
assertMapperClaims(token, isAddToAccessToken, isAuthCodeFlow);
assertInitClaims(token, isAuthCodeFlow);
}
private void assertTokenIntrospectionResponse(AccessToken token, boolean isAuthCodeFlow) {
assertTokenIntrospectionResponse(token, isAuthCodeFlow, true, false);
}
private void assertTokenIntrospectionResponse(AccessToken token, boolean isAuthCodeFlow, boolean isAddToIntrospect, boolean exchangeToken) {
assertNonce(token, isAuthCodeFlow, exchangeToken);
assertMapperClaims(token, isAddToIntrospect, isAuthCodeFlow);
assertInitClaims(token, isAuthCodeFlow);
assertIntrospectClaims(token);
}
protected RealmResource testRealm() {
return adminClient.realm("test");
}
private void setScopeProtocolMappers(boolean isIncludeAccessToken, boolean isIncludeIntrospection) {
setScopeProtocolMapper(ACR_SCOPE, ACR, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(PROFILE_CLAIM, FULL_NAME, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(EMAIL, EMAIL, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(EMAIL, EMAIL_VERIFIED, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(PROFILE_CLAIM, GIVEN_NAME, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(PROFILE_CLAIM, FAMILY_NAME, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(PROFILE_CLAIM, USERNAME, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(WEB_ORIGINS_SCOPE, ALLOWED_WEB_ORIGINS, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(ROLES_SCOPE, REALM_ROLES, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(ROLES_SCOPE, CLIENT_ROLES, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(ROLES_SCOPE, AUDIENCE_RESOLVE, isIncludeAccessToken, isIncludeIntrospection);
setScopeProtocolMapper(ADDRESS, ADDRESS, isIncludeAccessToken, isIncludeIntrospection);
}
private void setScopeProtocolMapper(String scopeName, String mapperName, boolean isIncludeAccessToken, boolean isIncludeIntrospection) {
ClientScopeResource scope = ApiUtil.findClientScopeByName(testRealm(), scopeName);
ProtocolMapperRepresentation protocolMapper = ApiUtil.findProtocolMapperByName(scope, mapperName);
Map<String, String> config = protocolMapper.getConfig();
if (isIncludeAccessToken) {
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
} else {
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
}
if (isIncludeIntrospection) {
config.put(INCLUDE_IN_INTROSPECTION, "true");
} else {
config.put(INCLUDE_IN_INTROSPECTION, "false");
}
scope.getProtocolMappers().update(protocolMapper.getId(), protocolMapper);
}
private ProtocolMappersResource setProtocolMappers(boolean isIncludeAccessToken, boolean isIncludeIntrospection, boolean setPairWise) {
setScopeProtocolMappers(isIncludeAccessToken, isIncludeIntrospection);
List<ProtocolMapperRepresentation> protocolMapperList = new ArrayList<>();
setExistingProtocolMappers(protocolMapperList, isIncludeAccessToken, isIncludeIntrospection, setPairWise);
ProtocolMappersResource protocolMappers = ApiUtil.findClientResourceByClientId(adminClient.realm("test"), "test-app").getProtocolMappers();
protocolMappers.createMapper(protocolMapperList);
return protocolMappers;
}
private void setExistingProtocolMappers(List<ProtocolMapperRepresentation> protocolMapperList, boolean isIncludeAccessToken, boolean isIncludeIntrospection, boolean setPairWise) {
Map<String, String> config = new HashMap<>();
if (isIncludeAccessToken) {
config.put(INCLUDE_IN_ACCESS_TOKEN, "true");
} else {
config.put(INCLUDE_IN_ACCESS_TOKEN, "false");
}
if (isIncludeIntrospection) {
config.put(INCLUDE_IN_INTROSPECTION, "true");
} else {
config.put(INCLUDE_IN_INTROSPECTION, "false");
}
ProtocolMapperRepresentation audienceProtocolMapper = createClaimMapper("audience", AudienceProtocolMapper.PROVIDER_ID, new HashMap<>(config) {{
put(INCLUDED_CLIENT_AUDIENCE, "account-console");
}});
protocolMapperList.add(audienceProtocolMapper);
ProtocolMapperRepresentation roleNameMapper = createClaimMapper("role-name", RoleNameMapper.PROVIDER_ID, new HashMap<>(config) {{
put(ROLE_CONFIG, "user");
put(NEW_ROLE_NAME, "new-role");
}});
protocolMapperList.add(roleNameMapper);
ProtocolMapperRepresentation groupMembershipMapper = createClaimMapper("group-member", GroupMembershipMapper.PROVIDER_ID, new HashMap<>(config) {{
put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "group-name");
}});
protocolMapperList.add(groupMembershipMapper);
ProtocolMapperRepresentation hardcodedClaim = createClaimMapper("hardcoded-claim", HardcodedClaim.PROVIDER_ID, new HashMap<>(config) {{
put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "test-claim");
put(CLAIM_VALUE, "test-value");
}});
protocolMapperList.add(hardcodedClaim);
ProtocolMapperRepresentation hardcodedRole = createClaimMapper("hardcoded-role", HardcodedRole.PROVIDER_ID, new HashMap<>(config) {{
put(ROLE_CONFIG, "hardcoded-role");
}});
protocolMapperList.add(hardcodedRole);
ProtocolMapperRepresentation userSessionNoteMapper = createClaimMapper("user-session-note", UserSessionNoteMapper.PROVIDER_ID, new HashMap<>(config) {{
put(OIDCAttributeMapperHelper.TOKEN_CLAIM_NAME, "user-session-note");
put(USER_SESSION_NOTE, "AUTH_TIME");
}});
protocolMapperList.add(userSessionNoteMapper);
if (setPairWise) {
ProtocolMapperRepresentation pairwiseSubMapper = createClaimMapper("pairwise-sub-mapper", "oidc-" + SHA256PairwiseSubMapper.PROVIDER_ID + PROVIDER_ID_SUFFIX, new HashMap<>(config) {{
put(PAIRWISE_SUB_ALGORITHM_SALT, "abc");
}});
protocolMapperList.add(pairwiseSubMapper);
}
}
private static ProtocolMapperRepresentation createClaimMapper(String name, String providerId, Map<String, String> config) {
ProtocolMapperModel mapper = new ProtocolMapperModel();
mapper.setName(name);
mapper.setProtocolMapper(providerId);
mapper.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
mapper.setConfig(config);
return ModelToRepresentation.toRepresentation(mapper);
}
private void deleteProtocolMappers(ProtocolMappersResource protocolMappers) {
List<String> mapperNames = new ArrayList<>(Arrays.asList("reference", "audience", "role-name", "group-member", "hardcoded-claim", "hardcoded-role", "user-session-note", "pairwise-sub-mapper"));
List<ProtocolMapperRepresentation> mappers = new ArrayList<>();
for (String mapperName : mapperNames) {
mappers.add(ProtocolMapperUtil.getMapperByNameAndProtocol(protocolMappers, OIDCLoginProtocol.LOGIN_PROTOCOL, mapperName));
}
for (ProtocolMapperRepresentation mapper : mappers) {
if (mapper != null) {
protocolMappers.delete(mapper.getId());
}
}
}
private TokenResponseContext browserLogin(String clientSecret, String username, String password) {
OAuthClient.AuthorizationEndpointResponse authsEndpointResponse = oauth.doLogin(username, password);
String userSessionId = authsEndpointResponse.getSessionState();
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), clientSecret);
return new TokenResponseContext(userSessionId, tokenResponse);
}
private class TokenResponseContext {
private final String userSessionId;
private final OAuthClient.AccessTokenResponse tokenResponse;
public TokenResponseContext(String userSessionId, OAuthClient.AccessTokenResponse tokenResponse) {
this.userSessionId = userSessionId;
this.tokenResponse = tokenResponse;
}
}
}

View file

@ -92,7 +92,7 @@ public class DeployedScriptMapperTest extends AbstractTestRealmKeycloakTest {
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app"); ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script", ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script",
"computed-via-script", "String", true, true, "'hello_' + user.username", false); "computed-via-script", "String", true, true, true, "'hello_' + user.username", false);
mapper.setProtocolMapper("script-mapper-a.js"); mapper.setProtocolMapper("script-mapper-a.js");

View file

@ -95,7 +95,7 @@ public class UndeployedScriptMapperNotAvailableTest extends AbstractTestRealmKey
ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app"); ClientResource app = findClientResourceByClientId(adminClient.realm("test"), "test-app");
{ {
ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script", ProtocolMapperRepresentation mapper = createScriptMapper("test-script-mapper1", "computed-via-script",
"computed-via-script", "String", true, true, "'hello_' + user.username", false); "computed-via-script", "String", true, true, true, "'hello_' + user.username", false);
mapper.setProtocolMapper("script-mapper-a.js"); mapper.setProtocolMapper("script-mapper-a.js");

View file

@ -47,8 +47,8 @@ public class ProtocolMapperUtil {
* @param accessToken * @param accessToken
* @return * @return
*/ */
public static ProtocolMapperRepresentation createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo) { public static ProtocolMapperRepresentation createAddressMapper(boolean idToken, boolean accessToken, boolean userInfo, boolean introspectionEndpoint) {
return ModelToRepresentation.toRepresentation(AddressMapper.createAddressMapper(idToken, accessToken, userInfo)); return ModelToRepresentation.toRepresentation(AddressMapper.createAddressMapper(idToken, accessToken, userInfo, introspectionEndpoint));
} }
/** /**
@ -65,9 +65,9 @@ public class ProtocolMapperUtil {
public static ProtocolMapperRepresentation createHardcodedClaim(String name, public static ProtocolMapperRepresentation createHardcodedClaim(String name,
String hardcodedName, String hardcodedName,
String hardcodedValue, String claimType, String hardcodedValue, String claimType,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return ModelToRepresentation.toRepresentation(HardcodedClaim.create(name, hardcodedName, hardcodedValue, return ModelToRepresentation.toRepresentation(HardcodedClaim.create(name, hardcodedName, hardcodedValue,
claimType, accessToken, idToken)); claimType, accessToken, idToken, introspectionEndpoint));
} }
/** /**
@ -85,64 +85,64 @@ public class ProtocolMapperUtil {
public static ProtocolMapperRepresentation createClaimMapper(String name, public static ProtocolMapperRepresentation createClaimMapper(String name,
String userAttribute, String userAttribute,
String tokenClaimName, String claimType, String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, boolean multivalued) { boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multivalued) {
return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName, return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName,
claimType, accessToken, idToken, multivalued, false)); claimType, accessToken, idToken, introspectionEndpoint, multivalued, false));
} }
public static ProtocolMapperRepresentation createClaimMapper(String name, public static ProtocolMapperRepresentation createClaimMapper(String name,
String userAttribute, String userAttribute,
String tokenClaimName, String claimType, String tokenClaimName, String claimType,
boolean accessToken, boolean idToken, boolean accessToken, boolean idToken, boolean introspectionEndpoint,
boolean multivalued, boolean aggregateAttrs) { boolean multivalued, boolean aggregateAttrs) {
return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName, return ModelToRepresentation.toRepresentation(UserAttributeMapper.createClaimMapper(name, userAttribute, tokenClaimName,
claimType, accessToken, idToken, multivalued, aggregateAttrs)); claimType, accessToken, idToken, introspectionEndpoint, multivalued, aggregateAttrs));
} }
public static ProtocolMapperRepresentation createClaimMapper(String name, public static ProtocolMapperRepresentation createClaimMapper(String name,
String userSessionNote, String userSessionNote,
String tokenClaimName, String jsonType, String tokenClaimName, String jsonType,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return ModelToRepresentation.toRepresentation(UserSessionNoteMapper.createClaimMapper(name, return ModelToRepresentation.toRepresentation(UserSessionNoteMapper.createClaimMapper(name,
userSessionNote, userSessionNote,
tokenClaimName, jsonType, tokenClaimName, jsonType,
accessToken, idToken)); accessToken, idToken, introspectionEndpoint));
} }
public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix, public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix,
String name, String name,
String tokenClaimName, String tokenClaimName,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return createUserRealmRoleMappingMapper(realmRolePrefix, name, tokenClaimName, accessToken, idToken, true); return createUserRealmRoleMappingMapper(realmRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, true);
} }
public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix, public static ProtocolMapperRepresentation createUserRealmRoleMappingMapper(String realmRolePrefix,
String name, String name,
String tokenClaimName, String tokenClaimName,
boolean accessToken, boolean idToken, boolean multiValued) { boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
return ModelToRepresentation.toRepresentation(UserRealmRoleMappingMapper.create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, multiValued)); return ModelToRepresentation.toRepresentation(UserRealmRoleMappingMapper.create(realmRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, multiValued));
} }
public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix, public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
String name, String name,
String tokenClaimName, String tokenClaimName,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return createUserClientRoleMappingMapper(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, true); return createUserClientRoleMappingMapper(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, true);
} }
public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix, public static ProtocolMapperRepresentation createUserClientRoleMappingMapper(String clientId, String clientRolePrefix,
String name, String name,
String tokenClaimName, String tokenClaimName,
boolean accessToken, boolean idToken, boolean multiValued) { boolean accessToken, boolean idToken, boolean introspectionEndpoint, boolean multiValued) {
return ModelToRepresentation.toRepresentation(UserClientRoleMappingMapper.create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, multiValued)); return ModelToRepresentation.toRepresentation(UserClientRoleMappingMapper.create(clientId, clientRolePrefix, name, tokenClaimName, accessToken, idToken, introspectionEndpoint, multiValued));
} }
public static ProtocolMapperRepresentation getMapperByNameAndProtocol(ProtocolMappersResource protocolMappers, String protocol, String name) { public static ProtocolMapperRepresentation getMapperByNameAndProtocol(ProtocolMappersResource protocolMappers, String protocol, String name) {
@ -160,11 +160,12 @@ public class ProtocolMapperUtil {
String claimType, String claimType,
boolean accessToken, boolean accessToken,
boolean idToken, boolean idToken,
boolean introspectionEndpoint,
String script, String script,
boolean multiValued) { boolean multiValued) {
return ModelToRepresentation.toRepresentation( return ModelToRepresentation.toRepresentation(
ScriptBasedOIDCProtocolMapper.create(name, userAttribute, tokenClaimName, claimType, accessToken, idToken, script, multiValued) ScriptBasedOIDCProtocolMapper.create(name, userAttribute, tokenClaimName, claimType, accessToken, idToken, introspectionEndpoint, script, multiValued)
); );
} }
@ -175,10 +176,10 @@ public class ProtocolMapperUtil {
public static ProtocolMapperRepresentation createAudienceMapper(String name, public static ProtocolMapperRepresentation createAudienceMapper(String name,
String includedClientAudience, String includedClientAudience,
String includedCustomAudience, String includedCustomAudience,
boolean accessToken, boolean idToken) { boolean accessToken, boolean idToken, boolean introspectionEndpoint) {
return ModelToRepresentation.toRepresentation( return ModelToRepresentation.toRepresentation(
AudienceProtocolMapper.createClaimMapper(name, includedClientAudience, includedCustomAudience, accessToken, idToken) AudienceProtocolMapper.createClaimMapper(name, includedClientAudience, includedCustomAudience, accessToken, idToken, introspectionEndpoint)
); );
} }
} }