diff --git a/.keycloakify/realm-kc-25.json b/.keycloakify/realm-kc-25.json new file mode 100644 index 0000000..eaf5f36 --- /dev/null +++ b/.keycloakify/realm-kc-25.json @@ -0,0 +1,2516 @@ +{ + "id": "5d0dd960-0478-4ca6-b64a-810a3f6f4071", + "realm": "myrealm", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "none", + "registrationAllowed": true, + "registrationEmailAsUsername": false, + "rememberMe": true, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxTemporaryLockouts": 0, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "cc4b5045-3bff-4aa7-889e-1492630c3002", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "5d0dd960-0478-4ca6-b64a-810a3f6f4071", + "attributes": {} + }, + { + "id": "e92017b2-18a0-49cd-956c-fad64f16b26b", + "name": "default-roles-myrealm", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": ["offline_access", "uma_authorization"], + "client": { + "account": ["delete-account", "manage-account", "view-profile"] + } + }, + "clientRole": false, + "containerId": "5d0dd960-0478-4ca6-b64a-810a3f6f4071", + "attributes": {} + }, + { + "id": "e8616113-e302-4abe-bd5c-d51f8221046b", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "5d0dd960-0478-4ca6-b64a-810a3f6f4071", + "attributes": {} + } + ], + "client": { + "myclient": [], + "realm-management": [ + { + "id": "b27b272d-d153-4ae7-9fe7-fd96582f057d", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "40fdfec8-f1b9-4c2b-81c5-a775bc047840", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "5f446f9a-d008-4067-8325-f4658a32d964", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "82bf956d-1fd1-4d20-a5a9-62b3e77e9d88", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "b41e1ce8-d63f-4cf4-9966-e6c9eab5da11", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "3198743d-fdfa-4a9c-a229-5fb979847ec2", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-users", "query-groups"] + } + }, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "e83c21cb-c84c-4824-9f7d-ce3574921800", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "3f6e2e81-e40d-40ff-a5f3-12ba2614fba5", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "63111288-7f3d-4570-838f-48405d70e212", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "a7f8f8ad-057b-485e-abfa-8a98e5e0c4ea", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "7783b160-2f1a-48c9-89fb-623a29f26c9a", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "b8b5341f-f44f-40a2-9ba4-e2d621b11b2f", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "6b9d72e9-949f-4897-b11a-c8aa9252f3f2", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "bfa94ba9-1d70-4259-b928-906e8bb815b2", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "96bb9322-5c1f-48f0-aa05-65521c77e742", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-users", + "view-authorization", + "manage-events", + "create-client", + "view-users", + "manage-clients", + "query-users", + "query-groups", + "view-realm", + "manage-realm", + "query-realms", + "query-clients", + "impersonation", + "view-events", + "manage-authorization", + "manage-identity-providers", + "view-identity-providers", + "view-clients" + ] + } + }, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "6e0ca5ce-f5db-4580-90e5-27c35804fc34", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "7499eb46-cf4a-4813-9bf9-42b1bbcadc0d", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "fcc99ef9-347d-4c21-b25c-8229e906a1a3", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-clients"] + } + }, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "7b024069-57d8-4368-9942-8790507c156d", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "3050eb8a-9a47-4a27-aece-be2e60fc7f73", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "f5e032da-c8ab-48c2-959c-8466ad1e6a09", + "attributes": {} + } + ], + "account": [ + { + "id": "d554d15b-d098-47a0-bdd5-d656b20f5643", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "aaf4946d-2cd4-43ba-ad7d-86be56b9ad2c", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "b417b187-18b7-41fa-9537-3313cf9b8ed4", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": ["manage-account-links"] + } + }, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "8bb5480d-83a3-4ea2-8e91-237b8870acec", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "e341c1b8-eaf7-467d-9986-d3f2356a60b9", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "98ccac20-3906-436f-8dc3-ae8d8ae25cbc", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "adfba539-826f-4fa7-86f5-8c1287152ed6", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "2516ab58-490c-444c-9e7d-0dd8b87a69f0", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": ["view-consent"] + } + }, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + } + ], + "dataspace": [] + } + }, + "groups": [], + "defaultRole": { + "id": "e92017b2-18a0-49cd-956c-fad64f16b26b", + "name": "default-roles-myrealm", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "5d0dd960-0478-4ca6-b64a-810a3f6f4071" + }, + "requiredCredentials": ["password"], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppFreeOTPName", + "totpAppGoogleName", + "totpAppMicrosoftAuthenticatorName" + ], + "localizationTexts": { + "de": { + "profile.attributes.favourite_pet": "" + }, + "no": { + "profile.attributes.favourite_pet": "" + }, + "fi": { + "profile.attributes.favourite_pet": "" + }, + "ru": { + "profile.attributes.favourite_pet": "" + }, + "pt": { + "profile.attributes.favourite_pet": "" + }, + "lt": { + "profile.attributes.favourite_pet": "" + }, + "lv": { + "profile.attributes.favourite_pet": "" + }, + "fr": { + "profile.attributes.favourite_pet": "Animal de compagnie préféré", + "profile.attributes.favourite_pet.cat": "Chat", + "profile.attributes.favourite_pet.dog": "Chien", + "profile.attributes.favourite_pet.bird": "Oiseau" + }, + "hu": { + "profile.attributes.favourite_pet": "" + }, + "zh-CN": { + "profile.attributes.favourite_pet": "" + }, + "uk": { + "profile.attributes.favourite_pet": "" + }, + "sk": { + "profile.attributes.favourite_pet": "" + }, + "ca": { + "profile.attributes.favourite_pet": "" + }, + "sv": { + "profile.attributes.favourite_pet": "" + }, + "zh-TW": { + "profile.attributes.favourite_pet": "" + }, + "pt-BR": { + "profile.attributes.favourite_pet": "" + }, + "en": { + "profile.attributes.favourite_pet": "Favourite Pet", + "profile.attributes.favourite_pet.cat": "Cat", + "profile.attributes.favourite_pet.dog": "Dog", + "profile.attributes.favourite_pet.bird": "Bird" + }, + "it": { + "profile.attributes.favourite_pet": "" + }, + "es": { + "profile.attributes.favourite_pet": "Mascota favorita", + "profile.attributes.favourite_pet.cat": "Gato", + "profile.attributes.favourite_pet.dog": "Perro", + "profile.attributes.favourite_pet.bird": "Pájaro" + }, + "cs": { + "profile.attributes.favourite_pet": "" + }, + "ar": { + "profile.attributes.favourite_pet": "" + }, + "th": { + "profile.attributes.favourite_pet": "" + }, + "ja": { + "profile.attributes.favourite_pet": "" + }, + "fa": { + "profile.attributes.favourite_pet": "" + }, + "pl": { + "profile.attributes.favourite_pet": "" + }, + "da": { + "profile.attributes.favourite_pet": "" + }, + "nl": { + "profile.attributes.favourite_pet": "" + }, + "tr": { + "profile.attributes.favourite_pet": "" + } + }, + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": ["ES256"], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyExtraOrigins": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "webAuthnPolicyPasswordlessExtraOrigins": [], + "users": [ + { + "id": "d93e1772-4916-4243-850f-a6d9b2615716", + "username": "testuser", + "firstName": "Test", + "lastName": "User", + "email": "testuser@gmail.com", + "emailVerified": true, + "attributes": { + "additional_emails": ["test.user@protonmail.com", "testuser@hotmail.com"], + "favorite_pet": ["cats"], + "gender": ["prefer_not_to_say"], + "bio": ["Hello I'm Test User and I do not exist."], + "favourite_pet": ["cat"], + "phone_number": ["1111111111"], + "locale": ["en"], + "favorite_media": ["movies", "series"] + }, + "createdTimestamp": 1716183898408, + "enabled": true, + "totp": false, + "credentials": [ + { + "id": "576982e2-6fb3-4752-8724-5ff390ea8301", + "type": "password", + "userLabel": "My password", + "createdDate": 1716183916529, + "secretData": "{\"value\":\"9hwJ989FAr0UgT0MfffNYSI6Zf/3qT/y17DTUcwbiEM=\",\"salt\":\"C3ZnHzgPd+0Lemw4olCOgA==\",\"additionalParameters\":{}}", + "credentialData": "{\"hashIterations\":5,\"algorithm\":\"argon2\",\"additionalParameters\":{\"hashLength\":[\"32\"],\"memory\":[\"7168\"],\"type\":[\"id\"],\"version\":[\"1.3\"],\"parallelism\":[\"1\"]}}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": ["default-roles-myrealm"], + "clientRoles": { + "realm-management": [ + "manage-users", + "create-client", + "view-users", + "view-realm", + "query-realms", + "impersonation", + "view-events", + "realm-admin", + "manage-authorization", + "manage-events", + "view-authorization", + "manage-clients", + "query-users", + "query-groups", + "manage-realm", + "query-clients", + "manage-identity-providers", + "view-clients", + "view-identity-providers" + ], + "broker": ["read-token"], + "account": [ + "delete-account", + "view-applications", + "manage-account", + "view-consent", + "view-groups", + "view-profile", + "manage-account-links", + "manage-consent" + ] + }, + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": ["offline_access"] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": ["manage-account", "view-groups"] + } + ] + }, + "clients": [ + { + "id": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/myrealm/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/realms/myrealm/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "d8f14dc4-5f0f-4a1d-8c0b-cfe78ee55cb3", + "clientId": "account-console", + "name": "${client_account-console}", + "description": "", + "rootUrl": "${authBaseUrl}", + "adminUrl": "", + "baseUrl": "/realms/myrealm/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["http://localhost*", "http://127.0.0.1*", "*"], + "webOrigins": ["*"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "post.logout.redirect.uris": "+", + "oauth2.device.authorization.grant.enabled": "false", + "display.on.consent.screen": "false", + "pkce.code.challenge.method": "S256", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "08d7bc08-2ff3-44ea-9d65-fa1c4ca35646", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "953c597f-faef-4abc-88dc-4fbc9501170c", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "f5e032da-c8ab-48c2-959c-8466ad1e6a09", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "c0802b64-b09f-4f7e-8e70-ee478a304b7f", + "clientId": "dataspace", + "name": "dataspace", + "description": "", + "rootUrl": "http://localhost:3000", + "adminUrl": "", + "baseUrl": "/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "http://localhost:3000/api/auth/callback/keycloak", + "http://localhost:3000/*" + ], + "webOrigins": ["http://localhost:3000"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "post.logout.redirect.uris": "http://localhost:3000/*", + "oauth2.device.authorization.grant.enabled": "false", + "display.on.consent.screen": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "client.secret.creation.time": "1788854696" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ], + "secret": "xR2Ef85sHKJsfVzGPhnAC6yDygtXYetH" + }, + { + "id": "8fba88fa-61e9-45a4-893d-ab102973ebf6", + "clientId": "myclient", + "name": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "https://my-theme.keycloakify.dev/*", + "http://localhost*", + "http://127.0.0.1*" + ], + "webOrigins": ["*"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "backchannel.logout.revoke.offline.tokens": "false", + "backchannel.logout.session.required": "true", + "display.on.consent.screen": "false", + "oauth2.device.authorization.grant.enabled": "false", + "oidc.ciba.grant.enabled": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ], + "adminUrl": "https://my-theme.keycloakify.dev", + "baseUrl": "https://my-theme.keycloakify.dev", + "description": "", + "rootUrl": "https://my-theme.keycloakify.dev", + "protocolMappers": [ + { + "consentRequired": false, + "id": "91a196c1-f93c-48a5-aced-b8d60fb09b62", + "name": "Favourite Pet", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "config": { + "access.token.claim": "true", + "claim.name": "favourite_pet", + "id.token.claim": "true", + "introspection.token.claim": "true", + "jsonType.label": "String", + "lightweight.claim": "false", + "user.attribute": "favourite_pet", + "userinfo.token.claim": "true" + } + } + ] + }, + { + "id": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "fce8a109-6f32-4814-9a20-2ff2435d2da6", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/myrealm/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "http://localhost*", + "http://127.0.0.1*", + "/admin/myrealm/console/*" + ], + "webOrigins": ["*"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ], + "protocolMappers": [ + { + "consentRequired": false, + "id": "52192d19-0406-41b7-b995-b099bdbaa448", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "config": { + "access.token.claim": "true", + "claim.name": "locale", + "id.token.claim": "true", + "introspection.token.claim": "true", + "jsonType.label": "String", + "user.attribute": "locale", + "userinfo.token.claim": "true" + } + }, + { + "consentRequired": false, + "id": "8fd0d584-7052-4d04-a615-d18a71050873", + "name": "allowed-origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-hardcoded-claim-mapper", + "config": { + "access.token.claim": "true", + "access.tokenResponse.claim": "false", + "claim.name": "allowed-origins", + "claim.value": "[\"*\"]", + "id.token.claim": "false", + "introspection.token.claim": "true", + "jsonType.label": "JSON", + "lightweight.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + } + ], + "clientScopes": [ + { + "id": "6a955b1e-f0e2-49fa-b3c9-bd59ed1fcd4f", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "3a392f70-ed70-424a-b60b-82db32b83df8", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "9cda058d-9935-4c8b-844d-c163d10f7c3c", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${addressScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "a053d8ec-b267-4e5a-a424-3b14bef9cd15", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "introspection.token.claim": "true", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "6225f4c7-ad5c-42ea-b7d4-5bb4e7c77459", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${phoneScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "5052be82-243f-41b0-a214-4f01935180e5", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "id": "4d31d278-e6ef-4b8b-97cb-4da9626d0e93", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "9357440c-6200-41a1-a447-0ec97895763e", + "name": "basic", + "description": "OpenID Connect scope for add all basic claims to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "bf9cb6c6-71a4-4bf9-8c60-ed58adcc2258", + "name": "auth_time", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "AUTH_TIME", + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "auth_time", + "jsonType.label": "long" + } + }, + { + "id": "679c8292-1abb-4d96-bacc-671303765f9b", + "name": "sub", + "protocol": "openid-connect", + "protocolMapper": "oidc-sub-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "0ec225e7-253b-4a01-85e1-68daf3df3eba", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "a55cf74e-ce68-4ebd-9c24-dc3fd6a9cfa5", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "e2f1dd86-00a2-4374-b888-7211f748c58d", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "e86456b8-0663-448e-ad16-7d520d0c448e", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${profileScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "569c799d-79f2-4b2b-a1ec-3661e3d8d433", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "2d01eb48-77c3-4c83-a864-755699cb7e7c", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "a9700270-006f-4a85-8458-f39644659029", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "3a7bca96-0839-4d1e-b37d-6e624f37facb", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "2a41be1c-872a-4b3e-9051-71ebd5d140c1", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "9fe5e57d-ee79-4b8b-9ab2-345093a1fdbf", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "bda9e4e7-4de0-455d-bace-4e94b1dab5ad", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "312a0b4d-46b8-42e0-b162-e5869b317b36", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "4f8ac9bc-e32d-4ebb-bb85-b9a94a459aa1", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "bebdf0c7-6f0f-4b08-a327-50af837c82b9", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "d96d9686-f4e0-479a-9855-cfc526a35294", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "66ad8239-e1df-4f9d-9cb7-d35f23f95f37", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "ece8245b-16ae-4322-bc78-f8d5f671640a", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "384cf049-0fed-47e2-8b11-06cf6c03465d", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "49e85de9-edd1-4a9e-a2b0-e9c663d4dd9a", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${emailScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "d458e6fc-b414-4b45-b9e1-99342d7d2bba", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "2b73ce63-0443-46dc-b35c-1148edb976ab", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "71303f6d-348a-4892-9d6f-dc9a2d2e4b14", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "498cbff6-a650-4a09-8192-5defaa50f33b", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "id": "eb8585bc-ca30-410e-9f92-0d63665f5ed6", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "62b8c264-2c10-48c6-803f-b7606a89e0d9", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "${rolesScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "0c18ca55-df63-4071-81f9-43f5d077c015", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "6de6510d-d7f3-4289-a10f-4c21289313a4", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + }, + { + "id": "a5851eb2-bfc5-4a0a-8a49-92f4fc8c5041", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "bfc69775-83af-4816-82fd-d1c42687fb5e", + "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": "8e2027d5-32dd-4a87-a7ec-00e5316c5617", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins", + "acr", + "basic" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "referrerPolicy": "no-referrer", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "loginTheme": "keycloakify-starter", + "accountTheme": "", + "adminTheme": "", + "emailTheme": "keycloakify-starter", + "eventsEnabled": false, + "eventsListeners": ["keycloakify-logging", "jboss-logging"], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "67526992-f0ce-42ff-a0fb-af267192ff70", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "64a2f718-da10-45d9-a75a-69c156a7ccd8", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-role-list-mapper", + "oidc-usermodel-property-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-full-name-mapper", + "saml-user-property-mapper", + "saml-user-attribute-mapper" + ] + } + }, + { + "id": "4d3e104f-6fdf-45eb-b756-5fef6840fbed", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "c647e85f-6700-4d66-84f2-4a869e467735", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": ["200"] + } + }, + { + "id": "51f41974-f7e5-4e7d-b486-5bd652a98e93", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-address-mapper", + "saml-user-attribute-mapper", + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "saml-role-list-mapper", + "oidc-usermodel-attribute-mapper" + ] + } + }, + { + "id": "8f7d6ece-e956-4e48-95ab-5ab72b2b7c9a", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "e60b1167-cdee-4173-be99-3dad6a536b4a", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": ["true"], + "client-uris-must-match": ["true"] + } + }, + { + "id": "5ba8b893-ab01-430b-9092-32646a50a662", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.userprofile.UserProfileProvider": [ + { + "id": "237022c6-9443-46b3-902e-210e14c3c9a8", + "providerId": "declarative-user-profile", + "subComponents": {}, + "config": { + "kc.user.profile.config": [ + "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"favourite_pet\",\"displayName\":\"${profile.attributes.favourite_pet}\",\"validations\":{\"options\":{\"options\":[\"cat\",\"dog\",\"bird\"]}},\"annotations\":{\"inputType\":\"select\",\"inputOptionLabelsI18nPrefix\":\"profile.attributes.favourite_pet\"},\"required\":{\"roles\":[\"admin\",\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}]}" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "5f3c1765-8810-419f-9c18-4a2db0e874e7", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEowIBAAKCAQEAso89qpvLhf9DIcCb2JAbxItRLSIvP/NCZhMdAExTHyrhM5B27ZQ6MZ7dJQbnMu7QJ7yiClsD1XnDN7Wlj07sY2As3lY3v9kjODBeADYlPuN1m7/fXFHX3qfRT+PwVSaAhMykmqvWp86UTg7t7rNjVBnXPPXItmRLIF+jZUMWQduwNznr6Jh54ZdIwEy4hvX1bpNw0nPl4KXiOi2elvg+rk7BhFywGwQ/HUCGkrcq0XS/aNOy1ChmqDbtq817mYpVeteCDe8xP3MPrZ/s2LiEt4Ip1cNo0dY+a4JwOzwL42h3GaR+80iK3pZNo+Mr0KBOY9GXvdV/MvcPHLQ7VujUGQIDAQABAoIBAAHV0OQwmDxUazqiVGe61Bzmcqs5q03SC1K/FmCi/YVikdskvGLaOmk5UQa4+1uDEq7J30onH9ML8+qeFRQek0rn2ZDfxtBpDqsx7LwTUmQtqc8z6buKQs37db5ctnhlk34UmAotQyDz5wMmCkzWWVUWCT02PdMev5qW/mKuIxaCWLHUFiMJaGrYCCwB/Ra8KLcadKgRbytSUth9qILC4krFfmWtzIx1P6nM1pzQ1nydxNnNPJKjoWtLRJ5b701Y5/h2vAAg6Mr+jKe1DPa9QmAqhQudjGbZ31av+0f1/I+XkflpZfokfU+MrAqNYRTYkevRYgc3wakK5mfVYUiMuOECgYEA7fk55O2OJFsR0Vjy4Dx4eSIwgwobvwEuHxlyWn0RC7nFb00eh6OPuc5sHrOk8bK3P367q67sEhxGyBF16nwxgX/T+c8gTC8QRuwNymosA4Je/zJHbKvyzLGOouCP5gYwq/wUmVWzNApVC7LBfxbsqYyivHABc5xgPmTgecY0VWkCgYEAwBXcUKoyq1KZegyNJcTuwuvBXoYVveFGm6QKKKwzojCCKaR3XXtdSon1qYfuKT0MLxgEDyyBks9DgfCodSsTmajX90Yolhyz3ptcOmRURqTRoJhM4g6qA+Ybd3uy8vAz32RdS+4rCTgnMG/5Xpn5B4ojOnhRcnA2TPCJgWz6QzECgYEAhj1FjD75JMb+mRJNB3L1HpfLt8+28RsQUli/ag4M1Il5txxQsYDxbYXk9biuvezrc/Tglqs43cp3nxpCYwClyIA8KjnN5UvTKb601M7pfx1GyzwokEO61f7/ECAO7FnnkMzFLe3rBdsiOFQg1LkwzT/Y+OVR3E6E+A1dlzPYh6kCgYBIP3CwfnO0cMr9Vv8394x+kEIZFYHT+4mdPOP9TFfXZztuAkhLRv1d7eoSq+fuZuHQTM4qDullmMOhei1CdMNYhmNExIS7gWw+DF1yMQ5py9B1ARPZ6v4TnVczZ7l1GtfH7G4TAy/4tcA3vcYjyPIb3d9GPL8VthMWeVqe7ahr4QKBgEwA7ASbs4NxfBsStEGQYQYAeWOoKnTc50FeYz38O4KrOirtTFPNsJcyCiTE0o4cqu/OebSA5irrauV7SEDl/gfH54g3ZWusQbLt2uMnZYtkd2+Ka3T9XM0QfQW/vYl3eJtdQj89TqzLzyP0AgvAyIgeG3RMH8ojqCh3YKY0FTv/" + ], + "certificate": [ + "MIICnTCCAYUCBgGTy2TGBjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdteXJlYWxtMB4XDTI0MTIxNTE3MzQ1OVoXDTM0MTIxNTE3MzYzOVowEjEQMA4GA1UEAwwHbXlyZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALKPPaqby4X/QyHAm9iQG8SLUS0iLz/zQmYTHQBMUx8q4TOQdu2UOjGe3SUG5zLu0Ce8ogpbA9V5wze1pY9O7GNgLN5WN7/ZIzgwXgA2JT7jdZu/31xR196n0U/j8FUmgITMpJqr1qfOlE4O7e6zY1QZ1zz1yLZkSyBfo2VDFkHbsDc56+iYeeGXSMBMuIb19W6TcNJz5eCl4jotnpb4Pq5OwYRcsBsEPx1AhpK3KtF0v2jTstQoZqg27avNe5mKVXrXgg3vMT9zD62f7Ni4hLeCKdXDaNHWPmuCcDs8C+NodxmkfvNIit6WTaPjK9CgTmPRl73VfzL3Dxy0O1bo1BkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAggzxmYvHqUaCPLxxSidLQMgpu1pTozg3rTq8dcxhcHINI//A/z7qQyDA/QQN5cuSpYvdt2MRWoNop+uRNKqSr3C8aRErbY0j4acl7yG/ghNfQUZ9KxDBxKrd0HLFUibdZobg10+Ih/qXo3Mi2VtkqyZQRl/iy0O3ITgqb7YJUEx5tuEWyGbn+SerFvqZNcmsLziOJefm1n4uqroHgIfmgY6Deh+wZK0DwO3WZ6ThjhMp5GFi1oNeZ9xoExNEXrYp07b2xTQFF57oypc7prf733lqGjPRLfoVJP6qcsjvAlOA7f8TG9sKwGuRsPfadYY9PxmdHxl2k7PHDJeDhA7VdQ==" + ], + "priority": ["100"] + } + }, + { + "id": "e586f825-a25a-4833-a38e-4c6484ad17fd", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEowIBAAKCAQEAxoEvnv+YHCqUWANGuku5QYscAZyUE0WHSlcAzZ0bQugPow63piQsuxPz0cpPIuLab6adssXUqKEFheT1H0BqtmT9L/7iOKB6MRuInN4aRzzTH9q02TKPkcpSAzAHTGcsJBMMawlbnIdMu5+mevMPxqeVVxvrnKG27S8H3W5jqIkQw8bo646Hr3l5Dxq/jY7slcSXXXe4ZdefeCvnSqea+fy5c+r/r546nX4FTGiklu6KLQaDc9SfGccrZDmljY7DX1kHrmvIdLShcuukTHc0hi2qbgMcUte/7/svSJLUWOZObKxetd4y1OA49v36xrMqGhwGDdwrWf0VuMBN8eHOCQIDAQABAoIBABz/hUXnFRZURWHKxLvKpnBZPTOiZzfzfxfl4tOmq54CtDoVQyXNq2J+6oOPWC/X+ky3hy+1BQ5x9hJrx+qTU04m2EfOe8da8M7DX28kZlauyjF2loG+MvP7ctn4BluWcip+RTZOYn2DfxBPpRcunR409V+JesoMY7fSwtrfA/Gm0PrXgBK7OuE0nxqFFWnsLOc+HxZECS5r0n1MHEBHe774HkqGcK91j8S+QU+/diTnK+N/ClnKWnabMK8bUO5wAUuKwf2deYkGP91pCEJlVnVZyaXshEM+uxTuMRUlq9h1QAIUatvdQwfOKqZ9XvmTVC8b79qLwmezjoDxNCKbaMMCgYEA71WDpMnA2uS2wCJ/MVwzWGSBDjfeKUPRy33BeUfwLGp4Dro+S1sTrLHgi1HGmvmC8ReZrifUlUHUi3ZHauR6vbNsEoSQ3hplO013kj12EfcBpvKYFg1ODCwevb/JtBTWbDG1P+E9DGiF/2u0aicoJoPolNeNVzgO6YK1OI/S/LMCgYEA1FPTqFPulXxcOK12LgYap8typqJ7zu4fByr42010yrKM+LLNA3bT/i/oRkKc7J1ztKSqlVckADWgK4Y27lI4j1tSgTOxFzwxnTZOeF7ZwGSxq9iy9A84nDiW+m6Hj5RDyBjTSoP2Qqv6d5kTUx+pczZvOVTWRlIEnFETbbxOoFMCgYEA0r1etHx+V4AqtxXpH6KLB5s/1DA3a+hu1BrAgLVqcwGxA27VKW9h7J+YE7UHBzELLpVUWfhyhJa5u6+DhUj4Fw/k6o1WLmvZlZVJ4zhBPeJczw8wAcLnZWp4CybUScBLamt+qGgBZGqpCtZgv1QJU5i09FK0/wa6grz4K3zhEGcCgYAlnGe8xIlZr3rCi2+IvYoROQepHtUhlaqnYWRNrI3IrhIsp7eLKoxo1WGmuHwFqepqEFUrORFmfBlQPGkUlDnyovGdc2OmQwJi39DMn7igzPVwBGXGt7+GZLvRxqx6sX/EPSmIZJHFw6MNdm8m5U/l2bmgBTgjormwWug/IwEmgwKBgEouISIuXsjGxeLmhrOXHKXb6IfKglNJeBM6lTQ6MLaVOso7KdelIntwZNtZwMIi3hlwaUb1X1QmztFbnrvnPhWwJR4ZgMEWanRHthtm0SHzg8EHKT40S91oKabsgHk3wpOvq/iWs+k8qWN4HYp6UO603uLMOfxPYJCFxRtg2TsJ" + ], + "certificate": [ + "MIICnTCCAYUCBgGTy2TG/jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdteXJlYWxtMB4XDTI0MTIxNTE3MzQ1OVoXDTM0MTIxNTE3MzYzOVowEjEQMA4GA1UEAwwHbXlyZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMaBL57/mBwqlFgDRrpLuUGLHAGclBNFh0pXAM2dG0LoD6MOt6YkLLsT89HKTyLi2m+mnbLF1KihBYXk9R9AarZk/S/+4jigejEbiJzeGkc80x/atNkyj5HKUgMwB0xnLCQTDGsJW5yHTLufpnrzD8anlVcb65yhtu0vB91uY6iJEMPG6OuOh695eQ8av42O7JXEl113uGXXn3gr50qnmvn8uXPq/6+eOp1+BUxopJbuii0Gg3PUnxnHK2Q5pY2Ow19ZB65ryHS0oXLrpEx3NIYtqm4DHFLXv+/7L0iS1FjmTmysXrXeMtTgOPb9+sazKhocBg3cK1n9FbjATfHhzgkCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAdUIlJ91E0UkFS45AByjFufRnQbAi1smnHkC3WSN39bhcFT7Hgip97qtABODR58zVHSTS0XcMiL4mMObH3Vyz9J3gmwWZnbokAuo9tYeyrhPh/gqXv3LGtGhTpWlUJ7JEJxH7RVI4UZZyG6Y6FR+3zwiZ0j1p3QsZclfcNmacoi/Ano+4TfloOnY4k8yP7G6LWUTJHpcRNWVVozM3RwekYgpJRAtXDoYfm9p2hRQ090e7NvbblSuVQ/FXhUn4g0wz91WdCWlwXZfvNaRjbynPCHejJpszqiyjPkx3aRKTWqer0ZocKNmY8+RO27XIsXmwOYcjdpX2TCFDv6O+VLfNdw==" + ], + "priority": ["100"], + "algorithm": ["RSA-OAEP"] + } + }, + { + "id": "d85dae25-3728-46a0-980b-46171ba50cdd", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "kid": ["95db7eb8-b57b-475e-90cd-58841a9388d3"], + "secret": ["dp6bv53YrC2PZuJCxa3aNA"], + "priority": ["100"] + } + }, + { + "id": "8c3bb039-6f5b-4bdc-9faa-e0f6038d9e6b", + "name": "hmac-generated-hs512", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "kid": ["d0254883-059e-4fdd-bf03-704c76650aab"], + "secret": [ + "bcW7E4rcbgSKZIQysWOSuhezRGYs5Kzmp3ZESthdTUMyFivK8RbBAdBE4PhFPk5B9TuByDO2RWvd8F7F5YhGJitf6cfYB1BfDuAk-2iBAtdZA98g7a2h4jpwzh-GIgtoRbGbH9qnquUn52f5qteo34g5WifKE2bWjOELza9FrTo" + ], + "priority": ["100"], + "algorithm": ["HS512"] + } + } + ] + }, + "internationalizationEnabled": true, + "supportedLocales": ["en", "fr", "es"], + "defaultLocale": "en", + "authenticationFlows": [ + { + "id": "0e1abbbe-40e3-4754-9fe2-8a7d1f82354e", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "f279cc4d-ebed-4390-a5d4-0cbb6dd662ae", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "6926f455-0fd0-4ac6-9fc1-333b86c4150f", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b11840e7-21ec-4200-bf3c-c7853646a908", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "615b4d0e-e71e-4c96-aed3-b03b34b61808", + "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", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "36958ec5-62d7-4d51-8b30-7a6709476aec", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "aa4a7ac2-ec63-48ea-a70f-b3f18992b99a", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "dafdfc68-72eb-49b2-a8f4-495ee25fba21", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "6a39b6db-c81e-4de4-92a8-a9e504593f2e", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "6fa840df-bc04-4045-9e33-8901d183b165", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "4aa24ca0-ad09-4f30-806b-4c699724d731", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "0a914ba4-f662-4b85-af64-74738a222b7f", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "9b40f15f-b690-4fe2-9fe8-07e77d965297", + "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", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "c8a9848f-8dd8-4e13-b521-0a537d92ec36", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "603957f8-b0a5-4885-aafd-e2757e431954", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "f41632f9-7fad-427d-ae7a-78ac9b1f51d0", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-terms-and-conditions", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 70, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "27a133ca-e05e-4c93-a3b7-ffe14b4e62ec", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "06cd7382-4944-4499-94dc-9908544e291b", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "5f953def-6f7c-430f-a33f-440ec2d2dddd", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "b3dad9a1-5b82-4e91-a250-157a45694e24", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": true, + "defaultAction": true, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": true, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "VERIFY_PROFILE", + "name": "Verify Profile", + "providerId": "VERIFY_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 90, + "config": {} + }, + { + "alias": "delete_credential", + "name": "Delete Credential", + "providerId": "delete_credential", + "enabled": true, + "defaultAction": false, + "priority": 100, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "firstBrokerLoginFlow": "first broker login", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaAuthRequestedUserHint": "login_hint", + "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5", + "realmReusableOtpCode": "false", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "organizationsEnabled": "false" + }, + "keycloakVersion": "25.0.6", + "userManagedAccessAllowed": false, + "organizationsEnabled": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } +} diff --git a/.storybook/preview.ts b/.storybook/preview.ts index a6de9cc..50b809d 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -2,6 +2,7 @@ import type { Preview } from "@storybook/react-vite"; const preview: Preview = { parameters: { + layout: "fullscreen", controls: { matchers: { color: /(background|color)$/i, diff --git a/package-lock.json b/package-lock.json index e929899..7d0fbe6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "@fontsource/inter": "^5.3.0", + "@fontsource/jetbrains-mono": "^5.3.0", "@keycloakify/email-native": "~260007.0.0", "@tabler/icons-react": "^3.46.0", "keycloakify": "^11.8.42", @@ -79,6 +81,7 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -304,7 +307,6 @@ "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -881,6 +883,24 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fontsource/inter": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.3.0.tgz", + "integrity": "sha512-RofMylZmjlJEfELXeNHFWBRcSs75rGU/6bV2S2jfnvv/3rPXPGe0LgUJTklcHZ9lM4OZmAVFhcJPnACfb91A3g==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/jetbrains-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.3.0.tgz", + "integrity": "sha512-fqDfB5I9f1p1TV486aUgB9t8zP84P0O1FtQR5Ol9vjwPy+S+EIGlVYm1cvj2W5shcZMTg2nZFdVMoH5wFu8a1A==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1604,8 +1624,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -1757,6 +1776,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.15.0.tgz", "integrity": "sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.15.0", "@typescript-eslint/types": "8.15.0", @@ -2041,6 +2061,7 @@ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2355,6 +2376,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.11.12", "caniuse-lite": "^1.0.30001809", @@ -2674,8 +2696,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/eastasianwidth": { "version": "0.2.0", @@ -2863,6 +2884,7 @@ "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "dev": true, "hasInstallScript": true, + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -2935,6 +2957,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -4336,7 +4359,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -4766,6 +4788,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.1.tgz", "integrity": "sha512-7CAwy5dRsxs8PHXT3twixW9/OEll8MLE0VRPCJyl7CkS6VHGPSlsVaWTiASPTyGyYRyApxlaWTzwUxVNrhcwDg==", "dev": true, + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -4782,7 +4805,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -4798,7 +4820,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -4811,8 +4832,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/prop-types": { "version": "15.8.1", @@ -4858,6 +4878,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -4901,6 +4922,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5044,6 +5066,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", "dev": true, + "peer": true, "dependencies": { "@types/estree": "1.0.5" }, @@ -5267,6 +5290,7 @@ "integrity": "sha512-6rME2tww6PFhm96iG2Xx44yzwLDWBiDWy+kJ2ub6x90werSTOiuo+tZJ94BgCfFutR0tEfLRIq59s+Zg6YyChA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@storybook/global": "^5.0.0", "@testing-library/jest-dom": "^6.6.3", @@ -5714,6 +5738,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5835,6 +5860,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz", "integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==", "dev": true, + "peer": true, "dependencies": { "esbuild": "^0.20.1", "postcss": "^8.4.38", @@ -6151,6 +6177,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, + "peer": true, "requires": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -6303,8 +6330,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", - "dev": true, - "peer": true + "dev": true }, "@babel/template": { "version": "7.29.7", @@ -6621,6 +6647,16 @@ "levn": "^0.4.1" } }, + "@fontsource/inter": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.3.0.tgz", + "integrity": "sha512-RofMylZmjlJEfELXeNHFWBRcSs75rGU/6bV2S2jfnvv/3rPXPGe0LgUJTklcHZ9lM4OZmAVFhcJPnACfb91A3g==" + }, + "@fontsource/jetbrains-mono": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.3.0.tgz", + "integrity": "sha512-fqDfB5I9f1p1TV486aUgB9t8zP84P0O1FtQR5Ol9vjwPy+S+EIGlVYm1cvj2W5shcZMTg2nZFdVMoH5wFu8a1A==" + }, "@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -7059,8 +7095,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "peer": true + "dev": true }, "@types/babel__core": { "version": "7.20.5", @@ -7190,6 +7225,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.15.0.tgz", "integrity": "sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/scope-manager": "8.15.0", "@typescript-eslint/types": "8.15.0", @@ -7353,7 +7389,8 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true + "dev": true, + "peer": true }, "acorn-jsx": { "version": "5.3.2", @@ -7563,6 +7600,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", "dev": true, + "peer": true, "requires": { "baseline-browser-mapping": "^2.11.12", "caniuse-lite": "^1.0.30001809", @@ -7776,8 +7814,7 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "peer": true + "dev": true }, "eastasianwidth": { "version": "0.2.0", @@ -7934,6 +7971,7 @@ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "dev": true, + "peer": true, "requires": { "@esbuild/aix-ppc64": "0.20.2", "@esbuild/android-arm": "0.20.2", @@ -7986,6 +8024,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", "dev": true, + "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -8946,8 +8985,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "peer": true + "dev": true }, "magic-string": { "version": "0.30.21", @@ -9228,14 +9266,14 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.1.tgz", "integrity": "sha512-7CAwy5dRsxs8PHXT3twixW9/OEll8MLE0VRPCJyl7CkS6VHGPSlsVaWTiASPTyGyYRyApxlaWTzwUxVNrhcwDg==", - "dev": true + "dev": true, + "peer": true }, "pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, - "peer": true, "requires": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -9246,15 +9284,13 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "peer": true + "dev": true }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "peer": true + "dev": true } } }, @@ -9285,6 +9321,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "requires": { "loose-envify": "^1.1.0" } @@ -9318,6 +9355,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "requires": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -9419,6 +9457,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", "dev": true, + "peer": true, "requires": { "@rollup/rollup-android-arm-eabi": "4.18.0", "@rollup/rollup-android-arm64": "4.18.0", @@ -9570,6 +9609,7 @@ "resolved": "https://registry.npmjs.org/storybook/-/storybook-9.1.20.tgz", "integrity": "sha512-6rME2tww6PFhm96iG2Xx44yzwLDWBiDWy+kJ2ub6x90werSTOiuo+tZJ94BgCfFutR0tEfLRIq59s+Zg6YyChA==", "dev": true, + "peer": true, "requires": { "@storybook/global": "^5.0.0", "@testing-library/jest-dom": "^6.6.3", @@ -9874,7 +9914,8 @@ "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true + "dev": true, + "peer": true }, "typescript-eslint": { "version": "8.15.0", @@ -9939,6 +9980,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz", "integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==", "dev": true, + "peer": true, "requires": { "esbuild": "^0.20.1", "fsevents": "~2.3.3", diff --git a/package.json b/package.json index a9db965..51bf8bc 100755 --- a/package.json +++ b/package.json @@ -18,15 +18,16 @@ "license": "MIT", "keywords": [], "dependencies": { + "@fontsource/inter": "^5.3.0", + "@fontsource/jetbrains-mono": "^5.3.0", + "@keycloakify/email-native": "~260007.0.0", + "@tabler/icons-react": "^3.46.0", "keycloakify": "^11.8.42", "react": "^18.2.0", - "react-dom": "^18.2.0", - "@keycloakify/email-native": "~260007.0.0", - "@tabler/icons-react": "^3.46.0" + "react-dom": "^18.2.0" }, "devDependencies": { "@eslint/js": "^9.15.0", - "storybook": "^9.0.16", "@storybook/react-vite": "^9.0.16", "@types/react": "^18.2.43", "@types/react-dom": "^18.2.17", @@ -41,6 +42,7 @@ "eslint-plugin-storybook": "^9.0.16", "globals": "^15.12.0", "prettier": "3.3.1", + "storybook": "^9.0.16", "typescript": "^5.2.2", "typescript-eslint": "^8.15.0", "vite": "^5.0.8" diff --git a/public/dataspacelogosep2025.png b/public/dataspacelogosep2025.png index cdf349d..4b10ff6 100644 Binary files a/public/dataspacelogosep2025.png and b/public/dataspacelogosep2025.png differ diff --git a/public/old logo.png b/public/old logo.png new file mode 100644 index 0000000..cdf349d Binary files /dev/null and b/public/old logo.png differ diff --git a/src/.gitignore b/src/.gitignore index c15e71b..4dd285c 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -4,66 +4,66 @@ # === Owned files end ===== # === @keycloakify/email-native v260007.0.0 === -/email/html/email-test.ftl /email/theme.properties -/email/messages/messages_cs.properties -/email/html/event-login_error.ftl +/email/html/email-test.ftl /email/html/email-update-confirmation.ftl +/email/html/email-verification-with-code.ftl /email/html/email-verification.ftl +/email/html/event-login_error.ftl +/email/html/event-remove_credential.ftl /email/html/event-remove_totp.ftl +/email/html/event-update_credential.ftl /email/html/event-update_password.ftl -/email/html/event-remove_credential.ftl -/email/html/email-verification-with-code.ftl +/email/html/event-update_totp.ftl /email/html/event-user_disabled_by_permanent_lockout.ftl -/email/text/email-test.ftl /email/html/event-user_disabled_by_temporary_lockout.ftl -/email/html/event-update_totp.ftl /email/html/executeActions.ftl -/email/html/event-update_credential.ftl /email/html/identity-provider-link.ftl /email/html/org-invite.ftl /email/html/password-reset.ftl /email/html/template.ftl -/email/messages/messages_ca.properties /email/messages/messages_ar.properties -/email/messages/messages_fa.properties -/email/messages/messages_es.properties -/email/messages/messages_fi.properties -/email/messages/messages_hu.properties +/email/messages/messages_ca.properties +/email/messages/messages_cs.properties +/email/messages/messages_da.properties /email/messages/messages_de.properties /email/messages/messages_el.properties -/email/messages/messages_ka.properties /email/messages/messages_en.properties -/email/messages/messages_ja.properties -/email/messages/messages_ru.properties -/email/messages/messages_pl.properties -/email/messages/messages_no.properties -/email/messages/messages_lt.properties +/email/messages/messages_es.properties +/email/messages/messages_fa.properties +/email/messages/messages_fi.properties /email/messages/messages_fr.properties +/email/messages/messages_hu.properties /email/messages/messages_it.properties -/email/messages/messages_sk.properties -/email/messages/messages_th.properties -/email/messages/messages_sv.properties +/email/messages/messages_ja.properties +/email/messages/messages_ka.properties +/email/messages/messages_lt.properties +/email/messages/messages_nl.properties +/email/messages/messages_no.properties +/email/messages/messages_pl.properties /email/messages/messages_pt_BR.properties -/email/messages/messages_da.properties -/email/messages/messages_zh_CN.properties -/email/messages/messages_uk.properties /email/messages/messages_pt.properties -/email/messages/messages_nl.properties +/email/messages/messages_ru.properties +/email/messages/messages_sk.properties +/email/messages/messages_sv.properties +/email/messages/messages_th.properties /email/messages/messages_tr.properties +/email/messages/messages_uk.properties +/email/messages/messages_zh_CN.properties +/email/messages/messages_zh_TW.properties +/email/text/email-test.ftl +/email/text/email-update-confirmation.ftl +/email/text/email-verification-with-code.ftl +/email/text/email-verification.ftl /email/text/event-login_error.ftl /email/text/event-remove_credential.ftl -/email/text/email-verification-with-code.ftl +/email/text/event-remove_totp.ftl /email/text/event-update_credential.ftl /email/text/event-update_password.ftl -/email/text/email-verification.ftl -/email/text/identity-provider-link.ftl -/email/text/password-reset.ftl -/email/text/event-remove_totp.ftl -/email/text/executeActions.ftl -/email/text/org-invite.ftl /email/text/event-update_totp.ftl -/email/text/event-user_disabled_by_temporary_lockout.ftl -/email/text/email-update-confirmation.ftl /email/text/event-user_disabled_by_permanent_lockout.ftl -/email/messages/messages_zh_TW.properties +/email/text/event-user_disabled_by_temporary_lockout.ftl +/email/text/executeActions.ftl +/email/text/identity-provider-link.ftl +/email/text/org-invite.ftl +/email/text/password-reset.ftl diff --git a/src/kc.gen.tsx b/src/kc.gen.tsx index 46c55c5..158222b 100644 --- a/src/kc.gen.tsx +++ b/src/kc.gen.tsx @@ -1,5 +1,5 @@ // This file is auto-generated by the `update-kc-gen` command. Do not edit it manually. -// Hash: 09b09a6c36072d5cf2f8484ab3dc720d28ec8c126df1bafb0b2214a0139848c7 +// Hash: b0aa286dba4ed04c111c1265bdb6a76ba45c22a3d9b9e8dcf36302267d40c51a /* eslint-disable */ @@ -47,3 +47,10 @@ export function KcPage(props: { kcContext: KcContext; fallback?: ReactNode }) { ); } + +// NOTE: This is exported here only because in Webpack environnement it works differently +export const BASE_URL = import.meta.env.BASE_URL; + +// NOTE: This is only exported here because you're supposed to import type from different packages +// Depending of if you are using Vite, Webpack, ect... +export type { Meta, StoryObj } from "@storybook/react-vite"; diff --git a/src/login/KcContext.ts b/src/login/KcContext.ts index 74cf007..4dcccea 100644 --- a/src/login/KcContext.ts +++ b/src/login/KcContext.ts @@ -10,6 +10,20 @@ export type KcContextExtension = { }; }; -export type KcContextExtensionPerPage = {}; +export type KcContextExtensionPerPage = { + "signed-in.ftl": {}; + "register.ftl": { + social?: { + displayInfo?: boolean; + providers?: { + loginUrl: string; + alias: string; + providerId: string; + displayName: string; + iconClasses?: string; + }[]; + }; + }; +}; export type KcContext = ExtendKcContext; diff --git a/src/login/KcPage.tsx b/src/login/KcPage.tsx index 0c54cf3..8fbb0d1 100644 --- a/src/login/KcPage.tsx +++ b/src/login/KcPage.tsx @@ -1,12 +1,27 @@ import type { ClassKey } from "keycloakify/login"; import DefaultPage from "keycloakify/login/DefaultPage"; -import { Suspense, lazy } from "react"; +import { Suspense, lazy, type ReactNode } from "react"; import type { KcContext } from "./KcContext"; +import "@fontsource/inter/400.css"; +import "@fontsource/inter/500.css"; +import "@fontsource/inter/600.css"; +import "@fontsource/inter/700.css"; +import "@fontsource/jetbrains-mono/400.css"; +import "@fontsource/jetbrains-mono/500.css"; +import "@fontsource/jetbrains-mono/600.css"; +import "@fontsource/jetbrains-mono/700.css"; import "./assets/civic-theme.css"; -import CustomTemplate, { type CustomTemplateProps } from "./components/CustomTemplate"; +import CustomTemplate from "./components/CustomTemplate"; import { useI18n } from "./i18n"; const Login = lazy(() => import("./pages/Login")); +const Error = lazy(() => import("./pages/Error")); +const LoginIdpLinkConfirm = lazy(() => import("./pages/LoginIdpLinkConfirm")); +const LoginIdpLinkConfirmOverride = lazy(() => import("./pages/LoginIdpLinkConfirmOverride")); +const Info = lazy(() => import("./pages/Info")); +const SignedIn = lazy(() => import("./pages/SignedIn")); +const IdpReviewUserProfile = lazy(() => import("./pages/IdpReviewUserProfile")); +const LoginResetPassword = lazy(() => import("./pages/LoginResetPassword")); const Register = lazy(() => import("./pages/Register")); const UserProfileFormFields = lazy(() => import("./UserProfileFormFields")); @@ -17,8 +32,22 @@ export default function KcPage(props: { kcContext: KcContext }) { const { i18n } = useI18n({ kcContext }); - const Template = (props: Omit) => ( - + const Template = (templateProps: { + children?: ReactNode; + displayMessage?: boolean; + socialProvidersNode?: ReactNode; + infoNode?: ReactNode; + headerNode?: ReactNode; + }) => ( + + {templateProps.children} + ); return ( @@ -35,6 +64,78 @@ export default function KcPage(props: { kcContext: KcContext }) { doUseDefaultCss={true} /> ); + case "error.ftl": + return ( + + ); + case "login-idp-link-confirm.ftl": + return ( + + ); + case "login-idp-link-confirm-override.ftl": + return ( + + ); + case "info.ftl": + return ( + + ); + case "signed-in.ftl": + return ( + + ); + case "idp-review-user-profile.ftl": + return ( + + ); + case "login-reset-password.ftl": + return ( + + ); case "register.ftl": return ( ) { - const { kcContext, i18n, kcClsx, onIsFormSubmittableValueChange, doMakeUserConfirmPassword, BeforeField, AfterField } = props; +type ProfileFormFieldsProps = UserProfileFormFieldsProps & { + shouldRevealErrors?: boolean; +}; + +export default function UserProfileFormFields(props: ProfileFormFieldsProps) { + const { + kcContext, + i18n, + kcClsx, + onIsFormSubmittableValueChange, + doMakeUserConfirmPassword, + BeforeField, + AfterField, + shouldRevealErrors = false + } = props; const { advancedMsg } = i18n; @@ -28,15 +42,47 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps< doMakeUserConfirmPassword }); + const formFieldStatesRef = useRef(formFieldStates); + formFieldStatesRef.current = formFieldStates; + useEffect(() => { onIsFormSubmittableValueChange(isFormSubmittable); }, [isFormSubmittable]); + useEffect(() => { + if (!shouldRevealErrors) { + return; + } + + for (const { attribute, valueOrValues } of formFieldStatesRef.current) { + if (attribute.annotations.inputType === "hidden") { + continue; + } + if (attribute.name === "password-confirm" && !doMakeUserConfirmPassword) { + continue; + } + + dispatchFormAction({ + action: "update", + name: attribute.name, + valueOrValues, + displayErrorsImmediately: true + }); + } + }, [shouldRevealErrors, dispatchFormAction, doMakeUserConfirmPassword]); + const groupNameRef = { current: "" }; + const fieldOrder = ["email", "username", "firstName", "lastName", "password", "password-confirm"]; + const orderedFields = [...formFieldStates].sort((a, b) => { + const aIndex = fieldOrder.indexOf(a.attribute.name); + const bIndex = fieldOrder.indexOf(b.attribute.name); + return (aIndex === -1 ? fieldOrder.length : aIndex) - (bIndex === -1 ? fieldOrder.length : bIndex); + }); + return ( <> - {formFieldStates.map(({ attribute, displayableErrors, valueOrValues }) => { + {orderedFields.map(({ attribute, displayableErrors, valueOrValues }) => { return ( @@ -62,9 +108,13 @@ export default function UserProfileFormFields(props: UserProfileFormFieldsProps< >
- {attribute.required && <> *}
{attribute.annotations.inputHelperTextBefore !== undefined && ( @@ -289,7 +339,15 @@ function InputTag(props: InputFieldByTypeProps & { fieldIndex: number | undefine disabled={attribute.readOnly} autoComplete={attribute.autocomplete} placeholder={ - attribute.annotations.inputTypePlaceholder === undefined ? undefined : advancedMsgStr(attribute.annotations.inputTypePlaceholder) + attribute.annotations.inputTypePlaceholder !== undefined + ? advancedMsgStr(attribute.annotations.inputTypePlaceholder) + : attribute.name === "email" + ? "you@example.org" + : attribute.name === "password" + ? "Create a password" + : attribute.name === "password-confirm" + ? "Re-enter your password" + : undefined } pattern={attribute.annotations.inputTypePattern} size={attribute.annotations.inputTypeSize === undefined ? undefined : parseInt(`${attribute.annotations.inputTypeSize}`)} @@ -562,71 +620,74 @@ function SelectTag(props: InputFieldByTypeProps) { const isMultiple = attribute.annotations.inputType === "multiselect"; return ( - + dispatchFormAction({ + action: "update", + name: attribute.name, + valueOrValues: (() => { + if (isMultiple) { + return Array.from(event.target.selectedOptions).map(option => option.value); + } - return event.target.value; - })() - }) - } - onBlur={() => - dispatchFormAction({ - action: "focus lost", - name: attribute.name, - fieldIndex: undefined - }) - } - > - {!isMultiple && } - {(() => { - const options = (() => { - walk: { - const { inputOptionsFromValidation } = attribute.annotations; + return event.target.value; + })() + }) + } + onBlur={() => + dispatchFormAction({ + action: "focus lost", + name: attribute.name, + fieldIndex: undefined + }) + } + > + {!isMultiple && } + {(() => { + const options = (() => { + walk: { + const { inputOptionsFromValidation } = attribute.annotations; + + if (inputOptionsFromValidation === undefined) { + break walk; + } - if (inputOptionsFromValidation === undefined) { - break walk; - } + assert(typeof inputOptionsFromValidation === "string"); - assert(typeof inputOptionsFromValidation === "string"); + const validator = (attribute.validators as Record)[inputOptionsFromValidation]; - const validator = (attribute.validators as Record)[inputOptionsFromValidation]; + if (validator === undefined) { + break walk; + } - if (validator === undefined) { - break walk; - } + if (validator.options === undefined) { + break walk; + } - if (validator.options === undefined) { - break walk; + return validator.options; } - return validator.options; - } - - return attribute.validators.options?.options ?? []; - })(); + return attribute.validators.options?.options ?? []; + })(); - return options.map(option => ( - - )); - })()} - + return options.map(option => ( + + )); + })()} + + {!isMultiple && } +
); } diff --git a/src/login/appHome.ts b/src/login/appHome.ts new file mode 100644 index 0000000..e3c854d --- /dev/null +++ b/src/login/appHome.ts @@ -0,0 +1,45 @@ +function httpOrigin(value: string): string | undefined { + try { + const url = new URL(value); + if (url.protocol !== "http:" && url.protocol !== "https:") { + return undefined; + } + return url.origin; + } catch { + return undefined; + } +} + +/** Keycloak 25+ moves `redirect_uri` into `client_data` (`ru`) after the first page. */ +function redirectUriFromClientData(clientData: string): string | undefined { + try { + const normalized = clientData.replace(/-/g, "+").replace(/_/g, "/"); + const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "="); + const parsed = JSON.parse(atob(padded)) as { ru?: unknown; redirectUri?: unknown }; + const redirectUri = typeof parsed.ru === "string" ? parsed.ru : parsed.redirectUri; + return typeof redirectUri === "string" ? redirectUri : undefined; + } catch { + return undefined; + } +} + +/** Home of the app that started this login (`redirect_uri` origin). */ +export function getAppHomeUrl(): string | undefined { + if (typeof window === "undefined") { + return undefined; + } + + const params = new URLSearchParams(window.location.search); + const clientData = params.get("client_data"); + const redirectUri = params.get("redirect_uri") ?? (clientData ? redirectUriFromClientData(clientData) : undefined); + return redirectUri ? httpOrigin(redirectUri) : undefined; +} + +export function getPrivacyPolicyUrl(homeHref: string | undefined): string | undefined { + if (!homeHref) { + return undefined; + } + + const home = homeHref.replace(/\/$/, ""); + return `${home}/privacy`; +} diff --git a/src/login/assets/civic-theme.css b/src/login/assets/civic-theme.css index cd3a121..ecc4b96 100644 --- a/src/login/assets/civic-theme.css +++ b/src/login/assets/civic-theme.css @@ -2,7 +2,7 @@ :root { /* DataSpace Brand Colors */ - --civic-primary-blue: #0B3865; + --civic-primary-blue: #0B2540; --civic-primary-blue-dark: #072745; --civic-primary-blue-light: #0D4A85; --civic-tertiary-accent: #F5A623; @@ -11,12 +11,19 @@ /* Base Colors */ --civic-white: #ffffff; + --civic-form-bg: #f3f5f8; --civic-text-dark: #1B4965; --civic-text-light: #666666; --civic-error: #E5484D; --civic-border: #DFE3E6; --civic-input-bg: #FFFFFF; --civic-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + --text: #101826; + --primary: #0b3865; + + /* Typography tokens */ + --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; } /* Add CivicDataSpace logo */ @@ -39,13 +46,22 @@ } body, html { - font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; - height: 100%; + font-family: var(--font-sans); + min-height: 100%; margin: 0; padding: 0; background: var(--civic-white); } +#root, +#storybook-root { + min-height: 100%; +} + +code, kbd, pre, samp { + font-family: var(--font-mono); +} + /* Split Screen Layout */ .login-pf-page { background: var(--civic-white); @@ -54,47 +70,71 @@ body, html { min-height: 100vh; } -/* Main wrapper for split layout */ +/* Page-level scroll (scrollbar on the far right). Brand panel stays sticky. */ .civic-split-wrapper { display: grid; grid-template-columns: 1fr 1fr; - min-height: calc(100vh - 60px); + min-height: 100vh; + min-height: 100dvh; position: relative; } -/* Left Panel - Form Section */ +/* Left Panel - Form Section (grows with content; window scroll moves this side) */ #kc-content-wrapper { display: flex; flex-direction: column; - justify-content: center; align-items: center; - padding: 60px 80px; - background: var(--civic-white); + padding: 32px 80px 60px; + background: var(--civic-form-bg); position: relative; grid-column: 1; + min-height: 100vh; + min-height: 100dvh; +} + +.civic-form-column { + width: 100%; + max-width: 450px; + margin-block: auto; +} + +a.subtle-link.back-home { + display: inline-flex; + align-items: center; + gap: 6px; + margin-bottom: 24px; + color: #5B6472; + font-size: 12.5px; + font-weight: 600; + line-height: 1; + text-decoration: none; + cursor: pointer; +} + +a.subtle-link.back-home svg { + display: block; + flex-shrink: 0; } -/* Right Panel - Brand Section */ +a.subtle-link.back-home:hover { + color: var(--text); +} + +/* Right Panel - Brand Section (fixed in the viewport while the page scrolls) */ .civic-brand-panel { - background: linear-gradient(135deg, var(--civic-primary-blue) 0%, var(--civic-primary-blue-dark) 100%); + background: var(--civic-primary-blue); display: flex; align-items: center; justify-content: center; - position: relative; + position: sticky; + top: 0; + align-self: start; + height: 100vh; + height: 100dvh; overflow: hidden; grid-column: 2; } -.civic-brand-panel::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: radial-gradient(circle at 30% 50%, rgba(255, 255, 255, 0.05) 0%, transparent 50%); -} - /* Logo in Right Panel */ .civic-brand-logo { position: relative; @@ -102,6 +142,7 @@ body, html { text-align: center; } +/* Legal links in the bottom-right of the brand panel */ .civic-legal-links { position: absolute; right: 32px; @@ -110,15 +151,56 @@ body, html { color: rgba(255, 255, 255, 0.85); font-size: 13px; letter-spacing: 0.02em; + white-space: nowrap; } .civic-legal-links a { color: inherit; text-decoration: none; + cursor: pointer; + transition: color 0.2s ease; } .civic-legal-links a:hover { - color: var(--civic-white); + color: #ffffff; +} + +/* Legal dialog */ +.civic-dialog-overlay { + position: fixed; + inset: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; + background: rgba(11, 37, 64, 0.55); +} + +.civic-dialog { + width: 100%; + max-width: 520px; + background: var(--civic-white); + border-radius: 12px; + box-shadow: 0 16px 48px rgba(0, 0, 0, 0.28); +} + +.civic-dialog-title { + margin: 0; + padding: 28px 32px 20px; + color: var(--civic-primary-blue); + font-size: 22px; + font-weight: 700; + line-height: 1.3; +} + +.civic-dialog-body { + padding: 24px 32px 32px; + border-top: 1px solid var(--civic-border); + color: #555555; + font-size: 15px; + font-weight: 400; + line-height: 1.6; } /* Form Container */ @@ -133,6 +215,30 @@ body, html { } /* Form Header */ +.civic-auth-header { + margin-bottom: 28px; +} + +.h1 { + margin: 0; + color: var(--primary); + font-size: 24px; + font-weight: 650; + letter-spacing: -0.02em; + text-align: left; + text-wrap: balance; +} + +.auth-subtitle { + margin: 8px 0 0; + color: #5B6472; + font-size: 13.5px; + font-weight: 400; + line-height: 1.45; + text-align: left; + text-wrap: balance; +} + #kc-header-wrapper { display: block !important; margin-bottom: 40px; @@ -150,17 +256,96 @@ body, html { /* Form Groups */ .form-group, .pf-c-form__group { - margin-bottom: 24px; + margin-bottom: 18px; position: relative; } .control-label { - font-size: 14px; - font-weight: 500; - color: var(--civic-text-dark); + display: block; + font-size: 13px; + font-weight: 600; + color: var(--text); + margin-bottom: 8px; +} + +.civic-label-row { + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 12px; margin-bottom: 8px; } +.civic-label-row .control-label { + margin-bottom: 0; +} + +.civic-forgot-link { + color: var(--primary); + font-size: 12px; + font-weight: 600; + text-decoration: none; + white-space: nowrap; +} + +.civic-forgot-link:hover { + text-decoration: underline; +} + +#kc-idp-review-profile { + display: flex; + flex-direction: column; + gap: 16px; +} + +#kc-idp-review-profile .subtle-link { + margin-bottom: 0; + align-self: flex-start; +} + +#kc-idp-review-profile-form #email { + padding-right: 100px; +} + +#kc-idp-review-profile-form #email:read-only { + background: var(--civic-input-bg); + color: var(--primary); + opacity: 1; +} + +#kc-idp-review-profile-form .form-group:has(#email) > div:last-child { + position: relative; +} + +#kc-idp-review-profile-form .civic-verified-badge { + position: absolute; + top: 15px; + right: 14px; + display: inline-flex; + align-items: center; + gap: 4px; + color: #2f9e5c; + font-size: 13px; + font-weight: 600; + pointer-events: none; +} + +#kc-idp-review-profile-form .civic-verified-badge svg { + display: block; +} + +#kc-idp-review-profile-form .civic-forgot-link { + display: block; + margin-top: 16px; +} + +#kc-form-login .required, +#kc-reset-password-form .required, +#kc-register-form .required { + color: inherit !important; + margin-left: 2px; +} + /* Input Fields */ input[type="text"], input[type="password"], @@ -169,45 +354,54 @@ input[type="email"], width: 100%; height: 48px; padding: 12px 16px; - font-size: 16px; - color: var(--civic-text-dark); + font-family: var(--font-sans); + font-size: 13.5px; + color: var(--primary); background: var(--civic-input-bg); border: 1px solid var(--civic-border); - border-radius: 4px; + border-radius: 8px; transition: all 0.2s ease; } +input[type="text"]::placeholder, +input[type="password"]::placeholder, +input[type="email"]::placeholder, +.form-control::placeholder { + font-family: var(--font-sans); + color: #9AA3AF; + font-size: 13.5px; +} + input[type="text"]:focus, input[type="password"]:focus, input[type="email"]:focus, .form-control:focus { outline: none; - border-color: var(--civic-primary-blue); - box-shadow: 0 0 0 3px rgba(27, 73, 101, 0.1); + border-color: var(--primary); + box-shadow: 0 0 0 3px rgba(11, 56, 101, 0.1); } -/* Error State */ +/* Shared field error state */ .has-error input, input.error { border-color: var(--civic-error); } -.pf-c-form__helper-text.pf-m-error, -.help-block, -#kc-register-form [id^="input-error-"] { +.civic-field-error { color: var(--civic-error); font-size: 13px; - margin-top: 2px !important; + line-height: 1.4; + margin-top: 6px; display: flex; - align-items: center; + align-items: flex-start; gap: 6px; } +/* Keeps the icon on the first line of multi-line messages */ .kc-field-error-icon { flex-shrink: 0; display: block; - position: relative; - top: 1px; + margin-top: 2px; } /* Form Options (Remember me, Forgot password) */ @@ -273,48 +467,80 @@ input[type="submit"] { width: 100%; height: 48px; padding: 12px 24px; - font-size: 16px; + font-family: inherit; + font-size: 14.5px; font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; + text-transform: none; + letter-spacing: 0; color: var(--civic-white); - background: var(--civic-tertiary-accent); + background: var(--primary); border: none; - border-radius: 4px; + border-radius: 8px; cursor: pointer; - transition: all 0.2s ease; + transition: background 0.2s ease; } #kc-login:hover, .btn-primary:hover, input[type="submit"]:hover { - background: var(--civic-tertiary-accent-dark); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(245, 166, 35, 0.3); + background: #082c4f; + transform: none; + box-shadow: none; } #kc-login:active, .btn-primary:active, input[type="submit"]:active { - transform: translateY(0); + transform: none; +} + +#kc-form-login #kc-form-buttons, +#kc-reset-password-form #kc-form-buttons, +#kc-register-form #kc-form-buttons { + margin-top: 8px; + margin-bottom: 0; +} + +/* Error page / IdP override */ +#kc-error-message, +#kc-idp-override { + display: flex; + flex-direction: column; + gap: 16px; +} + +#kc-error-message .btn-primary, +#kc-idp-override .btn-primary { + display: inline-flex; + align-items: center; + justify-content: center; + width: 100%; + font-family: inherit; + text-decoration: none; +} + +#kc-error-message .subtle-link, +#kc-idp-override .subtle-link { + margin-bottom: 0; + align-self: flex-start; } /* Registration Link */ #kc-registration { text-align: center; margin-top: 24px; - font-size: 14px; - color: var(--civic-text-light); + font-size: 13px; + color: #5b6472; } #kc-registration a { - color: var(--civic-tertiary-accent); + color: var(--primary); + font-size: 13px; text-decoration: none; - font-weight: 500; + font-weight: 600; } #kc-registration a:hover { - color: var(--civic-tertiary-accent-dark); text-decoration: underline; } @@ -329,30 +555,59 @@ input[type="submit"]:active { width: 100%; height: 48px; padding: 12px 16px; - font-size: 16px; - color: var(--civic-text-dark); + font-family: var(--font-sans); + font-size: 13.5px; + color: var(--primary); background: var(--civic-input-bg); border: 1px solid var(--civic-border); - border-radius: 4px; + border-radius: 8px; transition: all 0.2s ease; } +/* Terms checkbox + label spacing */ +#kc-register-form .form-group:has(#termsAccepted), #kc-register-form .civic-terms { - max-width: 100%; + display: flex; + align-items: flex-start; + flex-wrap: wrap; + gap: 7px; + margin: 4px 0 18px; + font-size: 13px; + color: #5b6472; + line-height: 1.45; + font-weight: 400; } -#kc-register-form .civic-terms .pf-c-check__label { - min-width: 0; +/* Spacing already comes from the row gap of .civic-terms */ +#kc-register-form .civic-terms #input-error-termsAccepted { + flex-basis: 100%; + margin-top: 0; +} + +#kc-register-form .civic-terms p { + margin: 0; } #kc-register-form .civic-terms a { - border: none; - background: none; - padding: 0; - font: inherit; - color: inherit; + color: var(--primary); text-decoration: underline; - text-underline-offset: 2px; + font-weight: 500; +} + +#kc-register-form #termsAccepted { + width: 18px; + height: 18px; + margin: 0; + padding: 0; + vertical-align: middle; + accent-color: var(--civic-primary-blue); + cursor: pointer; +} + +#kc-register-form label[for="termsAccepted"] { + display: inline; + margin: 0; + vertical-align: middle; cursor: pointer; } @@ -382,7 +637,7 @@ input[type="submit"]:active { /* Back to Login Link */ #kc-form-buttons { - margin-top: 24px; + margin-top: 8px; } #kc-form-buttons .btn-default { @@ -412,8 +667,8 @@ input[type="submit"]:active { .pf-c-form__label { font-size: 14px; - font-weight: 500; - color: var(--civic-text-dark); + font-weight: 650; + color: #101826; margin-bottom: 8px; } @@ -440,15 +695,43 @@ select.form-control, width: 100%; height: 48px; padding: 12px 16px; - font-size: 16px; - color: var(--civic-text-dark); + font-size: 15px; + color: var(--primary); background: var(--civic-input-bg); border: 1px solid var(--civic-border); - border-radius: 4px; + border-radius: 8px; transition: all 0.2s ease; cursor: pointer; } +.kc-select-wrap { + position: relative; + display: block; + width: 100%; +} + +.kc-select-wrap select.form-control, +.kc-select-wrap select.pf-c-form-control { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + padding-right: 40px; +} + +.kc-select-wrap select.form-control::-ms-expand, +.kc-select-wrap select.pf-c-form-control::-ms-expand { + display: none; +} + +.kc-select-chevron { + position: absolute; + right: 7px; + top: 50%; + transform: translateY(-50%); + pointer-events: none; + color: var(--primary); +} + select.form-control:focus, .pf-c-form-control:focus { outline: none; @@ -466,7 +749,7 @@ select.form-control:focus, /* Social Login Providers */ #kc-social-providers { - margin-top: 32px; + margin-top: 4px; } /* Social OR divider */ @@ -477,26 +760,39 @@ select.form-control:focus, } .kc-social-divider { + display: flex; + align-items: center; + gap: 16px; + margin: 20px 0; + color: #8891a0; + font-size: 11.5px; + line-height: 1; +} + +.kc-social-divider::before, +.kc-social-divider::after { + content: ""; + flex: 1; height: 1px; - margin: 24px 0 20px; background: var(--civic-border); - border: none; } -/* Provider list — shared columns so icons/text align while staying centered */ +.kc-social-divider span { + flex-shrink: 0; +} + +/* Provider list */ #kc-social-providers ul { list-style: none; padding: 0; margin: 0; - display: grid; - grid-template-columns: 1fr auto auto 1fr; - column-gap: 0; - row-gap: 12px; - align-items: center; + display: flex; + flex-direction: column; + gap: 12px; } #kc-social-providers li { - display: contents; + display: block; } /* Provider buttons */ @@ -504,24 +800,21 @@ select.form-control:focus, #kc-social-providers a.pf-c-button, #kc-social-providers a.pf-m-control { position: static; - display: grid; - grid-column: 1 / -1; - grid-template-columns: subgrid; + display: flex; align-items: center; - column-gap: 0; - row-gap: 0; - width: auto; + justify-content: center; + width: 100%; min-height: 48px; height: 48px; - padding: 0; - font-size: 16px; + padding: 0 16px; + font-size: 15px; font-weight: 600; - letter-spacing: 0.02em; + letter-spacing: 0; text-transform: none; - color: var(--civic-text-dark); + color: #101826; background: var(--civic-white) !important; border: 1px solid var(--civic-border) !important; - border-radius: 4px; + border-radius: 8px; text-decoration: none; transition: all 0.2s ease; transform: none; @@ -531,25 +824,28 @@ select.form-control:focus, #kc-social-providers a:hover, #kc-social-providers a.pf-c-button:hover, #kc-social-providers a.pf-m-control:hover { - border-color: var(--civic-primary-blue) !important; + border-color: #c5ccd6 !important; background: var(--civic-white) !important; - color: var(--civic-text-dark); - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08) !important; + color: #101826; + transform: none; + box-shadow: none !important; } -/* Flatten wrapper so icon + label join the shared columns */ -#kc-social-providers .kc-social-provider-content { - display: contents; +#kc-social-providers .kc-social-provider-content, +#kc-idp-link-confirm .kc-social-provider-content { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 10px; } -#kc-social-providers .kc-social-provider-icon-wrap { - grid-column: 2; +#kc-social-providers .kc-social-provider-icon-wrap, +#kc-idp-link-confirm .kc-social-provider-icon-wrap { display: inline-flex; align-items: center; justify-content: center; - width: 20px; - height: 20px; + width: 18px; + height: 18px; flex-shrink: 0; line-height: 0; } @@ -565,10 +861,9 @@ select.form-control:focus, #kc-social-providers .kc-social-provider-name, #kc-social-providers .kc-social-icon-text { - grid-column: 3; display: block; margin: 0; - padding-left: 12px; + padding: 0; line-height: 1.2; text-align: left; white-space: nowrap; @@ -643,7 +938,7 @@ select.form-control:focus, .civic-footer { width: 100%; padding: 16px 80px; - background: #0B3865; + background: var(--civic-primary-blue); color: var(--civic-white); display: grid; grid-template-columns: 1fr auto 1fr; @@ -711,27 +1006,26 @@ select.form-control:focus, transform: translateY(-2px); } -/* Alert Messages */ +/* Alert Messages: plain text notices, so spacing comes from margins only */ .pf-c-alert, .alert { display: flex !important; flex-direction: row !important; align-items: flex-start; gap: 8px; - padding: 12px 0; - margin-bottom: 20px; - border-radius: 4px; + margin: 4px 0 20px; font-size: 14px; line-height: 1.4; } +/* Keeps the icon on the first line of multi-line messages */ .pf-c-alert__icon { flex-shrink: 0; display: flex !important; - align-items: center; + align-items: flex-start; justify-content: center; line-height: 1.4; - margin: 2px 0 0; + margin-top: 1px; } .pf-c-alert__icon::before { @@ -746,9 +1040,8 @@ select.form-control:focus, } .pf-c-alert.pf-m-danger, +.pf-c-alert.pf-m-error, .alert-error { - background-color: rgba(231, 76, 60, 0.1); - border: 1px solid var(--civic-error); color: var(--civic-error); } @@ -757,47 +1050,94 @@ select.form-control:focus, color: var(--civic-text-dark); } -/* IdP link confirm — Review profile / Add to existing account */ -#kc-register-form .form-group:has(#updateProfile) { +/* IdP link confirm — existing account found */ +#kc-idp-link-confirm { display: flex; - flex-wrap: wrap; - gap: 12px; - margin-top: 8px; + flex-direction: column; + gap: 16px; } -#kc-register-form #updateProfile, -#kc-register-form #linkAccount { - flex: 1 1 auto; - width: auto; - min-width: 0; - height: 40px; - margin: 0; - padding: 8px 16px; - font-size: 14px; +#kc-idp-link-confirm .civic-idp-continue { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 48px; + padding: 0 16px; + font-family: inherit; + font-size: 15px; font-weight: 600; - letter-spacing: 0.3px; - text-transform: uppercase; - white-space: nowrap; - color: var(--civic-text-dark); + letter-spacing: 0; + text-transform: none; + color: var(--primary); background: var(--civic-white); border: 1px solid var(--civic-border); - border-radius: 4px; + border-radius: 8px; cursor: pointer; - transition: all 0.2s ease; + transition: border-color 0.2s ease; } -#kc-register-form #updateProfile:hover, -#kc-register-form #linkAccount:hover { - border-color: var(--civic-primary-blue); +#kc-idp-link-confirm .civic-idp-continue:hover { + border-color: #c5ccd6; background: var(--civic-white); - color: var(--civic-text-dark); - transform: translateY(-1px); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + color: var(--primary); +} + +#kc-idp-link-confirm .subtle-link { + margin-bottom: 0; + align-self: flex-start; +} + +/* Account verified / info page */ +.civic-auth-header:has(.civic-success-status) { + margin-bottom: 0; +} + +.civic-success-status { + text-align: center; +} + +.civic-success-status .h1, +.civic-success-status .auth-subtitle { + text-align: center; } -#kc-register-form #updateProfile:active, -#kc-register-form #linkAccount:active { - transform: translateY(0); +.civic-success-mark, +.civic-email-mark { + display: flex; + align-items: center; + justify-content: center; + width: 64px; + height: 64px; + margin: 0 auto 20px; + border-radius: 50%; +} + +.civic-success-mark { + background: #e8f8ee; + border: 1.5px solid #c6ebd4; + color: #2f9e5c; +} + +.civic-email-mark { + background: #e8eef5; + border: 1.5px solid #c5d4e4; + color: var(--primary); +} + +.civic-success-mark svg, +.civic-email-mark svg { + display: block; +} + +.civic-success-status .btn-primary { + display: inline-flex; + align-items: center; + justify-content: center; + width: 100%; + margin-top: 28px; + font-family: inherit; + text-decoration: none; } /* Responsive Design */ diff --git a/src/login/assets/dataspacelogosep2025.png b/src/login/assets/dataspacelogosep2025.png index cdf349d..4b10ff6 100644 Binary files a/src/login/assets/dataspacelogosep2025.png and b/src/login/assets/dataspacelogosep2025.png differ diff --git a/src/login/components/CivicLegalDialog.tsx b/src/login/components/CivicLegalDialog.tsx new file mode 100644 index 0000000..c6573b6 --- /dev/null +++ b/src/login/components/CivicLegalDialog.tsx @@ -0,0 +1,53 @@ +import React, { useEffect } from "react"; + +type CivicLegalDialogProps = { + open: boolean; + title: string; + children: React.ReactNode; + onClose: () => void; +}; + +export const CivicLegalDialog: React.FC = ({ + open, + title, + children, + onClose +}) => { + useEffect(() => { + if (!open) { + return; + } + + const onKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") { + onClose(); + } + }; + + document.addEventListener("keydown", onKeyDown); + return () => document.removeEventListener("keydown", onKeyDown); + }, [open, onClose]); + + if (!open) { + return null; + } + + return ( +
+
event.stopPropagation()} + > +

+ {title} +

+
{children}
+
+
+ ); +}; + +export default CivicLegalDialog; diff --git a/src/login/components/CivicSuccessStatus.tsx b/src/login/components/CivicSuccessStatus.tsx new file mode 100644 index 0000000..bb47f9c --- /dev/null +++ b/src/login/components/CivicSuccessStatus.tsx @@ -0,0 +1,22 @@ +import type { ReactNode } from "react"; +import { IconCheck, IconMail } from "@tabler/icons-react"; + +type CivicSuccessStatusProps = { + title: string; + subtitle: ReactNode; + mark?: "success" | "email"; + action?: ReactNode; +}; + +export default function CivicSuccessStatus({ title, subtitle, mark = "success", action }: CivicSuccessStatusProps) { + return ( +
+ +

{title}

+

{subtitle}

+ {action} +
+ ); +} diff --git a/src/login/components/CustomTemplate.tsx b/src/login/components/CustomTemplate.tsx index 2d71d94..e7653b6 100644 --- a/src/login/components/CustomTemplate.tsx +++ b/src/login/components/CustomTemplate.tsx @@ -1,21 +1,20 @@ import React from "react"; -import { IconAlertCircle, IconInfoCircle } from "@tabler/icons-react"; -import CivicFooter from "./CivicFooter"; +import { IconAlertCircle, IconArrowLeft, IconInfoCircle } from "@tabler/icons-react"; +import { getAppHomeUrl, getPrivacyPolicyUrl } from "../appHome"; import CivicLogo from "./CivicLogo"; -import { getPrivacyHref } from "../getPrivacyHref"; -import type { I18n } from "../i18n"; -export type CustomTemplateProps = { +type CustomTemplateProps = { children: React.ReactNode; socialProvidersNode?: React.ReactNode; infoNode?: React.ReactNode; - i18n: I18n; + headerNode?: React.ReactNode; kcContext: { message?: { type: string; summary: string; }; client?: { + clientId?: string; baseUrl?: string; }; }; @@ -26,19 +25,27 @@ export default function CustomTemplate({ children, socialProvidersNode, infoNode, - i18n, + headerNode, kcContext, displayMessage = true }: CustomTemplateProps) { - const { msg } = i18n; - const privacyHref = getPrivacyHref(kcContext.client?.baseUrl); + const homeHref = getAppHomeUrl() ?? kcContext.client?.baseUrl ?? "/"; + const privacyHref = getPrivacyPolicyUrl(homeHref); return ( <>
{/* Left Panel - Form Section */}
+
+ + + Back to Home +
+ {headerNode !== undefined && ( +
{headerNode}
+ )} {/* Messages */} {displayMessage && kcContext.message !== undefined && (
@@ -70,21 +77,19 @@ export default function CustomTemplate({ {/* Info Section (e.g., registration link) */} {infoNode}
+
{/* Right Panel with Logo */}
- - {/* Footer */} - ); } diff --git a/src/login/pages/Error.stories.tsx b/src/login/pages/Error.stories.tsx new file mode 100644 index 0000000..0821d91 --- /dev/null +++ b/src/login/pages/Error.stories.tsx @@ -0,0 +1,39 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { createKcPageStory } from "../KcPageStory"; + +const { KcPageStory } = createKcPageStory({ pageId: "error.ftl" }); + +const meta = { + title: "login/error.ftl", + component: KcPageStory +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const GoogleSignInFailed: Story = { + render: () => ( + + ) +}; + +export const AccountCreationFailed: Story = { + render: () => ( + + ) +}; diff --git a/src/login/pages/Error.tsx b/src/login/pages/Error.tsx new file mode 100644 index 0000000..87e2e02 --- /dev/null +++ b/src/login/pages/Error.tsx @@ -0,0 +1,51 @@ +import { IconArrowLeft } from "@tabler/icons-react"; +import type { PageProps } from "keycloakify/login/pages/PageProps"; +import type { KcContext } from "../KcContext"; +import type { I18n } from "../i18n"; + +function isAccountCreationError(summary: string | undefined) { + return /could not create|error creating|creating (your )?account|register/i.test(summary ?? ""); +} + +export default function Error(props: PageProps, I18n>) { + const { kcContext, i18n, doUseDefaultCss, Template, classes } = props; + const { url, message } = kcContext; + const accountCreationFailed = isAccountCreationError(message?.summary); + + return ( + + ); +} diff --git a/src/login/pages/IdpReviewUserProfile.stories.tsx b/src/login/pages/IdpReviewUserProfile.stories.tsx new file mode 100644 index 0000000..a45a273 --- /dev/null +++ b/src/login/pages/IdpReviewUserProfile.stories.tsx @@ -0,0 +1,37 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { createKcPageStory } from "../KcPageStory"; + +const { KcPageStory } = createKcPageStory({ pageId: "idp-review-user-profile.ftl" }); + +const meta = { + title: "login/idp-review-user-profile.ftl", + component: KcPageStory +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + + ) +}; diff --git a/src/login/pages/IdpReviewUserProfile.tsx b/src/login/pages/IdpReviewUserProfile.tsx new file mode 100644 index 0000000..4419848 --- /dev/null +++ b/src/login/pages/IdpReviewUserProfile.tsx @@ -0,0 +1,78 @@ +import { useState } from "react"; +import { IconArrowLeft, IconCheck } from "@tabler/icons-react"; +import type { JSX } from "keycloakify/tools/JSX"; +import type { LazyOrNot } from "keycloakify/tools/LazyOrNot"; +import { getKcClsx } from "keycloakify/login/lib/kcClsx"; +import type { PageProps } from "keycloakify/login/pages/PageProps"; +import type { UserProfileFormFieldsProps } from "keycloakify/login/UserProfileFormFieldsProps"; +import type { KcContext } from "../KcContext"; +import type { I18n } from "../i18n"; + +type IdpReviewUserProfileProps = PageProps, I18n> & { + UserProfileFormFields: LazyOrNot<(props: UserProfileFormFieldsProps) => JSX.Element>; + doMakeUserConfirmPassword: boolean; +}; + +export default function IdpReviewUserProfile(props: IdpReviewUserProfileProps) { + const { kcContext, i18n, doUseDefaultCss, Template, classes, UserProfileFormFields, doMakeUserConfirmPassword } = props; + const { kcClsx } = getKcClsx({ doUseDefaultCss, classes }); + const { url, messagesPerField } = kcContext; + const [isFormSubmittable, setIsFormSubmittable] = useState(false); + + return ( + + ); +} diff --git a/src/login/pages/Info.stories.tsx b/src/login/pages/Info.stories.tsx new file mode 100644 index 0000000..474cc8c --- /dev/null +++ b/src/login/pages/Info.stories.tsx @@ -0,0 +1,52 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { createKcPageStory } from "../KcPageStory"; + +const { KcPageStory } = createKcPageStory({ pageId: "info.ftl" }); + +const meta = { + title: "login/info.ftl", + component: KcPageStory +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const AccountVerified: Story = { + render: () => ( + + ) +}; + +export const AccountCreated: Story = { + render: () => ( + + ) +}; + +export const CheckYourEmail: Story = { + render: () => ( + + ) +}; diff --git a/src/login/pages/Info.tsx b/src/login/pages/Info.tsx new file mode 100644 index 0000000..d969d72 --- /dev/null +++ b/src/login/pages/Info.tsx @@ -0,0 +1,75 @@ +import type { PageProps } from "keycloakify/login/pages/PageProps"; +import type { KcContext } from "../KcContext"; +import type { I18n } from "../i18n"; +import CivicSuccessStatus from "../components/CivicSuccessStatus"; + +function isAccountCreated(summary: string | undefined) { + return /account created|account is ready|has been created/i.test(summary ?? ""); +} + +function isPasswordResetEmail(summary: string | undefined) { + return /further instructions|reset your password|check your email|we've sent/i.test(summary ?? ""); +} + +export default function Info(props: PageProps, I18n>) { + const { kcContext, i18n, doUseDefaultCss, Template, classes } = props; + const summary = kcContext.message?.summary; + const checkEmail = isPasswordResetEmail(summary); + const accountCreated = isAccountCreated(summary); + + return ( + + ); +} diff --git a/src/login/pages/Login.stories.tsx b/src/login/pages/Login.stories.tsx index adadf0a..24bb315 100644 --- a/src/login/pages/Login.stories.tsx +++ b/src/login/pages/Login.stories.tsx @@ -13,7 +13,24 @@ export default meta; type Story = StoryObj; export const Default: Story = { - render: () => + render: () => ( + + ) }; export const WithInvalidCredential: Story = { diff --git a/src/login/pages/Login.tsx b/src/login/pages/Login.tsx index c609853..aca6082 100644 --- a/src/login/pages/Login.tsx +++ b/src/login/pages/Login.tsx @@ -19,7 +19,7 @@ export default function Login(props: PageProps +

Welcome back

+

Sign in to continue to CivicDataSpace.

+ + } displayInfo={realm.password && realm.registrationAllowed && !registrationDisabled} infoNode={
- {msg("noAccount")}{" "} + Don't have an account?{" "} - {msg("doRegister")} + Create an account
@@ -48,7 +53,9 @@ export default function Login(props: PageProps {realm.password && social?.providers !== undefined && social.providers.length !== 0 && (
-