Changed userId value for refresh token events
Closes #28567 Signed-off-by: Giuseppe Graziano <g.graziano94@gmail.com>
This commit is contained in:
parent
33f580daa4
commit
33b747286e
11 changed files with 41 additions and 23 deletions
|
@ -103,6 +103,13 @@ You can use the `Subject (sub)` mapper to configure the `sub` claim only for acc
|
|||
|
||||
The mapper has no effects for service accounts, because no user session exists, and the`sub` claim is always added to the access token.
|
||||
|
||||
= Changed `userId` for events related to refresh token
|
||||
|
||||
The `userId` in the `REFRESH_TOKEN` event is now always taken from user session instead of `sub` claim in the refresh token. The `userId` in the `REFRESH_TOKEN_ERROR` event is now always null.
|
||||
The reason for this change is that the value of the `sub` claim in the refresh token may be null with the introduction of the optional `sub` claim or even different from the real user id when using pairwise subject identifiers or other ways to override the `sub` claim.
|
||||
|
||||
However a `refresh_token_sub` detail is now added as backwards compatibility to have info about the user in the case of missing userId in the `REFRESH_TOKEN_ERROR` event.
|
||||
|
||||
= Default `http-pool-max-threads` reduced
|
||||
|
||||
`http-pool-max-threads` if left unset will default to the greater of 50 or 4 x (available processors). Previously it defaulted to the greater of 200 or 8 x (available processors). Reducing the number or task threads for most usage scenarios will result in slightly higher performance due to less context switching among active threads.
|
||||
|
|
|
@ -51,6 +51,7 @@ public interface Details {
|
|||
String TOKEN_ID = "token_id";
|
||||
String REFRESH_TOKEN_ID = "refresh_token_id";
|
||||
String REFRESH_TOKEN_TYPE = "refresh_token_type";
|
||||
String REFRESH_TOKEN_SUB = "refresh_token_sub";
|
||||
String VALIDATE_ACCESS_TOKEN = "validate_access_token";
|
||||
String UPDATED_REFRESH_TOKEN_ID = "updated_refresh_token_id";
|
||||
String NODE_HOST = "node_host";
|
||||
|
|
|
@ -405,9 +405,14 @@ public class TokenManager {
|
|||
String encodedRefreshToken, EventBuilder event, HttpHeaders headers, HttpRequest request, String scopeParameter) throws OAuthErrorException {
|
||||
RefreshToken refreshToken = verifyRefreshToken(session, realm, authorizedClient, request, encodedRefreshToken, true);
|
||||
|
||||
event.user(refreshToken.getSubject()).session(refreshToken.getSessionState())
|
||||
event.session(refreshToken.getSessionState())
|
||||
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
|
||||
.detail(Details.REFRESH_TOKEN_TYPE, refreshToken.getType());
|
||||
|
||||
if (refreshToken.getSubject() != null) {
|
||||
event.detail(Details.REFRESH_TOKEN_SUB, refreshToken.getSubject());
|
||||
}
|
||||
|
||||
// Setup clientScopes from refresh token to the context
|
||||
String oldTokenScope = refreshToken.getScope();
|
||||
//The requested scope MUST NOT include any scope not originally granted by the resource owner
|
||||
|
@ -430,6 +435,8 @@ public class TokenManager {
|
|||
|
||||
validateTokenReuseForRefresh(session, realm, refreshToken, validation);
|
||||
|
||||
event.user(validation.userSession.getUser());
|
||||
|
||||
if (refreshToken.getAuthorization() != null) {
|
||||
validation.newToken.setAuthorization(refreshToken.getAuthorization());
|
||||
}
|
||||
|
|
|
@ -461,7 +461,7 @@ public class ConsentsTest extends AbstractKeycloakTest {
|
|||
Assert.assertEquals(OAuthErrorException.INVALID_SCOPE, refreshTokenResponse.getError());
|
||||
Assert.assertEquals("Client no longer has requested consent from user", refreshTokenResponse.getErrorDescription());
|
||||
|
||||
events.expectRefresh(accessTokenResponse.getRefreshToken(), sessionId).clearDetails().error(Errors.INVALID_TOKEN).assertEvent();
|
||||
events.expectRefresh(accessTokenResponse.getRefreshToken(), sessionId).user((String) null).clearDetails().error(Errors.INVALID_TOKEN).assertEvent();
|
||||
} finally {
|
||||
clientRepresentation.setConsentRequired(false);
|
||||
adminClient.realm(TEST_REALM_NAME).clients().get(clientRepresentation.getId()).update(clientRepresentation);
|
||||
|
|
|
@ -631,8 +631,8 @@ public final class KcOidcBrokerTransientSessionsTest extends AbstractAdvancedBro
|
|||
events.expectRefresh(offlineToken.getId(), newRefreshToken.getSessionState())
|
||||
.realm(consumerRealmRep)
|
||||
.client(CONSUMER_BROKER_APP_CLIENT_ID)
|
||||
.user((String) null)
|
||||
.error(Errors.INVALID_TOKEN)
|
||||
.user(lwUserId)
|
||||
.clearDetails()
|
||||
.assertEvent();
|
||||
} finally {
|
||||
|
|
|
@ -115,7 +115,7 @@ public class ClientAuthSignedJWTTest extends AbstractClientAuthSignedJWTTest {
|
|||
|
||||
events.expectRefresh(refreshToken.getId(), refreshToken.getSessionState())
|
||||
.client("client1")
|
||||
.user(client1SAUserId)
|
||||
.user((String) null)
|
||||
.removeDetail(Details.TOKEN_ID)
|
||||
.removeDetail(Details.UPDATED_REFRESH_TOKEN_ID)
|
||||
.detail(Details.CLIENT_AUTH_METHOD, JWTClientAuthenticator.PROVIDER_ID)
|
||||
|
|
|
@ -272,8 +272,8 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
|||
|
||||
events.expectRefresh(offlineToken.getId(), newRefreshToken.getSessionState())
|
||||
.client("offline-client")
|
||||
.user((String) null)
|
||||
.error(Errors.INVALID_TOKEN)
|
||||
.user(userId)
|
||||
.clearDetails()
|
||||
.assertEvent();
|
||||
|
||||
|
@ -401,8 +401,8 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
|||
Assert.assertEquals(400, response.getStatusCode());
|
||||
events.expectRefresh(offlineToken.getId(), token.getSessionState())
|
||||
.client("offline-client")
|
||||
.user((String) null)
|
||||
.error(Errors.INVALID_TOKEN)
|
||||
.user(userId)
|
||||
.clearDetails()
|
||||
.assertEvent();
|
||||
|
||||
|
@ -411,8 +411,8 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
|
|||
Assert.assertEquals(400, response2.getStatusCode());
|
||||
events.expectRefresh(offlineToken2.getId(), offlineToken2.getSessionState())
|
||||
.client("offline-client")
|
||||
.user((String) null)
|
||||
.error(Errors.INVALID_TOKEN)
|
||||
.user(userId)
|
||||
.clearDetails()
|
||||
.assertEvent();
|
||||
|
||||
|
|
|
@ -613,12 +613,12 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
|
||||
assertEquals(400, response3.getStatusCode());
|
||||
|
||||
events.expectRefresh(refreshToken1.getId(), sessionId).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
events.expectRefresh(refreshToken1.getId(), sessionId).user((String) null).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
|
||||
// Client session invalidated hence old refresh token not valid anymore
|
||||
OAuthClient.AccessTokenResponse response4 = oauth.doRefreshTokenRequest(response2.getRefreshToken(), "password");
|
||||
assertEquals(400, response4.getStatusCode());
|
||||
events.expectRefresh(refreshToken2.getId(), sessionId).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
events.expectRefresh(refreshToken2.getId(), sessionId).user((String) null).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
} finally {
|
||||
RealmManager.realm(adminClient.realm("test")).revokeRefreshToken(false);
|
||||
}
|
||||
|
@ -656,17 +656,18 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
// Second refresh (allowed).
|
||||
OAuthClient.AccessTokenResponse responseFirstReuse = oauth.doRefreshTokenRequest(initialResponse.getRefreshToken(), "password");
|
||||
RefreshToken newTokenFirstReuse = oauth.parseRefreshToken(responseFirstReuse.getRefreshToken());
|
||||
String userId = newTokenFirstReuse.getSubject();
|
||||
|
||||
assertEquals(200, responseFirstReuse.getStatusCode());
|
||||
|
||||
events.expectRefresh(initialRefreshToken.getId(), sessionId).assertEvent();
|
||||
events.expectRefresh(initialRefreshToken.getId(), sessionId).detail(Details.REFRESH_TOKEN_SUB, userId).assertEvent();
|
||||
|
||||
// Token reused twice, became invalid.
|
||||
OAuthClient.AccessTokenResponse responseSecondReuse = oauth.doRefreshTokenRequest(initialResponse.getRefreshToken(), "password");
|
||||
|
||||
assertEquals(400, responseSecondReuse.getStatusCode());
|
||||
|
||||
events.expectRefresh(initialRefreshToken.getId(), sessionId).removeDetail(Details.TOKEN_ID)
|
||||
events.expectRefresh(initialRefreshToken.getId(), sessionId).user((String) null).detail(Details.REFRESH_TOKEN_SUB, userId).removeDetail(Details.TOKEN_ID)
|
||||
.removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
|
||||
// Refresh token from first use became invalid.
|
||||
|
@ -675,7 +676,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
|
||||
assertEquals(400, responseUseOfInvalidatedRefreshToken.getStatusCode());
|
||||
|
||||
events.expectRefresh(newTokenFirstUse.getId(), sessionId).removeDetail(Details.TOKEN_ID)
|
||||
events.expectRefresh(newTokenFirstUse.getId(), sessionId).user((String) null).removeDetail(Details.TOKEN_ID)
|
||||
.removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
|
||||
// Refresh token from reuse is not valid. Client session was invalidated
|
||||
|
@ -684,7 +685,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
|
||||
assertEquals(400, responseUseOfValidRefreshToken.getStatusCode());
|
||||
|
||||
events.expectRefresh(newTokenFirstReuse.getId(), sessionId).removeDetail(Details.TOKEN_ID)
|
||||
events.expectRefresh(newTokenFirstReuse.getId(), sessionId).user((String) null).removeDetail(Details.TOKEN_ID)
|
||||
.removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
} finally {
|
||||
RealmManager.realm(adminClient.realm("test"))
|
||||
|
@ -725,7 +726,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
|
||||
assertEquals(400, responseReuseExceeded.getStatusCode());
|
||||
|
||||
events.expectRefresh(initialRefreshToken.getId(), sessionId).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
events.expectRefresh(initialRefreshToken.getId(), sessionId).user((String) null).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
} finally {
|
||||
RealmManager.realm(adminClient.realm("test"))
|
||||
.refreshTokenMaxReuse(0)
|
||||
|
@ -760,7 +761,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
|
||||
assertEquals(400, responseReuseExceeded.getStatusCode());
|
||||
|
||||
events.expectRefresh(initialRefreshToken.getId(), sessionId).removeDetail(Details.TOKEN_ID)
|
||||
events.expectRefresh(initialRefreshToken.getId(), sessionId).user((String) null).removeDetail(Details.TOKEN_ID)
|
||||
.removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
|
||||
RealmManager.realm(adminClient.realm("test")).revokeRefreshToken(false);
|
||||
|
@ -768,7 +769,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
// Config changed, token cannot be used again at this point due the client session invalidated
|
||||
OAuthClient.AccessTokenResponse responseReuseExceeded2 = oauth.doRefreshTokenRequest(initialResponse.getRefreshToken(), "password");
|
||||
assertEquals(400, responseReuseExceeded2.getStatusCode());
|
||||
events.expectRefresh(initialRefreshToken.getId(), sessionId).removeDetail(Details.TOKEN_ID)
|
||||
events.expectRefresh(initialRefreshToken.getId(), sessionId).user((String) null).removeDetail(Details.TOKEN_ID)
|
||||
.removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
} finally {
|
||||
RealmManager.realm(adminClient.realm("test"))
|
||||
|
@ -816,7 +817,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
|
||||
assertEquals(400, response3.getStatusCode());
|
||||
|
||||
events.expectRefresh(refreshToken1.getId(), sessionId).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
events.expectRefresh(refreshToken1.getId(), sessionId).removeDetail(Details.TOKEN_ID).user((String) null).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
|
||||
// No client sessions available after revoke
|
||||
Assert.assertFalse(hasClientSessionForTestApp());
|
||||
|
@ -863,7 +864,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
// Try to refresh with one of the old refresh tokens before SSO re-authentication - should fail
|
||||
OAuthClient.AccessTokenResponse response5 = oauth.doRefreshTokenRequest(response2.getRefreshToken(), "password");
|
||||
assertEquals(400, response5.getStatusCode());
|
||||
events.expectRefresh(refreshToken2.getId(), sessionId).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
events.expectRefresh(refreshToken2.getId(), sessionId).user((String) null).removeDetail(Details.TOKEN_ID).removeDetail(Details.UPDATED_REFRESH_TOKEN_ID).error("invalid_token").assertEvent();
|
||||
} finally {
|
||||
resetTimeOffset();
|
||||
RealmManager.realm(adminClient.realm("test")).revokeRefreshToken(false);
|
||||
|
@ -1559,7 +1560,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
assertEquals(400, response.getStatusCode());
|
||||
assertEquals("invalid_grant", response.getError());
|
||||
|
||||
events.expectRefresh(refreshToken.getId(), sessionId).clearDetails().error(Errors.INVALID_TOKEN).assertEvent();
|
||||
events.expectRefresh(refreshToken.getId(), sessionId).user((String) null).clearDetails().error(Errors.INVALID_TOKEN).assertEvent();
|
||||
} finally {
|
||||
UserManager.realm(adminClient.realm("test")).username("test-user@localhost").enabled(true);
|
||||
}
|
||||
|
@ -1589,7 +1590,7 @@ public class RefreshTokenTest extends AbstractKeycloakTest {
|
|||
assertEquals(400, response.getStatusCode());
|
||||
assertEquals("invalid_grant", response.getError());
|
||||
|
||||
events.expectRefresh(refreshToken.getId(), sessionId).user(userId).clearDetails().error(Errors.INVALID_TOKEN).assertEvent();
|
||||
events.expectRefresh(refreshToken.getId(), sessionId).user((String) null).clearDetails().error(Errors.INVALID_TOKEN).assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -452,7 +452,9 @@ public class ResourceOwnerPasswordCredentialsGrantTest extends AbstractKeycloakT
|
|||
assertEquals(400, response.getStatusCode());
|
||||
assertEquals("invalid_grant", response.getError());
|
||||
|
||||
events.expectRefresh(refreshToken.getId(), refreshToken.getSessionState()).client("resource-owner")
|
||||
events.expectRefresh(refreshToken.getId(), refreshToken.getSessionState())
|
||||
.client("resource-owner")
|
||||
.user((String) null)
|
||||
.removeDetail(Details.TOKEN_ID)
|
||||
.removeDetail(Details.UPDATED_REFRESH_TOKEN_ID)
|
||||
.error(Errors.INVALID_TOKEN).assertEvent();
|
||||
|
|
|
@ -240,7 +240,7 @@ public class ServiceAccountTest extends AbstractKeycloakTest {
|
|||
|
||||
events.expectRefresh(refreshToken.getId(), refreshToken.getSessionState())
|
||||
.client("service-account-cl-refresh-on")
|
||||
.user(userIdClRefreshOn)
|
||||
.user((String) null)
|
||||
.removeDetail(Details.TOKEN_ID)
|
||||
.removeDetail(Details.UPDATED_REFRESH_TOKEN_ID)
|
||||
.error(Errors.INVALID_TOKEN).assertEvent();
|
||||
|
|
|
@ -517,7 +517,7 @@ public class OIDCScopeTest extends AbstractOIDCScopeTest {
|
|||
assertEquals(400, refreshResponse.getStatusCode());
|
||||
events.expectRefresh(refreshToken1.getId(), idToken.getSessionState())
|
||||
.client("third-party")
|
||||
.user(userId)
|
||||
.user((String) null)
|
||||
.removeDetail(Details.TOKEN_ID)
|
||||
.removeDetail(Details.REFRESH_TOKEN_ID)
|
||||
.removeDetail(Details.UPDATED_REFRESH_TOKEN_ID)
|
||||
|
|
Loading…
Reference in a new issue