Thanks to visit codestin.com
Credit goes to github.com

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 @@ -449,7 +449,8 @@ detailsHelp=This is information about the details.
adminEvents=Admin events
serviceAccountHelp=Allows you to authenticate this client to Keycloak and retrieve access token dedicated to this client. In terms of OAuth2 specification, this enables support of 'Client Credentials Grant' for this client.
standardTokenExchangeEnabledHelp=Enable Standard Token Exchange V2 for this client.
enableRefreshRequestedTokenTypeHelp=If property is on, Standard Token Exchange V2 allows the urn:ietf:params:oauth:token-type:refresh_token value for the requested_token_type parameter and returns a refresh_token in the response. If it is off, an error is returned for that requested_token_type.
enableRefreshRequestedTokenTypeHelp=Controls if the Standard Token Exchange V2 allows to request a refresh token (parameter "requested_token_type" set to value "urn:ietf:params:oauth:token-type:refresh_token"). If this option is "No" (default), the refresh token request type is never allowed and an error is returned. If this option is "Same session", the returned refresh token is enforced to use the same session as the subject token, returning an error if that session is not available (for example if the subject token is transient).
sameSession=Same session
urisHelp=Set of URIs which are protected by resource.
eventTypes.IDENTITY_PROVIDER_RESPONSE.name=Identity provider response
confirmClientSecretTitle=Regenerate secret for this client?
Expand Down Expand Up @@ -3434,4 +3435,4 @@ resourceTypeSelectHelp=Select a resource type that is going to be used to query
grantedScope=Granted scope:
deniedScope=Denied scope:
evaluatedPolicy={{name}} voted to {{status}}
permissionEvaluationAlertTitle=The selected user does not have access to the selected resource(s)
permissionEvaluationAlertTitle=The selected user does not have access to the selected resource(s)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";

import { FormAccess } from "../../components/form/FormAccess";
import { HelpItem } from "@keycloak/keycloak-ui-shared";
import { HelpItem, SelectControl } from "@keycloak/keycloak-ui-shared";
import { convertAttributeNameToForm } from "../../util";
import { FormFields } from "../ClientDetails";
import useIsFeatureEnabled, { Feature } from "../../utils/useIsFeatureEnabled";
Expand Down Expand Up @@ -183,43 +183,25 @@ export const OpenIdConnectCompatibilityModes = ({
</FormGroup>

{isFeatureEnabled(Feature.StandardTokenExchangeV2) && (
<FormGroup
<SelectControl
name={convertAttributeNameToForm<FormFields>(
"attributes.standard.token.exchange.enableRefreshRequestedTokenType",
)}
label={t("enableRefreshRequestedTokenType")}
fieldId="enableRefreshRequestedTokenType"
hasNoPaddingTop
labelIcon={
<HelpItem
helpText={t("enableRefreshRequestedTokenTypeHelp")}
fieldLabelId="enableRefreshRequestedTokenType"
/>
labelIcon={t("enableRefreshRequestedTokenTypeHelp")}
controller={{
defaultValue: "",
}}
isDisabled={
tokenExchangeEnabled?.toString() !== "true" ||
useRefreshTokens?.toString() !== "true"
}
>
<Controller
name={convertAttributeNameToForm<FormFields>(
"attributes.standard.token.exchange.enableRefreshRequestedTokenType",
)}
defaultValue="false"
control={control}
render={({ field }) => (
<Switch
id="enableRefreshRequestedTokenType"
label={t("on")}
labelOff={t("off")}
isChecked={
field.value === "true" &&
tokenExchangeEnabled?.toString() === "true" &&
useRefreshTokens?.toString() === "true"
}
onChange={(_event, value) => field.onChange(value.toString())}
aria-label={t("enableRefreshRequestedTokenType")}
isDisabled={
tokenExchangeEnabled?.toString() !== "true" ||
useRefreshTokens?.toString() !== "true"
}
/>
)}
/>
</FormGroup>
options={[
{ key: "", value: t("choose") },
{ key: "NO", value: t("no") },
{ key: "SAME_SESSION", value: t("sameSession") },
]}
/>
)}
<ActionGroup>
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const SingleSelectControl = <
options,
controller,
labelIcon,
isDisabled,
...rest
}: SelectControlProps<T, P>) => {
const {
Expand Down Expand Up @@ -79,6 +80,7 @@ export const SingleSelectControl = <
isFullWidth
status={get(errors, name) ? MenuToggleStatus.danger : undefined}
aria-label="toggle"
isDisabled={isDisabled}
>
{isSelectBasedOptions(options)
? options.find(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.services.util.DPoPUtil;
import org.keycloak.utils.StringUtil;

import java.util.ArrayList;
Expand All @@ -36,6 +35,8 @@
*/
public class OIDCAdvancedConfigWrapper extends AbstractClientConfigWrapper {

public static enum TokenExchangeRefreshTokenEnabled {NO, SAME_SESSION};

private OIDCAdvancedConfigWrapper(ClientModel client, ClientRepresentation clientRep) {
super(client,clientRep);
}
Expand Down Expand Up @@ -240,12 +241,18 @@ public void setStandardTokenExchangeEnabled(boolean enable) {
setAttribute(OIDCConfigAttributes.STANDARD_TOKEN_EXCHANGE_ENABLED, val);
}

public boolean isStandardTokenExchangeRefreshEnabled() {
return Boolean.parseBoolean(getAttribute(OIDCConfigAttributes.STANDARD_TOKEN_EXCHANGE_REFRESH_ENABLED));
public TokenExchangeRefreshTokenEnabled getStandardTokenExchangeRefreshEnabled() {
final String value = getAttribute(OIDCConfigAttributes.STANDARD_TOKEN_EXCHANGE_REFRESH_ENABLED);
try {
return value == null? TokenExchangeRefreshTokenEnabled.NO : TokenExchangeRefreshTokenEnabled.valueOf(value);
} catch (IllegalArgumentException e) {
return TokenExchangeRefreshTokenEnabled.NO;
}
}

public void setStandardTokenExchangeRefreshEnabled(boolean enable) {
setAttribute(OIDCConfigAttributes.STANDARD_TOKEN_EXCHANGE_REFRESH_ENABLED, String.valueOf(enable));
public void setStandardTokenExchangeRefreshEnabled(TokenExchangeRefreshTokenEnabled enable) {
setAttribute(OIDCConfigAttributes.STANDARD_TOKEN_EXCHANGE_REFRESH_ENABLED,
enable == null || enable == TokenExchangeRefreshTokenEnabled.NO? null : enable.name());
}

public String getTlsClientAuthSubjectDn() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
import org.keycloak.common.Profile;
Expand Down Expand Up @@ -208,20 +207,22 @@ protected Response exchangeClientToOIDCClient(UserModel targetUser, UserSessionM
RootAuthenticationSessionModel rootAuthSession = new AuthenticationSessionManager(session).createAuthenticationSession(realm, false);
AuthenticationSessionModel authSession = createSessionModel(targetUserSession, rootAuthSession, targetUser, client, scope);

boolean newUserSessionCreated = false;
if (targetUserSession == null || targetUserSession.isOffline()) {
// if no session is associated with a subject_token, a new session will be created, only persistent if refresh token type requested
// The new session created also when original session was offline (assuming we don't allow offline-access from token exchange)
// if no session is associated with the subject_token or it is offline, check no online session is needed
if (OAuth2Constants.REFRESH_TOKEN_TYPE.equals(requestedTokenType)) {
event.detail(Details.REASON, "Refresh token not valid as requested_token_type because creating a new session is needed");
event.error(Errors.INVALID_REQUEST);
throw new CorsErrorResponseException(cors, OAuthErrorException.INVALID_REQUEST,
"Refresh token not valid as requested_token_type because creating a new session is needed", Response.Status.BAD_REQUEST);
}
// create a transient session now for the token exchange
targetUserSession = new UserSessionManager(session).createUserSession(authSession.getParentSession().getId(), realm, targetUser, targetUser.getUsername(),
clientConnection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null,
requestedTokenType.equals(OAuth2Constants.REFRESH_TOKEN_TYPE)
? UserSessionModel.SessionPersistenceState.PERSISTENT
: UserSessionModel.SessionPersistenceState.TRANSIENT);
if (targetUserSession.getPersistenceState() == UserSessionModel.SessionPersistenceState.PERSISTENT) {
newUserSessionCreated = true;
}
UserSessionModel.SessionPersistenceState.TRANSIENT);
}
boolean newClientSessionCreated = !newUserSessionCreated && targetUserSession.getAuthenticatedClientSessionByClient(client.getId()) == null;

final boolean newClientSessionCreated = targetUserSession.getPersistenceState() != UserSessionModel.SessionPersistenceState.TRANSIENT
&& targetUserSession.getAuthenticatedClientSessionByClient(client.getId()) == null;

event.session(targetUserSession);

Expand Down Expand Up @@ -297,11 +298,7 @@ protected Response exchangeClientToOIDCClient(UserModel targetUser, UserSessionM
res.setOtherClaims(OAuth2Constants.ISSUED_TOKEN_TYPE, requestedTokenType);

if (responseBuilder.getAccessToken().getAudience() != null) {
StringJoiner joiner = new StringJoiner(" ");
for (String s : List.of(responseBuilder.getAccessToken().getAudience())) {
joiner.add(s);
}
event.detail(Details.AUDIENCE, joiner.toString());
event.detail(Details.AUDIENCE, CollectionUtil.join(List.of(responseBuilder.getAccessToken().getAudience()), " "));
}
event.success();

Expand All @@ -311,10 +308,6 @@ protected Response exchangeClientToOIDCClient(UserModel targetUser, UserSessionM
if (newClientSessionCreated) {
targetUserSession.removeAuthenticatedClientSessions(Set.of(client.getId()));
}
// Cleanup user-session if created in this request
if (newUserSessionCreated) {
session.sessions().removeUserSession(realm, targetUserSession);
}

throw e;
}
Expand Down Expand Up @@ -363,7 +356,8 @@ protected String getRequestedTokenType() {
}
OIDCAdvancedConfigWrapper oidcClient = OIDCAdvancedConfigWrapper.fromClientModel(client);
if (requestedTokenType.equals(OAuth2Constants.REFRESH_TOKEN_TYPE)
&& oidcClient.isUseRefreshToken() && oidcClient.isStandardTokenExchangeRefreshEnabled()) {
&& oidcClient.isUseRefreshToken()
&& oidcClient.getStandardTokenExchangeRefreshEnabled() != OIDCAdvancedConfigWrapper.TokenExchangeRefreshTokenEnabled.NO) {
return requestedTokenType;
}

Expand Down
Loading
Loading