Java Code Examples for org.keycloak.models.UserSessionModel#getNote()

The following examples show how to use org.keycloak.models.UserSessionModel#getNote() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: OIDCIdentityProvider.java    From keycloak with Apache License 2.0 6 votes vote down vote up
private String getIDTokenForLogout(KeycloakSession session, UserSessionModel userSession) {
    String tokenExpirationString = userSession.getNote(FEDERATED_TOKEN_EXPIRATION);
    long exp = tokenExpirationString == null ? 0 : Long.parseLong(tokenExpirationString);
    int currentTime = Time.currentTime();
    if (exp > 0 && currentTime > exp) {
        String response = refreshTokenForLogout(session, userSession);
        AccessTokenResponse tokenResponse = null;
        try {
            tokenResponse = JsonSerialization.readValue(response, AccessTokenResponse.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return tokenResponse.getIdToken();
    } else {
        return userSession.getNote(FEDERATED_ID_TOKEN);

    }
}
 
Example 2
Source File: OIDCLoginProtocol.java    From keycloak with Apache License 2.0 6 votes vote down vote up
@Override
public Response finishLogout(UserSessionModel userSession) {
    String redirectUri = userSession.getNote(OIDCLoginProtocol.LOGOUT_REDIRECT_URI);
    String state = userSession.getNote(OIDCLoginProtocol.LOGOUT_STATE_PARAM);
    event.event(EventType.LOGOUT);
    if (redirectUri != null) {
        event.detail(Details.REDIRECT_URI, redirectUri);
    }
    event.user(userSession.getUser()).session(userSession).success();

    if (redirectUri != null) {
        UriBuilder uriBuilder = UriBuilder.fromUri(redirectUri);
        if (state != null)
            uriBuilder.queryParam(STATE_PARAM, state);
        return Response.status(302).location(uriBuilder.build()).build();
    } else {
        // TODO Empty content with ok makes no sense. Should it display a page? Or use noContent?
        session.getProvider(SecurityHeadersProvider.class).options().allowEmptyContentType();
        return Response.ok().build();
    }
}
 
Example 3
Source File: OIDCLoginProtocol.java    From keycloak with Apache License 2.0 6 votes vote down vote up
protected boolean isAuthTimeExpired(UserSessionModel userSession, AuthenticationSessionModel authSession) {
    String authTime = userSession.getNote(AuthenticationManager.AUTH_TIME);
    String maxAge = authSession.getClientNote(OIDCLoginProtocol.MAX_AGE_PARAM);
    if (maxAge == null) {
        return false;
    }

    int authTimeInt = authTime==null ? 0 : Integer.parseInt(authTime);
    int maxAgeInt = Integer.parseInt(maxAge);

    if (authTimeInt + maxAgeInt < Time.currentTime()) {
        logger.debugf("Authentication time is expired, needs to reauthenticate. userSession=%s, clientId=%s, maxAge=%d, authTime=%d", userSession.getId(),
                authSession.getClient().getId(), maxAgeInt, authTimeInt);
        return true;
    }

    return false;
}
 
Example 4
Source File: AuthenticationManager.java    From keycloak with Apache License 2.0 6 votes vote down vote up
public static Response finishBrowserLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
    final AuthenticationSessionManager asm = new AuthenticationSessionManager(session);
    AuthenticationSessionModel logoutAuthSession = createOrJoinLogoutSession(session, realm, asm, userSession, true);

    checkUserSessionOnlyHasLoggedOutClients(realm, userSession, logoutAuthSession);

    expireIdentityCookie(realm, uriInfo, connection);
    expireRememberMeCookie(realm, uriInfo, connection);
    userSession.setState(UserSessionModel.State.LOGGED_OUT);
    String method = userSession.getNote(KEYCLOAK_LOGOUT_PROTOCOL);
    EventBuilder event = new EventBuilder(realm, session, connection);
    LoginProtocol protocol = session.getProvider(LoginProtocol.class, method);
    protocol.setRealm(realm)
            .setHttpHeaders(headers)
            .setUriInfo(uriInfo)
            .setEventBuilder(event);
    Response response = protocol.finishLogout(userSession);
    session.sessions().removeUserSession(realm, userSession);
    session.authenticationSessions().removeRootAuthenticationSession(realm, logoutAuthSession.getParentSession());
    return response;
}
 
Example 5
Source File: AuthenticationManager.java    From keycloak with Apache License 2.0 6 votes vote down vote up
private static void backchannelLogoutAll(KeycloakSession session, RealmModel realm,
  UserSessionModel userSession, AuthenticationSessionModel logoutAuthSession, UriInfo uriInfo,
  HttpHeaders headers, boolean logoutBroker) {
    userSession.getAuthenticatedClientSessions().values().forEach(
      clientSession -> backchannelLogoutClientSession(session, realm, clientSession, logoutAuthSession, uriInfo, headers)
    );
    if (logoutBroker) {
        String brokerId = userSession.getNote(Details.IDENTITY_PROVIDER);
        if (brokerId != null) {
            IdentityProvider identityProvider = IdentityBrokerService.getIdentityProvider(session, realm, brokerId);
            try {
                identityProvider.backchannelLogout(session, userSession, uriInfo, realm);
            } catch (Exception e) {
                logger.warn("Exception at broker backchannel logout for broker " + brokerId, e);
            }
        }
    }
}
 
Example 6
Source File: AbstractOAuth2IdentityProvider.java    From keycloak with Apache License 2.0 6 votes vote down vote up
protected Response exchangeSessionToken(UriInfo uriInfo, EventBuilder event, ClientModel authorizedClient, UserSessionModel tokenUserSession, UserModel tokenSubject) {
    String accessToken = tokenUserSession.getNote(FEDERATED_ACCESS_TOKEN);
    if (accessToken == null) {
        event.detail(Details.REASON, "requested_issuer is not linked");
        event.error(Errors.INVALID_TOKEN);
        return exchangeTokenExpired(uriInfo, authorizedClient, tokenUserSession, tokenSubject);
    }
    AccessTokenResponse tokenResponse = new AccessTokenResponse();
    tokenResponse.setToken(accessToken);
    tokenResponse.setIdToken(null);
    tokenResponse.setRefreshToken(null);
    tokenResponse.setRefreshExpiresIn(0);
    tokenResponse.getOtherClaims().clear();
    tokenResponse.getOtherClaims().put(OAuth2Constants.ISSUED_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE);
    tokenResponse.getOtherClaims().put(ACCOUNT_LINK_URL, getLinkingUrl(uriInfo, authorizedClient, tokenUserSession));
    event.success();
    return Response.ok(tokenResponse).type(MediaType.APPLICATION_JSON_TYPE).build();
}
 
Example 7
Source File: TwitterIdentityProvider.java    From keycloak with Apache License 2.0 6 votes vote down vote up
@Override
public Response exchangeFromToken(UriInfo uriInfo, EventBuilder builder, ClientModel authorizedClient, UserSessionModel tokenUserSession, UserModel tokenSubject, MultivaluedMap<String, String> params) {
    String requestedType = params.getFirst(OAuth2Constants.REQUESTED_TOKEN_TYPE);
    if (requestedType != null && !requestedType.equals(TWITTER_TOKEN_TYPE)) {
        return exchangeUnsupportedRequiredType();
    }
    if (!getConfig().isStoreToken()) {
        String brokerId = tokenUserSession.getNote(Details.IDENTITY_PROVIDER);
        if (brokerId == null || !brokerId.equals(getConfig().getAlias())) {
            return exchangeNotLinkedNoStore(uriInfo, authorizedClient, tokenUserSession, tokenSubject);
        }
        return exchangeSessionToken(uriInfo, authorizedClient, tokenUserSession, tokenSubject);
    } else {
        return exchangeStoredToken(uriInfo, authorizedClient, tokenUserSession, tokenSubject);
    }
}
 
Example 8
Source File: AbstractOAuth2IdentityProvider.java    From keycloak with Apache License 2.0 5 votes vote down vote up
@Override
public Response exchangeFromToken(UriInfo uriInfo, EventBuilder event, ClientModel authorizedClient, UserSessionModel tokenUserSession, UserModel tokenSubject, MultivaluedMap<String, String> params) {
    // check to see if we have a token exchange in session
    // in other words check to see if this session was created by an external exchange
    Response tokenResponse = hasExternalExchangeToken(event, tokenUserSession, params);
    if (tokenResponse != null) return tokenResponse;

    // going further we only support access token type?  Why?
    String requestedType = params.getFirst(OAuth2Constants.REQUESTED_TOKEN_TYPE);
    if (requestedType != null && !requestedType.equals(OAuth2Constants.ACCESS_TOKEN_TYPE)) {
        event.detail(Details.REASON, "requested_token_type unsupported");
        event.error(Errors.INVALID_REQUEST);
        return exchangeUnsupportedRequiredType();
    }
    if (!getConfig().isStoreToken()) {
        // if token isn't stored, we need to see if this session has been linked
        String brokerId = tokenUserSession.getNote(Details.IDENTITY_PROVIDER);
        brokerId = brokerId == null ? tokenUserSession.getNote(IdentityProvider.EXTERNAL_IDENTITY_PROVIDER) : brokerId;
        if (brokerId == null || !brokerId.equals(getConfig().getAlias())) {
            event.detail(Details.REASON, "requested_issuer has not linked");
            event.error(Errors.INVALID_REQUEST);
            return exchangeNotLinkedNoStore(uriInfo, authorizedClient, tokenUserSession, tokenSubject);
        }
        return exchangeSessionToken(uriInfo, event, authorizedClient, tokenUserSession, tokenSubject);
    } else {
        return exchangeStoredToken(uriInfo, event, authorizedClient, tokenUserSession, tokenSubject);
    }
}
 
Example 9
Source File: UserSessionNoteMapper.java    From keycloak-protocol-cas with Apache License 2.0 5 votes vote down vote up
@Override
public void setAttribute(Map<String, Object> attributes, ProtocolMapperModel mappingModel, UserSessionModel userSession,
                         KeycloakSession session, ClientSessionContext clientSessionCt) {
    String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE);
    String noteValue = userSession.getNote(noteName);
    if (noteValue == null) return;
    setMappedAttribute(attributes, mappingModel, noteValue);
}
 
Example 10
Source File: OIDCIdentityProvider.java    From keycloak with Apache License 2.0 5 votes vote down vote up
/**
 * Returns access token response as a string from a refresh token invocation on the remote OIDC broker
 *
 * @param session
 * @param userSession
 * @return
 */
public String refreshTokenForLogout(KeycloakSession session, UserSessionModel userSession) {
    String refreshToken = userSession.getNote(FEDERATED_REFRESH_TOKEN);
    try (VaultStringSecret vaultStringSecret = session.vault().getStringSecret(getConfig().getClientSecret())) {
        return getRefreshTokenRequest(session, refreshToken, getConfig().getClientId(), vaultStringSecret.get().orElse(getConfig().getClientSecret())).asString();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
 
Example 11
Source File: AuthenticationManager.java    From keycloak with Apache License 2.0 5 votes vote down vote up
public static Response browserLogout(KeycloakSession session,
                                     RealmModel realm,
                                     UserSessionModel userSession,
                                     UriInfo uriInfo,
                                     ClientConnection connection,
                                     HttpHeaders headers,
                                     String initiatingIdp) {
    if (userSession == null) return null;

    if (logger.isDebugEnabled()) {
        UserModel user = userSession.getUser();
        logger.debugv("Logging out: {0} ({1})", user.getUsername(), userSession.getId());
    }
    
    if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
        userSession.setState(UserSessionModel.State.LOGGING_OUT);
    }

    final AuthenticationSessionManager asm = new AuthenticationSessionManager(session);
    AuthenticationSessionModel logoutAuthSession = createOrJoinLogoutSession(session, realm, asm, userSession, true);

    Response response = browserLogoutAllClients(userSession, session, realm, headers, uriInfo, logoutAuthSession);
    if (response != null) {
        return response;
    }

    String brokerId = userSession.getNote(Details.IDENTITY_PROVIDER);
    if (brokerId != null && !brokerId.equals(initiatingIdp)) {
        IdentityProvider identityProvider = IdentityBrokerService.getIdentityProvider(session, realm, brokerId);
        response = identityProvider.keycloakInitiatedBrowserLogout(session, userSession, uriInfo, realm);
        if (response != null) {
            return response;
        }
    }

    return finishBrowserLogout(session, realm, userSession, uriInfo, connection, headers);
}
 
Example 12
Source File: UserSessionNoteStatementMapper.java    From keycloak with Apache License 2.0 5 votes vote down vote up
@Override
public void transformAttributeStatement(AttributeStatementType attributeStatement, ProtocolMapperModel mappingModel, KeycloakSession session, UserSessionModel userSession, AuthenticatedClientSessionModel clientSession) {
    String note = mappingModel.getConfig().get("note");
    String value = userSession.getNote(note);
    if (value == null) return;
    AttributeStatementHelper.addAttribute(attributeStatement, mappingModel, value);

}
 
Example 13
Source File: OIDCLoginProtocol.java    From keycloak with Apache License 2.0 5 votes vote down vote up
protected boolean isReAuthRequiredForKcAction(UserSessionModel userSession, AuthenticationSessionModel authSession) {
    if (authSession.getClientNote(Constants.KC_ACTION) != null) {
        String authTime = userSession.getNote(AuthenticationManager.AUTH_TIME);
        int authTimeInt = authTime == null ? 0 : Integer.parseInt(authTime);
        int maxAgeInt = Constants.KC_ACTION_MAX_AGE;
        return authTimeInt + maxAgeInt < Time.currentTime();
    } else {
        return false;
    }
}
 
Example 14
Source File: UserSessionNoteMapper.java    From keycloak with Apache License 2.0 5 votes vote down vote up
protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession) {

        String noteName = mappingModel.getConfig().get(ProtocolMapperUtils.USER_SESSION_NOTE);
        String noteValue = userSession.getNote(noteName);
        if (noteValue == null) return;
        OIDCAttributeMapperHelper.mapClaim(token, mappingModel, noteValue);
    }
 
Example 15
Source File: TokenManager.java    From keycloak with Apache License 2.0 5 votes vote down vote up
protected AccessToken initToken(RealmModel realm, ClientModel client, UserModel user, UserSessionModel session,
                                ClientSessionContext clientSessionCtx, UriInfo uriInfo) {
    AccessToken token = new AccessToken();
    token.id(KeycloakModelUtils.generateId());
    token.type(TokenUtil.TOKEN_TYPE_BEARER);
    token.subject(user.getId());
    token.issuedNow();
    token.issuedFor(client.getClientId());

    AuthenticatedClientSessionModel clientSession = clientSessionCtx.getClientSession();
    token.issuer(clientSession.getNote(OIDCLoginProtocol.ISSUER));
    token.setNonce(clientSessionCtx.getAttribute(OIDCLoginProtocol.NONCE_PARAM, String.class));
    token.setScope(clientSessionCtx.getScopeString());

    // Best effort for "acr" value. Use 0 if clientSession was authenticated through cookie ( SSO )
    // TODO: Add better acr support. See KEYCLOAK-3314
    String acr = (AuthenticationManager.isSSOAuthentication(clientSession)) ? "0" : "1";
    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
        : clientSessionCtx.getClientScopeIds().contains(offlineAccessScope.getId());
    token.expiration(getTokenExpiration(realm, client, session, clientSession, offlineTokenRequested));

    return token;
}
 
Example 16
Source File: DeviceActivityManager.java    From keycloak with Apache License 2.0 5 votes vote down vote up
/** Returns the device information associated with the given {@code userSession}.
 * 
 * 
 * @param userSession the userSession
 * @return the device information or null if no device is attached to the user session
 */
public static DeviceRepresentation getCurrentDevice(UserSessionModel userSession) {
    String deviceInfo = userSession.getNote(DEVICE_NOTE);

    if (deviceInfo == null) {
        return null;
    }

    try {
        return JsonSerialization.readValue(Base64.decode(deviceInfo), DeviceRepresentation.class);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
 
Example 17
Source File: IdentityBrokerService.java    From keycloak with Apache License 2.0 4 votes vote down vote up
private Response performAccountLinking(AuthenticationSessionModel authSession, UserSessionModel userSession, BrokeredIdentityContext context, FederatedIdentityModel newModel, UserModel federatedUser) {
    logger.debugf("Will try to link identity provider [%s] to user [%s]", context.getIdpConfig().getAlias(), userSession.getUser().getUsername());

    this.event.event(EventType.FEDERATED_IDENTITY_LINK);



    UserModel authenticatedUser = userSession.getUser();
    authSession.setAuthenticatedUser(authenticatedUser);

    if (federatedUser != null && !authenticatedUser.getId().equals(federatedUser.getId())) {
        return redirectToErrorWhenLinkingFailed(authSession, Messages.IDENTITY_PROVIDER_ALREADY_LINKED, context.getIdpConfig().getAlias());
    }

    if (!authenticatedUser.hasRole(this.realmModel.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).getRole(AccountRoles.MANAGE_ACCOUNT))) {
        return redirectToErrorPage(authSession, Response.Status.FORBIDDEN, Messages.INSUFFICIENT_PERMISSION);
    }

    if (!authenticatedUser.isEnabled()) {
        return redirectToErrorWhenLinkingFailed(authSession, Messages.ACCOUNT_DISABLED);
    }



    if (federatedUser != null) {
        if (context.getIdpConfig().isStoreToken()) {
            FederatedIdentityModel oldModel = this.session.users().getFederatedIdentity(federatedUser, context.getIdpConfig().getAlias(), this.realmModel);
            if (!ObjectUtil.isEqualOrBothNull(context.getToken(), oldModel.getToken())) {
                this.session.users().updateFederatedIdentity(this.realmModel, federatedUser, newModel);
                if (isDebugEnabled()) {
                    logger.debugf("Identity [%s] update with response from identity provider [%s].", federatedUser, context.getIdpConfig().getAlias());
                }
            }
        }
    } else {
        this.session.users().addFederatedIdentity(this.realmModel, authenticatedUser, newModel);
    }
    context.getIdp().authenticationFinished(authSession, context);

    AuthenticationManager.setClientScopesInSession(authSession);
    TokenManager.attachAuthenticationSession(session, userSession, authSession);

    if (isDebugEnabled()) {
        logger.debugf("Linking account [%s] from identity provider [%s] to user [%s].", newModel, context.getIdpConfig().getAlias(), authenticatedUser);
    }

    this.event.user(authenticatedUser)
            .detail(Details.USERNAME, authenticatedUser.getUsername())
            .detail(Details.IDENTITY_PROVIDER, newModel.getIdentityProvider())
            .detail(Details.IDENTITY_PROVIDER_USERNAME, newModel.getUserName())
            .success();

    // we do this to make sure that the parent IDP is logged out when this user session is complete.
    // But for the case when userSession was previously authenticated with broker1 and now is linked to another broker2, we shouldn't override broker1 notes with the broker2 for sure.
    // Maybe broker logout should be rather always skiped in case of broker-linking
    if (userSession.getNote(Details.IDENTITY_PROVIDER) == null) {
        userSession.setNote(Details.IDENTITY_PROVIDER, context.getIdpConfig().getAlias());
        userSession.setNote(Details.IDENTITY_PROVIDER_USERNAME, context.getUsername());
    }

    return Response.status(302).location(UriBuilder.fromUri(authSession.getRedirectUri()).build()).build();
}
 
Example 18
Source File: SamlProtocol.java    From keycloak with Apache License 2.0 4 votes vote down vote up
@Override
public Response finishLogout(UserSessionModel userSession) {
    logger.debug("finishLogout");
    String logoutBindingUri = userSession.getNote(SAML_LOGOUT_BINDING_URI);
    if (logoutBindingUri == null) {
        logger.error("Can't finish SAML logout as there is no logout binding set.  Please configure the logout service url in the admin console for your client applications.");
        return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.FAILED_LOGOUT);

    }
    String logoutRelayState = userSession.getNote(SAML_LOGOUT_RELAY_STATE);
    SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
    builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
    builder.destination(logoutBindingUri);
    builder.issuer(getResponseIssuer(realm));
    JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder(session);
    binding.relayState(logoutRelayState);
    String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
    boolean postBinding = isLogoutPostBindingForInitiator(userSession);
    if (signingAlgorithm != null) {
        SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
        String canonicalization = userSession.getNote(SAML_LOGOUT_CANONICALIZATION);
        if (canonicalization != null) {
            binding.canonicalizationMethod(canonicalization);
        }
        KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm);
        XmlKeyInfoKeyNameTransformer transformer = XmlKeyInfoKeyNameTransformer.from(
          userSession.getNote(SAML_SERVER_SIGNATURE_KEYINFO_KEY_NAME_TRANSFORMER),
          SamlClient.DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER);
        String keyName = transformer.getKeyName(keys.getKid(), keys.getCertificate());
        binding.signatureAlgorithm(algorithm).signWith(keyName, keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
        boolean addExtension = (! postBinding) && Objects.equals("true", userSession.getNote(SamlProtocol.SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO));
        if (addExtension) {    // Only include extension if REDIRECT binding and signing whole SAML protocol message
            builder.addExtension(new KeycloakKeySamlExtensionGenerator(keyName));
        }
    }
    Response response;
    try {
        response = buildLogoutResponse(userSession, logoutBindingUri, builder, binding);
    } catch (ConfigurationException | ProcessingException  | IOException e) {
        throw new RuntimeException(e);
    }
    if (logoutBindingUri != null) {
        event.detail(Details.REDIRECT_URI, logoutBindingUri);
    }
    event.event(EventType.LOGOUT)
            .detail(Details.AUTH_METHOD, userSession.getAuthMethod())
            .client(session.getContext().getClient())
            .user(userSession.getUser())
            .session(userSession)
            .detail(Details.USERNAME, userSession.getLoginUsername())
            .detail(Details.RESPONSE_MODE, postBinding ? SamlProtocol.SAML_POST_BINDING : SamlProtocol.SAML_REDIRECT_BINDING)
            .detail(SamlProtocol.SAML_LOGOUT_REQUEST_ID, userSession.getNote(SAML_LOGOUT_REQUEST_ID))
            .success();
    return response;
}
 
Example 19
Source File: SamlProtocol.java    From keycloak with Apache License 2.0 4 votes vote down vote up
public static boolean isLogoutPostBindingForInitiator(UserSessionModel session) {
    String note = session.getNote(SamlProtocol.SAML_LOGOUT_BINDING);
    return SamlProtocol.SAML_POST_BINDING.equals(note);
}
 
Example 20
Source File: OIDCIdentityProvider.java    From keycloak with Apache License 2.0 4 votes vote down vote up
@Override
protected Response exchangeSessionToken(UriInfo uriInfo, EventBuilder event, ClientModel authorizedClient, UserSessionModel tokenUserSession, UserModel tokenSubject) {
    String refreshToken = tokenUserSession.getNote(FEDERATED_REFRESH_TOKEN);
    String accessToken = tokenUserSession.getNote(FEDERATED_ACCESS_TOKEN);
    String idToken = tokenUserSession.getNote(FEDERATED_ID_TOKEN);

    if (accessToken == null) {
        event.detail(Details.REASON, "requested_issuer is not linked");
        event.error(Errors.INVALID_TOKEN);
        return exchangeTokenExpired(uriInfo, authorizedClient, tokenUserSession, tokenSubject);
    }
    try (VaultStringSecret vaultStringSecret = session.vault().getStringSecret(getConfig().getClientSecret())) {
        long expiration = Long.parseLong(tokenUserSession.getNote(FEDERATED_TOKEN_EXPIRATION));
        if (expiration == 0 || expiration > Time.currentTime()) {
            AccessTokenResponse tokenResponse = new AccessTokenResponse();
            tokenResponse.setExpiresIn(expiration);
            tokenResponse.setToken(accessToken);
            tokenResponse.setIdToken(null);
            tokenResponse.setRefreshToken(null);
            tokenResponse.setRefreshExpiresIn(0);
            tokenResponse.getOtherClaims().put(OAuth2Constants.ISSUED_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE);
            tokenResponse.getOtherClaims().put(ACCOUNT_LINK_URL, getLinkingUrl(uriInfo, authorizedClient, tokenUserSession));
            event.success();
            return Response.ok(tokenResponse).type(MediaType.APPLICATION_JSON_TYPE).build();
        }
        String response = getRefreshTokenRequest(session, refreshToken, getConfig().getClientId(), vaultStringSecret.get().orElse(getConfig().getClientSecret())).asString();
        if (response.contains("error")) {
            logger.debugv("Error refreshing token, refresh token expiration?: {0}", response);
            event.detail(Details.REASON, "requested_issuer token expired");
            event.error(Errors.INVALID_TOKEN);
            return exchangeTokenExpired(uriInfo, authorizedClient, tokenUserSession, tokenSubject);
        }
        AccessTokenResponse newResponse = JsonSerialization.readValue(response, AccessTokenResponse.class);
        long accessTokenExpiration = newResponse.getExpiresIn() > 0 ? Time.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());
        tokenUserSession.setNote(FEDERATED_ID_TOKEN, newResponse.getIdToken());
        newResponse.setIdToken(null);
        newResponse.setRefreshToken(null);
        newResponse.setRefreshExpiresIn(0);
        newResponse.getOtherClaims().clear();
        newResponse.getOtherClaims().put(OAuth2Constants.ISSUED_TOKEN_TYPE, OAuth2Constants.ACCESS_TOKEN_TYPE);
        newResponse.getOtherClaims().put(ACCOUNT_LINK_URL, getLinkingUrl(uriInfo, authorizedClient, tokenUserSession));
        event.success();
        return Response.ok(newResponse).type(MediaType.APPLICATION_JSON_TYPE).build();
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}