From 0993164062e2e23b76ab1d54485e68f0271e0fea Mon Sep 17 00:00:00 2001 From: Urban Modig Date: Mon, 6 Oct 2025 17:06:36 +0200 Subject: [PATCH] Update Keycloak configuration and enable persistent storage Updated Keycloak settings in `realm-hemhub.json` to include additional roles, user attributes, client scopes, and OpenID Connect configurations. Modified `application.yml` to replace `issuer-uri` with `jwk-set-uri` for JWT handling. Enhanced `docker-compose.yml` to include persistent volumes, updated Keycloak image, and environment variables for better container interoperability. --- docker-compose.yml | 16 ++- keycloak/realm-hemhub.json | 192 +++++++++++++++++++++++++---- src/main/resources/application.yml | 2 +- 3 files changed, 185 insertions(+), 25 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6755a23..2a9f7f7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,14 +15,23 @@ services: retries: 10 keycloak: - image: quay.io/keycloak/keycloak:24.0 + image: quay.io/keycloak/keycloak:24.0.5 command: ["start-dev","--http-port=8081","--import-realm"] environment: KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin + # Make issuer consistent & reachable from other containers + KC_HOSTNAME: keycloak + KC_HTTP_ENABLED: "true" + KC_HOSTNAME_STRICT: "false" + KC_PROXY: edge + ports: + - "8081:8081" volumes: - - ./keycloak:/opt/keycloak/data/import - ports: ["8081:8081"] + # persist state + - keycloak_data:/opt/keycloak/data + # import our realm once + - ./keycloak/realm-hemhub.json:/opt/keycloak/data/import/realm-hemhub.json:ro api: @@ -39,3 +48,4 @@ services: volumes: pgdata: + keycloak_data: diff --git a/keycloak/realm-hemhub.json b/keycloak/realm-hemhub.json index cfe0bae..648cf40 100644 --- a/keycloak/realm-hemhub.json +++ b/keycloak/realm-hemhub.json @@ -2,55 +2,205 @@ "realm": "hemhub", "enabled": true, "displayName": "HemHub", + "registrationAllowed": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + + "roles": { + "realm": [ + { "name": "OWNER" }, + { "name": "MEMBER" }, + { "name": "ADMIN" } + ] + }, + "users": [ { "username": "maria", "email": "maria@example.com", + "firstName": "Maria", + "lastName": "Andersson", "enabled": true, "emailVerified": true, "attributes": { "household_id": ["H-ANDERSSON"] }, - "credentials": [{ "type": "password", "value": "Passw0rd!", "temporary": false }], + "credentials": [{ "type": "password", "value": "Passw0rd", "temporary": false }], "realmRoles": ["OWNER","MEMBER"] }, { "username": "ulf", "email": "ulf@example.com", + "firstName": "Ulf", + "lastName": "Svensson", "enabled": true, "emailVerified": true, "attributes": { "household_id": ["H-ANDERSSON"] }, - "credentials": [{ "type": "password", "value": "Passw0rd!", "temporary": false }], + "credentials": [{ "type": "password", "value": "Passw0rd", "temporary": false }], "realmRoles": ["MEMBER"] } ], - "roles": { - "realm": [ - {"name":"OWNER","composite":false}, - {"name":"MEMBER","composite":false}, - {"name":"ADMIN","composite":false} - ] - }, + + "clientScopes": [ + { + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { "include.in.token.scope": "true" }, + "protocolMappers": [ + { + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true", + "usermodel.realmRoleMapping.rolePrefix": "", + "access.token.claim": "true", + "id.token.claim": "false" + } + } + ] + }, + { + "name": "profile", + "description": "Standard OpenID Connect profile claims", + "protocol": "openid-connect", + "attributes": { "include.in.token.scope": "true" }, + "protocolMappers": [ + { + "name": "preferred username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "config": { + "user.attribute": "username", + "claim.name": "preferred_username", + "jsonType.label": "String", + "access.token.claim": "true", + "id.token.claim": "true" + } + }, + { + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "config": { + "user.attribute": "firstName", + "claim.name": "given_name", + "jsonType.label": "String", + "access.token.claim": "true", + "id.token.claim": "true" + } + }, + { + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "config": { + "user.attribute": "lastName", + "claim.name": "family_name", + "jsonType.label": "String", + "access.token.claim": "true", + "id.token.claim": "true" + } + }, + { + "name": "name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "config": { + "access.token.claim": "true", + "id.token.claim": "true" + } + } + ] + }, + { + "name": "email", + "description": "Standard OpenID Connect email claims", + "protocol": "openid-connect", + "attributes": { "include.in.token.scope": "true" }, + "protocolMappers": [ + { + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "config": { + "user.attribute": "email", + "claim.name": "email", + "jsonType.label": "String", + "access.token.claim": "true", + "id.token.claim": "true" + } + }, + { + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "config": { + "user.attribute": "emailVerified", + "claim.name": "email_verified", + "jsonType.label": "boolean", + "access.token.claim": "true", + "id.token.claim": "true" + } + } + ] + }, + { + "name": "hemhub-extra", + "description": "Custom claims for HemHub", + "protocol": "openid-connect", + "attributes": { "include.in.token.scope": "true" }, + "protocolMappers": [ + { + "name": "household_id", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "config": { + "user.attribute": "household_id", + "claim.name": "household_id", + "jsonType.label": "String", + "access.token.claim": "true", + "id.token.claim": "true" + } + } + ] + } + ], + + "defaultDefaultClientScopes": [ "roles", "profile", "email", "hemhub-extra" ], + "defaultOptionalClientScopes": [ "offline_access" ], + "clients": [ { "clientId": "hemhub-public", + "name": "HemHub Public", + "enabled": true, "publicClient": true, - "redirectUris": ["http://localhost:5173/*","http://localhost:8080/swagger-ui/*"], + "protocol": "openid-connect", "standardFlowEnabled": true, - "implicitFlowEnabled": false, "directAccessGrantsEnabled": true, - "attributes": { "pkce.code.challenge.method": "S256" } + "serviceAccountsEnabled": false, + "attributes": { "pkce.code.challenge.method": "S256" }, + "redirectUris": [ + "http://localhost:8080/swagger-ui/*", + "http://localhost:5173/*" + ], + "webOrigins": ["*"] }, { "clientId": "hemhub-service", - "serviceAccountsEnabled": true, - "secret": "dev-secret", + "name": "HemHub Service", + "enabled": true, "publicClient": false, - "redirectUris": [], + "protocol": "openid-connect", + "standardFlowEnabled": false, "directAccessGrantsEnabled": false, - "standardFlowEnabled": false + "serviceAccountsEnabled": true, + "secret": "dev-secret" } - ], - "clientScopes": [ - {"name":"roles","protocol":"openid-connect"} - ], - "defaultDefaultClientScopes": ["roles", "profile", "email"] + ] } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index c1b310a..3b3950c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -15,4 +15,4 @@ spring: oauth2: resourceserver: jwt: - issuer-uri: http://localhost:8082/realms/hemhub + jwk-set-uri: http://keycloak:8081/realms/hemhub/protocol/openid-connect/certs