New default client scope for 'basic' claims with 'auth_time' protocol mapper
Closes #27623 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
parent
96672a2a6d
commit
fe06df67c2
22 changed files with 196 additions and 33 deletions
|
@ -75,7 +75,7 @@ Further, when the resource server acquires the PII removed from the access token
|
|||
|
||||
Information that cannot be removed from a lightweight access token::
|
||||
Protocol mappers can controls which information is put onto an access token and the lightweight access token use the protocol mappers. Therefore, the following information cannot be removed from the lightweight access. +
|
||||
`exp`, `iat`, `auth_time`, `jti`, `iss`, `sub`, `typ`, `azp`, `nonce`, `session_state`, `sid`, `scope`, `cnf`
|
||||
`exp`, `iat`, `jti`, `iss`, `sub`, `typ`, `azp`, `nonce`, `session_state`, `sid`, `scope`, `cnf`
|
||||
|
||||
Using a lightweight access token in {project_name}::
|
||||
By applying `use-lightweight-access-token` executor of <<_client_policies, client policies>> to a client, the client can receive a lightweight access token instead of an access token. The lightweight access token contains a claim controlled by a protocol mapper where its setting `Add to lightweight access token`(default OFF) is turned ON. Also, by turning ON its setting `Add to token introspection` of the protocol mapper, the client can obtain the claim by sending the access token to {project_name}'s token introspection endpoint.
|
||||
|
|
|
@ -68,3 +68,13 @@ How kcadm parses and handles options and parameters has changed. Error messages
|
|||
= Removing custom user attribute indexes
|
||||
|
||||
When searching for users by user attribute, Keycloak no longer searches for user attribute names forcing lower case comparisons. This means Keycloak's native index on the user attribute table will now be used when searching. If you have created your own index based on `lower(name)`to speed up searches, you can now remove it.
|
||||
|
||||
= New default client scope `basic`
|
||||
|
||||
The new client scope named `basic` is added as a realm "default" client scope and hence will be added to all newly created clients. The client scope is also automatically added to all existing clients during migration.
|
||||
|
||||
This scope contains preconfigured protocol mappers for the following claims:
|
||||
|
||||
* `auth_time`
|
||||
|
||||
This helps to reduce even more the number of claims in a lightweight access token, but also gives the chance to configure claims that were always added automatically.
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2023 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
*
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.keycloak.migration.migrators;
|
||||
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.migration.MigrationProvider;
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:ggrazian@redhat.com">Giuseppe Graziano</a>
|
||||
*/
|
||||
public class MigrateTo25_0_0 implements Migration {
|
||||
|
||||
public static final ModelVersion VERSION = new ModelVersion("25.0.0");
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MigrateTo25_0_0.class);
|
||||
|
||||
@Override
|
||||
public ModelVersion getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(KeycloakSession session) {
|
||||
session.realms().getRealmsStream().forEach(realm -> migrateRealm(session, realm));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrateImport(KeycloakSession session, RealmModel realm, RealmRepresentation rep, boolean skipUserDependent) {
|
||||
migrateRealm(session, realm);
|
||||
}
|
||||
protected void migrateRealm(KeycloakSession session, RealmModel realm) {
|
||||
MigrationProvider migrationProvider = session.getProvider(MigrationProvider.class);
|
||||
|
||||
// create 'basic' client scope in the realm.
|
||||
ClientScopeModel basicScope = migrationProvider.addOIDCBasicClientScope(realm);
|
||||
|
||||
//add basic scope to existing clients
|
||||
realm.getClientsStream().forEach(c-> c.addClientScope(basicScope, true));
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ import org.keycloak.migration.migrators.MigrateTo21_0_0;
|
|||
import org.keycloak.migration.migrators.MigrateTo22_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo23_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo24_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo25_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo2_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo2_1_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo2_2_0;
|
||||
|
@ -113,7 +114,8 @@ public class DefaultMigrationManager implements MigrationManager {
|
|||
new MigrateTo21_0_0(),
|
||||
new MigrateTo22_0_0(),
|
||||
new MigrateTo23_0_0(),
|
||||
new MigrateTo24_0_0()
|
||||
new MigrateTo24_0_0(),
|
||||
new MigrateTo25_0_0()
|
||||
};
|
||||
|
||||
private final KeycloakSession session;
|
||||
|
|
|
@ -77,4 +77,12 @@ public interface MigrationProvider extends Provider {
|
|||
* @return created or already existing client scope 'acr'
|
||||
*/
|
||||
void addOIDCAcrClientScope(RealmModel realm);
|
||||
|
||||
/**
|
||||
* Add 'basic' client scope or return it if already exists
|
||||
*
|
||||
* @param realm
|
||||
* @return created or already existing client scope 'basic'
|
||||
*/
|
||||
ClientScopeModel addOIDCBasicClientScope(RealmModel realm);
|
||||
}
|
||||
|
|
|
@ -164,7 +164,6 @@ public class AccessTokenIntrospectionProvider implements TokenIntrospectionProvi
|
|||
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.
|
||||
|
|
|
@ -48,6 +48,7 @@ import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
|
|||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -97,6 +98,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
public static final String WEB_ORIGINS_SCOPE = "web-origins";
|
||||
public static final String MICROPROFILE_JWT_SCOPE = "microprofile-jwt";
|
||||
public static final String ACR_SCOPE = "acr";
|
||||
public static final String BASIC_SCOPE = "basic";
|
||||
|
||||
public static final String PROFILE_SCOPE_CONSENT_TEXT = "${profileScopeConsentText}";
|
||||
public static final String EMAIL_SCOPE_CONSENT_TEXT = "${emailScopeConsentText}";
|
||||
|
@ -220,6 +222,11 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
model = AcrProtocolMapper.create(ACR, true, true, true);
|
||||
builtins.put(ACR, model);
|
||||
}
|
||||
|
||||
model = UserSessionNoteMapper.createClaimMapper(IDToken.AUTH_TIME, AuthenticationManager.AUTH_TIME,
|
||||
IDToken.AUTH_TIME, "long",
|
||||
true, true, false, true);
|
||||
builtins.put(BASIC_SCOPE, model);
|
||||
}
|
||||
|
||||
private void createUserAttributeMapper(String name, String attrName, String claimName, String type) {
|
||||
|
@ -298,6 +305,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
addWebOriginsClientScope(newRealm);
|
||||
addMicroprofileJWTClientScope(newRealm);
|
||||
addAcrClientScope(newRealm);
|
||||
addBasicClientScope(newRealm);
|
||||
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.ORGANIZATION)) {
|
||||
ClientScopeModel organizationScope = newRealm.addClientScope(OAuth2Constants.ORGANIZATION);
|
||||
|
@ -404,6 +412,25 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
|
|||
}
|
||||
}
|
||||
|
||||
public ClientScopeModel addBasicClientScope(RealmModel newRealm) {
|
||||
ClientScopeModel basicScope = KeycloakModelUtils.getClientScopeByName(newRealm, BASIC_SCOPE);
|
||||
if (basicScope == null) {
|
||||
basicScope = newRealm.addClientScope(BASIC_SCOPE);
|
||||
basicScope.setDescription("OpenID Connect scope for add all basic claims to the token");
|
||||
basicScope.setDisplayOnConsentScreen(false);
|
||||
basicScope.setIncludeInTokenScope(false);
|
||||
basicScope.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
basicScope.addProtocolMapper(builtins.get(BASIC_SCOPE));
|
||||
|
||||
newRealm.addDefaultClientScope(basicScope, true);
|
||||
|
||||
logger.debugf("Client scope '%s' created in the realm '%s'.", BASIC_SCOPE, newRealm.getName());
|
||||
} else {
|
||||
logger.debugf("Client scope '%s' already exists in realm '%s'. Skip creating it.", BASIC_SCOPE, newRealm.getName());
|
||||
}
|
||||
return basicScope;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDefaults(ClientModel client) {
|
||||
}
|
||||
|
|
|
@ -991,12 +991,6 @@ public class TokenManager {
|
|||
token.setAcr(acr);
|
||||
}
|
||||
|
||||
String authTime = session.getNote(AuthenticationManager.AUTH_TIME);
|
||||
if (authTime != null) {
|
||||
token.setAuthTime(Integer.parseInt(authTime));
|
||||
}
|
||||
|
||||
|
||||
token.setSessionState(session.getId());
|
||||
ClientScopeModel offlineAccessScope = KeycloakModelUtils.getClientScopeByName(realm, OAuth2Constants.OFFLINE_ACCESS);
|
||||
boolean offlineTokenRequested = offlineAccessScope == null ? false
|
||||
|
@ -1195,7 +1189,6 @@ public class TokenManager {
|
|||
idToken.issuedFor(accessToken.getIssuedFor());
|
||||
idToken.issuer(accessToken.getIssuer());
|
||||
idToken.setNonce(clientSessionCtx.getAttribute(OIDCLoginProtocol.NONCE_PARAM, String.class));
|
||||
idToken.setAuthTime(accessToken.getAuthTime());
|
||||
idToken.setSessionState(accessToken.getSessionState());
|
||||
idToken.expiration(accessToken.getExpiration());
|
||||
|
||||
|
|
|
@ -110,6 +110,13 @@ public class OIDCAttributeMapperHelper {
|
|||
tmpToken.put(IDToken.ACR, (claim, mapperName, token, value) -> {
|
||||
token.setAcr(value.toString());
|
||||
});
|
||||
tmpToken.put(IDToken.AUTH_TIME, (claim, mapperName, token, value) -> {
|
||||
try {
|
||||
token.setAuth_time(Long.parseLong(value.toString()));
|
||||
} catch (NumberFormatException ignored){
|
||||
|
||||
}
|
||||
});
|
||||
tmpToken.put("aud", (claim, mapperName, token, value) -> {
|
||||
if (value instanceof Collection) {
|
||||
String[] audiences = ((Collection<?>) value).stream().map(Object::toString).toArray(String[]::new);
|
||||
|
@ -129,7 +136,6 @@ public class OIDCAttributeMapperHelper {
|
|||
tmpToken.put("iss", notAllowedInToken);
|
||||
tmpToken.put("scope", notAllowedInToken);
|
||||
tmpToken.put(IDToken.NONCE, notAllowedInToken);
|
||||
tmpToken.put(IDToken.AUTH_TIME, notAllowedInToken);
|
||||
tmpToken.put(IDToken.SESSION_STATE, notAllowedInToken);
|
||||
tokenPropertySetters = Collections.unmodifiableMap(tmpToken);
|
||||
|
||||
|
|
|
@ -107,6 +107,11 @@ public class DefaultMigrationProvider implements MigrationProvider {
|
|||
getOIDCLoginProtocolFactory().addAcrClientScope(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientScopeModel addOIDCBasicClientScope(RealmModel realm) {
|
||||
return getOIDCLoginProtocolFactory().addBasicClientScope(realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
|
|
@ -724,6 +724,7 @@ public class ExportImportUtil {
|
|||
OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE,
|
||||
OIDCLoginProtocolFactory.MICROPROFILE_JWT_SCOPE,
|
||||
OIDCLoginProtocolFactory.ACR_SCOPE,
|
||||
OIDCLoginProtocolFactory.BASIC_SCOPE,
|
||||
SamlProtocolFactory.SCOPE_ROLE_LIST
|
||||
));
|
||||
|
||||
|
@ -745,7 +746,8 @@ public class ExportImportUtil {
|
|||
OAuth2Constants.SCOPE_EMAIL,
|
||||
OIDCLoginProtocolFactory.ROLES_SCOPE,
|
||||
OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE,
|
||||
OIDCLoginProtocolFactory.ACR_SCOPE
|
||||
OIDCLoginProtocolFactory.ACR_SCOPE,
|
||||
OIDCLoginProtocolFactory.BASIC_SCOPE
|
||||
));
|
||||
|
||||
Set<String> optionalClientScopes = realm.getDefaultOptionalClientScopes()
|
||||
|
|
|
@ -418,6 +418,11 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
}
|
||||
}
|
||||
|
||||
protected void testMigrationTo25_0_0() {
|
||||
// check that all expected scopes exist in the migrated realm.
|
||||
testRealmDefaultClientScopes(migrationRealm);
|
||||
}
|
||||
|
||||
protected void testDeleteAccount(RealmResource realm) {
|
||||
ClientRepresentation accountClient = realm.clients().findByClientId(ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
|
||||
ClientResource accountResource = realm.clients().get(accountClient.getId());
|
||||
|
@ -1110,6 +1115,10 @@ public abstract class AbstractMigrationTest extends AbstractKeycloakTest {
|
|||
testMigrationTo24_0_0(testUserProfileMigration, testLdapUseTruststoreSpiMigration);
|
||||
}
|
||||
|
||||
protected void testMigrationTo25_x() {
|
||||
testMigrationTo25_0_0();
|
||||
}
|
||||
|
||||
protected void testMigrationTo7_x(boolean supportedAuthzServices) {
|
||||
if (supportedAuthzServices) {
|
||||
testDecisionStrategySetOnResourceServer();
|
||||
|
|
|
@ -68,6 +68,7 @@ public class JsonFileImport1903MigrationTest extends AbstractJsonFileImportMigra
|
|||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(true);
|
||||
testMigrationTo24_x(true, true);
|
||||
testMigrationTo25_0_0();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -79,6 +79,7 @@ public class JsonFileImport198MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(false);
|
||||
testMigrationTo24_x(false);
|
||||
testMigrationTo25_0_0();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -73,6 +73,7 @@ public class JsonFileImport255MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(false);
|
||||
testMigrationTo24_x(false);
|
||||
testMigrationTo25_0_0();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ public class JsonFileImport343MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(false);
|
||||
testMigrationTo24_x(false);
|
||||
testMigrationTo25_0_0();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public class JsonFileImport483MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(false);
|
||||
testMigrationTo24_x(false);
|
||||
testMigrationTo25_0_0();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ public class JsonFileImport903MigrationTest extends AbstractJsonFileImportMigrat
|
|||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(false);
|
||||
testMigrationTo24_x(false);
|
||||
testMigrationTo25_0_0();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -70,5 +70,6 @@ public class MigrationTest extends AbstractMigrationTest {
|
|||
testMigrationTo22_x();
|
||||
testMigrationTo23_x(true);
|
||||
testMigrationTo24_x(true, true);
|
||||
testMigrationTo25_0_0();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
OAuthClient.AccessTokenResponse response = browserLogin(TEST_CLIENT_SECRET, TEST_USER_NAME, TEST_USER_PASSWORD).tokenResponse;
|
||||
String accessToken = response.getAccessToken();
|
||||
logger.debug("accessToken:" + accessToken);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, false);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, false, false);
|
||||
|
||||
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
|
||||
String tokenResponse = oauth.introspectAccessTokenWithClientCredential(RESOURCE_SERVER_CLIENT_ID, RESOURCE_SERVER_CLIENT_PASSWORD, accessToken);
|
||||
|
@ -158,7 +158,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
OAuthClient.AccessTokenResponse response = browserLogin(TEST_CLIENT_SECRET, TEST_USER_NAME, TEST_USER_PASSWORD).tokenResponse;
|
||||
String accessToken = response.getAccessToken();
|
||||
logger.debug("accessToken:" + accessToken);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true, false);
|
||||
|
||||
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
|
||||
String tokenResponse = oauth.introspectAccessTokenWithClientCredential(RESOURCE_SERVER_CLIENT_ID, RESOURCE_SERVER_CLIENT_PASSWORD, accessToken);
|
||||
|
@ -180,7 +180,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
OAuthClient.AccessTokenResponse response = browserLogin(TEST_CLIENT_SECRET, TEST_USER_NAME, TEST_USER_PASSWORD).tokenResponse;
|
||||
String accessToken = response.getAccessToken();
|
||||
logger.debug("accessToken:" + accessToken);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true, false);
|
||||
|
||||
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
|
||||
String tokenResponse = oauth.introspectAccessTokenWithClientCredential(RESOURCE_SERVER_CLIENT_ID, RESOURCE_SERVER_CLIENT_PASSWORD, accessToken);
|
||||
|
@ -204,7 +204,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
String accessToken = response.getAccessToken();
|
||||
logger.debug("accessToken:" + accessToken);
|
||||
logger.debug("idtoken:" + response.getIdToken());
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, false);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, false, false);
|
||||
|
||||
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
|
||||
removeSession(ctx.userSessionId);
|
||||
|
@ -227,7 +227,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
OAuthClient.AccessTokenResponse response = oauth.doClientCredentialsGrantAccessTokenRequest(TEST_CLIENT_SECRET);
|
||||
String accessToken = response.getAccessToken();
|
||||
logger.debug("accessToken:" + accessToken);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), false, false);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), false, false, true);
|
||||
|
||||
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
|
||||
String tokenResponse = oauth.introspectAccessTokenWithClientCredential(RESOURCE_SERVER_CLIENT_ID, RESOURCE_SERVER_CLIENT_PASSWORD, accessToken);
|
||||
|
@ -249,7 +249,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
OAuthClient.AccessTokenResponse response = browserLogin(TEST_CLIENT_SECRET, TEST_USER_NAME, TEST_USER_PASSWORD).tokenResponse;
|
||||
String accessToken = response.getAccessToken();
|
||||
logger.debug("accessToken:" + accessToken);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, false);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, false, false);
|
||||
response = oauth.doTokenExchange(TEST, accessToken, null, TEST_CLIENT, TEST_CLIENT_SECRET);
|
||||
String exchangedTokenString = response.getAccessToken();
|
||||
logger.debug("exchangedTokenString:" + exchangedTokenString);
|
||||
|
@ -276,7 +276,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
OAuthClient.AuthorizationEndpointResponse authsEndpointResponse = oauth.doLogin(TEST_USER_NAME, TEST_USER_PASSWORD);
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), TEST_CLIENT_SECRET);
|
||||
String accessToken = tokenResponse.getAccessToken();
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, false);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, false, true);
|
||||
logger.debug("lightweight access token:" + accessToken);
|
||||
|
||||
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
|
||||
|
@ -292,7 +292,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), TEST_CLIENT_SECRET);
|
||||
accessToken = tokenResponse.getAccessToken();
|
||||
logger.debug("access token:" + accessToken);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true, false);
|
||||
} finally {
|
||||
deleteProtocolMappers(protocolMappers);
|
||||
}
|
||||
|
@ -312,7 +312,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), TEST_CLIENT_SECRET);
|
||||
String accessToken = tokenResponse.getAccessToken();
|
||||
logger.debug("access token:" + accessToken);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true, true);
|
||||
|
||||
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
|
||||
String introspectResponse = oauth.introspectAccessTokenWithClientCredential(RESOURCE_SERVER_CLIENT_ID, RESOURCE_SERVER_CLIENT_PASSWORD, accessToken);
|
||||
|
@ -337,7 +337,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
OAuthClient.AuthorizationEndpointResponse authsEndpointResponse = oauth.doLogin(TEST_USER_NAME, TEST_USER_PASSWORD);
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), TEST_CLIENT_SECRET);
|
||||
String accessToken = tokenResponse.getAccessToken();
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, false);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, false, true);
|
||||
logger.debug("lightweight access token:" + accessToken);
|
||||
|
||||
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
|
||||
|
@ -353,7 +353,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), TEST_CLIENT_SECRET);
|
||||
accessToken = tokenResponse.getAccessToken();
|
||||
logger.debug("access token:" + accessToken);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true, false);
|
||||
} finally {
|
||||
deleteProtocolMappers(protocolMappers);
|
||||
}
|
||||
|
@ -373,7 +373,7 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), TEST_CLIENT_SECRET);
|
||||
String accessToken = tokenResponse.getAccessToken();
|
||||
logger.debug("access token:" + accessToken);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true);
|
||||
assertAccessToken(oauth.verifyToken(accessToken), true, true, true);
|
||||
|
||||
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
|
||||
String introspectResponse = oauth.introspectAccessTokenWithClientCredential(RESOURCE_SERVER_CLIENT_ID, RESOURCE_SERVER_CLIENT_PASSWORD, accessToken);
|
||||
|
@ -441,10 +441,8 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
Assert.assertNotNull(token.getType());
|
||||
if (isAuthCodeFlow) {
|
||||
Assert.assertNotNull(token.getSessionId());
|
||||
Assert.assertNotNull(token.getAuth_time());
|
||||
} else {
|
||||
Assert.assertNull(token.getSessionId());
|
||||
Assert.assertNull(token.getAuth_time());
|
||||
}
|
||||
Assert.assertNotNull(token.getIssuedFor());
|
||||
Assert.assertNotNull(token.getScope());
|
||||
|
@ -452,16 +450,25 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
|
|||
Assert.assertNotNull(token.getSubject());
|
||||
}
|
||||
|
||||
private void assertBasicClaims(AccessToken token, boolean missing) {
|
||||
if (missing) {
|
||||
Assert.assertNull(token.getAuth_time());
|
||||
} else {
|
||||
Assert.assertNotNull(token.getAuth_time());
|
||||
}
|
||||
}
|
||||
|
||||
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 assertAccessToken(AccessToken token, boolean isAuthCodeFlow, boolean isAddToAccessToken) {
|
||||
private void assertAccessToken(AccessToken token, boolean isAuthCodeFlow, boolean isAddToAccessToken, boolean missingBasicClaims) {
|
||||
Assert.assertNull(token.getNonce());
|
||||
assertMapperClaims(token, isAddToAccessToken, isAuthCodeFlow);
|
||||
assertInitClaims(token, isAuthCodeFlow);
|
||||
assertBasicClaims(token, missingBasicClaims);
|
||||
}
|
||||
|
||||
private void assertTokenIntrospectionResponse(AccessToken token, boolean isAuthCodeFlow) {
|
||||
|
|
|
@ -421,7 +421,7 @@ public class OIDCWellKnownProviderTest extends AbstractKeycloakTest {
|
|||
|
||||
private void assertScopesSupportedMatchesWithRealm(OIDCConfigurationRepresentation oidcConfig) {
|
||||
Assert.assertNames(oidcConfig.getScopesSupported(), OAuth2Constants.SCOPE_OPENID, OAuth2Constants.OFFLINE_ACCESS,
|
||||
OAuth2Constants.SCOPE_PROFILE, OAuth2Constants.SCOPE_EMAIL, OAuth2Constants.SCOPE_PHONE, OAuth2Constants.SCOPE_ADDRESS, OIDCLoginProtocolFactory.ACR_SCOPE,
|
||||
OAuth2Constants.SCOPE_PROFILE, OAuth2Constants.SCOPE_EMAIL, OAuth2Constants.SCOPE_PHONE, OAuth2Constants.SCOPE_ADDRESS, OIDCLoginProtocolFactory.ACR_SCOPE, OIDCLoginProtocolFactory.BASIC_SCOPE,
|
||||
OIDCLoginProtocolFactory.ROLES_SCOPE, OIDCLoginProtocolFactory.WEB_ORIGINS_SCOPE, OIDCLoginProtocolFactory.MICROPROFILE_JWT_SCOPE);
|
||||
}
|
||||
|
||||
|
|
|
@ -1158,8 +1158,32 @@
|
|||
"consentRequired" : false,
|
||||
"config" : { }
|
||||
} ]
|
||||
} ],
|
||||
"defaultDefaultClientScopes" : [ "roles", "profile", "role_list", "email", "web-origins" ],
|
||||
}, {
|
||||
"id": "6e26ea6e-8d9e-4bb4-a335-df23baea8a89",
|
||||
"name": "acr",
|
||||
"description": "OpenID Connect scope for add acr (authentication context class reference) to the token",
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"include.in.token.scope": "false",
|
||||
"display.on.consent.screen": "false"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"id": "7b7b4a03-9b67-4a0c-a154-e36d8b2e251c",
|
||||
"name": "acr loa level",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-acr-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"id.token.claim": "true",
|
||||
"introspection.token.claim": "true",
|
||||
"access.token.claim": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"defaultDefaultClientScopes" : [ "roles", "profile", "role_list", "email", "web-origins", "acr" ],
|
||||
"defaultOptionalClientScopes" : [ "phone", "offline_access", "address", "microprofile-jwt" ],
|
||||
"browserSecurityHeaders" : {
|
||||
"contentSecurityPolicyReportOnly" : "",
|
||||
|
@ -2892,7 +2916,7 @@
|
|||
"config" : { }
|
||||
} ]
|
||||
} ],
|
||||
"defaultDefaultClientScopes" : [ "web-origins", "roles", "role_list", "email", "profile" ],
|
||||
"defaultDefaultClientScopes" : [ "web-origins", "roles", "role_list", "email", "profile", "acr" ],
|
||||
"defaultOptionalClientScopes" : [ "address", "offline_access", "microprofile-jwt", "phone" ],
|
||||
"browserSecurityHeaders" : {
|
||||
"contentSecurityPolicyReportOnly" : "",
|
||||
|
@ -2953,7 +2977,7 @@
|
|||
"id": "88cef18c-bcd8-40d2-9e7d-d257298317f2",
|
||||
"providerId": "declarative-user-profile",
|
||||
"subComponents": {},
|
||||
"config": {
|
||||
"config": {
|
||||
"config-piece-0" : [ "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}}},{\"name\":\"email\",\"displayName\":\"${email}\",\"permissions\":{\"edit\":[\"admin\",\"user\"],\"view\":[\"admin\",\"user\"]},\"validations\":{\"email\":{},\"length\":{\"max\":255},\"pattern\":{\"pattern\":\"[a-zA-Z0-9!#$%&'*+\/=?^_`{|}~.-]+@example.nl\",\"error-message\":\"Invalid domain selected\"}},\"annotations\":{\"\":\"\"},\"required\":{\"roles\":[\"user\"]},\"group\":null},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}}},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}}}]}" ],
|
||||
"config-pieces-count" : [ "1" ]
|
||||
}
|
||||
|
@ -5140,7 +5164,7 @@
|
|||
}
|
||||
} ]
|
||||
} ],
|
||||
"defaultDefaultClientScopes" : [ "profile", "role_list", "roles", "web-origins", "email" ],
|
||||
"defaultDefaultClientScopes" : [ "profile", "role_list", "roles", "web-origins", "email", "acr" ],
|
||||
"defaultOptionalClientScopes" : [ "offline_access", "phone", "microprofile-jwt", "address" ],
|
||||
"browserSecurityHeaders" : {
|
||||
"contentSecurityPolicyReportOnly" : "",
|
||||
|
|
Loading…
Reference in a new issue