8000 Issue 38664 contextual mac algorithm by jimmychakkalakal · Pull Request #54482 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
Open
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 @@ -142,12 +142,12 @@ image:images/client-federated-jwt.png[]

*Signed JWT with Client Secret*

If you select this option, you can use a JWT signed by client secret instead of the private key.
If you select this option, the client can authenticate with a JWT using an HMAC and the client secret instead of asymmetric signing with a private key.
The client secret can also reference a value stored in an <<_vault-administration,external vault>>.

The client secret will be used to sign the JWT by the client.
The client uses the shared secret to create a message authentication code for the JWT.

Like in the *Signed JWT* authenticator you can configure the *Signature algorithm* and the *Max expiration* for the JWT token.
You can configure the *MAC algorithm* and the *Max expiration* for the JWT. The MAC algorithm uses the shared secret to provide message authentication and integrity.

*X509 Certificate*

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ JSON document with the claims that should be used to obtain user profile informa
|Client Secret
|Client secret from an external <<_vault-administration,vault>>. This secret is necessary if you are using the Authorization Code Flow.

|Client Assertion Signature Algorithm
|Signature algorithm to create JWT assertion as client authentication.
In the case of JWT signed with private key or Client secret as jwt, it is required. If no algorithm is specified, the following algorithm is adapted. `RS256` is adapted in the case of JWT signed with private key. `HS256` is adapted in the case of Client secret as jwt.
|Client Assertion Algorithm
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.

This documentation looks correct to me. However the behavior in the admin console is not aligned with that. It seems what admin console does is that option name is changing dynamically between Client assertion signature algorithm or Client assertion MAC algorithm according to which "Client authentication" method is selected.

IMO it would be easier to keep the documentation and rather simplify the logic in the admin console. So that the admin console in the IDP page will have just option Client Assertion Algorithm with the tooltip aligned to what you mentioned in this documentation. The option will allow both signing and MAC algorithms.

|For `private_key_jwt`, the signature algorithm used to sign the JWT assertion. For `client_secret_jwt`, the HMAC algorithm used to create a message authentication code with the client secret. If no algorithm is specified, `RS256` is used with a private key and `HS256` with the client secret.

|Client Assertion Audience
|The audience to use for the client assertion. The default value is the IDP's token endpoint URL.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ image:images/oidc-add-identity-provider.png[Add Identity Provider]
|Client Secret
|Client secret from an external <<_vault-administration,vault>>. This secret is necessary if you are using the Authorization Code Flow.

|Client Assertion Signature Algorithm
|Signature algorithm to create JWT assertion as client authentication.
In the case of JWT signed with private key or Client secret as jwt, it is required. If no algorithm is specified, the following algorithm is adapted. `RS256` is adapted in the case of JWT signed with private key. `HS256` is adapted in the case of Client secret as jwt.
|Client Assertion Algorithm
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.

Same like previous comment from oauth2.adoc

|For `private_key_jwt`, the signature algorithm used to sign the JWT assertion. For `client_secret_jwt`, the HMAC algorithm used to create a message authentication code with the client secret. If no algorithm is specified, `RS256` is used with a private key and `HS256` with the client secret.

|Client Assertion Audience
|The audience to use for the client assertion. The default value is the IDP's token endpoint URL.
Expand Down
10BC8
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ eventTypes.VALIDATE_ACCESS_TOKEN.description=Validate access token
dedicatedScopeExplain=This is a client scope which includes the dedicated mappers and scope
updateOtpError=Could not update OTP policy\: {{error}}
addressClaim.postal_code.label=User Attribute Name for Postal Code
macAlgorithmHelp=HMAC algorithm used to authenticate a JWT assertion with the client secret.
defaultRoles=Default roles
samlSignatureKeyNameHelp=Signed SAML documents contain identification of signing key in KeyName element. For Keycloak / RH-SSO counter-party, use KEY_ID, for MS AD FS use CERT_SUBJECT, for others check and use NONE if no other option works.
clientScopeTypes.default=Default
Expand Down Expand Up @@ -865,7 +866,7 @@ validity=Certificate expiration
validityHelp=Number of years the generated certificate is valid for.
membershipUserLdapAttributeHelp=Used only if the Membership Attribute Type is UID. It is the name of the LDAP attribute on the user, which is used for membership mappings. It is typically 'uid'. For example, if the value of 'Membership User LDAP Attribute' is 'uid' and the LDAP group has 'memberUid\: john', it is expected that particular LDAP user will have the attribute 'uid\: john'.
samlCapabilityConfig=SAML capabilities
accessTokenSignatureAlgorithmHelp=JWA algorithm used for signing access tokens.
accessTokenSignatureAlgorithmHelp=JWA algorithm used to sign/authenticate access tokens. Asymmetric algorithms create a digital signature with a realm private key; HS algorithms create an HMAC with a realm secret key.
derFormatted=DER formatted
periodicFullSyncHelp=If 'Import users' is enabled, a periodic task is started to synchronize users from LDAP to Keycloak. This type of synchronization fetches all users from LDAP on every run, importing new users or updating existing ones. If you have a large number of users in LDAP, this may be inefficient. In that case, you can disable this option and use 'Periodic changed users sync' instead.
periodicChangedUsersSyncHelp=If 'Import users' is enabled, a periodic task is started to synchronize users from LDAP to Keycloak based on their created or last updated timestamps. This type of synchronization is more efficient than a full sync, especially if you have a large number of users in LDAP. However, it requires that your LDAP server supports searching using the attributes 'createTimestamp' and 'modifyTimestamp' in query predicates.
Expand Down Expand Up @@ -1860,7 +1861,7 @@ newClientProfile=Create client profile
consoleDisplayConnectionUrlHelp=Connection URL to your LDAP server
enabledWhen=Enabled when
enabledWhenTooltip=Specifies when the attribute is available. When scopes are used, the attribute is available when at least one of the configured scopes is applied in the login request. Both default and optional scopes of the current request are evaluated. In the Admin Console and Account Console contexts, scopes are not evaluated and the attribute is always available (if not further restricted by permissions).
clientAssertionSigningAlg=Client assertion signature algorithm
clientAssertionSigningAlg=Client assertion algorithm
homeURLHelp=Default URL to use when the auth server needs to redirect or link back to the client.
ldapAttribute=LDAP attribute
fullScopeAllowedHelp=Allows you to disable all restrictions. This switch is deprecated and will be removed in the future. It is recommended to have this disabled and configure just the needed roles to the client role scope.
Expand Down Expand Up @@ -2257,7 +2258,7 @@ eventTypes.OAUTH2_DEVICE_AUTH.description=OAuth2 device authentication
notBeforeClearedSuccess=Success\! "Not Before" cleared for realm.
policySaveError=Could not update the policy: {{error}}
experimental=Experimental
idTokenSignatureAlgorithmHelp=JWA algorithm used for signing ID tokens.
idTokenSignatureAlgorithmHelp=JWA algorithm used to sign/authenticate ID tokens. Asymmetric algorithms create a digital signature with a realm private key; HS algorithms create an HMAC with a realm secret key.
deleteResourceConfirm=If you delete this resource, some permissions will be affected.
httpPostBindingResponse=HTTP-POST binding response
artifactBindingResponse=ARTIFACT binding response
Expand Down Expand Up @@ -2417,6 +2418,7 @@ validatingPublicKey=Validating public key
permissionsListIntro=Edit the permission list by clicking the scope-name. It then redirects to the permission details page of the client named <1>{{realm}}</1>
deleteClientConditionSuccess=Condition deleted successfully.
signatureAlgorithm=Signature algorithm
macAlgorithm=MAC algorithm
deleteConfirmIdentityProvider=Are you sure you want to permanently delete the provider '{{provider}}'?
resetActions=Reset Actions
cibaExpiresInHelp=The expiration time of the "auth_req_id" in seconds since the authentication request was received.
Expand Down Expand Up @@ -2452,7 +2454,7 @@ generalSettings=General settings
addClientProfileError=Could not create client profile\: '{{error}}'
overallResults=Overall Results
requiredUserActionsHelp=Require an action when the user logs in. 'Verify email' sends an email to the user to verify that user's email address. 'Update profile' requires the user to enter new personal information. 'Update password' requires the user to enter a new password. 'Configure OTP' requires setup of a mobile password generator.
requestObjectSignatureAlgorithmHelp=JWA algorithm, which the client needs to use when sending an OIDC request object specified by the 'request' or 'request_uri' parameters. If set to 'any', the Request object can be signed by any algorithm (including 'none').
requestObjectSignatureAlgorithmHelp=JWA algorithm used to sign/authenticate an OIDC request object sent in the 'request' or 'request_uri' parameter. Asymmetric algorithms use a client private key to sign the request object; HS algorithms use the client secret to create an HMAC of the request object. If set to 'any', the request object can use any algorithm (including 'none').
ldapKerberosSettingsDescription=This section contains options useful for the Kerberos integration. This is used only when the LDAP server is used together with Kerberos/SPNEGO for user authentication.
deleteEvents=Clear events
termsOfServiceUrlHelp=URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms of service
Expand Down Expand Up @@ -2572,7 +2574,7 @@ client=Client
eventTypes.OAUTH2_DEVICE_AUTH_ERROR.name=OAuth2 device authentication error
addSubFlowHelp=Sub-Flows can be either generic or form. The form type is used to construct a sub-flow that generates a single flow for the user. Sub-flows are a special type of execution that evaluate as successful depending on how the executions they contain evaluate.
implicitFlow=Implicit flow
authorizationSignedResponseAlgHelp=JWA algorithm used for signing authorization response tokens when the response mode is jwt.
authorizationSignedResponseAlgHelp=JWA algorithm used to sign/authenticate authorization response tokens when the response mode is jwt. Asymmetric algorithms create a digital signature with a realm private key; HS algorithms create an HMAC with a realm secret key.
associatedRolesRemoved=Associated roles have been removed
keyAliasHelp=Alias for the private key
whoWillAppearLinkTextUsers=Who will appear in this group list?
Expand Down Expand Up @@ -3074,7 +3076,7 @@ noMappersInstructions=There are currently no mappers for this identity provider.
deleteConfirmFlow=Delete flow?
addRole=Add role
FAIL=Fail import
userInfoSignedResponseAlgorithmHelp=JWA algorithm used for signed User Info Endpoint response. If set to 'unsigned', User Info Response won't be signed and will be returned in application/json format.
userInfoSignedResponseAlgorithmHelp=JWA algorithm used to sign/authenticate the User Info Endpoint response. Asymmetric algorithms create a digital signature with a realm private key; HS algorithms create an HMAC with a realm secret key. If set to 'unsigned', User Info Response won't be signed and will be returned in application/json format.
lastName=Last name
isAccessTokenJWT=Access Token is JWT
deleteConfirmDialog_one=Are you sure you want to permanently delete {{count}} selected user
Expand Down Expand Up @@ -3129,7 +3131,7 @@ strictTransportSecurityHelp=The Strict-Transport-Security HTTP header tells brow
authenticationExplain=Authentication is the area where you can configure and manage different credential types.
passwordPoliciesHelp.hashIterations=The number of times a password is hashed before storage or verification. Default\: -1 in case argon2 is used as the hashing algorithm; 210,000 in case pbkdf2-sha512 is used as the hashing algorithm; 600,000 if the pbkdf2-sha256 algorithm is used as the hashing algorithm; 1,300,000 if the pbkdf2 algorithm is used as the hashing algorithm.
dropNonexistingGroupsDuringSync=Drop non-existing groups during sync
clientAssertionSigningAlgHelp=Signature algorithm to create JWT assertion as client authentication. In the case of JWT signed with private key or JWT signed with client secret, it is required. If no algorithm is specified, the following algorithm is adapted. RS256 is adapted in the case of JWT signed with private key. HS256 is adapted in the case of JWT signed with client secret.
clientAssertionSigningAlgHelp=For private_key_jwt, the signature algorithm used to sign the JWT assertion. For client_secret_jwt, the HMAC (MAC) algorithm used to create a message authentication code with the client secret. If no algorithm is specified, RS256 is used with a private key and HS256 with the client secret.
jwtX509HeadersEnabledHelp=If enabled, the x5t (X.509 Certificate SHA-1 Thumbprint) header will be added to the JWT to reference the certificate used to sign it. Otherwise, the kid (Key ID) header will be used instead.
addProvider_other=Add {{provider}} providers
resetAction=Reset action
Expand Down
7 changes: 5 additions & 2 deletions js/apps/admin-ui/src/clients/credentials/SignedJWT.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@ export const SignedJWT = ({ clientAuthenticatorType }: SignedJWTProps) => {

const { t } = useTranslation();
const { control } = useFormContext<FormFields>();
const isClientSecretJwt = clientAuthenticatorType === "client-secret-jwt";

return (
<>
<SelectControl
name={convertAttributeNameToForm<FormFields>(
"attributes.token.endpoint.auth.signing.alg",
)}
label={t("signatureAlgorithm")}
labelIcon={t("signatureAlgorithmHelp")}
label={t(isClientSecretJwt ? "macAlgorithm" : "signatureAlgorithm")}
labelIcon={t(
isClientSecretJwt ? "macAlgorithmHelp" : "signatureAlgorithmHelp",
)}
controller={{
defaultValue: "",
}}
Expand Down
24 changes: 24 additions & 0 deletions js/apps/admin-ui/test/clients/details.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import { clickTableRowItem, searchItem } from "../utils/table.ts";
import { continueNext, createClient, save } from "./utils.ts";
import {
assertDedicatedScopeMapper,
assertJwtAlgorithmOptions,
assertKeyForCodeExchangeInput,
assertMacAlgorithmLabel,
assertSignatureAlgorithmLabel,
goToCredentialsTab,
selectClientAuthenticator,
enableJwtAuthorizationGrant,
getJwtAuthorizationGrantIdpOptions,
goToDedicatedScope,
Expand Down Expand Up @@ -146,6 +151,25 @@ test.describe.serial("Clients details test", () => {
const client2 = await adminClient.getClient(clientId, realmName);
expect(client2?.attributes?.["post.logout.redirect.uris"]).toBeUndefined();
});

test("Should use MAC terminology for client secret JWT algorithms", async ({
page,
}) => {
await clickTableRowItem(page, clientId);
await goToCredentialsTab(page);

await selectClientAuthenticator(page, "Signed JWT with Client Secret");
await assertMacAlgorithmLabel(page);
await assertJwtAlgorithmOptions(
page,
["HS256", "HS384", "HS512"],
["RS256", "ES256"],
);

await selectClientAuthenticator(page, "Signed JWT");
await assertSignatureAlgorithmLabel(page);
await assertJwtAlgorithmOptions(page, ["RS256", "ES256"], ["HS256"]);
});
});

test.describe.serial("Client admin without manage-realm", () => {
Expand Down
52 changes: 50 additions & 2 deletions js/apps/admin-ui/test/clients/details.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { Page } from "@playwright/test";
import { expect } from "@playwright/test";
import { type Page, expect } from "@playwright/test";
import {
selectItem,
assertSelectValue,
Expand All @@ -11,6 +10,55 @@ function getKeyForCodeExchangeInput(page: Page) {
return page.locator("#keyForCodeExchange");
}

function getClientAuthenticatorInput(page: Page) {
return page.locator("#clientAuthenticatorType");
}

function getJwtAlgorithmInput(page: Page) {
return page.locator("#attributes\\.token🍺endpoint🍺auth🍺signing🍺alg");
}

export async function goToCredentialsTab(page: Page) {
await page.getByRole("tab", { name: "Credentials" }).click();
}

export async function selectClientAuthenticator(page: Page, value: string) {
await selectItem(page, getClientAuthenticatorInput(page), value);
}

export async function assertMacAlgorithmLabel(page: Page) {
await expect(page.getByText("MAC algorithm", { exact: true })).toBeVisible();
await expect(
page.getByText("Signature algorithm", { exact: true }),
).toHaveCount(0);
}

export async function assertSignatureAlgorithmLabel(page: Page) {
await expect(
page.getByText("Signature algorithm", { exact: true }),
).toBeVisible();
await expect(page.getByText("MAC algorithm", { exact: true })).toHaveCount(0);
}

export async function assertJwtAlgorithmOptions(
page: Page,
expected: string[],
unexpected: string[] = [],
) {
await getJwtAlgorithmInput(page).click();
for (const algorithm of expected) {
await expect(
page.getByRole("option", { name: algorithm, exact: true }),
).toBeVisible();
}
for (const algorithm of unexpected) {
await expect(
page.getByRole("option", { name: algorithm, exact: true }),
).toHaveCount(0);
}
await page.keyboard.press("Escape");
}

export async function selectKeyForCodeExchangeInput(page: Page, value: string) {
await switchOn(page, page.getByTestId("pkce-required"));
await selectItem(page, getKeyForCodeExchangeInput(page), value);
Expand Down
Loading
0