Add option to clients to use lightweight access token

Add an "Always use lightweight access token" option on the client's Advanced tab in the "Advanced Settings" section that uses the already existing Constants.USE_LIGHTWEIGHT_ACCESS_TOKEN_ENABLED to store a boolean client attribute.
The attribute value is used to enable or disable the lightweight access token.
Closes #27238

Signed-off-by: graziang <g.graziano94@gmail.com>
This commit is contained in:
graziang 2024-02-26 16:04:15 +01:00 committed by Marek Posolda
parent 693aa1710f
commit 16a854c91b
5 changed files with 103 additions and 1 deletions

View file

@ -2977,6 +2977,8 @@ authenticatorRefConfig.maxAge.help=The max age in seconds that the authenticator
authenticatorRefConfig.maxAge.label=Authenticator Reference Max Age
includeInLightweight.label=Add to lightweight access token
includeInLightweight.tooltip=Should the claim be added to the lightweight access token?
lightweightAccessToken=Always use lightweight access token
lightweightAccessTokenHelp=If it is On, lightweight access tokens are always used. If it is Off, they are not used by default, but it is still possible to enable them with client policy executor
welcomeTabTitle=Welcome
welcomeTo=Welcome to {{realmDisplayInfo}}
welcomeText=Keycloak provides user federation, strong authentication, user management, fine-grained authorization, and more. Add authentication to applications and secure services with minimum effort. No need to deal with storing users or authenticating users.

View file

@ -266,6 +266,34 @@ export const AdvancedSettings = ({
)}
/>
</FormGroup>
<FormGroup
label={t("lightweightAccessToken")}
fieldId="lightweightAccessToken"
labelIcon={
<HelpItem
helpText={t("lightweightAccessTokenHelp")}
fieldLabelId="lightweightAccessToken"
/>
}
>
<Controller
name={convertAttributeNameToForm<FormFields>(
"attributes.client.use.lightweight.access.token.enabled",
)}
defaultValue="false"
control={control}
render={({ field }) => (
<Switch
id="lightweightAccessToken"
label={t("on")}
labelOff={t("off")}
isChecked={field.value === "true"}
onChange={(value) => field.onChange(value.toString())}
aria-label={t("lightweightAccessToken")}
/>
)}
/>
</FormGroup>
<FormGroup
label={t("acrToLoAMapping")}
fieldId="acrToLoAMapping"

View file

@ -83,7 +83,7 @@ public abstract class AbstractOIDCProtocolMapper implements ProtocolMapper {
boolean getShouldUseLightweightToken(KeycloakSession session) {
Object attributeValue = session.getAttribute(Constants.USE_LIGHTWEIGHT_ACCESS_TOKEN_ENABLED);
return (attributeValue != null) ? (boolean) attributeValue : false;
return Boolean.parseBoolean(session.getContext().getClient().getAttribute(Constants.USE_LIGHTWEIGHT_ACCESS_TOKEN_ENABLED)) || (attributeValue != null && (boolean) attributeValue);
}
public AccessToken transformAccessToken(AccessToken token, ProtocolMapperModel mappingModel, KeycloakSession session,

View file

@ -324,6 +324,68 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
}
}
@Test
public void testAlwaysUseLightWeightFalseTest() throws Exception {
alwaysUseLightWeightAccessToken(true);
ProtocolMappersResource protocolMappers = setProtocolMappers(true, true, false, true);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId(TEST_CLIENT);
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);
logger.debug("lightweight access token:" + accessToken);
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
String introspectResponse = oauth.introspectAccessTokenWithClientCredential(RESOURCE_SERVER_CLIENT_ID, RESOURCE_SERVER_CLIENT_PASSWORD, accessToken);
assertTokenIntrospectionResponse(JsonSerialization.readValue(introspectResponse, AccessToken.class), true, true, false);
logger.debug("tokenResponse:" + introspectResponse);
oauth.clientId(TEST_CLIENT);
alwaysUseLightWeightAccessToken(false);
oauth.doLogout(tokenResponse.getRefreshToken(), TEST_CLIENT_SECRET);
authsEndpointResponse = oauth.doLogin(TEST_USER_NAME, TEST_USER_PASSWORD);
tokenResponse = oauth.doAccessTokenRequest(authsEndpointResponse.getCode(), TEST_CLIENT_SECRET);
accessToken = tokenResponse.getAccessToken();
logger.debug("access token:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), true, true);
} finally {
deleteProtocolMappers(protocolMappers);
}
}
@Test
public void testAlwaysUseLightWeightTrueTest() throws Exception {
alwaysUseLightWeightAccessToken(true);
ProtocolMappersResource protocolMappers = setProtocolMappers(false, true, true, true);
try {
oauth.nonce("123456");
oauth.scope("address");
oauth.clientId(TEST_CLIENT);
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();
logger.debug("access token:" + accessToken);
assertAccessToken(oauth.verifyToken(accessToken), true, true);
oauth.clientId(RESOURCE_SERVER_CLIENT_ID);
String introspectResponse = oauth.introspectAccessTokenWithClientCredential(RESOURCE_SERVER_CLIENT_ID, RESOURCE_SERVER_CLIENT_PASSWORD, accessToken);
logger.debug("tokenResponse:" + introspectResponse);
assertTokenIntrospectionResponse(JsonSerialization.readValue(introspectResponse, AccessToken.class), true, true, false);
} finally {
deleteProtocolMappers(protocolMappers);
alwaysUseLightWeightAccessToken(false);
}
}
private void removeSession(final String sessionId) {
testingClient.testing().removeExpired(REALM_NAME);
try {
@ -593,4 +655,8 @@ public class LightWeightAccessTokenTest extends AbstractClientPoliciesTest {
).toString();
updatePolicies(json);
}
private void alwaysUseLightWeightAccessToken(boolean enable){
ClientManager.realm(adminClient.realm(REALM_NAME)).clientId(TEST_CLIENT).alwaysUseLightweightAccessToken(enable);
}
}

View file

@ -2,6 +2,7 @@ package org.keycloak.testsuite.util;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.models.Constants;
import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
@ -92,6 +93,11 @@ public class ClientManager {
return this;
}
public ClientManagerBuilder alwaysUseLightweightAccessToken(boolean enable) {
updateAttribute(Constants.USE_LIGHTWEIGHT_ACCESS_TOKEN_ENABLED, String.valueOf(enable));
return this;
}
public ClientManagerBuilder fullScopeAllowed(boolean enable) {
ClientRepresentation app = clientResource.toRepresentation();
app.setFullScopeAllowed(enable);