8000 Add idp alias to store federated tokens in user session notes by graziang · Pull Request #50592 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public C getConfig() {
return this.config;
}

protected String getFederatedAccessTokenKey() {
return FEDERATED_ACCESS_TOKEN + ":" + getConfig().getAlias();
}

@Override
public void close() {
// no-op
Expand Down Expand Up @@ -230,14 +234,34 @@ public IdentityProviderDataMarshaller getMarshaller() {
return new DefaultDataMarshaller();
}

protected String getFederatedAccessToken(UserSessionModel userSession) {
// return the FEDERATED_ACCESS_TOKEN but just if logged in using this identity provider
if (getConfig().getAlias().equals(userSession.getNote(Details.IDENTITY_PROVIDER))) {
return userSession.getNote(FEDERATED_ACCESS_TOKEN);
protected String getFederatedTokenNote(UserSessionModel userSession, String namespacedKey, String legacyKey) {
String value = userSession.getNote(namespacedKey);
if (value != null) {
return value;
}
// Fallback to un-namespaced key for backward compatibility
String brokerId = userSession.getNote(Details.IDENTITY_PROVIDER);
if (brokerId == null) {
brokerId = userSession.getNote(EXTERNAL_IDENTITY_PROVIDER);
}
if (getConfig().getAlias().equals(brokerId)) {
return userSession.getNote(legacyKey);
}
return null;
}

protected String getFederatedAccessToken(UserSessionModel userSession) {
return getFederatedTokenNote(userSession, getFederatedAccessTokenKey(), FEDERATED_ACCESS_TOKEN);
}

protected void setFederatedAccessToken(UserSessionModel userSession, String token) {
userSession.setNote(getFederatedAccessTokenKey(), token);
}

protected void setFederatedAccessToken(AuthenticationSessionModel authSession, String token) {
authSession.setUserSessionNote(getFederatedAccessTokenKey(), token);
}

protected Response buildTokenResponse(UriInfo uriInfo, EventBuilder event, ClientModel authorizedClient,
UserSessionModel tokenUserSession, AccessTokenResponse tokenResponse, String issuedTokenType) {
tokenResponse.setIdToken(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,55 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro

public static final String FEDERATED_REFRESH_TOKEN = "FEDERATED_REFRESH_TOKEN";
public static final String FEDERATED_TOKEN_EXPIRATION = "FEDERATED_TOKEN_EXPIRATION";

protected String getFederatedRefreshTokenKey() {
return FEDERATED_REFRESH_TOKEN + ":" + getConfig().getAlias();
}

protected String getFederatedTokenExpirationKey() {
return FEDERATED_TOKEN_EXPIRATION + ":" + getConfig().getAlias();
}

protected String getFederatedIdTokenKey() {
return OIDCIdentityProvider.FEDERATED_ID_TOKEN + ":" + getConfig().getAlias();
}

protected String getFederatedRefreshToken(UserSessionModel userSession) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While an alias check is performed in AbstractIdentityProvider.getFederatedAccessToken(), a similar check is missing from getFederatedRefreshToken(), getFederatedTokenExpiration(), and getFederatedIdToken(). Is this intentional?"

return getFederatedTokenNote(userSession, getFederatedRefreshTokenKey(), FEDERATED_REFRESH_TOKEN);
}

protected String getFederatedTokenExpiration(UserSessionModel userSession) {
return getFederatedTokenNote(userSession, getFederatedTokenExpirationKey(), FEDERATED_TOKEN_EXPIRATION);
}

protected String getFederatedIdToken(UserSessionModel userSession) {
return getFederatedTokenNote(userSession, getFederatedIdTokenKey(), OIDCIdentityProvider.FEDERATED_ID_TOKEN);
}

protected void setFederatedRefreshToken(UserSessionModel userSession, String token) {
userSession.setNote(getFederatedRefreshTokenKey(), token);
}

protected void setFederatedRefreshToken(AuthenticationSessionModel authSession, String token) {
authSession.setUserSessionNote(getFederatedRefreshTokenKey(), token);
}

protected void setFederatedTokenExpiration(UserSessionModel userSession, String value) {
userSession.setNote(getFederatedTokenExpirationKey(), value);
}

protected void setFederatedTokenExpiration(AuthenticationSessionModel authSession, String value) {
authSession.setUserSessionNote(getFederatedTokenExpirationKey(), value);
}

protected void setFederatedIdToken(UserSessionModel userSession, String token) {
userSession.setNote(getFederatedIdTokenKey(), token);
}

protected void setFederatedIdToken(AuthenticationSessionModel authSession, String token) {
authSession.setUserSessionNote(getFederatedIdTokenKey(), token);
}

public static final String ACCESS_DENIED = "access_denied";
protected static ObjectMapper mapper = new ObjectMapper();

Expand Down < 8000 tool-tip id="tooltip-d049f37a-6166-4dad-9837-016fa6e1286d" for="expand-up-link-56-diff-bac0e59c0cd0e28567be1faa0d9e80772ce6673025e8bd7ad5117728c8d2b15c" popover="manual" data-direction="ne" data-type="label" data-view-component="true" class="sr-only position-absolute">Expand Up @@ -312,10 +361,8 @@ public Response retrieveToken(KeycloakSession session, FederatedIdentityModel id
Response response = null;
if (userSession != null && getConfig().isStoreTokenInSession()) {
// use the session if present and configured to be used, only in V2
String brokerId = userSession.getNote(Details.IDENTITY_PROVIDER);
brokerId = brokerId == null ? userSession.getNote(UserAuthenticationIdentityProvider.EXTERNAL_IDENTITY_PROVIDER) : brokerId;
String federatedAccessToken = userSession.getNote(FEDERATED_ACCESS_TOKEN);
if (getConfig().getAlias().equals(brokerId) && federatedAccessToken != null) {
String federatedAccessToken = getFederatedAccessToken(userSession);
if (federatedAccessToken != null) {
response = exchangeSessionToken(uriInfo, null, null, userSession, user);
if (Response.Status.Family.familyOf(response.getStatus()) == Response.Status.Family.SUCCESSFUL) {
return response;
Expand Down Expand Up @@ -413,17 +460,16 @@ public Response exchangeFromToken(UriInfo uriInfo, EventBuilder event, ClientMod
*/
protected Response hasExternalExchangeToken(EventBuilder event, UserSessionModel tokenUserSession, MultivaluedMap<String, String> params) {
if (getConfig().getAlias().equals(tokenUserSession.getNote(OIDCIdentityProvider.EXCHANGE_PROVIDER))) {

String requestedType = params.getFirst(OAuth2Constants.REQUESTED_TOKEN_TYPE);
if ((requestedType == null || requestedType.equals(OAuth2Constants.ACCESS_TOKEN_TYPE))) {
String accessToken = tokenUserSession.getNote(FEDERATED_ACCESS_TOKEN);
String accessToken = getFederatedAccessToken(tokenUserSession);
if (accessToken != null) {
AccessTokenResponse tokenResponse = new AccessTokenResponse();
tokenResponse.setToken(accessToken);
return buildTokenResponse(session.getContext().getUri(), event, null, tokenUserSession, tokenResponse, OAuth2Constants.ACCESS_TOKEN_TYPE);
}
} else if (OAuth2Constants.ID_TOKEN_TYPE.equals(requestedType)) {
String idToken = tokenUserSession.getNote(OIDCIdentityProvider.FEDERATED_ID_TOKEN);
String idToken = getFederatedIdToken(tokenUserSession);
if (idToken != null) {
AccessTokenResponse tokenResponse = new AccessTokenResponse();
tokenResponse.setToken(null);
Expand Down Expand Up @@ -472,7 +518,7 @@ protected Response exchangeStoredToken(UriInfo uriInfo, EventBuilder event, Clie
}

protected Response exchangeSessionToken(UriInfo uriInfo, EventBuilder event, ClientModel authorizedClient, UserSessionModel tokenUserSession, UserModel tokenSubject) {
String accessToken = tokenUserSession.getNote(FEDERATED_ACCESS_TOKEN);
String accessToken = getFederatedAccessToken(tokenUserSession);

if (accessToken == null) {
if (event != null) {
Expand Down Expand Up @@ -620,7 +666,9 @@ public JsonNode asJsonNode(String json) throws IOException {
@Override
public void authenticationFinished(AuthenticationSessionModel authSession, BrokeredIdentityContext context) {
String token = (String) context.getContextData().get(FEDERATED_ACCESS_TOKEN);
if (token != null) authSession.setUserSessionNote(FEDERATED_ACCESS_TOKEN, token);
if (token != null) {
setFederatedAccessToken(authSession, token);
}
}

public SimpleHttpRequest authenticateTokenRequest(final SimpleHttpRequest tokenRequest) {
Expand Down Expand Up @@ -1076,10 +1124,12 @@ protected BrokeredIdentityContext exchangeExternalUserInfoValidationOnly(EventBu
public void exchangeExternalComplete(UserSessionModel userSession, BrokeredIdentityContext context, MultivaluedMap<String, String> params) {
if (getConfig().isStoreTokenInSession()) {
if (context.getContextData().containsKey(OIDCIdentityProvider.VALIDATED_ACCESS_TOKEN)) {
userSession.setNote(FEDERATED_ACCESS_TOKEN, params.getFirst(OAuth2Constants.SUBJECT_TOKEN));
String subjectToken = params.getFirst(OAuth2Constants.SUBJECT_TOKEN);
setFederatedAccessToken(userSession, subjectToken);
}
if (context.getContextData().containsKey(OIDCIdentityProvider.VALIDATED_ID_TOKEN)) {
userSession.setNote(OIDCIdentityProvider.FEDERATED_ID_TOKEN, params.getFirst(OAuth2Constants.SUBJECT_TOKEN));
String subjectToken = params.getFirst(OAuth2Constants.SUBJECT_TOKEN);
setFederatedIdToken(userSession, subjectToken);
}
userSession.setNote(OIDCIdentityProvider.EXCHANGE_PROVIDER, getConfig().getAlias());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public boolean isType(KeycloakSession session, IdentityProviderType type) {
* @return
*/
public String refreshTokenForLogout(KeycloakSession session, UserSessionModel userSession) {
String refreshToken = userSession.getNote(FEDERATED_REFRESH_TOKEN);
String refreshToken = getFederatedRefreshToken(userSession);
try (VaultStringSecret vaultStringSecret = session.vault().getStringSecret(getConfig().getClientSecret())) {
return getRefreshTokenRequest(session, refreshToken, getConfig().getClientId(), vaultStringSecret.get().orElse(getConfig().getClientSecret())).asString();
} catch (IOException e) {
Expand All @@ -164,7 +164,7 @@ public String refreshTokenForLogout(KeycloakSession session, UserSessionModel us
public void backchannelLogout(KeycloakSession session, UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
if (getConfig().getLogoutUrl() == null || getConfig().getLogoutUrl().trim().equals("") || !getConfig().isBackchannelSupported())
return;
String idToken = userSession.getNote(FEDERATED_ID_TOKEN);
String idToken = getFederatedIdToken(userSession);
if (idToken == null) return;
backchannelLogout(userSession, idToken);
}
Expand Down Expand Up @@ -195,7 +195,7 @@ protected void backchannelLogout(UserSessionModel userSession, String idToken) {
@Override
public Response keycloakInitiatedBrowserLogout(KeycloakSession session, UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
if (getConfig().getLogoutUrl() == null || getConfig().getLogoutUrl().trim().equals("")) return null;
String idToken = userSession.getNote(FEDERATED_ID_TOKEN);
String idToken = getFederatedIdToken(userSession);
if (getConfig().isBackchannelSupported()) {
backchannelLogout(userSession, idToken);
return null;
Expand Down Expand Up @@ -257,7 +257,7 @@ protected Response exchangeStoredToken(UriInfo uriInfo, EventBuilder event, Clie
updateStoredTokenModel(realm, tokenSubject, model, currentTime, newResponse, tokenResponse);

if (tokenUserSession != null) {
String oldToken = tokenUserSession.getNote(FEDERATED_ACCESS_TOKEN);
String oldToken = getFederatedAccessToken(tokenUserSession);
if (oldToken != null && oldToken.equals(tokenResponse.getToken())) {
updateUserSessionFromRefresh(tokenUserSession, newResponse, currentTime);
}
Expand All @@ -277,8 +277,8 @@ protected Response exchangeStoredToken(UriInfo uriInfo, EventBuilder event, Clie
@Override
protected Response exchangeSessionToken(UriInfo uriInfo, EventBuilder event, ClientModel authorizedClient, UserSessionModel tokenUserSession, UserModel tokenSubject) {
RealmModel realm = authorizedClient != null ? authorizedClient.getRealm() : session.getContext().getRealm();
String refreshToken = tokenUserSession.getNote(FEDERATED_REFRESH_TOKEN);
String accessToken = tokenUserSession.getNote(FEDERATED_ACCESS_TOKEN);
String refreshToken = getFederatedRefreshToken(tokenUserSession);
String accessToken = getFederatedAccessToken(tokenUserSession);

if (accessToken == null) {
if (event != null) {
Expand All @@ -289,7 +289,8 @@ protected Response exchangeSessionToken(UriInfo uriInfo, EventBuilder event, Cli
}

try {
long expiration = Long.parseLong(tokenUserSession.getNote(FEDERATED_TOKEN_EXPIRATION));
String expirationNote = getFederatedTokenExpiration(tokenUserSession);
long expiration = Long.parseLong(expirationNote);
final int currentTime = Time.currentTime();

if (expiration == 0 || expiration > currentTime + getConfig().getMinValidityToken()) {
Expand Down Expand Up @@ -360,12 +361,13 @@ private void updateUserSessionFromRefresh(UserSessionModel tokenUserSession, Acc
final boolean isStoreTokenInSession = getConfig().isStoreTokenInSession();
if (isStoreTokenInSession) {
long accessTokenExpiration = newResponse.getExpiresIn() > 0 ? currentTime + newResponse.getExpiresIn() : 0;
tokenUserSession.setNote(FEDERATED_TOKEN_EXPIRATION, Long.toString(accessTokenExpiration));
tokenUserSession.setNote(FEDERATED_REFRESH_TOKEN, newResponse.getRefreshToken());
tokenUserSession.setNote(FEDERATED_ACCESS_TOKEN, newResponse.getToken());
String expirationStr = Long.toString(accessTokenExpiration);
setFederatedTokenExpiration(tokenUserSession, expirationStr);
setFederatedRefreshToken(tokenUserSession, newResponse.getRefreshToken());
setFederatedAccessToken(tokenUserSession, newResponse.getToken());
}
if (newResponse.getIdToken() != null && (isStoreTokenInSession || getConfig().isSendIdTokenOnLogout())) {
tokenUserSession.setNote(FEDERATED_ID_TOKEN, newResponse.getIdToken());
setFederatedIdToken(tokenUserSession, newResponse.getIdToken());
}
}

Expand Down Expand Up @@ -843,12 +845,13 @@ public void authenticationFinished(AuthenticationSessionModel authSession, Broke
if (isStoreTokenInSession) {
int currentTime = Time.currentTime();
long expiration = tokenResponse.getExpiresIn() > 0 ? tokenResponse.getExpiresIn() + currentTime : 0;
authSession.setUserSessionNote(FEDERATED_TOKEN_EXPIRATION, Long.toString(expiration));
authSession.setUserSessionNote(FEDERATED_REFRESH_TOKEN, tokenResponse.getRefreshToken());
authSession.setUserSessionNote(FEDERATED_ACCESS_TOKEN, tokenResponse.getToken());
String expirationStr = Long.toString(expiration);
setFederatedTokenExpiration(authSession, expirationStr);
setFederatedRefreshToken(authSession, tokenResponse.getRefreshToken());
setFederatedAccessToken(authSession, tokenResponse.getToken());
}
if (isStoreTokenInSession || getConfig().isSendIdTokenOnLogout()) {
authSession.setUserSessionNote(FEDERATED_ID_TOKEN, tokenResponse.getIdToken());
setFederatedIdToken(authSession, tokenResponse.getIdToken());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,8 @@ public void authenticationFinished(AuthenticationSessionModel authSession, Broke

}
if (context.getContextData().containsKey(FEDERATED_ACCESS_TOKEN)) {
authSession.setUserSessionNote(FEDERATED_ACCESS_TOKEN, (String) context.getContextData().get(FEDERATED_ACCESS_TOKEN));
String token = (String) context.getContextData().get(FEDERATED_ACCESS_TOKEN);
setFederatedAccessToken(authSession, token);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,9 @@ protected Response exchangeExternalToken(String subjectIssuer, String subjectTok
externalExchangeContext.provider().exchangeExternalComplete(userSession, context, formParams);

// this must exist so that we can obtain access token from user session if idp's store tokens is off
userSession.setNote(UserAuthenticationIdentityProvider.EXTERNAL_IDENTITY_PROVIDER, externalExchangeContext.idpModel().getAlias());
userSession.setNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN, subjectToken);
String idpAlias = externalExchangeContext.idpModel().getAlias();
userSession.setNote(UserAuthenticationIdentityProvider.EXTERNAL_IDENTITY_PROVIDER, idpAlias);
setFederatedAccessTokenNote(userSession, idpAlias, subjectToken);

context.addSessionNotesToUserSession(userSession);

Expand Down Expand Up @@ -433,6 +434,10 @@ protected UserModel importUserFromExternalIdentity(BrokeredIdentityContext conte
return user;
}

private void setFederatedAccessTokenNote(UserSessionModel userSession, String idpAlias, String token) {
userSession.setNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN + ":" + idpAlias, token);
}

// TODO: move to utility class
protected void updateUserSessionFromClientAuth(UserSessionModel userSession) {
for (Map.Entry<String, String> attr : clientAuthAttributes.entrySet()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ protected Response exchangeStoredToken(UriInfo uriInfo, ClientModel authorizedCl
}

protected Response exchangeSessionToken(UriInfo uriInfo, ClientModel authorizedClient, UserSessionModel tokenUserSession, UserModel tokenSubject) {
String accessToken = tokenUserSession.getNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN);
String accessToken = getFederatedAccessToken(tokenUserSession);
if (accessToken == null) {
return exchangeTokenExpired(uriInfo, authorizedClient, tokenUserSession, tokenSubject);
}
Expand Down Expand Up @@ -296,8 +296,8 @@ public Response retrieveToken(KeycloakSession session, FederatedIdentityModel id

@Override
public void authenticationFinished(AuthenticationSessionModel authSession, BrokeredIdentityContext context) {
authSession.setUserSessionNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN, (String) context.getContextData().get(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN));

String token = (String) context.getContextData().get(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN);
setFederatedAccessToken(authSession, token);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,12 @@ default String getTokenFromSession(String realmName, String sessionId) {
return getRunOnServer().fetch(session -> {
RealmModel r = session.realms().getRealmByName(realmName);
UserSessionModel userSession = session.sessions().getUserSession(r, sessionId);
return userSession.getNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN);
// Try alias-namespaced key first, then fall back to legacy un-namespaced key
String token = userSession.getNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN + ":" + IDP_ALIAS);
if (token == null) {
token = userSession.getNote(UserAuthenticationIdentityProvider.FEDERATED_ACCESS_TOKEN);
}
Comment on lines +333 to +337
return token;
}, String.class);
}

Expand Down
Loading
Loading
0