From d963ea5c905e4b4a7f5c7302d8abed4063c61f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar?= Date: Tue, 7 Mar 2023 12:22:17 +0100 Subject: [PATCH] FEAT: Update Keycloak config for client roles rather than realm roles (#43) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Closes #42 and implements new methods for checking roles belonging to a client. Also performs some cleanup of configurations and upgrade to latest version of Keycloak. ## Related Issue Closes #42. ## Motivation and Context Allows better and more flexible management of roles for different resources (APIs) and allows to check for them beyond the standard `role` claim in the tokens. ## Types of changes - [x] Bug fix (non-breaking change which fixes an issue) - [X] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Checklist: - [X] My code follows the code style of this project. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [X] I have read the **CONTRIBUTING** document. - [ ] I have added tests to cover my changes. - [X] All new and existing tests passed. --------- Co-authored-by: Gonzalo Fernández --- .../appsettings.json | 8 +- .../Extensions/ClaimsPrincipalExtensions.cs | 51 ++ .../Solution/realm-export-template.json | 536 ++++++++++-------- 3 files changed, 355 insertions(+), 240 deletions(-) create mode 100644 src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Extensions/ClaimsPrincipalExtensions.cs diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Api/appsettings.json b/src/Content/Backend/Solution/Monaco.Template.Backend.Api/appsettings.json index fff10da..b6b3d86 100644 --- a/src/Content/Backend/Solution/Monaco.Template.Backend.Api/appsettings.json +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Api/appsettings.json @@ -16,18 +16,12 @@ //#if (!disableAuth) "SSO": { - "Authority": "http://localhost:8080/auth/realms/monaco-template", + "Authority": "http://localhost:8080/realms/monaco-template", "Audience": "monaco-template-api", "SwaggerUIClientId": "monaco-template-api-swagger-ui", "SwaggerUIClientSecret": "" }, - - "KeyCloak": { - "Host": "http://localhost:8080", - "Realm": "monaco-template", - "ClientSecret": "" - }, //#endif //#if (massTransitIntegration) diff --git a/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Extensions/ClaimsPrincipalExtensions.cs b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Extensions/ClaimsPrincipalExtensions.cs new file mode 100644 index 0000000..3f517f3 --- /dev/null +++ b/src/Content/Backend/Solution/Monaco.Template.Backend.Common.Application/Extensions/ClaimsPrincipalExtensions.cs @@ -0,0 +1,51 @@ +using Microsoft.IdentityModel.JsonWebTokens; +using System.Security.Claims; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Monaco.Template.Backend.Common.Application.Extensions; + +public static class ClaimsPrincipalExtensions +{ + private const string ResourceAccessClaimName = "resource_access"; + + /// + /// Retrieves the User Id from the "sub" claim + /// + /// + /// + public static Guid? GetUserId(this ClaimsPrincipal principal) => + principal.HasClaim(c => c.Type == JwtRegisteredClaimNames.Sub) + ? Guid.Parse(principal.FindFirst(JwtRegisteredClaimNames.Sub)!.Value) + : null; + + /// + /// Determines if the user has the specified role on the specified client + /// + /// + /// + /// + /// + public static bool IsInClientRole(this ClaimsPrincipal principal, string clientName, string roleName) + { + var resourceAccessClaim = principal.FindFirst(ResourceAccessClaimName); + if (resourceAccessClaim is null) + return false; + + var clients = JsonSerializer.Deserialize>(resourceAccessClaim.Value, + new JsonSerializerOptions(JsonSerializerDefaults.Web)); + return clients is not null && + clients.ContainsKey(clientName) && + (clients[clientName][principal.Identities.First().RoleClaimType]?.Deserialize()?.Contains(roleName) ?? false); + } + + /// + /// Determines if the user has the specified role in the client specified by the Audience (aud) claim + /// + /// + /// + /// + public static bool IsInClientRole(this ClaimsPrincipal principal, string roleName) => + principal.HasClaim(c => c.Type == JwtRegisteredClaimNames.Aud) && + principal.IsInClientRole(principal.FindFirst(JwtRegisteredClaimNames.Aud)!.Value, roleName); +} \ No newline at end of file diff --git a/src/Content/Backend/Solution/realm-export-template.json b/src/Content/Backend/Solution/realm-export-template.json index 9d63e07..c09a489 100644 --- a/src/Content/Backend/Solution/realm-export-template.json +++ b/src/Content/Backend/Solution/realm-export-template.json @@ -46,14 +46,6 @@ "failureFactor": 30, "roles": { "realm": [ - { - "id": "eda2cf1e-7707-458d-b91b-956b9a9be260", - "name": "Customer", - "composite": false, - "clientRole": false, - "containerId": "monaco-template", - "attributes": {} - }, { "id": "80c3602f-f30d-4f1c-83f4-3e8395bdce25", "name": "uma_authorization", @@ -84,14 +76,6 @@ "containerId": "monaco-template", "attributes": {} }, - { - "id": "b0464d8d-2dbd-461a-9861-38c0747b23a6", - "name": "Administrator", - "composite": false, - "clientRole": false, - "containerId": "monaco-template", - "attributes": {} - }, { "id": "95c05d66-9802-411a-9f82-4b9ab0b34685", "name": "offline_access", @@ -376,6 +360,15 @@ "containerId": "b3a43f06-b62f-4308-9a51-f5159e3359a3", "attributes": {} }, + { + "id": "7d7112cb-ffed-4b52-b440-3fdbd7a63c5a", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "b3a43f06-b62f-4308-9a51-f5159e3359a3", + "attributes": {} + }, { "id": "c443d55b-32e6-47e9-a057-96a7eab1d688", "name": "manage-consent", @@ -411,7 +404,26 @@ "attributes": {} } ], - "monaco-template-api": [] + "monaco-template-api": [ + { + "id": "2a0fe481-1bc6-4fa4-a02d-0aace321c60e", + "name": "Administrator", + "description": "", + "composite": false, + "clientRole": true, + "containerId": "cc450c6d-bf28-45e4-9148-6bb7d4187641", + "attributes": {} + }, + { + "id": "53405b4d-d952-47d8-9bc7-8f52a15522e8", + "name": "Customer", + "description": "", + "composite": false, + "clientRole": true, + "containerId": "cc450c6d-bf28-45e4-9148-6bb7d4187641", + "attributes": {} + } + ] } }, "groups": [], @@ -432,9 +444,11 @@ "otpPolicyDigits": 6, "otpPolicyLookAheadWindow": 1, "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, "otpSupportedApplications": [ - "FreeOTP", - "Google Authenticator" + "totpAppGoogleName", + "totpAppFreeOTPName", + "totpAppMicrosoftAuthenticatorName" ], "webAuthnPolicyRpEntityName": "keycloak", "webAuthnPolicySignatureAlgorithms": [ @@ -461,19 +475,6 @@ "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, "webAuthnPolicyPasswordlessAcceptableAaguids": [], "scopeMappings": [ - { - "clientScope": "companies:read", - "roles": [ - "Administrator", - "Customer" - ] - }, - { - "clientScope": "companies:write", - "roles": [ - "Administrator" - ] - }, { "clientScope": "offline_access", "roles": [ @@ -486,7 +487,23 @@ { "client": "account-console", "roles": [ - "manage-account" + "manage-account", + "view-groups" + ] + } + ], + "monaco-template-api": [ + { + "clientScope": "companies:read", + "roles": [ + "Administrator", + "Customer" + ] + }, + { + "clientScope": "companies:write", + "roles": [ + "Administrator" ] } ] @@ -516,7 +533,9 @@ "publicClient": true, "frontchannelLogout": false, "protocol": "openid-connect", - "attributes": {}, + "attributes": { + "post.logout.redirect.uris": "+" + }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": false, "nodeReRegistrationTimeout": 0, @@ -558,6 +577,7 @@ "frontchannelLogout": false, "protocol": "openid-connect", "attributes": { + "post.logout.redirect.uris": "+", "pkce.code.challenge.method": "S256" }, "authenticationFlowBindingOverrides": {}, @@ -606,7 +626,9 @@ "publicClient": true, "frontchannelLogout": false, "protocol": "openid-connect", - "attributes": {}, + "attributes": { + "post.logout.redirect.uris": "+" + }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": false, "nodeReRegistrationTimeout": 0, @@ -643,7 +665,9 @@ "publicClient": false, "frontchannelLogout": false, "protocol": "openid-connect", - "attributes": {}, + "attributes": { + "post.logout.redirect.uris": "+" + }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": false, "nodeReRegistrationTimeout": 0, @@ -665,6 +689,7 @@ "clientId": "monaco-template-api", "name": "Monaco Template API", "description": "Monaco Template API", + "adminUrl": "", "surrogateAuthRequired": false, "enabled": true, "alwaysDisplayInConsole": false, @@ -687,6 +712,7 @@ "saml.force.post.binding": "false", "saml.multivalued.roles": "false", "saml.encrypt": "false", + "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", "backchannel.logout.revoke.offline.tokens": "false", "saml.server.signature": "false", @@ -752,6 +778,7 @@ "saml.force.post.binding": "false", "saml.multivalued.roles": "false", "saml.encrypt": "false", + "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", "backchannel.logout.revoke.offline.tokens": "false", "saml.server.signature": "false", @@ -792,6 +819,9 @@ { "id": "09fb98e3-c6c4-4151-bf3c-1fbc804b080e", "clientId": "monaco-template-backend", + "name": "Monaco Template Backend", + "description": "Access to the backend through the API Gateway", + "adminUrl": "", "surrogateAuthRequired": false, "enabled": true, "alwaysDisplayInConsole": false, @@ -809,26 +839,31 @@ "frontchannelLogout": false, "protocol": "openid-connect", "attributes": { - "id.token.as.detached.signature": "false", - "saml.assertion.signature": "false", "saml.force.post.binding": "false", "saml.multivalued.roles": "false", - "saml.encrypt": "false", + "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", "backchannel.logout.revoke.offline.tokens": "false", - "saml.server.signature": "false", "saml.server.signature.keyinfo.ext": "false", "use.refresh.tokens": "true", - "exclude.session.state.from.auth.response": "false", "oidc.ciba.grant.enabled": "false", - "saml.artifact.binding": "false", "backchannel.logout.session.required": "true", "client_credentials.use_refresh_token": "false", - "saml_force_name_id_format": "false", "saml.client.signature": "false", + "require.pushed.authorization.requests": "false", + "id.token.as.detached.signature": "false", + "saml.assertion.signature": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "exclude.session.state.from.auth.response": "false", + "tls-client-certificate-bound-access-tokens": "false", + "saml.artifact.binding": "false", + "saml_force_name_id_format": "false", "tls.client.certificate.bound.access.tokens": "false", + "acr.loa.map": "{}", "saml.authnstatement": "false", "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false", "saml.onetimeuse.condition": "false" }, "authenticationFlowBindingOverrides": {}, @@ -877,6 +912,7 @@ "saml.force.post.binding": "false", "saml.multivalued.roles": "false", "saml.encrypt": "false", + "post.logout.redirect.uris": "+", "oauth2.device.authorization.grant.enabled": "false", "backchannel.logout.revoke.offline.tokens": "false", "saml.server.signature": "false", @@ -935,7 +971,9 @@ "publicClient": false, "frontchannelLogout": false, "protocol": "openid-connect", - "attributes": {}, + "attributes": { + "post.logout.redirect.uris": "+" + }, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": false, "nodeReRegistrationTimeout": 0, @@ -979,6 +1017,7 @@ "frontchannelLogout": false, "protocol": "openid-connect", "attributes": { + "post.logout.redirect.uris": "+", "pkce.code.challenge.method": "S256" }, "authenticationFlowBindingOverrides": {}, @@ -992,12 +1031,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "locale", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "locale", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } } ], @@ -1023,8 +1062,8 @@ "protocol": "openid-connect", "attributes": { "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${addressScopeConsentText}" + "consent.screen.text": "${addressScopeConsentText}", + "display.on.consent.screen": "true" }, "protocolMappers": [ { @@ -1065,7 +1104,8 @@ "config": { "included.client.audience": "monaco-template-api", "id.token.claim": "false", - "access.token.claim": "true" + "access.token.claim": "true", + "userinfo.token.claim": "false" } } ] @@ -1073,10 +1113,12 @@ { "id": "2ee7b9e4-6df5-4a45-b78b-9b1c145c0723", "name": "companies:read", + "description": "Read companies from API", "protocol": "openid-connect", "attributes": { "include.in.token.scope": "true", - "display.on.consent.screen": "false" + "display.on.consent.screen": "false", + "gui.order": "" } }, { @@ -1106,10 +1148,12 @@ { "id": "91c57951-bafb-409f-b271-a1b4b3e3aa26", "name": "companies:write", + "description": "Create, edit and delete companies in API", "protocol": "openid-connect", "attributes": { "include.in.token.scope": "true", - "display.on.consent.screen": "false" + "display.on.consent.screen": "false", + "gui.order": "" } }, { @@ -1119,8 +1163,8 @@ "protocol": "openid-connect", "attributes": { "include.in.token.scope": "false", - "display.on.consent.screen": "true", - "consent.screen.text": "${rolesScopeConsentText}" + "consent.screen.text": "${rolesScopeConsentText}", + "display.on.consent.screen": "true" }, "protocolMappers": [ { @@ -1144,12 +1188,12 @@ "protocolMapper": "oidc-usermodel-realm-role-mapper", "consentRequired": false, "config": { - "multivalued": "true", - "userinfo.token.claim": "true", "user.attribute": "foo", "access.token.claim": "true", "claim.name": "roles", - "jsonType.label": "String" + "jsonType.label": "String", + "multivalued": "true", + "userinfo.token.claim": "true" } }, { @@ -1162,6 +1206,29 @@ } ] }, + { + "id": "194ef116-fa08-407a-b535-1d5a60356c7e", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "a0f31e04-653c-4783-8bb0-d4a689c1a36e", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, { "id": "d07a981d-4198-4f2c-be6c-acddc3aa98d3", "name": "phone", @@ -1169,8 +1236,8 @@ "protocol": "openid-connect", "attributes": { "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${phoneScopeConsentText}" + "consent.screen.text": "${phoneScopeConsentText}", + "display.on.consent.screen": "true" }, "protocolMappers": [ { @@ -1180,12 +1247,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "phoneNumber", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "phone_number", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1195,12 +1262,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "phoneNumberVerified", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "phone_number_verified", - "jsonType.label": "boolean" + "jsonType.label": "boolean", + "userinfo.token.claim": "true" } } ] @@ -1248,12 +1315,12 @@ "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "username", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "upn", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } } ] @@ -1265,8 +1332,8 @@ "protocol": "openid-connect", "attributes": { "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${emailScopeConsentText}" + "consent.screen.text": "${emailScopeConsentText}", + "display.on.consent.screen": "true" }, "protocolMappers": [ { @@ -1276,12 +1343,12 @@ "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "email", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "email", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1291,12 +1358,12 @@ "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "emailVerified", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "email_verified", - "jsonType.label": "boolean" + "jsonType.label": "boolean", + "userinfo.token.claim": "true" } } ] @@ -1319,7 +1386,8 @@ "config": { "included.client.audience": "monaco-template-backend", "id.token.claim": "false", - "access.token.claim": "true" + "access.token.claim": "true", + "userinfo.token.claim": "false" } } ] @@ -1331,8 +1399,8 @@ "protocol": "openid-connect", "attributes": { "include.in.token.scope": "true", - "display.on.consent.screen": "true", - "consent.screen.text": "${profileScopeConsentText}" + "consent.screen.text": "${profileScopeConsentText}", + "display.on.consent.screen": "true" }, "protocolMappers": [ { @@ -1342,12 +1410,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "middleName", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "middle_name", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1357,12 +1425,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "gender", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "gender", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1372,12 +1440,12 @@ "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "lastName", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "family_name", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1387,12 +1455,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "zoneinfo", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "zoneinfo", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1402,12 +1470,12 @@ "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "firstName", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "given_name", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1417,12 +1485,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "nickname", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "nickname", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1432,12 +1500,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "locale", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "locale", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1447,12 +1515,12 @@ "protocolMapper": "oidc-usermodel-property-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "username", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "preferred_username", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1462,12 +1530,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "birthdate", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "birthdate", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1477,12 +1545,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "updatedAt", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "updated_at", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1493,8 +1561,8 @@ "consentRequired": false, "config": { "id.token.claim": "true", - "access.token.claim": "true", - "userinfo.token.claim": "true" + "userinfo.token.claim": "true", + "access.token.claim": "true" } }, { @@ -1504,12 +1572,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "picture", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "picture", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1519,12 +1587,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "profile", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "profile", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } }, { @@ -1534,12 +1602,12 @@ "protocolMapper": "oidc-usermodel-attribute-mapper", "consentRequired": false, "config": { - "userinfo.token.claim": "true", "user.attribute": "website", "id.token.claim": "true", "access.token.claim": "true", "claim.name": "website", - "jsonType.label": "String" + "jsonType.label": "String", + "userinfo.token.claim": "true" } } ] @@ -1551,8 +1619,8 @@ "protocol": "openid-connect", "attributes": { "include.in.token.scope": "false", - "display.on.consent.screen": "false", - "consent.screen.text": "" + "consent.screen.text": "", + "display.on.consent.screen": "false" }, "protocolMappers": [ { @@ -1571,7 +1639,8 @@ "profile", "email", "roles", - "web-origins" + "web-origins", + "acr" ], "defaultOptionalClientScopes": [ "offline_access", @@ -1622,14 +1691,14 @@ "subComponents": {}, "config": { "allowed-protocol-mapper-types": [ - "oidc-address-mapper", - "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", + "oidc-full-name-mapper", + "oidc-address-mapper", "saml-role-list-mapper", "saml-user-attribute-mapper", - "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper", - "oidc-usermodel-property-mapper" + "oidc-usermodel-property-mapper", + "saml-user-property-mapper" ] } }, @@ -1655,12 +1724,12 @@ "allowed-protocol-mapper-types": [ "oidc-full-name-mapper", "saml-user-attribute-mapper", - "saml-role-list-mapper", - "oidc-usermodel-property-mapper", "saml-user-property-mapper", + "saml-role-list-mapper", + "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", - "oidc-usermodel-attribute-mapper" + "oidc-usermodel-property-mapper" ] } }, @@ -1766,7 +1835,7 @@ "supportedLocales": [], "authenticationFlows": [ { - "id": "e0705766-2e91-4735-b9fa-8590bc4faae9", + "id": "d5186dab-c3c4-4b19-9c3d-cdd0787d054d", "alias": "Account verification options", "description": "Method with which to verity the existing account", "providerId": "basic-flow", @@ -1778,21 +1847,21 @@ "authenticatorFlow": false, "requirement": "ALTERNATIVE", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticatorFlow": true, "requirement": "ALTERNATIVE", "priority": 20, + "autheticatorFlow": true, "flowAlias": "Verify Existing Account by Re-authentication", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "c8675fd0-5ddb-4628-82d4-fb063859505e", + "id": "3ad8d43f-1b8e-4bd2-90dd-ed7a882ac0a2", "alias": "Authentication Options", "description": "Authentication options.", "providerId": "basic-flow", @@ -1804,29 +1873,29 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "basic-auth-otp", "authenticatorFlow": false, "requirement": "DISABLED", "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "auth-spnego", "authenticatorFlow": false, "requirement": "DISABLED", "priority": 30, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false } ] }, { - "id": "d686d005-b309-4551-bfb1-d71a4f6e0d11", + "id": "e98b79cb-6c9e-473c-b020-7c9b6b6ba3d9", "alias": "Browser - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1838,21 +1907,21 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "auth-otp-form", "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false } ] }, { - "id": "7f4c80ee-159d-4ae4-b928-47ac3684d45b", + "id": "d89c2378-e9c2-40dc-9352-6e43192ad7be", "alias": "Direct Grant - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1864,21 +1933,21 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "direct-grant-validate-otp", "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false } ] }, { - "id": "0c134675-a8e6-4146-a67e-bdd048197008", + "id": "8826380e-bd70-4792-9aea-7d29de403aa9", "alias": "First broker login - Conditional OTP", "description": "Flow to determine if the OTP is required for the authentication", "providerId": "basic-flow", @@ -1890,21 +1959,21 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "auth-otp-form", "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false } ] }, { - "id": "362c21ef-ac96-43cf-8aba-51e4e16bfa25", + "id": "09e74503-9899-45dc-8148-0d3d8746a3c3", "alias": "Handle Existing Account", "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId": "basic-flow", @@ -1916,21 +1985,21 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticatorFlow": true, "requirement": "REQUIRED", "priority": 20, + "autheticatorFlow": true, "flowAlias": "Account verification options", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "2533aeba-e2b1-41c4-9d1a-a07cd95fbc18", + "id": "68454eeb-b718-48bf-bb67-a2cbc5a25777", "alias": "Reset - Conditional OTP", "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId": "basic-flow", @@ -1942,21 +2011,21 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "reset-otp", "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false } ] }, { - "id": "e0a5b338-b3b0-41cf-ae0f-cc4cabe5c1f2", + "id": "9f8ab0da-336f-4a1c-b997-ca6061caa6e7", "alias": "User creation or linking", "description": "Flow for the existing/non-existing user alternatives", "providerId": "basic-flow", @@ -1969,21 +2038,21 @@ "authenticatorFlow": false, "requirement": "ALTERNATIVE", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticatorFlow": true, "requirement": "ALTERNATIVE", "priority": 20, + "autheticatorFlow": true, "flowAlias": "Handle Existing Account", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "b1dbc131-9a1b-4a9c-a985-b7fb4d2a0127", + "id": "a9690e57-94b3-4de7-a453-7b4711eb1e63", "alias": "Verify Existing Account by Re-authentication", "description": "Reauthentication of existing account", "providerId": "basic-flow", @@ -1995,21 +2064,21 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticatorFlow": true, "requirement": "CONDITIONAL", "priority": 20, + "autheticatorFlow": true, "flowAlias": "First broker login - Conditional OTP", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "adef851f-c566-4eb1-bd8a-692cd3a37704", + "id": "9c3710b8-3810-4600-978a-8d0cb523fe27", "alias": "browser", "description": "browser based authentication", "providerId": "basic-flow", @@ -2021,37 +2090,37 @@ "authenticatorFlow": false, "requirement": "ALTERNATIVE", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "auth-spnego", "authenticatorFlow": false, "requirement": "DISABLED", "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "identity-provider-redirector", "authenticatorFlow": false, "requirement": "ALTERNATIVE", "priority": 25, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticatorFlow": true, "requirement": "ALTERNATIVE", "priority": 30, + "autheticatorFlow": true, "flowAlias": "forms", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "fec3ed3d-3aef-48d2-a699-ed31349121da", + "id": "3b20d736-76b1-4f31-b8bb-4307f40dc554", "alias": "clients", "description": "Base authentication for clients", "providerId": "client-flow", @@ -2063,37 +2132,37 @@ "authenticatorFlow": false, "requirement": "ALTERNATIVE", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "client-jwt", "authenticatorFlow": false, "requirement": "ALTERNATIVE", "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "client-secret-jwt", "authenticatorFlow": false, "requirement": "ALTERNATIVE", "priority": 30, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "client-x509", "authenticatorFlow": false, "requirement": "ALTERNATIVE", "priority": 40, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false } ] }, { - "id": "d5c72026-48b1-4b49-86e3-160fb70fd05a", + "id": "58607617-6a87-4efe-95f2-db07a4d293a3", "alias": "direct grant", "description": "OpenID Connect Resource Owner Grant", "providerId": "basic-flow", @@ -2105,29 +2174,29 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "direct-grant-validate-password", "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticatorFlow": true, "requirement": "CONDITIONAL", "priority": 30, + "autheticatorFlow": true, "flowAlias": "Direct Grant - Conditional OTP", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "feaf9331-9f7a-4143-9247-27a0ea83f7e9", + "id": "c55758f6-ba86-46b0-8d48-0aedd12e6176", "alias": "docker auth", "description": "Used by Docker clients to authenticate against the IDP", "providerId": "basic-flow", @@ -2139,13 +2208,13 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false } ] }, { - "id": "c0336213-5b51-431b-b324-67f774557d54", + "id": "75788d22-37e1-4b23-bcca-7c929176fbab", "alias": "first broker login", "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId": "basic-flow", @@ -2158,21 +2227,21 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticatorFlow": true, "requirement": "REQUIRED", "priority": 20, + "autheticatorFlow": true, "flowAlias": "User creation or linking", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "10ce57b2-2a55-43de-914a-56c517f43242", + "id": "8a7860c9-e200-45bf-a6f6-353a47c9f683", "alias": "forms", "description": "Username, password, otp and other auth forms.", "providerId": "basic-flow", @@ -2184,21 +2253,21 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticatorFlow": true, "requirement": "CONDITIONAL", "priority": 20, + "autheticatorFlow": true, "flowAlias": "Browser - Conditional OTP", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "c87701f8-addf-44e1-a630-113b6a1871ca", + "id": "58a93edb-1989-4507-b840-8c6b2339cd1c", "alias": "http challenge", "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId": "basic-flow", @@ -2210,21 +2279,21 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticatorFlow": true, "requirement": "REQUIRED", "priority": 20, + "autheticatorFlow": true, "flowAlias": "Authentication Options", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "dd4429b1-7ecc-4239-9ee3-6dc0c57c7492", + "id": "ecb459b7-4ec9-432d-9e7f-7579e56d7d2e", "alias": "registration", "description": "registration flow", "providerId": "basic-flow", @@ -2236,14 +2305,14 @@ "authenticatorFlow": true, "requirement": "REQUIRED", "priority": 10, + "autheticatorFlow": true, "flowAlias": "registration form", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "0d500e1f-2389-45d0-8603-45ff007e0314", + "id": "22121960-e79a-4349-b530-6c145d2e1296", "alias": "registration form", "description": "registration form", "providerId": "form-flow", @@ -2255,37 +2324,37 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "registration-profile-action", "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 40, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "registration-password-action", "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 50, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "registration-recaptcha-action", "authenticatorFlow": false, "requirement": "DISABLED", "priority": 60, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false } ] }, { - "id": "92d80a4c-c3c1-4a81-a72d-6044516e63f7", + "id": "b10d27db-2ceb-445a-a703-17bf0512ef5d", "alias": "reset credentials", "description": "Reset credentials for a user if they forgot their password or something", "providerId": "basic-flow", @@ -2297,37 +2366,37 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "reset-credential-email", "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 20, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticator": "reset-password", "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 30, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false }, { "authenticatorFlow": true, "requirement": "CONDITIONAL", "priority": 40, + "autheticatorFlow": true, "flowAlias": "Reset - Conditional OTP", - "userSetupAllowed": false, - "autheticatorFlow": true + "userSetupAllowed": false } ] }, { - "id": "e05767a5-c141-4f01-b7a2-4efb5e2a26a3", + "id": "3dd963c5-65cb-4b48-8a35-e7b681c8ab4c", "alias": "saml ecp", "description": "SAML ECP Profile Authentication Flow", "providerId": "basic-flow", @@ -2339,22 +2408,22 @@ "authenticatorFlow": false, "requirement": "REQUIRED", "priority": 10, - "userSetupAllowed": false, - "autheticatorFlow": false + "autheticatorFlow": false, + "userSetupAllowed": false } ] } ], "authenticatorConfig": [ { - "id": "82f114ba-9845-4838-9a3b-83bc743050bd", + "id": "9b21e277-95c8-4186-ba65-51a82f75be53", "alias": "create unique user config", "config": { "require.password.update.after.registration": "false" } }, { - "id": "f8db2596-279d-4254-b6fe-1b8224d9aee5", + "id": "4b221ac9-c081-4a71-a50f-32ea01e20599", "alias": "review profile config", "config": { "update.profile.on.first.login": "missing" @@ -2434,19 +2503,20 @@ "dockerAuthenticationFlow": "docker auth", "attributes": { "cibaBackchannelTokenDeliveryMode": "poll", - "cibaExpiresIn": "120", "cibaAuthRequestedUserHint": "login_hint", - "oauth2DeviceCodeLifespan": "600", "clientOfflineSessionMaxLifespan": "0", "oauth2DevicePollingInterval": "5", "clientSessionIdleTimeout": "0", "userProfileEnabled": "false", - "clientSessionMaxLifespan": "0", - "parRequestUriLifespan": "60", "clientOfflineSessionIdleTimeout": "0", - "cibaInterval": "5" + "cibaInterval": "5", + "realmReusableOtpCode": "false", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0" }, - "keycloakVersion": "14.0.0", + "keycloakVersion": "21.0.0", "userManagedAccessAllowed": false, "clientProfiles": { "profiles": []