Repository: thomasdarimont/spring-boot-2-keycloak-oauth-example Branch: master Commit: 4110f38c4a55 Files: 13 Total size: 80.5 KB Directory structure: gitextract_swett08m/ ├── .gitignore ├── .mvn/ │ └── wrapper/ │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── demo-realm.json ├── mvnw ├── mvnw.cmd ├── pom.xml ├── readme.md └── src/ └── main/ ├── java/ │ └── demo/ │ └── SpringBoot2App.java └── resources/ ├── application.yml └── templates/ ├── admin.html ├── app.html └── index.html ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ target/ !.mvn/wrapper/maven-wrapper.jar ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans ### IntelliJ IDEA ### .idea *.iws *.iml *.ipr ### NetBeans ### nbproject/private/ build/ nbbuild/ dist/ nbdist/ .nb-gradle/ ================================================ FILE: .mvn/wrapper/maven-wrapper.properties ================================================ distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip ================================================ FILE: demo-realm.json ================================================ { "id" : "demo", "realm" : "demo", "notBefore" : 0, "revokeRefreshToken" : false, "refreshTokenMaxReuse" : 0, "accessTokenLifespan" : 300, "accessTokenLifespanForImplicitFlow" : 900, "ssoSessionIdleTimeout" : 1800, "ssoSessionMaxLifespan" : 36000, "offlineSessionIdleTimeout" : 2592000, "accessCodeLifespan" : 60, "accessCodeLifespanUserAction" : 300, "accessCodeLifespanLogin" : 1800, "actionTokenGeneratedByAdminLifespan" : 43200, "actionTokenGeneratedByUserLifespan" : 300, "enabled" : true, "sslRequired" : "external", "registrationAllowed" : false, "registrationEmailAsUsername" : false, "rememberMe" : false, "verifyEmail" : false, "loginWithEmailAllowed" : true, "duplicateEmailsAllowed" : false, "resetPasswordAllowed" : false, "editUsernameAllowed" : false, "bruteForceProtected" : false, "permanentLockout" : false, "maxFailureWaitSeconds" : 900, "minimumQuickLoginWaitSeconds" : 60, "waitIncrementSeconds" : 60, "quickLoginCheckMilliSeconds" : 1000, "maxDeltaTimeSeconds" : 43200, "failureFactor" : 30, "roles" : { "realm" : [ { "id" : "9cec708f-b60c-4e72-87bd-bd6cc270804f", "name" : "offline_access", "description" : "${role_offline-access}", "scopeParamRequired" : true, "composite" : false, "clientRole" : false, "containerId" : "demo" }, { "id" : "b377ec00-d24e-481c-88d1-fc236fcbb4f8", "name" : "uma_authorization", "description" : "${role_uma_authorization}", "scopeParamRequired" : false, "composite" : false, "clientRole" : false, "containerId" : "demo" } ], "client" : { "realm-management" : [ { "id" : "e20d8308-6e6e-4814-8b6c-f31e429d83d8", "name" : "query-clients", "description" : "${role_query-clients}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "b64456f6-a12d-455d-8f91-5b2a508d1326", "name" : "view-identity-providers", "description" : "${role_view-identity-providers}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "968aa5a6-220f-4c73-a1d9-d1eb4b3f7f20", "name" : "query-groups", "description" : "${role_query-groups}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "54db3285-f76c-462f-9dea-3f0da29a8277", "name" : "view-events", "description" : "${role_view-events}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "f63004bf-394b-426f-b609-19ca807f3a57", "name" : "manage-users", "description" : "${role_manage-users}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "273b678a-2018-4480-9007-b0b5d318019e", "name" : "query-users", "description" : "${role_query-users}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "b0bcfe2c-e426-4cc8-8161-f6608203409b", "name" : "view-users", "description" : "${role_view-users}", "scopeParamRequired" : false, "composite" : true, "composites" : { "client" : { "realm-management" : [ "query-groups", "query-users" ] } }, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "4f2d2465-9e75-4975-8748-ec2bbb2f1b96", "name" : "query-realms", "description" : "${role_query-realms}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "65c72650-d874-4a6b-a065-4558a3e2fc6a", "name" : "create-client", "description" : "${role_create-client}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "a56996bc-9589-476b-9e54-88b5e6355fca", "name" : "manage-clients", "description" : "${role_manage-clients}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "6e8e2d8d-1632-4893-8428-3ec4c438bead", "name" : "view-clients", "description" : "${role_view-clients}", "scopeParamRequired" : false, "composite" : true, "composites" : { "client" : { "realm-management" : [ "query-clients" ] } }, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "25c953c3-a411-4237-8501-7b8ebef5988f", "name" : "manage-realm", "description" : "${role_manage-realm}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "dc1a53ec-d0e1-441f-8e72-8ca411ab21a0", "name" : "view-authorization", "description" : "${role_view-authorization}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "30bbf430-98a6-46d5-a25c-48992aebd848", "name" : "realm-admin", "description" : "${role_realm-admin}", "scopeParamRequired" : false, "composite" : true, "composites" : { "client" : { "realm-management" : [ "query-clients", "query-groups", "view-identity-providers", "view-events", "manage-users", "query-users", "query-realms", "view-users", "create-client", "manage-clients", "view-clients", "manage-realm", "view-authorization", "manage-events", "impersonation", "manage-authorization", "view-realm", "manage-identity-providers" ] } }, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "2f93f784-ece8-4205-a9c2-b933b6beee09", "name" : "manage-events", "description" : "${role_manage-events}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "eb6ab687-958a-4c24-a97d-569e31101c41", "name" : "manage-authorization", "description" : "${role_manage-authorization}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "e4d149d9-5c8e-4f1c-9a40-ac371aaf3794", "name" : "impersonation", "description" : "${role_impersonation}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "3f9d491f-7df0-4f91-ab0d-5f5531b41f9d", "name" : "view-realm", "description" : "${role_view-realm}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" }, { "id" : "11ba2522-4090-431b-9842-0e9f31884d0a", "name" : "manage-identity-providers", "description" : "${role_manage-identity-providers}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd" } ], "security-admin-console" : [ ], "admin-cli" : [ ], "app-demo" : [ { "id" : "57b84603-ceef-4a48-bd08-f46467eb7bd9", "name" : "admin", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "ec2975dc-bde7-4012-89c4-06a499c00235" }, { "id" : "754c1364-9444-4477-bcc1-b07794bee2fc", "name" : "user", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "ec2975dc-bde7-4012-89c4-06a499c00235" } ], "broker" : [ { "id" : "f08f6cf6-09ed-41f6-9c05-164dc8f8ab5e", "name" : "read-token", "description" : "${role_read-token}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "0536d12c-2c36-444a-898b-ed7b527aac76" } ], "account" : [ { "id" : "c4cf29c5-3375-45d1-acd2-5962af1c733a", "name" : "manage-account", "description" : "${role_manage-account}", "scopeParamRequired" : false, "composite" : true, "composites" : { "client" : { "account" : [ "manage-account-links" ] } }, "clientRole" : true, "containerId" : "2e441b12-8370-49b8-b8aa-2c7546144b4e" }, { "id" : "5d703753-792e-4167-ad2b-c5b7f6a959d7", "name" : "view-profile", "description" : "${role_view-profile}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "2e441b12-8370-49b8-b8aa-2c7546144b4e" }, { "id" : "8eb0ae88-2819-47ab-9a93-6ff34b43ae88", "name" : "manage-account-links", "description" : "${role_manage-account-links}", "scopeParamRequired" : false, "composite" : false, "clientRole" : true, "containerId" : "2e441b12-8370-49b8-b8aa-2c7546144b4e" } ] } }, "groups" : [ ], "defaultRoles" : [ "offline_access", "uma_authorization" ], "requiredCredentials" : [ "password" ], "otpPolicyType" : "totp", "otpPolicyAlgorithm" : "HmacSHA1", "otpPolicyInitialCounter" : 0, "otpPolicyDigits" : 6, "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpSupportedApplications" : [ "FreeOTP", "Google Authenticator" ], "users" : [ { "id" : "8d7a7207-94c0-4ba7-ac3a-efb79412dd44", "createdTimestamp" : 1523988975613, "username" : "admin", "enabled" : true, "totp" : false, "emailVerified" : false, "firstName" : "Arno", "lastName" : "Admin", "email" : "tom+admin@localhost", "credentials" : [ { "type" : "password", "hashedSaltedValue" : "ZcC45qA9JiFQYuJau1w/pxfBmZusDDoCO0Sx4z+uFWdE4CA1FUibOqpqHh21GwRKcdRSseqrBBaaPbBznIWYRQ==", "salt" : "sTMbn/fDpC/KX549o9XoHg==", "hashIterations" : 27500, "counter" : 0, "algorithm" : "pbkdf2-sha256", "digits" : 0, "period" : 0, "createdDate" : 1523988982527, "config" : { } } ], "disableableCredentialTypes" : [ "password" ], "requiredActions" : [ ], "realmRoles" : [ "offline_access", "uma_authorization" ], "clientRoles" : { "app-demo" : [ "admin", "user" ], "account" : [ "manage-account", "view-profile" ] }, "notBefore" : 0, "groups" : [ ] }, { "id" : "91591318-6c82-4250-b6fb-9a5e257959a5", "createdTimestamp" : 1523983635756, "username" : "tester", "enabled" : true, "totp" : false, "emailVerified" : false, "firstName" : "Theo", "lastName" : "Tester", "email" : "tom+test@localhost", "credentials" : [ { "type" : "password", "hashedSaltedValue" : "h/aCiQS3fdB/euTSwOz/ww/+L0b6c9WGsRsMMSPr899rQIFLlIhjWjAgA9BbRAKsSLfo+nsNUKaXGfhwgi2r7g==", "salt" : "W3mOjqoG8mctglGSgbYSKQ==", "hashIterations" : 27500, "counter" : 0, "algorithm" : "pbkdf2-sha256", "digits" : 0, "period" : 0, "createdDate" : 1523983645059, "config" : { } } ], "disableableCredentialTypes" : [ "password" ], "requiredActions" : [ ], "realmRoles" : [ "offline_access", "uma_authorization" ], "clientRoles" : { "app-demo" : [ "user" ], "account" : [ "manage-account", "view-profile" ] }, "notBefore" : 0, "groups" : [ ] } ], "clients" : [ { "id" : "2e441b12-8370-49b8-b8aa-2c7546144b4e", "clientId" : "account", "name" : "${client_account}", "baseUrl" : "/auth/realms/demo/account", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", "secret" : "85e4ed69-3da1-4d34-ba4e-31980b4b386d", "defaultRoles" : [ "view-profile", "manage-account" ], "redirectUris" : [ "/auth/realms/demo/account/*" ], "webOrigins" : [ ], "notBefore" : 0, "bearerOnly" : false, "consentRequired" : false, "standardFlowEnabled" : true, "implicitFlowEnabled" : false, "directAccessGrantsEnabled" : false, "serviceAccountsEnabled" : false, "publicClient" : false, "frontchannelLogout" : false, "protocol" : "openid-connect", "attributes" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, "protocolMappers" : [ { "id" : "61173bf1-f182-4c08-81f1-45ded5285a96", "name" : "given name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${givenName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "firstName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "given_name", "jsonType.label" : "String" } }, { "id" : "3fc7edb3-23e1-4211-9a71-61700c849841", "name" : "family name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${familyName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "lastName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "family_name", "jsonType.label" : "String" } }, { "id" : "33bcaadd-4cae-44fd-8f53-cfbb9b1804a3", "name" : "email", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${email}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "email", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "email", "jsonType.label" : "String" } }, { "id" : "b7ea4f28-5d0c-4ec3-b03f-bef776943c65", "name" : "username", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${username}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "username", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "preferred_username", "jsonType.label" : "String" } }, { "id" : "493cb2a0-3b7d-46de-9aa4-e9b36e9b75c5", "name" : "role list", "protocol" : "saml", "protocolMapper" : "saml-role-list-mapper", "consentRequired" : false, "config" : { "single" : "false", "attribute.nameformat" : "Basic", "attribute.name" : "Role" } }, { "id" : "f2e9d26c-4703-4fac-b5d5-cbbb8cbf9fbc", "name" : "full name", "protocol" : "openid-connect", "protocolMapper" : "oidc-full-name-mapper", "consentRequired" : true, "consentText" : "${fullName}", "config" : { "id.token.claim" : "true", "access.token.claim" : "true" } } ], "useTemplateConfig" : false, "useTemplateScope" : false, "useTemplateMappers" : false }, { "id" : "c0d68b59-146c-462e-910d-eae82c190c61", "clientId" : "admin-cli", "name" : "${client_admin-cli}", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", "secret" : "e4491867-8a42-4ca4-93ee-34a898ea6775", "redirectUris" : [ ], "webOrigins" : [ ], "notBefore" : 0, "bearerOnly" : false, "consentRequired" : false, "standardFlowEnabled" : false, "implicitFlowEnabled" : false, "directAccessGrantsEnabled" : true, "serviceAccountsEnabled" : false, "publicClient" : true, "frontchannelLogout" : false, "protocol" : "openid-connect", "attributes" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, "protocolMappers" : [ { "id" : "e005a327-9dcb-447d-80c2-f7c668d0afaa", "name" : "full name", "protocol" : "openid-connect", "protocolMapper" : "oidc-full-name-mapper", "consentRequired" : true, "consentText" : "${fullName}", "config" : { "id.token.claim" : "true", "access.token.claim" : "true" } }, { "id" : "a4cab9da-4684-4bdb-8351-abf2d9428845", "name" : "given name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${givenName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "firstName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "given_name", "jsonType.label" : "String" } }, { "id" : "b2fc3109-62de-4b87-8c31-29e895329f93", "name" : "family name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${familyName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "lastName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "family_name", "jsonType.label" : "String" } }, { "id" : "90622937-00bc-4e2e-a5d6-3f573712b2d2", "name" : "username", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${username}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "username", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "preferred_username", "jsonType.label" : "String" } }, { "id" : "4960d696-da1f-4fd2-8e15-49cdad809b03", "name" : "email", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${email}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "email", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "email", "jsonType.label" : "String" } }, { "id" : "c175c2f3-d399-4171-a46a-ac74a5b835f0", "name" : "role list", "protocol" : "saml", "protocolMapper" : "saml-role-list-mapper", "consentRequired" : false, "config" : { "single" : "false", "attribute.nameformat" : "Basic", "attribute.name" : "Role" } } ], "useTemplateConfig" : false, "useTemplateScope" : false, "useTemplateMappers" : false }, { "id" : "ec2975dc-bde7-4012-89c4-06a499c00235", "clientId" : "app-demo", "baseUrl" : "http://localhost:8082/", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", "secret" : "e3f519b4-0272-4261-9912-8b7453ac4ecd", "redirectUris" : [ "http://localhost:8082/*" ], "webOrigins" : [ "http://localhost:8082" ], "notBefore" : 0, "bearerOnly" : false, "consentRequired" : false, "standardFlowEnabled" : true, "implicitFlowEnabled" : false, "directAccessGrantsEnabled" : true, "serviceAccountsEnabled" : false, "publicClient" : false, "frontchannelLogout" : false, "protocol" : "openid-connect", "attributes" : { "saml.assertion.signature" : "false", "saml.force.post.binding" : "false", "saml.multivalued.roles" : "false", "saml.encrypt" : "false", "saml_force_name_id_format" : "false", "saml.client.signature" : "false", "saml.authnstatement" : "false", "saml.server.signature" : "false", "saml.server.signature.keyinfo.ext" : "false", "saml.onetimeuse.condition" : "false" }, "fullScopeAllowed" : true, "nodeReRegistrationTimeout" : -1, "protocolMappers" : [ { "id" : "f632a9a7-5cff-4254-9941-82fabfdb3216", "name" : "role list", "protocol" : "saml", "protocolMapper" : "saml-role-list-mapper", "consentRequired" : false, "config" : { "single" : "false", "attribute.nameformat" : "Basic", "attribute.name" : "Role" } }, { "id" : "488d9ab7-7585-41dc-9d09-dd6d639b7a24", "name" : "username", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${username}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "username", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "preferred_username", "jsonType.label" : "String" } }, { "id" : "de28c7ae-a613-4f9d-8edb-1b9aa507efb9", "name" : "email", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${email}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "email", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "email", "jsonType.label" : "String" } }, { "id" : "6611aecf-daad-4ad2-aa74-c33de47037b0", "name" : "full name", "protocol" : "openid-connect", "protocolMapper" : "oidc-full-name-mapper", "consentRequired" : true, "consentText" : "${fullName}", "config" : { "id.token.claim" : "true", "access.token.claim" : "true" } }, { "id" : "cb5a11ce-3311-4a78-9061-e6221184b6f5", "name" : "given name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${givenName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "firstName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "given_name", "jsonType.label" : "String" } }, { "id" : "4ec046bf-9c35-44ac-9395-fcda434fc061", "name" : "family name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${familyName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "lastName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "family_name", "jsonType.label" : "String" } } ], "useTemplateConfig" : false, "useTemplateScope" : false, "useTemplateMappers" : false }, { "id" : "0536d12c-2c36-444a-898b-ed7b527aac76", "clientId" : "broker", "name" : "${client_broker}", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", "secret" : "577625be-1736-4bee-9bb2-608aff465c64", "redirectUris" : [ ], "webOrigins" : [ ], "notBefore" : 0, "bearerOnly" : false, "consentRequired" : false, "standardFlowEnabled" : true, "implicitFlowEnabled" : false, "directAccessGrantsEnabled" : false, "serviceAccountsEnabled" : false, "publicClient" : false, "frontchannelLogout" : false, "protocol" : "openid-connect", "attributes" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, "protocolMappers" : [ { "id" : "5f5e550c-ff29-482d-bb1f-0215d6bf081d", "name" : "family name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${familyName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "lastName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "family_name", "jsonType.label" : "String" } }, { "id" : "9239d7be-795f-4307-b0fe-ae0635fc367f", "name" : "full name", "protocol" : "openid-connect", "protocolMapper" : "oidc-full-name-mapper", "consentRequired" : true, "consentText" : "${fullName}", "config" : { "id.token.claim" : "true", "access.token.claim" : "true" } }, { "id" : "c7be1c7e-a0d2-4fe3-869c-de00d694f067", "name" : "email", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${email}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "email", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "email", "jsonType.label" : "String" } }, { "id" : "e0c76ba5-d83d-4366-9b16-7b334fa587d4", "name" : "role list", "protocol" : "saml", "protocolMapper" : "saml-role-list-mapper", "consentRequired" : false, "config" : { "single" : "false", "attribute.nameformat" : "Basic", "attribute.name" : "Role" } }, { "id" : "d97fa215-8d04-466b-8c19-a7c5f14ed1fa", "name" : "username", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${username}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "username", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "preferred_username", "jsonType.label" : "String" } }, { "id" : "b5359a60-d80b-4c5d-aa7c-ff3d1959aa9b", "name" : "given name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${givenName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "firstName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "given_name", "jsonType.label" : "String" } } ], "useTemplateConfig" : false, "useTemplateScope" : false, "useTemplateMappers" : false }, { "id" : "4e57a54d-42c8-400e-9cc1-95a36fbe02fd", "clientId" : "realm-management", "name" : "${client_realm-management}", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", "secret" : "05f5e2a2-83c9-45be-bc8b-7d9c2a924ed4", "redirectUris" : [ ], "webOrigins" : [ ], "notBefore" : 0, "bearerOnly" : true, "consentRequired" : false, "standardFlowEnabled" : true, "implicitFlowEnabled" : false, "directAccessGrantsEnabled" : false, "serviceAccountsEnabled" : false, "publicClient" : false, "frontchannelLogout" : false, "protocol" : "openid-connect", "attributes" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, "protocolMappers" : [ { "id" : "b699f558-b852-4013-a8c2-177abf9a8353", "name" : "full name", "protocol" : "openid-connect", "protocolMapper" : "oidc-full-name-mapper", "consentRequired" : true, "consentText" : "${fullName}", "config" : { "id.token.claim" : "true", "access.token.claim" : "true" } }, { "id" : "1377d3f0-8587-4458-8252-1f740f00dbe1", "name" : "email", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${email}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "email", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "email", "jsonType.label" : "String" } }, { "id" : "ca0e2b5c-6580-4949-80bd-fb3b87a0c7b5", "name" : "role list", "protocol" : "saml", "protocolMapper" : "saml-role-list-mapper", "consentRequired" : false, "config" : { "single" : "false", "attribute.nameformat" : "Basic", "attribute.name" : "Role" } }, { "id" : "101f9558-3e9c-4f34-b11b-b7e9f6ee7645", "name" : "family name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${familyName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "lastName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "family_name", "jsonType.label" : "String" } }, { "id" : "be6fa79f-ade5-44c9-afa0-93b8170c47b9", "name" : "username", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${username}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "username", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "preferred_username", "jsonType.label" : "String" } }, { "id" : "b5d07f15-6a26-4350-9bf9-54d5359c0706", "name" : "given name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${givenName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "firstName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "given_name", "jsonType.label" : "String" } } ], "useTemplateConfig" : false, "useTemplateScope" : false, "useTemplateMappers" : false }, { "id" : "8878b917-5b73-4950-b014-8100b0c405ed", "clientId" : "security-admin-console", "name" : "${client_security-admin-console}", "baseUrl" : "/auth/admin/demo/console/index.html", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", "secret" : "351b34ac-3a0f-4f09-af5a-6b9278c09b85", "redirectUris" : [ "/auth/admin/demo/console/*" ], "webOrigins" : [ ], "notBefore" : 0, "bearerOnly" : false, "consentRequired" : false, "standardFlowEnabled" : true, "implicitFlowEnabled" : false, "directAccessGrantsEnabled" : false, "serviceAccountsEnabled" : false, "publicClient" : true, "frontchannelLogout" : false, "protocol" : "openid-connect", "attributes" : { }, "fullScopeAllowed" : false, "nodeReRegistrationTimeout" : 0, "protocolMappers" : [ { "id" : "9a97567c-d9d6-4f5c-8d0f-ed1e060e598a", "name" : "role list", "protocol" : "saml", "protocolMapper" : "saml-role-list-mapper", "consentRequired" : false, "config" : { "single" : "false", "attribute.nameformat" : "Basic", "attribute.name" : "Role" } }, { "id" : "6dae9b5b-0948-4591-a69f-51a1590e892f", "name" : "username", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${username}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "username", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "preferred_username", "jsonType.label" : "String" } }, { "id" : "085240de-a1c4-4735-af6a-2c565e9612cf", "name" : "email", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${email}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "email", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "email", "jsonType.label" : "String" } }, { "id" : "594a456d-2cdb-49a3-8c40-05145a406f92", "name" : "family name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${familyName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "lastName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "family_name", "jsonType.label" : "String" } }, { "id" : "00f77d41-e3d3-443d-9e0f-098379d88ca7", "name" : "given name", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-property-mapper", "consentRequired" : true, "consentText" : "${givenName}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "firstName", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "given_name", "jsonType.label" : "String" } }, { "id" : "b12cbc25-fe43-4597-84e5-77b506712186", "name" : "full name", "protocol" : "openid-connect", "protocolMapper" : "oidc-full-name-mapper", "consentRequired" : true, "consentText" : "${fullName}", "config" : { "id.token.claim" : "true", "access.token.claim" : "true" } }, { "id" : "08f6ee38-4048-407a-8baa-a2e770ae5123", "name" : "locale", "protocol" : "openid-connect", "protocolMapper" : "oidc-usermodel-attribute-mapper", "consentRequired" : false, "consentText" : "${locale}", "config" : { "userinfo.token.claim" : "true", "user.attribute" : "locale", "id.token.claim" : "true", "access.token.claim" : "true", "claim.name" : "locale", "jsonType.label" : "String" } } ], "useTemplateConfig" : false, "useTemplateScope" : false, "useTemplateMappers" : false } ], "clientTemplates" : [ ], "browserSecurityHeaders" : { "xContentTypeOptions" : "nosniff", "xRobotsTag" : "none", "xFrameOptions" : "SAMEORIGIN", "xXSSProtection" : "1; mode=block", "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", "strictTransportSecurity" : "max-age=31536000; includeSubDomains" }, "smtpServer" : { }, "eventsEnabled" : false, "eventsListeners" : [ "jboss-logging" ], "enabledEventTypes" : [ ], "adminEventsEnabled" : false, "adminEventsDetailsEnabled" : false, "components" : { "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { "id" : "6908d5c3-07a7-4c00-91d7-462affb7425a", "name" : "Consent Required", "providerId" : "consent-required", "subType" : "anonymous", "subComponents" : { }, "config" : { } }, { "id" : "f2fbbda7-bc89-474c-80cd-c7817d27e852", "name" : "Allowed Client Templates", "providerId" : "allowed-client-templates", "subType" : "anonymous", "subComponents" : { }, "config" : { } }, { "id" : "e98e7d78-69d1-4f78-86a9-6aac95c7ce28", "name" : "Trusted Hosts", "providerId" : "trusted-hosts", "subType" : "anonymous", "subComponents" : { }, "config" : { "host-sending-registration-request-must-match" : [ "true" ], "client-uris-must-match" : [ "true" ] } }, { "id" : "9643017a-e4a3-435e-9c92-a14298a5f7ed", "name" : "Allowed Protocol Mapper Types", "providerId" : "allowed-protocol-mappers", "subType" : "anonymous", "subComponents" : { }, "config" : { "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper" ], "consent-required-for-all-mappers" : [ "true" ] } }, { "id" : "a72ad67b-392d-462b-a24d-359584948a39", "name" : "Max Clients Limit", "providerId" : "max-clients", "subType" : "anonymous", "subComponents" : { }, "config" : { "max-clients" : [ "200" ] } }, { "id" : "39d518a7-6678-415f-aee0-497ddf0bbb8f", "name" : "Allowed Protocol Mapper Types", "providerId" : "allowed-protocol-mappers", "subType" : "authenticated", "subComponents" : { }, "config" : { "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "oidc-address-mapper", "saml-role-list-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper" ], "consent-required-for-all-mappers" : [ "true" ] } }, { "id" : "f690379c-6479-4533-ac55-660d4d519849", "name" : "Full Scope Disabled", "providerId" : "scope", "subType" : "anonymous", "subComponents" : { }, "config" : { } }, { "id" : "fda00934-5922-4028-9e7c-c123100c248f", "name" : "Allowed Client Templates", "providerId" : "allowed-client-templates", "subType" : "authenticated", "subComponents" : { }, "config" : { } } ], "org.keycloak.keys.KeyProvider" : [ { "id" : "1cf453a3-7702-4d4d-8614-72c1fc6b17b6", "name" : "hmac-generated", "providerId" : "hmac-generated", "subComponents" : { }, "config" : { "kid" : [ "012044b2-dfa4-49cb-a4c5-916c19f9b96e" ], "secret" : [ "nEC6RCMTKBsF49bfzb3Vv21Vh4TLEaFiRAfvQp2YOOE" ], "priority" : [ "100" ] } }, { "id" : "83881f72-2def-46a5-b8ba-99ad0b1e4c95", "name" : "rsa-generated", "providerId" : "rsa-generated", "subComponents" : { }, "config" : { "privateKey" : [ "MIIEowIBAAKCAQEAjc06HxUS54Afmxiw0GXEcD8LS05UVU5KF0hc77CPuT6Sj9atTc1ccRinc24QuE2YdTtYGpELsYGkyul+5r6849vbCsQdcrq1JpLhre/renbjUWz9wBqlphIwPcAR7sSJKHnh+g5K+iOQ7AQaiJGo+tlZrXKfERnahTNVWaZHCIM+82oUvyFaXhV0XyhxQy9RtDq47GnWEgZ9xCvCWPDZJjG6Gm/6rIfZpdqqN5h8GqsOoCUxnLJS0XZP/8Gt/oZF/Y8yMKrOigs83hm1ireMFZDsGgp+z/8+m92CpbASSbO5HXbphO9LZQTZ0BFHHH7DBjF9QZeifQbUH1tyGMzDLQIDAQABAoIBAAavkHf6tGN55QOZDG0m/zYTouKwFMXY5SFn5gnfF0eyrZJemtk6P4uNkAefPuaRsgiE/chy4kxCQpTWoYcboyhTK2a8jG7mYrgo4L8kusORa3JhYy11lC3aWa/vqGjLl3PzGzaBCgP1PVYDc4TQ6Vi6esH0z48s2uLA4ttRPbQm+gJWMTmJpLNydDUBzuQT6OERald4/Ssp0qgZm0Y5WUu0mMiRT8LhNMzJNbZt3S/a22lKA1G/HpyaFD9Fb5hq09TyJqHefzbrWCg6sch8+tKvH6mxqHV2TmrLUtMHzW7tC0tkMh2MShzqqEIYNVFuqzAMEzNczrAYk2L1J2u0JjkCgYEAzWzZW3w9IGAq2yfCiHic3WpgWyadZGHJXRq/x7bkXxowtaHi2A4yc4x0obGbSvqPvFRlYt2v9f5XLmDGLVvS34BeHbZIEK1GUV+5lpzejJ1GkYdSnhPquLbmRkDrYG1a/dU4DLSKvKc45jmhwRdheeTvFW3z1XZqshrTIxBwwLsCgYEAsLZvShoO9M5uhWCnsvMZ6gRm7Nw5FmCfZm0WM1Cj9mWngFCcWhxZEheTOhiqNcnGi8e8xlDJAI4iimUD4ZRqeTs/CIvbHNq8DICf6aSMAaba+jxycEzVu/xZsNkzV1SaKcPll+fjV8vRKA0JAaioQMkrmhRCsu/ODNjo4MYd4TcCgYB3b8oQfvOrVz6TnVJltnq94ZOUa6cnLhO9pkfK/nNZOAkKEc2qgNakiA1n1bPMrFHV1dv815fvFqnptSqERNceS6xs6gaMa7JurZFiGGLsrqTRCDTo4/uc2fqY7jXsIqi83LNx76aaeT+D3A5IDigGjyzS8NLMUcVHOIF56Z0k8QKBgAstLrLAHSwMll7nMxQVXhE4AA6BCzdIGyzOP3Y1ZnliCosKDimA231ScmRdRP6r6VvT4TOlzmCznKmmI+2zhlxui9kNh2k2yGf+Bp8vYg+ErxrekdpacbY3CrEtu5qNZka39iB6cR2PbYYb9PLe6O1YC+Lt3x4UHrMfUPNOdHtXAoGBAKpncRXGyL05nnzFvK68BfoxSpd4aJk0Wkr9nOxZGFJj8v/LeUuxN6LOGqw4z8Sfdx3AI/6+JJAWk9sUlQ4ejAMFwR67UXI1eLkvs5x+U9g6DaZUMpgTUQhkfinZ3b822L6B20O+5Xz6aOGDbcWR+X/MAyj/gfnYR/TvXCq5v+sE" ], "certificate" : [ "MIIClzCCAX8CBgFi1H8iNDANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARkZW1vMB4XDTE4MDQxNzE2NDM0NFoXDTI4MDQxNzE2NDUyNFowDzENMAsGA1UEAwwEZGVtbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI3NOh8VEueAH5sYsNBlxHA/C0tOVFVOShdIXO+wj7k+ko/WrU3NXHEYp3NuELhNmHU7WBqRC7GBpMrpfua+vOPb2wrEHXK6tSaS4a3v63p241Fs/cAapaYSMD3AEe7EiSh54foOSvojkOwEGoiRqPrZWa1ynxEZ2oUzVVmmRwiDPvNqFL8hWl4VdF8ocUMvUbQ6uOxp1hIGfcQrwljw2SYxuhpv+qyH2aXaqjeYfBqrDqAlMZyyUtF2T//Brf6GRf2PMjCqzooLPN4ZtYq3jBWQ7BoKfs//PpvdgqWwEkmzuR126YTvS2UE2dARRxx+wwYxfUGXon0G1B9bchjMwy0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAavonr2grkWPCSLDRRCyTly2q/By5FYhkN5Bu7c2pxQy/k1OKJUBz+GIPxSL6MrPmHoWHM9fpD9nLCa1VHbHkEiu9p0c+2CDPVAyYvVbADVdgL+xp19TMyvETMfS1tJKVyJbqL8RsxzjWm1qI19VmVWNNHUGXpuPp35DL5tL/RkooY6+oNamsiNwR6xXmfqY7l6w8HA4y8It3yaeHy6N2ffjI/2AtXTyISuEEv1Lybr5op4jKRYLqv+ly7aBeNIGGnHFXlGyCqv8dSWSYXA1S83x4LeTHrj7rAC4fORY2Q5VNmhUAYYE5Vy+zxAHSoQn+cEf2qJsr9xRlYy8nzyXukw==" ], "priority" : [ "100" ] } }, { "id" : "46800710-eced-4804-a79e-32f7c5797b04", "name" : "aes-generated", "providerId" : "aes-generated", "subComponents" : { }, "config" : { "kid" : [ "2b922283-df55-4424-8bef-3f2ff5ba0bfa" ], "secret" : [ "Ry49w356XdzGeEQbsupsvA" ], "priority" : [ "100" ] } } ] }, "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { "id" : "a48d9b79-b52f-4d29-b831-ecb9098352ad", "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", "requirement" : "REQUIRED", "priority" : 10, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "idp-email-verification", "requirement" : "ALTERNATIVE", "priority" : 20, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "requirement" : "ALTERNATIVE", "priority" : 30, "flowAlias" : "Verify Existing Account by Re-authentication", "userSetupAllowed" : false, "autheticatorFlow" : true } ] }, { "id" : "03731267-4cd9-478d-ac04-403d1d832d46", "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", "requirement" : "REQUIRED", "priority" : 10, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "auth-otp-form", "requirement" : "OPTIONAL", "priority" : 20, "userSetupAllowed" : false, "autheticatorFlow" : false } ] }, { "id" : "bb3fec2b-49dc-4ca3-bb31-8acb74b3e1bd", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", "topLevel" : true, "builtIn" : true, "authenticationExecutions" : [ { "authenticator" : "auth-cookie", "requirement" : "ALTERNATIVE", "priority" : 10, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "auth-spnego", "requirement" : "DISABLED", "priority" : 20, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "identity-provider-redirector", "requirement" : "ALTERNATIVE", "priority" : 25, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "requirement" : "ALTERNATIVE", "priority" : 30, "flowAlias" : "forms", "userSetupAllowed" : false, "autheticatorFlow" : true } ] }, { "id" : "84567fef-a17f-424c-9f30-c5ae041beeee", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", "topLevel" : true, "builtIn" : true, "authenticationExecutions" : [ { "authenticator" : "client-secret", "requirement" : "ALTERNATIVE", "priority" : 10, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "client-jwt", "requirement" : "ALTERNATIVE", "priority" : 20, "userSetupAllowed" : false, "autheticatorFlow" : false } ] }, { "id" : "731a2721-79ff-46ec-8dcb-ea7d19f86d33", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", "topLevel" : true, "builtIn" : true, "authenticationExecutions" : [ { "authenticator" : "direct-grant-validate-username", "requirement" : "REQUIRED", "priority" : 10, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "direct-grant-validate-password", "requirement" : "REQUIRED", "priority" : 20, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "direct-grant-validate-otp", "requirement" : "OPTIONAL", "priority" : 30, "userSetupAllowed" : false, "autheticatorFlow" : false } ] }, { "id" : "3c5c6d83-ccb5-420d-8370-253c43a6d598", "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", "requirement" : "REQUIRED", "priority" : 10, "userSetupAllowed" : false, "autheticatorFlow" : false } ] }, { "id" : "81f1a3b5-7737-4351-a4bd-25e98b536798", "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", "requirement" : "REQUIRED", "priority" : 10, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticatorConfig" : "create unique user config", "authenticator" : "idp-create-user-if-unique", "requirement" : "ALTERNATIVE", "priority" : 20, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "requirement" : "ALTERNATIVE", "priority" : 30, "flowAlias" : "Handle Existing Account", "userSetupAllowed" : false, "autheticatorFlow" : true } ] }, { "id" : "46ba7967-ea06-45e3-98af-11200a34473e", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", "topLevel" : false, "builtIn" : true, "authenticationExecutions" : [ { "authenticator" : "auth-username-password-form", "requirement" : "REQUIRED", "priority" : 10, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "auth-otp-form", "requirement" : "OPTIONAL", "priority" : 20, "userSetupAllowed" : false, "autheticatorFlow" : false } ] }, { "id" : "4f2620ae-bb89-4d85-b81e-6e5d20c78a47", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", "topLevel" : true, "builtIn" : true, "authenticationExecutions" : [ { "authenticator" : "registration-page-form", "requirement" : "REQUIRED", "priority" : 10, "flowAlias" : "registration form", "userSetupAllowed" : false, "autheticatorFlow" : true } ] }, { "id" : "7fb032fb-dac9-4ea8-9d63-678753437440", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", "topLevel" : false, "builtIn" : true, "authenticationExecutions" : [ { "authenticator" : "registration-user-creation", "requirement" : "REQUIRED", "priority" : 20, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "registration-profile-action", "requirement" : "REQUIRED", "priority" : 40, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "registration-password-action", "requirement" : "REQUIRED", "priority" : 50, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "registration-recaptcha-action", "requirement" : "DISABLED", "priority" : 60, "userSetupAllowed" : false, "autheticatorFlow" : false } ] }, { "id" : "75600e80-cae6-4347-a506-ddf6477cd7e9", "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", "requirement" : "REQUIRED", "priority" : 10, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "reset-credential-email", "requirement" : "REQUIRED", "priority" : 20, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "reset-password", "requirement" : "REQUIRED", "priority" : 30, "userSetupAllowed" : false, "autheticatorFlow" : false }, { "authenticator" : "reset-otp", "requirement" : "OPTIONAL", "priority" : 40, "userSetupAllowed" : false, "autheticatorFlow" : false } ] }, { "id" : "4990dbc0-bb0b-48b5-8f72-945adcf77d39", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", "topLevel" : true, "builtIn" : true, "authenticationExecutions" : [ { "authenticator" : "http-basic-authenticator", "requirement" : "REQUIRED", "priority" : 10, "userSetupAllowed" : false, "autheticatorFlow" : false } ] } ], "authenticatorConfig" : [ { "id" : "da120730-f96f-4a0b-9f97-48d63823b067", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { "id" : "0c76ade9-f703-4606-bae9-28f0bf131e11", "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, "config" : { } }, { "alias" : "UPDATE_PASSWORD", "name" : "Update Password", "providerId" : "UPDATE_PASSWORD", "enabled" : true, "defaultAction" : false, "config" : { } }, { "alias" : "UPDATE_PROFILE", "name" : "Update Profile", "providerId" : "UPDATE_PROFILE", "enabled" : true, "defaultAction" : false, "config" : { } }, { "alias" : "VERIFY_EMAIL", "name" : "Verify Email", "providerId" : "VERIFY_EMAIL", "enabled" : true, "defaultAction" : false, "config" : { } }, { "alias" : "terms_and_conditions", "name" : "Terms and Conditions", "providerId" : "terms_and_conditions", "enabled" : false, "defaultAction" : false, "config" : { } } ], "browserFlow" : "browser", "registrationFlow" : "registration", "directGrantFlow" : "direct grant", "resetCredentialsFlow" : "reset credentials", "clientAuthenticationFlow" : "clients", "dockerAuthenticationFlow" : "docker auth", "attributes" : { "_browser_header.xXSSProtection" : "1; mode=block", "_browser_header.xFrameOptions" : "SAMEORIGIN", "_browser_header.strictTransportSecurity" : "max-age=31536000; includeSubDomains", "permanentLockout" : "false", "quickLoginCheckMilliSeconds" : "1000", "_browser_header.xRobotsTag" : "none", "maxFailureWaitSeconds" : "900", "minimumQuickLoginWaitSeconds" : "60", "failureFactor" : "30", "actionTokenGeneratedByUserLifespan" : "300", "maxDeltaTimeSeconds" : "43200", "_browser_header.xContentTypeOptions" : "nosniff", "actionTokenGeneratedByAdminLifespan" : "43200", "bruteForceProtected" : "false", "_browser_header.contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", "waitIncrementSeconds" : "60" }, "keycloakVersion" : "3.4.3.Final" } ================================================ FILE: mvnw ================================================ #!/bin/sh # ---------------------------------------------------------------------------- # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- # Maven2 Start Up Batch script # # Required ENV vars: # ------------------ # JAVA_HOME - location of a JDK home dir # # Optional ENV vars # ----------------- # M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 # MAVEN_SKIP_RC - flag to disable loading of mavenrc files # ---------------------------------------------------------------------------- if [ -z "$MAVEN_SKIP_RC" ] ; then if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi if [ -f "$HOME/.mavenrc" ] ; then . "$HOME/.mavenrc" fi fi # OS specific support. $var _must_ be set to either true or false. cygwin=false; darwin=false; mingw=false case "`uname`" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then export JAVA_HOME="`/usr/libexec/java_home`" else export JAVA_HOME="/Library/Java/Home" fi fi ;; esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then JAVA_HOME=`java-config --jre-home` fi fi if [ -z "$M2_HOME" ] ; then ## resolve links - $0 may be a link to maven's home PRG="$0" # need this for relative symlinks while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG="`dirname "$PRG"`/$link" fi done saveddir=`pwd` M2_HOME=`dirname "$PRG"`/.. # make it fully qualified M2_HOME=`cd "$M2_HOME" && pwd` cd "$saveddir" # echo Using m2 at $M2_HOME fi # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --unix "$CLASSPATH"` fi # For Migwn, ensure paths are in UNIX format before anything is touched if $mingw ; then [ -n "$M2_HOME" ] && M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" # TODO classpath? fi if [ -z "$JAVA_HOME" ]; then javaExecutable="`which javac`" if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. readLink=`which readlink` if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then if $darwin ; then javaHome="`dirname \"$javaExecutable\"`" javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" else javaExecutable="`readlink -f \"$javaExecutable\"`" fi javaHome="`dirname \"$javaExecutable\"`" javaHome=`expr "$javaHome" : '\(.*\)/bin'` JAVA_HOME="$javaHome" export JAVA_HOME fi fi fi if [ -z "$JAVACMD" ] ; then if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi else JAVACMD="`which java`" fi fi if [ ! -x "$JAVACMD" ] ; then echo "Error: JAVA_HOME is not defined correctly." >&2 echo " We cannot execute $JAVACMD" >&2 exit 1 fi if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" return 1 fi basedir="$1" wdir="$1" while [ "$wdir" != '/' ] ; do if [ -d "$wdir"/.mvn ] ; then basedir=$wdir break fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then wdir=`cd "$wdir/.."; pwd` fi # end of workaround done echo "${basedir}" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then echo "$(tr -s '\n' ' ' < "$1")" fi } BASE_DIR=`find_maven_basedir "$(pwd)"` if [ -z "$BASE_DIR" ]; then exit 1; fi export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} echo $MAVEN_PROJECTBASEDIR MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then [ -n "$M2_HOME" ] && M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` [ -n "$CLASSPATH" ] && CLASSPATH=`cygpath --path --windows "$CLASSPATH"` [ -n "$MAVEN_PROJECTBASEDIR" ] && MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ $MAVEN_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" ================================================ FILE: mvnw.cmd ================================================ @REM ---------------------------------------------------------------------------- @REM Licensed to the Apache Software Foundation (ASF) under one @REM or more contributor license agreements. See the NOTICE file @REM distributed with this work for additional information @REM regarding copyright ownership. The ASF licenses this file @REM to you under the Apache License, Version 2.0 (the @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM @REM http://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @REM KIND, either express or implied. See the License for the @REM specific language governing permissions and limitations @REM under the License. @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- @REM Maven2 Start Up Batch script @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars @REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @REM e.g. to debug Maven itself, use @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files @REM ---------------------------------------------------------------------------- @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' @echo off @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% @REM set %HOME% to equivalent of $HOME if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" :skipRcPre @setlocal set ERROR_CODE=0 @REM To isolate internal variables from possible post scripts, we use another setlocal @setlocal @REM ==== START VALIDATION ==== if not "%JAVA_HOME%" == "" goto OkJHome echo. echo Error: JAVA_HOME not found in your environment. >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error :OkJHome if exist "%JAVA_HOME%\bin\java.exe" goto init echo. echo Error: JAVA_HOME is set to an invalid directory. >&2 echo JAVA_HOME = "%JAVA_HOME%" >&2 echo Please set the JAVA_HOME variable in your environment to match the >&2 echo location of your Java installation. >&2 echo. goto error @REM ==== END VALIDATION ==== :init @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". @REM Fallback to current working directory if not found. set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir set EXEC_DIR=%CD% set WDIR=%EXEC_DIR% :findBaseDir IF EXIST "%WDIR%"\.mvn goto baseDirFound cd .. IF "%WDIR%"=="%CD%" goto baseDirNotFound set WDIR=%CD% goto findBaseDir :baseDirFound set MAVEN_PROJECTBASEDIR=%WDIR% cd "%EXEC_DIR%" goto endDetectBaseDir :baseDirNotFound set MAVEN_PROJECTBASEDIR=%EXEC_DIR% cd "%EXEC_DIR%" :endDetectBaseDir IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig @setlocal EnableExtensions EnableDelayedExpansion for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% :endReadAdditionalConfig SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end :error set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' if "%MAVEN_BATCH_PAUSE%" == "on" pause if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% exit /B %ERROR_CODE% ================================================ FILE: pom.xml ================================================ 4.0.0 de.tdlabs.training spring-boot-2-oauth-example 0.0.1-SNAPSHOT jar spring-boot-2-oauth-example Demo project for Spring Boot org.springframework.boot spring-boot-starter-parent 2.0.1.RELEASE UTF-8 UTF-8 1.8 Finchley.M9 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-thymeleaf org.thymeleaf.extras thymeleaf-extras-springsecurity4 org.springframework.boot spring-boot-starter-security org.springframework.security spring-security-oauth2-client org.springframework.security spring-security-oauth2-jose org.projectlombok lombok true org.springframework.boot spring-boot-devtools true runtime org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin spring-snapshots Spring Snapshots https://repo.spring.io/snapshot true spring-milestones Spring Milestones https://repo.spring.io/milestone false spring-snapshots Spring Snapshots https://repo.spring.io/snapshot true spring-milestones Spring Milestones https://repo.spring.io/milestone false ================================================ FILE: readme.md ================================================ # PoC for Spring Boot 2 + Spring Security 5 + Keycloak 3.4.3 This example project uses the OpenID Connect support in Spring Security 5 without using the Keycloak adapter and is inspired by [this](http://info.michael-simons.eu/2017/12/28/use-keycloak-with-your-spring-boot-2-application/) blog post by Michael Simons. Features: - SSO / SLO - Support for extracting roles from Keycloak AccessToken - Link to Keycloak Account page with back-link to the application ## Setup Import the `demo` realm into Keycloak via ``` bin/standalone.sh -Dkeycloak.migration.action=import -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.file=/path/to/demo-realm.json -Dkeycloak.migration.strategy=OVERWRITE_EXISTING ``` Keycloak is assumed to run on port 8080 on localhost. The demo realm contains two users `tester` and `admin` both with password `test`. The example runs on port 8082. ================================================ FILE: src/main/java/demo/SpringBoot2App.java ================================================ package demo; import static org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI; import java.security.Principal; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; import org.springframework.security.oauth2.core.OAuth2ErrorCodes; import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtException; import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.client.RestTemplate; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.util.UriComponentsBuilder; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @SpringBootApplication public class SpringBoot2App { public static void main(String[] args) { SpringApplication.run(SpringBoot2App.class, args); } } @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) class WebSecurityConfig { @Bean public WebSecurityConfigurerAdapter webSecurityConfigurer( // @Value("${kc.realm}") String realm, // KeycloakOauth2UserService keycloakOidcUserService, // KeycloakLogoutHandler keycloakLogoutHandler // ) { return new WebSecurityConfigurerAdapter() { @Override public void configure(HttpSecurity http) throws Exception { http // Configure session management to your needs. // I need this as a basis for a classic, server side rendered application .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED).and() // Depends on your taste. You can configure single paths here // or allow everything a I did and then use method based security // like in the controller below .authorizeRequests().anyRequest().permitAll().and() // Propagate logouts via /logout to Keycloak .logout().addLogoutHandler(keycloakLogoutHandler).and() // This is the point where OAuth2 login of Spring 5 gets enabled .oauth2Login().userInfoEndpoint().oidcUserService(keycloakOidcUserService).and() // I don't want a page with different clients as login options // So i use the constant from OAuth2AuthorizationRequestRedirectFilter // plus the configured realm as immediate redirect to Keycloak .loginPage(DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/" + realm); } }; } @Bean KeycloakOauth2UserService keycloakOidcUserService(OAuth2ClientProperties oauth2ClientProperties) { // TODO use default JwtDecoder - where to grab? NimbusJwtDecoderJwkSupport jwtDecoder = new NimbusJwtDecoderJwkSupport( oauth2ClientProperties.getProvider().get("keycloak").getJwkSetUri()); SimpleAuthorityMapper authoritiesMapper = new SimpleAuthorityMapper(); authoritiesMapper.setConvertToUpperCase(true); return new KeycloakOauth2UserService(jwtDecoder, authoritiesMapper); } @Bean KeycloakLogoutHandler keycloakLogoutHandler() { return new KeycloakLogoutHandler(new RestTemplate()); } } @RequiredArgsConstructor class KeycloakOauth2UserService extends OidcUserService { private final OAuth2Error INVALID_REQUEST = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST); private final JwtDecoder jwtDecoder; private final GrantedAuthoritiesMapper authoritiesMapper; /** * Augments {@link OidcUserService#loadUser(OidcUserRequest)} to add authorities * provided by Keycloak. * * Needed because {@link OidcUserService#loadUser(OidcUserRequest)} (currently) * does not provide a hook for adding custom authorities from a * {@link OidcUserRequest}. */ @Override public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { OidcUser user = super.loadUser(userRequest); Set authorities = new LinkedHashSet<>(); authorities.addAll(user.getAuthorities()); authorities.addAll(extractKeycloakAuthorities(userRequest)); return new DefaultOidcUser(authorities, userRequest.getIdToken(), user.getUserInfo(), "preferred_username"); } /** * Extracts {@link GrantedAuthority GrantedAuthorities} from the AccessToken in * the {@link OidcUserRequest}. * * @param userRequest * @return */ private Collection extractKeycloakAuthorities(OidcUserRequest userRequest) { Jwt token = parseJwt(userRequest.getAccessToken().getTokenValue()); // Would be great if Spring Security would provide something like a plugable // OidcUserRequestAuthoritiesExtractor interface to hide the junk below... @SuppressWarnings("unchecked") Map resourceMap = (Map) token.getClaims().get("resource_access"); String clientId = userRequest.getClientRegistration().getClientId(); @SuppressWarnings("unchecked") Map> clientResource = (Map>) resourceMap.get(clientId); if (CollectionUtils.isEmpty(clientResource)) { return Collections.emptyList(); } @SuppressWarnings("unchecked") List clientRoles = (List) clientResource.get("roles"); if (CollectionUtils.isEmpty(clientRoles)) { return Collections.emptyList(); } Collection authorities = AuthorityUtils .createAuthorityList(clientRoles.toArray(new String[0])); if (authoritiesMapper == null) { return authorities; } return authoritiesMapper.mapAuthorities(authorities); } private Jwt parseJwt(String accessTokenValue) { try { // Token is already verified by spring security infrastructure return jwtDecoder.decode(accessTokenValue); } catch (JwtException e) { throw new OAuth2AuthenticationException(INVALID_REQUEST, e); } } } /** * Propagates logouts to Keycloak. * * Necessary because Spring Security 5 (currently) doesn't support * end-session-endpoints. */ @Slf4j @RequiredArgsConstructor class KeycloakLogoutHandler extends SecurityContextLogoutHandler { private final RestTemplate restTemplate; @Override public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { super.logout(request, response, authentication); propagateLogoutToKeycloak((OidcUser) authentication.getPrincipal()); } private void propagateLogoutToKeycloak(OidcUser user) { String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout"; UriComponentsBuilder builder = UriComponentsBuilder // .fromUriString(endSessionEndpoint) // .queryParam("id_token_hint", user.getIdToken().getTokenValue()); ResponseEntity logoutResponse = restTemplate.getForEntity(builder.toUriString(), String.class); if (logoutResponse.getStatusCode().is2xxSuccessful()) { log.info("Successfulley logged out in Keycloak"); } else { log.info("Could not propagate logout to Keycloak"); } } } @Controller class DemoController { @PreAuthorize("hasRole('ROLE_USER')") @GetMapping("/protected") public ModelAndView protectedPage(Principal principal) { return new ModelAndView("app", Collections.singletonMap("principal", principal)); } @PreAuthorize("hasRole('ROLE_ADMIN')") @GetMapping("/admin") public ModelAndView adminPage(Principal principal) { return new ModelAndView("admin", Collections.singletonMap("principal", principal)); } @GetMapping("/") public String unprotectedPage(Model model, Principal principal) { model.addAttribute("principal", principal); return "index"; } @GetMapping("/account") public String redirectToAccountPage(@AuthenticationPrincipal OAuth2AuthenticationToken authToken) { if (authToken == null) { return "redirect:/"; } OidcUser user = (OidcUser) authToken.getPrincipal(); // Provides a back-link to the application return "redirect:" + user.getIssuer() + "/account?referrer=" + user.getIdToken().getAuthorizedParty(); } } ================================================ FILE: src/main/resources/application.yml ================================================ server: port: 8082 kc: base-url: http://localhost:8080/auth realm: demo realm-url: ${kc.base-url}/realms/${kc.realm} spring: security: oauth2: client: registration: demo: client-id: app-demo client-name: Demo App client-secret: e3f519b4-0272-4261-9912-8b7453ac4ecd provider: keycloak authorization-grant-type: authorization_code scope: openid, profile redirect-uri-template: "{baseUrl}/login/oauth2/code/{registrationId}" provider: keycloak: authorization-uri: ${kc.realm-url}/protocol/openid-connect/auth jwk-set-uri: ${kc.realm-url}/protocol/openid-connect/certs token-uri: ${kc.realm-url}/protocol/openid-connect/token # would be cool if there was a end-session-uri to propagate logouts # User info endpoint not needed since Keycloak uses self-contained value tokens # user-info-uri: ${kc.realm-url}/protocol/openid-connect/userinfo user-name-attribute: preferred_username ================================================ FILE: src/main/resources/templates/admin.html ================================================ Admin Area

My cool App (Admin)

Current User

Home ================================================ FILE: src/main/resources/templates/app.html ================================================ App

My cool App

Current User

Home ================================================ FILE: src/main/resources/templates/index.html ================================================ Demo App