Ignore include in token scope for refresh token
Closes #12326 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
parent
5e00fe8b10
commit
8c3f7cc6e9
4 changed files with 60 additions and 7 deletions
|
@ -54,6 +54,8 @@ public interface ClientSessionContext {
|
|||
|
||||
String getScopeString();
|
||||
|
||||
String getScopeString(boolean ignoreIncludeInTokenScope);
|
||||
|
||||
void setAttribute(String name, Object value);
|
||||
|
||||
<T> T getAttribute(String attribute, Class<T> clazz);
|
||||
|
|
|
@ -1094,6 +1094,7 @@ public class TokenManager {
|
|||
ClientScopeModel offlineAccessScope = KeycloakModelUtils.getClientScopeByName(realm, OAuth2Constants.OFFLINE_ACCESS);
|
||||
boolean offlineTokenRequested = offlineAccessScope==null ? false : clientSessionCtx.getClientScopeIds().contains(offlineAccessScope.getId());
|
||||
generateRefreshToken(offlineTokenRequested);
|
||||
refreshToken.setScope(clientSessionCtx.getScopeString(true));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -166,8 +166,13 @@ public class DefaultClientSessionContext implements ClientSessionContext {
|
|||
|
||||
@Override
|
||||
public String getScopeString() {
|
||||
return getScopeString(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScopeString(boolean ignoreIncludeInTokenScope) {
|
||||
if (Profile.isFeatureEnabled(Profile.Feature.DYNAMIC_SCOPES)) {
|
||||
String scopeParam = buildScopesStringFromAuthorizationRequest();
|
||||
String scopeParam = buildScopesStringFromAuthorizationRequest(ignoreIncludeInTokenScope);
|
||||
logger.tracef("Generated scope param with Dynamic Scopes enabled: %1s", scopeParam);
|
||||
String scopeSent = clientSession.getNote(OAuth2Constants.SCOPE);
|
||||
if (TokenUtil.isOIDCRequest(scopeSent)) {
|
||||
|
@ -178,7 +183,7 @@ public class DefaultClientSessionContext implements ClientSessionContext {
|
|||
// Add both default and optional scopes to scope parameter. Don't add client itself
|
||||
String scopeParam = getClientScopesStream()
|
||||
.filter(((Predicate<ClientScopeModel>) ClientModel.class::isInstance).negate())
|
||||
.filter(ClientScopeModel::isIncludeInTokenScope)
|
||||
.filter(scope-> scope.isIncludeInTokenScope() || ignoreIncludeInTokenScope)
|
||||
.map(ClientScopeModel::getName)
|
||||
.collect(Collectors.joining(" "));
|
||||
|
||||
|
@ -196,12 +201,14 @@ public class DefaultClientSessionContext implements ClientSessionContext {
|
|||
* they should be included in tokens or not.
|
||||
* Then return the scope name from the data stored in the RAR object representation.
|
||||
*
|
||||
* @param ignoreIncludeInTokenScope ignore include in token scope from client scope options
|
||||
*
|
||||
* @return see description
|
||||
*/
|
||||
private String buildScopesStringFromAuthorizationRequest() {
|
||||
private String buildScopesStringFromAuthorizationRequest(boolean ignoreIncludeInTokenScope) {
|
||||
return AuthorizationContextUtil.getAuthorizationRequestContextFromScopes(session, clientSession.getNote(OAuth2Constants.SCOPE)).getAuthorizationDetailEntries().stream()
|
||||
.filter(authorizationDetails -> authorizationDetails.getSource().equals(AuthorizationRequestSource.SCOPE))
|
||||
.filter(authorizationDetails -> authorizationDetails.getClientScope().isIncludeInTokenScope())
|
||||
.filter(authorizationDetails -> authorizationDetails.getClientScope().isIncludeInTokenScope() || ignoreIncludeInTokenScope)
|
||||
.filter(authorizationDetails -> isClientScopePermittedForUser(authorizationDetails.getClientScope()))
|
||||
.map(authorizationDetails -> authorizationDetails.getAuthorizationDetails().getScopeNameFromCustomData())
|
||||
.collect(Collectors.joining(" "));
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
import org.keycloak.models.utils.SessionTimeoutHelper;
|
||||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.IDToken;
|
||||
|
@ -537,7 +538,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
oauth.scope(optionalScope);
|
||||
OAuthClient.AccessTokenResponse response1 = oauth.doGrantAccessTokenRequest("password", "test-user@localhost", "password");
|
||||
RefreshToken refreshToken1 = oauth.parseRefreshToken(response1.getRefreshToken());
|
||||
AbstractOIDCScopeTest.assertScopes("openid email phone address profile", refreshToken1.getScope());
|
||||
AbstractOIDCScopeTest.assertScopes("openid basic email roles web-origins acr profile address phone", refreshToken1.getScope());
|
||||
|
||||
setTimeOffset(2);
|
||||
|
||||
|
@ -548,7 +549,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
AbstractOIDCScopeTest.assertScopes("openid email phone profile", response2.getScope());
|
||||
RefreshToken refreshToken2 = oauth.parseRefreshToken(response2.getRefreshToken());
|
||||
assertNotNull(refreshToken2);
|
||||
AbstractOIDCScopeTest.assertScopes("openid email phone address profile", refreshToken2.getScope());
|
||||
AbstractOIDCScopeTest.assertScopes("openid acr roles phone address email profile basic web-origins", refreshToken2.getScope());
|
||||
|
||||
} finally {
|
||||
setTimeOffset(0);
|
||||
|
@ -566,7 +567,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
|
||||
OAuthClient.AccessTokenResponse response1 = oauth.doAccessTokenRequest(code, "password");
|
||||
RefreshToken refreshToken1 = oauth.parseRefreshToken(response1.getRefreshToken());
|
||||
AbstractOIDCScopeTest.assertScopes("openid email profile", refreshToken1.getScope());
|
||||
AbstractOIDCScopeTest.assertScopes("openid basic email roles web-origins acr profile", refreshToken1.getScope());
|
||||
|
||||
setTimeOffset(2);
|
||||
|
||||
|
@ -582,6 +583,48 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshWithOptionalClientScopeWithIncludeInTokenScopeDisabled() throws Exception {
|
||||
//set roles client scope as optional
|
||||
ClientScopeRepresentation rolesScope = ApiUtil.findClientScopeByName(adminClient.realm("test"), OIDCLoginProtocolFactory.ROLES_SCOPE).toRepresentation();
|
||||
ClientManager.realm(adminClient.realm("test")).clientId(oauth.getClientId()).removeClientScope(rolesScope.getId(),true);
|
||||
ClientManager.realm(adminClient.realm("test")).clientId(oauth.getClientId()).addClientScope(rolesScope.getId(),false);
|
||||
|
||||
try {
|
||||
oauth.scope("roles");
|
||||
oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
|
||||
OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(code, "password");
|
||||
AccessToken accessToken = oauth.verifyToken(response.getAccessToken());
|
||||
RefreshToken refreshToken = oauth.parseRefreshToken(response.getRefreshToken());
|
||||
|
||||
AbstractOIDCScopeTest.assertScopes("openid email profile", accessToken.getScope());
|
||||
AbstractOIDCScopeTest.assertScopes("openid basic email roles web-origins acr profile", refreshToken.getScope());
|
||||
|
||||
Assert.assertNotNull(accessToken.getRealmAccess());
|
||||
Assert.assertNotNull(accessToken.getResourceAccess());
|
||||
|
||||
oauth.scope(null);
|
||||
|
||||
response = oauth.doRefreshTokenRequest(response.getRefreshToken(), "password");
|
||||
|
||||
accessToken = oauth.verifyToken(response.getAccessToken());
|
||||
refreshToken = oauth.parseRefreshToken(response.getRefreshToken());
|
||||
|
||||
AbstractOIDCScopeTest.assertScopes("openid email profile", accessToken.getScope());
|
||||
AbstractOIDCScopeTest.assertScopes("openid basic email roles web-origins acr profile", refreshToken.getScope());
|
||||
|
||||
Assert.assertNotNull(accessToken.getRealmAccess());
|
||||
Assert.assertNotNull(accessToken.getResourceAccess());
|
||||
|
||||
} finally {
|
||||
ClientManager.realm(adminClient.realm("test")).clientId(oauth.getClientId()).removeClientScope(rolesScope.getId(),false);
|
||||
ClientManager.realm(adminClient.realm("test")).clientId(oauth.getClientId()).addClientScope(rolesScope.getId(),true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshTokenReuseTokenWithRefreshTokensRevoked() throws Exception {
|
||||
try {
|
||||
|
|
Loading…
Reference in a new issue