8000 Set custom audiences per client in the jwt audience executor by rmartinc · Pull Request #46532 · 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 @@ -154,9 +154,6 @@ One of several purposes for this executor is to realize the security requirement
* Enforce SAML Redirect binding cannot be used or SAML requests and assertions are signed
* Enforce scopes granted in link:{securing_apps_token_exchange_link}#_standard-token-exchange[Standard token exchange] or in JWT Authorization Grant are restricted to the ones present in the initial `subject_token` or `assertion` JWT. This executor only allows downscoping of the presented assertion. An error is returned if any extra scope, not originally granted to the JWT, is requested.
* Enforce claims for assertion grants (`subject_token` in Token Exchange and `assertion` in JWT Authorization Grant). The executor enforces the presence and specific values of a claim in a JWT. It uses a Java regex so it is quite versatile.
* Allow different valid audiences for the JWT Authorization Grant. This executor configures one or more valid audiences for the assertion instead of the ones defined in the standard (issuer or token endpoint URL) or the client ID. This behavior is not covered by the standard and can have major security implications.
* Enforce client URIs to comply with security criteria, for example to prevent SSRF attacks allowing only trusted patterns or domains. Check the <<ssrf,dedicated section>> for more details.


Another available executor is the `auth-flow-enforce`, which can be used to enforce an authentication flow during an authentication request. For instance, it can be used to select a flow based on certain conditions, such as a specific scope or an ACR value. For more details, see the <<_client-policy-auth-flow, related documentation>>.

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 6 additions & 17 deletions docs/guides/securing-apps/jwt-authorization-grant.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ Only confidential clients can request a JWT authorization grant. In order to all
.Client configuration for JWT Authorization Grant
image::jwt-authorization-grant-client-config.png[Client configuration for JWT Authorization Grant]

NOTE: In the **Advanced** tab, section **OpenID Connect Compatibility Modes**, the configuration option **Custom audience mapping** allows to configure specific custom valid audiences for individual Identity Providers. The map key is the Identity Provider alias, and the value is the custom audience that will be accepted for that provider. Note this behavior is not covered by the standard and can have major security implications.

The Identity Provider (both types commented in the introduction) needs to also be configured to establish the relationship that will validate the assertion. In **Identity providers** -> select your OIDC or JWT provider -> **Settings** tab -> **Authorization Grant Settings** section.

. Enable the option **JWT Authorization Grant** switch.
Expand Down Expand Up @@ -157,12 +159,8 @@ The enforcer can be used for any request that uses an assertion parameter. Curre
* Executor **jwt-claim-enforcer**. This executor allows to configure extra requirements for claims in the JWT assertion token. For example, if we want the assertion to contain an `iat` claim or a custom claim with a specific value. The configuration allows us to set any claim name and any claim value (using a java regex). If the claim in the JWT assertion does not match the regex, the request does not proceed and an error is returned.
+
As the previous executor, for the moment this enforcer can be used for JWT Authorization Grant and the Standard Token exchange.
* Executor **jwt-authorization-grant-audience**. The executor allows to configure different and exclusive valid audiences for the assertion. The JWT Authorization Grant processing ensures, by specification, that the audience of the assertion is the {project_name} issuer or token endpoint URL. You can also use the Client ID as valid audience (option **Allows Client ID as audience for assertions**) for the OpenID Connect identity providers. Using this executor, you can configure one or more valid audiences instead of the default ones for the requests processed by the client policy.
+
WARNING: The **jwt-authorization-grant-audience** executor changes the validation of the assertion audience out of the standard compliance. This behavior can have major security implications.


=== JWT Authorization Grant for Google Identity Provider
== JWT Authorization Grant for Google Identity Provider

Google Identity Provider supports the JWT Authorization Grant, allowing the use of a Google ID Token as an assertion.
According to RFC 7523, the assertion MUST be a JWT. Since Google issues JWTs only for ID Tokens (and not for access tokens), only the ID Token can be used for this authorization grant.
Expand Down Expand Up @@ -197,19 +195,10 @@ In a scenario where a mobile application already supports login with Google with

In this case, the Google ID token cannot be used with the JWT Authorization Grant because the `aud` claim in the ID Token does not match the client id configured in the Google Identity Provider.

To address this issue, you can use link:{adminguide_link}#_client_policies[Client Policies] with the `jwt-authorization-grant-audience` executor to explicitly set the Google ID Token audience as the trusted audience, replacing the default audience validation configured for the Identity Provider.

**Example setup:**

Assume you have an OIDC client named `foo`, which is a mobile application receiving a Google ID token with the audience `my-mobile-app.apps.googleusercontent.com`, and you have an already configured Google Identity Provider with a different client-id.

Follow these steps to configure the validation of the audience:

. **Create a Client Policy Profile:** Create a profile and add the `jwt-authorization-grant-audience` executor. Configure the allowed audience in the executor to `my-mobile-app.apps.googleusercontent.com`.
. **Create a Client Policy:** Create a policy that uses this profile. For the condition, you can use the **client-role** condition and specify a role, for example, `foo-role`.
. **Configure the Client:** Navigate to the client `foo` settings. Open the **Roles** tab and create a new role named `foo-role`. This role acts effectively as a "marker" to ensure the client policy is triggered. You should create this same role in any other client that requires this specific audience validation.
. **Validate:** When you send the JWT authorization grant request with your Google ID token from client `foo`, the request will now be accepted, provided that `my-mobile-app.apps.googleusercontent.com` is present as a valid audience in the token.
To address this issue, you can use the **Custom audience mapping** configuration option commented in the client configuration section. For example, if the Identity Provider alias is `google`, the previous audience can be configured as valid with the following mapping.

.Custom audience mapping for the google provider
image::jwt-authorization-grant-google-custom-audiences.png[Custom audience mapping for the google provider]

</@tmpl.guide>

Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,8 @@ serviceAccountHelp=Allows you to authenticate this client to Keycloak and retrie
standardTokenExchangeEnabledHelp=Enable Standard Token Exchange V2 for this client.
jwtAuthorizationGrantEnabledHelp=Enable JSON Web Token (JWT) Profile for OAuth 2.0 Authorization Grant.
jwtAuthorizationGrantIdpHelp=Select Allowed Identity Providers that can be used to for JWT authorization grant validation
jwtAuthorizationGrantAudience=Custom audience mapping
jwtAuthorizationGrantAudienceHelp=Custom audience mapping for the JWT Authorization Grant. The key is the Identity Provider alias and the value is the custom audience enforced for this provider.
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.
Expand Down
1 change: 1 addition & 0 deletions js/apps/admin-ui/src/clients/AdvancedTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export const AdvancedTab = ({ save, client }: AdvancedProps) => {
"use.refresh.tokens",
"client_credentials.use_refresh_token",
"token.response.type.bearer.lower-case",
"oauth2.jwt.authorization.grant.audience",
])
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { HelpItem, SelectControl } from "@keycloak/keycloak-ui-shared";
import { convertAttributeNameToForm } from "../../util";
import { FormFields } from "../ClientDetails";
import useIsFeatureEnabled, { Feature } from "../../utils/useIsFeatureEnabled";
import { MapComponent } from "../../components/dynamic/MapComponent";

type OpenIdConnectCompatibilityModesProps = {
save: () => void;
Expand All @@ -31,6 +32,17 @@ export const OpenIdConnectCompatibilityModes = ({
convertAttributeNameToForm<FormFields>("attributes.use.refresh.tokens"),
"true",
);
const jwtAuthorizationGrantEnabled = watch(
convertAttributeNameToForm<FormFields>(
"attributes.oauth2.jwt.authorization.grant.enabled",
),
false,
);
const jwtAuthorizationGrantIdP = watch(
convertAttributeNameToForm<FormFields>(
"attributes.oauth2.jwt.authorization.grant.idp",
),
);
return (
<FormAccess
role="manage-clients"
Expand Down Expand Up @@ -204,6 +216,17 @@ export const OpenIdConnectCompatibilityModes = ({
]}
/>
)}
{isFeatureEnabled(Feature.JWTAuthorizationGrant) &&
jwtAuthorizationGrantEnabled.toString() === "true" &&
jwtAuthorizationGrantIdP && (
<MapComponent
name="attributes.oauth2.jwt.authorization.grant.audience"
label="jwtAuthorizationGrantAudience"
helpText="jwtAuthorizationGrantAudienceHelp"
convertToName={convertAttributeNameToForm}
options={jwtAuthorizationGrantIdP.split("##")}
/>
)}
<ActionGroup>
<Button
variant="secondary"
Expand Down
Loading
Loading
0