├── .gitignore ├── LICENSE ├── README.md ├── keycloak └── demo-realm.json ├── pom.xml └── src └── main ├── java └── de │ └── slackspace │ └── keycloaktutorial │ ├── KeyCloakTutorial.java │ ├── contract │ ├── domain │ │ └── Contract.java │ └── web │ │ └── ContractResource.java │ └── security │ ├── domain │ ├── CurrentUser.java │ └── UserDetails.java │ └── web │ ├── config │ └── WebConfig.java │ └── resolver │ └── UserDetailsArgumentResolver.java ├── resources ├── application.properties └── logback.xml └── webapp ├── index.html ├── js ├── lib │ ├── angular-1.5.0.min.js │ └── angular-route-1.5.0.min.js ├── main-controller.js └── main.js ├── keycloak └── keycloak.json └── views └── main.html /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings/ 4 | target/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Authentication with Spring Boot, AngularJS and Keycloak 2 | 3 | - Make sure to start Keycloak before using the application 4 | 5 | ## Run with standalone Tomcat 6 | 7 | To run the project with embedded Tomcat by maven: 8 | 9 | mvn spring-boot:run 10 | 11 | Then navigate to [http://localhost:8000](http://localhost:8000) to see the application in action. 12 | -------------------------------------------------------------------------------- /keycloak/demo-realm.json: -------------------------------------------------------------------------------- 1 | { 2 | "id" : "Demo-Realm", 3 | "realm" : "Demo-Realm", 4 | "notBefore" : 0, 5 | "revokeRefreshToken" : false, 6 | "accessTokenLifespan" : 300, 7 | "accessTokenLifespanForImplicitFlow" : 900, 8 | "ssoSessionIdleTimeout" : 1800, 9 | "ssoSessionMaxLifespan" : 36000, 10 | "offlineSessionIdleTimeout" : 2592000, 11 | "accessCodeLifespan" : 60, 12 | "accessCodeLifespanUserAction" : 300, 13 | "accessCodeLifespanLogin" : 1800, 14 | "enabled" : true, 15 | "sslRequired" : "external", 16 | "registrationAllowed" : true, 17 | "registrationEmailAsUsername" : false, 18 | "rememberMe" : false, 19 | "verifyEmail" : false, 20 | "resetPasswordAllowed" : true, 21 | "editUsernameAllowed" : false, 22 | "bruteForceProtected" : false, 23 | "maxFailureWaitSeconds" : 900, 24 | "minimumQuickLoginWaitSeconds" : 60, 25 | "waitIncrementSeconds" : 60, 26 | "quickLoginCheckMilliSeconds" : 1000, 27 | "maxDeltaTimeSeconds" : 43200, 28 | "failureFactor" : 30, 29 | "privateKey" : "MIIEowIBAAKCAQEAkRS35Z5lblNpvfHr8C6XEGi9vwYew737+LmcpDbxamLVSdNEWi01Gh2TXCWjDsONaUmvQBIVPvJM5dRmnR5lNtYFnRr6vrT/18I/+E/pU/5a9fj0hMyib1HEsKslI6P2os1vunIiHgkDJkePo9tTbRsG71WH7fXM7BSienZO0ZLV1TbgiLj+PSoFLzAHF0P3BXzHIGTqHzs/UHZ/7ub9Oc2vpSXi4IZKGVLQnc9VNZaMu0QWgnqjMCr7CQRORMQ+y4QGw+DFaZ1pBNpZ+bgy6wKSf17U/5Va/CWs/+UjGj50L/1QqertuMcFKqnnQDLhRmnQErLtdpXzop0YQxka/QIDAQABAoIBAQCA7xu7mjhkgwbvgsd+7bjdIWi4EnVW2knLMxGtsxJ7zJfnAzW0uoZ9zhj/BIfUACB1mMGBvFBbX7TNcCU9rxyeTjgw9iAvSeYxVnXJdSNUvTETdovAVahqLA4UQBVChl4TM2X/GZ2Oec3twB08rxeGWEWif63M4eybRn9bPDcx8elBryS2yQCi8k2WTCM8za+e1yXJT7tsbrpzmRuJ5Nsx/5mSvK94NwOGwGvgdZ36So1NFNSTYHESTE+rf0st6ec3sWR/jqQl+AaVLQzk11JU4Z39TPOs+NTXIBUwRDFa4rkbb1j023Izq3pAE5oJBaX/fIW/esKboFr4SvDyvjl1AoGBAMEDZfAIuF0AZ4YKM0NhBVdfAyzIy2ukkyurJ0z030u/UnFoB0yWADfFhQyXep7L/lq8n6v+81wvSJbDDNWkhOoDKGSrSIGOWKhaiBFgnZsd9eSK4JLtwseuen72KeKjmzBEZsAyL/op82aVGhnUSIAbE0P/y7bReeYIZfV3qnHfAoGBAMBs/ARNLzFPLBkAhkakRJK3frQDeWMRnpnPDwEIMLKGclBHwVIVhan5K9rVNvwUYiWpslQ18g6D5CwFqHc38dgwSvKO3pDnocqiFgpMZ1Fj+U4lHFjEhxFqb89hy6py5r5BkYsHm1gOxwpNwC1uctxAoaMDaECpCynZQRhDxKajAoGAdLeoWin7ywmmkLD0ZQdzom0rT1axZIumD581ad0edwDgSwH8f/ypbxB7r6yekw6K5rXv3KOU6KcTP0/+H+eYFKcKMy4871N0G4J7i/6f8CCDBMxSl6c9xWCmZMZZd9s1yk9iz1DKIH5j2SHLIdl0ajt1QAacWxZ7n3ZuqazOnoMCgYA5JiUTAflqI6FtK4K1qEPVbznqLL34dlssQxqpq80KuHNq9GSuBEZWXPJxY7GY3FTp6qRT7lGdjf5ONL/76FmjY3Re5Z+kWabrj4gMNvTYb2CznMH2eyfCPGJIrTrXvJhbGQ4oxHGWjYz/umfBFnR2hZt71p831PzDyQpUMoxNEQKBgDQcwO4lYmX0bNE/D9QVhHlBKuMdI5x2ctBLT7UoUjiOUZtUWcAZbrmA7sCd4CrhqQTj/moi/eTLRVSE57PgU5Vi57S1jG+JJs7ztUXygIHQe0MshPDYSh/hdDdpK8GDe6rS59/4CQNrEgsf6fSltdsr+AsTeAYFTg+agfvN3qJx", 30 | "publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkRS35Z5lblNpvfHr8C6XEGi9vwYew737+LmcpDbxamLVSdNEWi01Gh2TXCWjDsONaUmvQBIVPvJM5dRmnR5lNtYFnRr6vrT/18I/+E/pU/5a9fj0hMyib1HEsKslI6P2os1vunIiHgkDJkePo9tTbRsG71WH7fXM7BSienZO0ZLV1TbgiLj+PSoFLzAHF0P3BXzHIGTqHzs/UHZ/7ub9Oc2vpSXi4IZKGVLQnc9VNZaMu0QWgnqjMCr7CQRORMQ+y4QGw+DFaZ1pBNpZ+bgy6wKSf17U/5Va/CWs/+UjGj50L/1QqertuMcFKqnnQDLhRmnQErLtdpXzop0YQxka/QIDAQAB", 31 | "certificate" : "MIICozCCAYsCBgFTeOXgTzANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDDApEZW1vLVJlYWxtMB4XDTE2MDMxNTA2MDkwNFoXDTI2MDMxNTA2MTA0NFowFTETMBEGA1UEAwwKRGVtby1SZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJEUt+WeZW5Tab3x6/AulxBovb8GHsO9+/i5nKQ28Wpi1UnTRFotNRodk1wlow7DjWlJr0ASFT7yTOXUZp0eZTbWBZ0a+r60/9fCP/hP6VP+WvX49ITMom9RxLCrJSOj9qLNb7pyIh4JAyZHj6PbU20bBu9Vh+31zOwUonp2TtGS1dU24Ii4/j0qBS8wBxdD9wV8xyBk6h87P1B2f+7m/TnNr6Ul4uCGShlS0J3PVTWWjLtEFoJ6ozAq+wkETkTEPsuEBsPgxWmdaQTaWfm4MusCkn9e1P+VWvwlrP/lIxo+dC/9UKnq7bjHBSqp50Ay4UZp0BKy7XaV86KdGEMZGv0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAbE1X1HgHRnV1WGLA4PazmTJi28goqYIjSWPnfTuMjp2CcGgD/EqVr8GZ3UaN3frEwlj/HFEMMW0dbkDr/rM/aZDLRG/DvtuU4gGwa4nlC/C9UkzmZ2OQJA3VX2yWapZ9R1I6mdkGkY2whaQ2+GyjmvsYG1VtiiiAiaPK2CVnmdYu0cBO4leg+jhdtzzRCpV4ZCwonDbaiB80DkFHebyhqMKkUrKtbXu6UUNndRdZbjFQxqVh98QY2744x1Rn2MjeKKN7B9STsag0Kcsxn21twfyaXiQH8+4rVZtbQy2+5h5Cxqfbv6LTTsP497H/nd9wdsif1/0ZTP3Mb7nwuOFeYg==", 32 | "codeSecret" : "d87cee5a-cdab-496b-bb9d-6c6708a5f019", 33 | "roles" : { 34 | "realm" : [ { 35 | "id" : "4254637a-117e-4c3c-b4a0-fb1ac8f0414d", 36 | "name" : "admin", 37 | "description" : "Admin privileges", 38 | "scopeParamRequired" : false, 39 | "composite" : false 40 | }, { 41 | "id" : "8a6a900d-b729-4eff-8571-0297b95e511c", 42 | "name" : "offline_access", 43 | "description" : "${role_offline-access}", 44 | "scopeParamRequired" : true, 45 | "composite" : false 46 | }, { 47 | "id" : "a5fab8b0-455b-40a0-9dd7-55b89cc5947f", 48 | "name" : "manager", 49 | "description" : "Manager privileges", 50 | "scopeParamRequired" : false, 51 | "composite" : false 52 | } ], 53 | "client" : { 54 | "realm-management" : [ { 55 | "id" : "9917c8b1-d571-4a89-8e65-4632ae46fc5b", 56 | "name" : "view-users", 57 | "description" : "${role_view-users}", 58 | "scopeParamRequired" : false, 59 | "composite" : false 60 | }, { 61 | "id" : "3000c11d-cfec-40b8-8135-830eb656c7bb", 62 | "name" : "create-client", 63 | "description" : "${role_create-client}", 64 | "scopeParamRequired" : false, 65 | "composite" : false 66 | }, { 67 | "id" : "284cf448-3497-4e5f-afa3-138d3428b7ca", 68 | "name" : "view-events", 69 | "description" : "${role_view-events}", 70 | "scopeParamRequired" : false, 71 | "composite" : false 72 | }, { 73 | "id" : "fed7dcfb-96b7-4b49-b548-baf1e7dbbf68", 74 | "name" : "impersonation", 75 | "description" : "${role_impersonation}", 76 | "scopeParamRequired" : false, 77 | "composite" : false 78 | }, { 79 | "id" : "3bd419d8-b42c-4135-bd97-1a504a02f8b8", 80 | "name" : "manage-clients", 81 | "description" : "${role_manage-clients}", 82 | "scopeParamRequired" : false, 83 | "composite" : false 84 | }, { 85 | "id" : "3a37e85c-f6e4-4f07-bd04-e67df8b1a89e", 86 | "name" : "manage-identity-providers", 87 | "description" : "${role_manage-identity-providers}", 88 | "scopeParamRequired" : false, 89 | "composite" : false 90 | }, { 91 | "id" : "b392a539-7412-4719-add7-fed829668220", 92 | "name" : "view-realm", 93 | "description" : "${role_view-realm}", 94 | "scopeParamRequired" : false, 95 | "composite" : false 96 | }, { 97 | "id" : "e5a59d7b-eac5-49d2-b768-2fb7192e328e", 98 | "name" : "view-identity-providers", 99 | "description" : "${role_view-identity-providers}", 100 | "scopeParamRequired" : false, 101 | "composite" : false 102 | }, { 103 | "id" : "cee1733d-18f5-4506-9d17-1e768b393f3c", 104 | "name" : "manage-users", 105 | "description" : "${role_manage-users}", 106 | "scopeParamRequired" : false, 107 | "composite" : false 108 | }, { 109 | "id" : "6dc60c5a-8822-4442-934c-7e9f5db92a67", 110 | "name" : "realm-admin", 111 | "description" : "${role_realm-admin}", 112 | "scopeParamRequired" : false, 113 | "composite" : true, 114 | "composites" : { 115 | "client" : { 116 | "realm-management" : [ "view-users", "create-client", "view-events", "impersonation", "manage-clients", "manage-events", "manage-identity-providers", "view-realm", "view-clients", "view-identity-providers", "manage-realm", "manage-users" ] 117 | } 118 | } 119 | }, { 120 | "id" : "52e781f7-a943-47f9-a60c-e1b79076e91d", 121 | "name" : "manage-events", 122 | "description" : "${role_manage-events}", 123 | "scopeParamRequired" : false, 124 | "composite" : false 125 | }, { 126 | "id" : "bf938e74-bfdf-432c-8733-456e5f86cab2", 127 | "name" : "view-clients", 128 | "description" : "${role_view-clients}", 129 | "scopeParamRequired" : false, 130 | "composite" : false 131 | }, { 132 | "id" : "445353d6-62d9-453e-b1b2-4e140c77cbce", 133 | "name" : "manage-realm", 134 | "description" : "${role_manage-realm}", 135 | "scopeParamRequired" : false, 136 | "composite" : false 137 | } ], 138 | "security-admin-console" : [ ], 139 | "tutorial-frontend" : [ ], 140 | "tutorial-backend" : [ ], 141 | "admin-cli" : [ ], 142 | "broker" : [ { 143 | "id" : "006a5eed-67b3-4872-86c8-139a7447fdc1", 144 | "name" : "read-token", 145 | "description" : "${role_read-token}", 146 | "scopeParamRequired" : false, 147 | "composite" : false 148 | } ], 149 | "account" : [ { 150 | "id" : "647a4866-8e2f-453b-ba06-c2e83717919e", 151 | "name" : "view-profile", 152 | "description" : "${role_view-profile}", 153 | "scopeParamRequired" : false, 154 | "composite" : false 155 | }, { 156 | "id" : "0e542b15-ffaf-4c5c-aca2-9ff45ef9f5c7", 157 | "name" : "manage-account", 158 | "description" : "${role_manage-account}", 159 | "scopeParamRequired" : false, 160 | "composite" : false 161 | } ] 162 | } 163 | }, 164 | "groups" : [ ], 165 | "defaultRoles" : [ "offline_access" ], 166 | "requiredCredentials" : [ "password" ], 167 | "otpPolicyType" : "totp", 168 | "otpPolicyAlgorithm" : "HmacSHA1", 169 | "otpPolicyInitialCounter" : 0, 170 | "otpPolicyDigits" : 6, 171 | "otpPolicyLookAheadWindow" : 1, 172 | "otpPolicyPeriod" : 30, 173 | "users" : [ { 174 | "id" : "70d61083-1c38-4452-8dc6-4331727e962c", 175 | "createdTimestamp" : 1458022938861, 176 | "username" : "admin", 177 | "enabled" : true, 178 | "totp" : false, 179 | "emailVerified" : true, 180 | "firstName" : "Demo", 181 | "lastName" : "Admin", 182 | "email" : "admin@test.com", 183 | "credentials" : [ { 184 | "type" : "password", 185 | "hashedSaltedValue" : "mPk1tRhBX+UEqy+dHqE335jieI4TNALjMP207dl8UpFu285zqUqkj//cIjGUrt+Ct1octyN0yMFTgdzR/QLOpA==", 186 | "salt" : "Kx9bYgVhO/uNK/RXiAfyXA==", 187 | "hashIterations" : 1, 188 | "counter" : 0, 189 | "algorithm" : "pbkdf2", 190 | "digits" : 0, 191 | "createdDate" : 1458022947000 192 | } ], 193 | "requiredActions" : [ ], 194 | "realmRoles" : [ "admin" ], 195 | "clientRoles" : { 196 | "account" : [ "view-profile", "manage-account" ] 197 | }, 198 | "groups" : [ ] 199 | }, { 200 | "id" : "ae208b36-e8f6-4e5a-88a3-de89b38a72c7", 201 | "createdTimestamp" : 1458035651958, 202 | "username" : "flash", 203 | "enabled" : true, 204 | "totp" : false, 205 | "emailVerified" : false, 206 | "firstName" : "Christian", 207 | "lastName" : "Ternes", 208 | "email" : "fu@you.com", 209 | "credentials" : [ { 210 | "type" : "password", 211 | "hashedSaltedValue" : "fFbMeK0aNC621SrbP/MqkG1LukOZub0486pWS7EiiI3IMBHJWtuT54b9CU3LjkKOrekiWBbwjgDYUaCaxi/E0g==", 212 | "salt" : "LI/n39LBnGnJvNhY2ogOmw==", 213 | "hashIterations" : 1, 214 | "counter" : 0, 215 | "algorithm" : "pbkdf2", 216 | "digits" : 0, 217 | "createdDate" : 1458035651000 218 | } ], 219 | "requiredActions" : [ ], 220 | "realmRoles" : [ "offline_access" ], 221 | "clientRoles" : { 222 | "account" : [ "view-profile", "manage-account" ] 223 | }, 224 | "groups" : [ ] 225 | }, { 226 | "id" : "15bbfc96-4d14-415d-947c-6ecf3d5d4ce0", 227 | "createdTimestamp" : 1458022750667, 228 | "username" : "manager", 229 | "enabled" : true, 230 | "totp" : false, 231 | "emailVerified" : true, 232 | "firstName" : "Demo", 233 | "lastName" : "Manager", 234 | "email" : "manager@test.com", 235 | "credentials" : [ { 236 | "type" : "password", 237 | "hashedSaltedValue" : "nN2f51lSHkPJAtD5nV7EWQ02icSjTm7PwYvJq82YndK1ZMZfRjAsaMqVm+F0Kbi2s5uBJ8cdw8NhbixLWTQhuA==", 238 | "salt" : "RI+rwSRf5gUVlCT10kR56Q==", 239 | "hashIterations" : 1, 240 | "counter" : 0, 241 | "algorithm" : "pbkdf2", 242 | "digits" : 0, 243 | "createdDate" : 1458022822000 244 | } ], 245 | "requiredActions" : [ ], 246 | "realmRoles" : [ "manager" ], 247 | "clientRoles" : { 248 | "account" : [ "view-profile", "manage-account" ] 249 | }, 250 | "groups" : [ ] 251 | } ], 252 | "clientScopeMappings" : { 253 | "realm-management" : [ { 254 | "client" : "admin-cli", 255 | "roles" : [ "realm-admin" ] 256 | }, { 257 | "client" : "security-admin-console", 258 | "roles" : [ "realm-admin" ] 259 | } ] 260 | }, 261 | "clients" : [ { 262 | "id" : "2e585fe5-c78e-4c31-aa89-8261cc052670", 263 | "clientId" : "account", 264 | "name" : "${client_account}", 265 | "baseUrl" : "/auth/realms/Demo-Realm/account", 266 | "surrogateAuthRequired" : false, 267 | "enabled" : true, 268 | "clientAuthenticatorType" : "client-secret", 269 | "secret" : "4a714b70-a70e-4034-b277-58220ae9fe1d", 270 | "defaultRoles" : [ "view-profile", "manage-account" ], 271 | "redirectUris" : [ "/auth/realms/Demo-Realm/account/*" ], 272 | "webOrigins" : [ ], 273 | "notBefore" : 0, 274 | "bearerOnly" : false, 275 | "consentRequired" : false, 276 | "standardFlowEnabled" : true, 277 | "implicitFlowEnabled" : false, 278 | "directAccessGrantsEnabled" : false, 279 | "serviceAccountsEnabled" : false, 280 | "publicClient" : false, 281 | "frontchannelLogout" : false, 282 | "attributes" : { }, 283 | "fullScopeAllowed" : false, 284 | "nodeReRegistrationTimeout" : 0, 285 | "protocolMappers" : [ { 286 | "id" : "cf38642b-f5c4-4f77-8331-51a7bc9d4548", 287 | "name" : "full name", 288 | "protocol" : "openid-connect", 289 | "protocolMapper" : "oidc-full-name-mapper", 290 | "consentRequired" : true, 291 | "consentText" : "${fullName}", 292 | "config" : { 293 | "id.token.claim" : "true", 294 | "access.token.claim" : "true" 295 | } 296 | }, { 297 | "id" : "e7e1a235-9653-4327-804a-4e97a338418d", 298 | "name" : "family name", 299 | "protocol" : "openid-connect", 300 | "protocolMapper" : "oidc-usermodel-property-mapper", 301 | "consentRequired" : true, 302 | "consentText" : "${familyName}", 303 | "config" : { 304 | "user.attribute" : "lastName", 305 | "id.token.claim" : "true", 306 | "access.token.claim" : "true", 307 | "claim.name" : "family_name", 308 | "jsonType.label" : "String" 309 | } 310 | }, { 311 | "id" : "1097009c-b38b-4aaa-ad87-2cbcf1aa2cfe", 312 | "name" : "role list", 313 | "protocol" : "saml", 314 | "protocolMapper" : "saml-role-list-mapper", 315 | "consentRequired" : false, 316 | "config" : { 317 | "single" : "false", 318 | "attribute.nameformat" : "Basic", 319 | "attribute.name" : "Role" 320 | } 321 | }, { 322 | "id" : "38dc44c0-7b01-4c54-99cb-5579dca1c885", 323 | "name" : "given name", 324 | "protocol" : "openid-connect", 325 | "protocolMapper" : "oidc-usermodel-property-mapper", 326 | "consentRequired" : true, 327 | "consentText" : "${givenName}", 328 | "config" : { 329 | "user.attribute" : "firstName", 330 | "id.token.claim" : "true", 331 | "access.token.claim" : "true", 332 | "claim.name" : "given_name", 333 | "jsonType.label" : "String" 334 | } 335 | }, { 336 | "id" : "339d24b4-a0a0-47b8-b9ed-32fbc42c4767", 337 | "name" : "email", 338 | "protocol" : "openid-connect", 339 | "protocolMapper" : "oidc-usermodel-property-mapper", 340 | "consentRequired" : true, 341 | "consentText" : "${email}", 342 | "config" : { 343 | "user.attribute" : "email", 344 | "id.token.claim" : "true", 345 | "access.token.claim" : "true", 346 | "claim.name" : "email", 347 | "jsonType.label" : "String" 348 | } 349 | }, { 350 | "id" : "8b878ba0-af6a-4587-b08b-d5c3ba62be7a", 351 | "name" : "username", 352 | "protocol" : "openid-connect", 353 | "protocolMapper" : "oidc-usermodel-property-mapper", 354 | "consentRequired" : true, 355 | "consentText" : "${username}", 356 | "config" : { 357 | "user.attribute" : "username", 358 | "id.token.claim" : "true", 359 | "access.token.claim" : "true", 360 | "claim.name" : "preferred_username", 361 | "jsonType.label" : "String" 362 | } 363 | } ], 364 | "useTemplateConfig" : false, 365 | "useTemplateScope" : false, 366 | "useTemplateMappers" : false 367 | }, { 368 | "id" : "4f2d1d4f-2093-4e01-97ec-eaa6b809c05f", 369 | "clientId" : "admin-cli", 370 | "name" : "${client_admin-cli}", 371 | "surrogateAuthRequired" : false, 372 | "enabled" : true, 373 | "clientAuthenticatorType" : "client-secret", 374 | "secret" : "acac87e7-ba9c-411f-b81b-52c823f3054c", 375 | "redirectUris" : [ ], 376 | "webOrigins" : [ ], 377 | "notBefore" : 0, 378 | "bearerOnly" : false, 379 | "consentRequired" : false, 380 | "standardFlowEnabled" : false, 381 | "implicitFlowEnabled" : false, 382 | "directAccessGrantsEnabled" : true, 383 | "serviceAccountsEnabled" : false, 384 | "publicClient" : true, 385 | "frontchannelLogout" : false, 386 | "attributes" : { }, 387 | "fullScopeAllowed" : false, 388 | "nodeReRegistrationTimeout" : 0, 389 | "protocolMappers" : [ { 390 | "id" : "46754b22-f0cb-48b7-9d3e-cc2c83eb68ee", 391 | "name" : "role list", 392 | "protocol" : "saml", 393 | "protocolMapper" : "saml-role-list-mapper", 394 | "consentRequired" : false, 395 | "config" : { 396 | "single" : "false", 397 | "attribute.nameformat" : "Basic", 398 | "attribute.name" : "Role" 399 | } 400 | }, { 401 | "id" : "a9d20255-ca37-44ad-b5fb-99a55524c954", 402 | "name" : "family name", 403 | "protocol" : "openid-connect", 404 | "protocolMapper" : "oidc-usermodel-property-mapper", 405 | "consentRequired" : true, 406 | "consentText" : "${familyName}", 407 | "config" : { 408 | "user.attribute" : "lastName", 409 | "id.token.claim" : "true", 410 | "access.token.claim" : "true", 411 | "claim.name" : "family_name", 412 | "jsonType.label" : "String" 413 | } 414 | }, { 415 | "id" : "6629701d-bafc-42e3-b87d-7046a8c50376", 416 | "name" : "full name", 417 | "protocol" : "openid-connect", 418 | "protocolMapper" : "oidc-full-name-mapper", 419 | "consentRequired" : true, 420 | "consentText" : "${fullName}", 421 | "config" : { 422 | "id.token.claim" : "true", 423 | "access.token.claim" : "true" 424 | } 425 | }, { 426 | "id" : "131752d5-a79b-4838-afc4-8248652dfc54", 427 | "name" : "given name", 428 | "protocol" : "openid-connect", 429 | "protocolMapper" : "oidc-usermodel-property-mapper", 430 | "consentRequired" : true, 431 | "consentText" : "${givenName}", 432 | "config" : { 433 | "user.attribute" : "firstName", 434 | "id.token.claim" : "true", 435 | "access.token.claim" : "true", 436 | "claim.name" : "given_name", 437 | "jsonType.label" : "String" 438 | } 439 | }, { 440 | "id" : "ad431aa1-8c52-422d-99df-d823485a3d2c", 441 | "name" : "email", 442 | "protocol" : "openid-connect", 443 | "protocolMapper" : "oidc-usermodel-property-mapper", 444 | "consentRequired" : true, 445 | "consentText" : "${email}", 446 | "config" : { 447 | "user.attribute" : "email", 448 | "id.token.claim" : "true", 449 | "access.token.claim" : "true", 450 | "claim.name" : "email", 451 | "jsonType.label" : "String" 452 | } 453 | }, { 454 | "id" : "888b7631-befd-4dbf-94ba-453ae7eca705", 455 | "name" : "username", 456 | "protocol" : "openid-connect", 457 | "protocolMapper" : "oidc-usermodel-property-mapper", 458 | "consentRequired" : true, 459 | "consentText" : "${username}", 460 | "config" : { 461 | "user.attribute" : "username", 462 | "id.token.claim" : "true", 463 | "access.token.claim" : "true", 464 | "claim.name" : "preferred_username", 465 | "jsonType.label" : "String" 466 | } 467 | } ], 468 | "useTemplateConfig" : false, 469 | "useTemplateScope" : false, 470 | "useTemplateMappers" : false 471 | }, { 472 | "id" : "5764b22c-4ac3-4aa5-a490-2654208ab712", 473 | "clientId" : "broker", 474 | "name" : "${client_broker}", 475 | "surrogateAuthRequired" : false, 476 | "enabled" : true, 477 | "clientAuthenticatorType" : "client-secret", 478 | "secret" : "e9940f28-f206-407e-bea6-0bf598b8d693", 479 | "redirectUris" : [ ], 480 | "webOrigins" : [ ], 481 | "notBefore" : 0, 482 | "bearerOnly" : false, 483 | "consentRequired" : false, 484 | "standardFlowEnabled" : true, 485 | "implicitFlowEnabled" : false, 486 | "directAccessGrantsEnabled" : false, 487 | "serviceAccountsEnabled" : false, 488 | "publicClient" : false, 489 | "frontchannelLogout" : false, 490 | "attributes" : { }, 491 | "fullScopeAllowed" : false, 492 | "nodeReRegistrationTimeout" : 0, 493 | "protocolMappers" : [ { 494 | "id" : "d77a3cb8-fc62-4085-ae41-8c8434a3e7de", 495 | "name" : "full name", 496 | "protocol" : "openid-connect", 497 | "protocolMapper" : "oidc-full-name-mapper", 498 | "consentRequired" : true, 499 | "consentText" : "${fullName}", 500 | "config" : { 501 | "id.token.claim" : "true", 502 | "access.token.claim" : "true" 503 | } 504 | }, { 505 | "id" : "95028eb1-0376-432e-8480-561ab9e1f04a", 506 | "name" : "role list", 507 | "protocol" : "saml", 508 | "protocolMapper" : "saml-role-list-mapper", 509 | "consentRequired" : false, 510 | "config" : { 511 | "single" : "false", 512 | "attribute.nameformat" : "Basic", 513 | "attribute.name" : "Role" 514 | } 515 | }, { 516 | "id" : "1a3303a7-204f-40dc-94a5-f86e2c13663c", 517 | "name" : "username", 518 | "protocol" : "openid-connect", 519 | "protocolMapper" : "oidc-usermodel-property-mapper", 520 | "consentRequired" : true, 521 | "consentText" : "${username}", 522 | "config" : { 523 | "user.attribute" : "username", 524 | "id.token.claim" : "true", 525 | "access.token.claim" : "true", 526 | "claim.name" : "preferred_username", 527 | "jsonType.label" : "String" 528 | } 529 | }, { 530 | "id" : "fa6c419b-9226-4112-be85-22fd6831de3b", 531 | "name" : "family name", 532 | "protocol" : "openid-connect", 533 | "protocolMapper" : "oidc-usermodel-property-mapper", 534 | "consentRequired" : true, 535 | "consentText" : "${familyName}", 536 | "config" : { 537 | "user.attribute" : "lastName", 538 | "id.token.claim" : "true", 539 | "access.token.claim" : "true", 540 | "claim.name" : "family_name", 541 | "jsonType.label" : "String" 542 | } 543 | }, { 544 | "id" : "dd73f369-05a9-4b44-b90e-503337cbf2b5", 545 | "name" : "given name", 546 | "protocol" : "openid-connect", 547 | "protocolMapper" : "oidc-usermodel-property-mapper", 548 | "consentRequired" : true, 549 | "consentText" : "${givenName}", 550 | "config" : { 551 | "user.attribute" : "firstName", 552 | "id.token.claim" : "true", 553 | "access.token.claim" : "true", 554 | "claim.name" : "given_name", 555 | "jsonType.label" : "String" 556 | } 557 | }, { 558 | "id" : "936533a8-8cbd-4ed0-b011-f7e8db3c7f4a", 559 | "name" : "email", 560 | "protocol" : "openid-connect", 561 | "protocolMapper" : "oidc-usermodel-property-mapper", 562 | "consentRequired" : true, 563 | "consentText" : "${email}", 564 | "config" : { 565 | "user.attribute" : "email", 566 | "id.token.claim" : "true", 567 | "access.token.claim" : "true", 568 | "claim.name" : "email", 569 | "jsonType.label" : "String" 570 | } 571 | } ], 572 | "useTemplateConfig" : false, 573 | "useTemplateScope" : false, 574 | "useTemplateMappers" : false 575 | }, { 576 | "id" : "ab83748f-91a6-48f5-a512-e70584598358", 577 | "clientId" : "realm-management", 578 | "name" : "${client_realm-management}", 579 | "surrogateAuthRequired" : false, 580 | "enabled" : true, 581 | "clientAuthenticatorType" : "client-secret", 582 | "secret" : "4aecf645-c218-4b0a-9e41-983849406444", 583 | "redirectUris" : [ ], 584 | "webOrigins" : [ ], 585 | "notBefore" : 0, 586 | "bearerOnly" : true, 587 | "consentRequired" : false, 588 | "standardFlowEnabled" : true, 589 | "implicitFlowEnabled" : false, 590 | "directAccessGrantsEnabled" : false, 591 | "serviceAccountsEnabled" : false, 592 | "publicClient" : false, 593 | "frontchannelLogout" : false, 594 | "attributes" : { }, 595 | "fullScopeAllowed" : false, 596 | "nodeReRegistrationTimeout" : 0, 597 | "protocolMappers" : [ { 598 | "id" : "5fb10988-92c5-497a-b7e3-255b27aa4cfe", 599 | "name" : "username", 600 | "protocol" : "openid-connect", 601 | "protocolMapper" : "oidc-usermodel-property-mapper", 602 | "consentRequired" : true, 603 | "consentText" : "${username}", 604 | "config" : { 605 | "user.attribute" : "username", 606 | "id.token.claim" : "true", 607 | "access.token.claim" : "true", 608 | "claim.name" : "preferred_username", 609 | "jsonType.label" : "String" 610 | } 611 | }, { 612 | "id" : "1a471858-db64-49f2-a182-b589ebef7384", 613 | "name" : "given name", 614 | "protocol" : "openid-connect", 615 | "protocolMapper" : "oidc-usermodel-property-mapper", 616 | "consentRequired" : true, 617 | "consentText" : "${givenName}", 618 | "config" : { 619 | "user.attribute" : "firstName", 620 | "id.token.claim" : "true", 621 | "access.token.claim" : "true", 622 | "claim.name" : "given_name", 623 | "jsonType.label" : "String" 624 | } 625 | }, { 626 | "id" : "05470906-6dae-4478-8708-d65ad77ec4a9", 627 | "name" : "full name", 628 | "protocol" : "openid-connect", 629 | "protocolMapper" : "oidc-full-name-mapper", 630 | "consentRequired" : true, 631 | "consentText" : "${fullName}", 632 | "config" : { 633 | "id.token.claim" : "true", 634 | "access.token.claim" : "true" 635 | } 636 | }, { 637 | "id" : "beff6109-6404-4fce-a7d1-c5f54d4fa255", 638 | "name" : "role list", 639 | "protocol" : "saml", 640 | "protocolMapper" : "saml-role-list-mapper", 641 | "consentRequired" : false, 642 | "config" : { 643 | "single" : "false", 644 | "attribute.nameformat" : "Basic", 645 | "attribute.name" : "Role" 646 | } 647 | }, { 648 | "id" : "a84402bd-eff0-4371-9935-3c70cbb0d420", 649 | "name" : "email", 650 | "protocol" : "openid-connect", 651 | "protocolMapper" : "oidc-usermodel-property-mapper", 652 | "consentRequired" : true, 653 | "consentText" : "${email}", 654 | "config" : { 655 | "user.attribute" : "email", 656 | "id.token.claim" : "true", 657 | "access.token.claim" : "true", 658 | "claim.name" : "email", 659 | "jsonType.label" : "String" 660 | } 661 | }, { 662 | "id" : "2ef1de5a-b10f-4fc7-a479-573d2a0e7da7", 663 | "name" : "family name", 664 | "protocol" : "openid-connect", 665 | "protocolMapper" : "oidc-usermodel-property-mapper", 666 | "consentRequired" : true, 667 | "consentText" : "${familyName}", 668 | "config" : { 669 | "user.attribute" : "lastName", 670 | "id.token.claim" : "true", 671 | "access.token.claim" : "true", 672 | "claim.name" : "family_name", 673 | "jsonType.label" : "String" 674 | } 675 | } ], 676 | "useTemplateConfig" : false, 677 | "useTemplateScope" : false, 678 | "useTemplateMappers" : false 679 | }, { 680 | "id" : "8ccbc483-a610-4808-8fa3-fbdf5a6cbb55", 681 | "clientId" : "security-admin-console", 682 | "name" : "${client_security-admin-console}", 683 | "baseUrl" : "/auth/admin/Demo-Realm/console/index.html", 684 | "surrogateAuthRequired" : false, 685 | "enabled" : true, 686 | "clientAuthenticatorType" : "client-secret", 687 | "secret" : "f03ea485-2e70-499c-af59-2f339c74f094", 688 | "redirectUris" : [ "/auth/admin/Demo-Realm/console/*" ], 689 | "webOrigins" : [ ], 690 | "notBefore" : 0, 691 | "bearerOnly" : false, 692 | "consentRequired" : false, 693 | "standardFlowEnabled" : true, 694 | "implicitFlowEnabled" : false, 695 | "directAccessGrantsEnabled" : false, 696 | "serviceAccountsEnabled" : false, 697 | "publicClient" : true, 698 | "frontchannelLogout" : false, 699 | "attributes" : { }, 700 | "fullScopeAllowed" : false, 701 | "nodeReRegistrationTimeout" : 0, 702 | "protocolMappers" : [ { 703 | "id" : "6c50b719-f894-425d-81b2-5d35a9356e87", 704 | "name" : "locale", 705 | "protocol" : "openid-connect", 706 | "protocolMapper" : "oidc-usermodel-attribute-mapper", 707 | "consentRequired" : false, 708 | "consentText" : "${locale}", 709 | "config" : { 710 | "user.attribute" : "locale", 711 | "id.token.claim" : "true", 712 | "access.token.claim" : "true", 713 | "claim.name" : "locale", 714 | "jsonType.label" : "String" 715 | } 716 | }, { 717 | "id" : "a608a000-66a9-499c-b3d8-65aa898bbc6a", 718 | "name" : "given name", 719 | "protocol" : "openid-connect", 720 | "protocolMapper" : "oidc-usermodel-property-mapper", 721 | "consentRequired" : true, 722 | "consentText" : "${givenName}", 723 | "config" : { 724 | "user.attribute" : "firstName", 725 | "id.token.claim" : "true", 726 | "access.token.claim" : "true", 727 | "claim.name" : "given_name", 728 | "jsonType.label" : "String" 729 | } 730 | }, { 731 | "id" : "4203188e-98a7-48c0-94d6-48791b6d290a", 732 | "name" : "full name", 733 | "protocol" : "openid-connect", 734 | "protocolMapper" : "oidc-full-name-mapper", 735 | "consentRequired" : true, 736 | "consentText" : "${fullName}", 737 | "config" : { 738 | "id.token.claim" : "true", 739 | "access.token.claim" : "true" 740 | } 741 | }, { 742 | "id" : "f88bf021-c176-4f75-9729-4dac0b445e8d", 743 | "name" : "role list", 744 | "protocol" : "saml", 745 | "protocolMapper" : "saml-role-list-mapper", 746 | "consentRequired" : false, 747 | "config" : { 748 | "single" : "false", 749 | "attribute.nameformat" : "Basic", 750 | "attribute.name" : "Role" 751 | } 752 | }, { 753 | "id" : "c5dd16b8-8fcd-4620-9c69-bedabbf54309", 754 | "name" : "email", 755 | "protocol" : "openid-connect", 756 | "protocolMapper" : "oidc-usermodel-property-mapper", 757 | "consentRequired" : true, 758 | "consentText" : "${email}", 759 | "config" : { 760 | "user.attribute" : "email", 761 | "id.token.claim" : "true", 762 | "access.token.claim" : "true", 763 | "claim.name" : "email", 764 | "jsonType.label" : "String" 765 | } 766 | }, { 767 | "id" : "2cd0fb1a-7db7-47dd-8aab-d3d38045b277", 768 | "name" : "username", 769 | "protocol" : "openid-connect", 770 | "protocolMapper" : "oidc-usermodel-property-mapper", 771 | "consentRequired" : true, 772 | "consentText" : "${username}", 773 | "config" : { 774 | "user.attribute" : "username", 775 | "id.token.claim" : "true", 776 | "access.token.claim" : "true", 777 | "claim.name" : "preferred_username", 778 | "jsonType.label" : "String" 779 | } 780 | }, { 781 | "id" : "60c10fef-1560-4a77-a829-267b1f028f90", 782 | "name" : "family name", 783 | "protocol" : "openid-connect", 784 | "protocolMapper" : "oidc-usermodel-property-mapper", 785 | "consentRequired" : true, 786 | "consentText" : "${familyName}", 787 | "config" : { 788 | "user.attribute" : "lastName", 789 | "id.token.claim" : "true", 790 | "access.token.claim" : "true", 791 | "claim.name" : "family_name", 792 | "jsonType.label" : "String" 793 | } 794 | } ], 795 | "useTemplateConfig" : false, 796 | "useTemplateScope" : false, 797 | "useTemplateMappers" : false 798 | }, { 799 | "id" : "e64771af-cc67-48bf-a075-c364f8499684", 800 | "clientId" : "tutorial-backend", 801 | "surrogateAuthRequired" : false, 802 | "enabled" : true, 803 | "clientAuthenticatorType" : "client-secret", 804 | "secret" : "923fd957-d761-4cee-a9ed-1be6c791a96c", 805 | "redirectUris" : [ ], 806 | "webOrigins" : [ ], 807 | "notBefore" : 0, 808 | "bearerOnly" : true, 809 | "consentRequired" : false, 810 | "standardFlowEnabled" : true, 811 | "implicitFlowEnabled" : false, 812 | "directAccessGrantsEnabled" : true, 813 | "serviceAccountsEnabled" : false, 814 | "publicClient" : false, 815 | "frontchannelLogout" : false, 816 | "protocol" : "openid-connect", 817 | "attributes" : { 818 | "saml.assertion.signature" : "false", 819 | "saml.force.post.binding" : "false", 820 | "saml.multivalued.roles" : "false", 821 | "saml.encrypt" : "false", 822 | "saml_force_name_id_format" : "false", 823 | "saml.client.signature" : "false", 824 | "saml.authnstatement" : "false", 825 | "saml.server.signature" : "false" 826 | }, 827 | "fullScopeAllowed" : true, 828 | "nodeReRegistrationTimeout" : -1, 829 | "protocolMappers" : [ { 830 | "id" : "528a3c1c-dc09-4067-abc5-5e2aca18823a", 831 | "name" : "full name", 832 | "protocol" : "openid-connect", 833 | "protocolMapper" : "oidc-full-name-mapper", 834 | "consentRequired" : true, 835 | "consentText" : "${fullName}", 836 | "config" : { 837 | "id.token.claim" : "true", 838 | "access.token.claim" : "true" 839 | } 840 | }, { 841 | "id" : "743912ea-8934-4e6c-a805-ddfb8327da46", 842 | "name" : "role list", 843 | "protocol" : "saml", 844 | "protocolMapper" : "saml-role-list-mapper", 845 | "consentRequired" : false, 846 | "config" : { 847 | "single" : "false", 848 | "attribute.nameformat" : "Basic", 849 | "attribute.name" : "Role" 850 | } 851 | }, { 852 | "id" : "f2ebb2bc-d5a6-426b-bf05-b57a5da5d3d4", 853 | "name" : "email", 854 | "protocol" : "openid-connect", 855 | "protocolMapper" : "oidc-usermodel-property-mapper", 856 | "consentRequired" : true, 857 | "consentText" : "${email}", 858 | "config" : { 859 | "user.attribute" : "email", 860 | "id.token.claim" : "true", 861 | "access.token.claim" : "true", 862 | "claim.name" : "email", 863 | "jsonType.label" : "String" 864 | } 865 | }, { 866 | "id" : "aaf4a2cd-6375-4248-b7b5-22690900aa3e", 867 | "name" : "family name", 868 | "protocol" : "openid-connect", 869 | "protocolMapper" : "oidc-usermodel-property-mapper", 870 | "consentRequired" : true, 871 | "consentText" : "${familyName}", 872 | "config" : { 873 | "user.attribute" : "lastName", 874 | "id.token.claim" : "true", 875 | "access.token.claim" : "true", 876 | "claim.name" : "family_name", 877 | "jsonType.label" : "String" 878 | } 879 | }, { 880 | "id" : "f089a66c-9fb5-4ec1-a11f-c75d258f4afa", 881 | "name" : "given name", 882 | "protocol" : "openid-connect", 883 | "protocolMapper" : "oidc-usermodel-property-mapper", 884 | "consentRequired" : true, 885 | "consentText" : "${givenName}", 886 | "config" : { 887 | "user.attribute" : "firstName", 888 | "id.token.claim" : "true", 889 | "access.token.claim" : "true", 890 | "claim.name" : "given_name", 891 | "jsonType.label" : "String" 892 | } 893 | }, { 894 | "id" : "3069943a-adc0-4afc-89ed-1c62fdf4f05a", 895 | "name" : "username", 896 | "protocol" : "openid-connect", 897 | "protocolMapper" : "oidc-usermodel-property-mapper", 898 | "consentRequired" : true, 899 | "consentText" : "${username}", 900 | "config" : { 901 | "user.attribute" : "username", 902 | "id.token.claim" : "true", 903 | "access.token.claim" : "true", 904 | "claim.name" : "preferred_username", 905 | "jsonType.label" : "String" 906 | } 907 | } ], 908 | "useTemplateConfig" : false, 909 | "useTemplateScope" : false, 910 | "useTemplateMappers" : false 911 | }, { 912 | "id" : "a1a248ec-b2c2-4663-9430-6a0ccdc6e8cc", 913 | "clientId" : "tutorial-frontend", 914 | "baseUrl" : "http://localhost:8000/index.html", 915 | "surrogateAuthRequired" : false, 916 | "enabled" : true, 917 | "clientAuthenticatorType" : "client-secret", 918 | "secret" : "7ad446c9-5aa2-43da-a751-eca95923724f", 919 | "redirectUris" : [ "http://localhost:8000/*" ], 920 | "webOrigins" : [ "http://localhost:8000" ], 921 | "notBefore" : 0, 922 | "bearerOnly" : false, 923 | "consentRequired" : false, 924 | "standardFlowEnabled" : true, 925 | "implicitFlowEnabled" : false, 926 | "directAccessGrantsEnabled" : true, 927 | "serviceAccountsEnabled" : false, 928 | "publicClient" : true, 929 | "frontchannelLogout" : false, 930 | "protocol" : "openid-connect", 931 | "attributes" : { 932 | "saml.assertion.signature" : "false", 933 | "saml.force.post.binding" : "false", 934 | "saml.multivalued.roles" : "false", 935 | "saml.encrypt" : "false", 936 | "saml_force_name_id_format" : "false", 937 | "saml.client.signature" : "false", 938 | "saml.authnstatement" : "false", 939 | "saml.server.signature" : "false" 940 | }, 941 | "fullScopeAllowed" : true, 942 | "nodeReRegistrationTimeout" : -1, 943 | "protocolMappers" : [ { 944 | "id" : "9bcd6d3f-9707-4219-a248-269e58e972f2", 945 | "name" : "email", 946 | "protocol" : "openid-connect", 947 | "protocolMapper" : "oidc-usermodel-property-mapper", 948 | "consentRequired" : true, 949 | "consentText" : "${email}", 950 | "config" : { 951 | "user.attribute" : "email", 952 | "id.token.claim" : "true", 953 | "access.token.claim" : "true", 954 | "claim.name" : "email", 955 | "jsonType.label" : "String" 956 | } 957 | }, { 958 | "id" : "2fa6664f-1f0f-40b2-9a59-e106f7eef8d9", 959 | "name" : "role list", 960 | "protocol" : "saml", 961 | "protocolMapper" : "saml-role-list-mapper", 962 | "consentRequired" : false, 963 | "config" : { 964 | "single" : "false", 965 | "attribute.nameformat" : "Basic", 966 | "attribute.name" : "Role" 967 | } 968 | }, { 969 | "id" : "38021b22-34bf-49c4-9346-be90b12fdbe0", 970 | "name" : "family name", 971 | "protocol" : "openid-connect", 972 | "protocolMapper" : "oidc-usermodel-property-mapper", 973 | "consentRequired" : true, 974 | "consentText" : "${familyName}", 975 | "config" : { 976 | "user.attribute" : "lastName", 977 | "id.token.claim" : "true", 978 | "access.token.claim" : "true", 979 | "claim.name" : "family_name", 980 | "jsonType.label" : "String" 981 | } 982 | }, { 983 | "id" : "51ea3ea5-903b-42f0-8612-4a7f5a69e714", 984 | "name" : "username", 985 | "protocol" : "openid-connect", 986 | "protocolMapper" : "oidc-usermodel-property-mapper", 987 | "consentRequired" : true, 988 | "consentText" : "${username}", 989 | "config" : { 990 | "user.attribute" : "username", 991 | "id.token.claim" : "true", 992 | "access.token.claim" : "true", 993 | "claim.name" : "preferred_username", 994 | "jsonType.label" : "String" 995 | } 996 | }, { 997 | "id" : "05b36b5b-7270-49ae-8b61-51306862b4b1", 998 | "name" : "full name", 999 | "protocol" : "openid-connect", 1000 | "protocolMapper" : "oidc-full-name-mapper", 1001 | "consentRequired" : true, 1002 | "consentText" : "${fullName}", 1003 | "config" : { 1004 | "id.token.claim" : "true", 1005 | "access.token.claim" : "true" 1006 | } 1007 | }, { 1008 | "id" : "f502cb31-c7c7-4b7d-9207-6c12f031207c", 1009 | "name" : "given name", 1010 | "protocol" : "openid-connect", 1011 | "protocolMapper" : "oidc-usermodel-property-mapper", 1012 | "consentRequired" : true, 1013 | "consentText" : "${givenName}", 1014 | "config" : { 1015 | "user.attribute" : "firstName", 1016 | "id.token.claim" : "true", 1017 | "access.token.claim" : "true", 1018 | "claim.name" : "given_name", 1019 | "jsonType.label" : "String" 1020 | } 1021 | } ], 1022 | "useTemplateConfig" : false, 1023 | "useTemplateScope" : false, 1024 | "useTemplateMappers" : false 1025 | } ], 1026 | "clientTemplates" : [ ], 1027 | "browserSecurityHeaders" : { 1028 | "xFrameOptions" : "SAMEORIGIN", 1029 | "contentSecurityPolicy" : "frame-src 'self'" 1030 | }, 1031 | "smtpServer" : { }, 1032 | "eventsEnabled" : false, 1033 | "eventsListeners" : [ "jboss-logging" ], 1034 | "enabledEventTypes" : [ ], 1035 | "adminEventsEnabled" : false, 1036 | "adminEventsDetailsEnabled" : false, 1037 | "internationalizationEnabled" : false, 1038 | "supportedLocales" : [ ], 1039 | "authenticationFlows" : [ { 1040 | "id" : "75f8a89d-e6bf-415c-9e18-2d9b437874ae", 1041 | "alias" : "Handle Existing Account", 1042 | "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", 1043 | "providerId" : "basic-flow", 1044 | "topLevel" : false, 1045 | "builtIn" : true, 1046 | "authenticationExecutions" : [ { 1047 | "authenticator" : "idp-confirm-link", 1048 | "requirement" : "REQUIRED", 1049 | "priority" : 10, 1050 | "userSetupAllowed" : false, 1051 | "autheticatorFlow" : false 1052 | }, { 1053 | "authenticator" : "idp-email-verification", 1054 | "requirement" : "ALTERNATIVE", 1055 | "priority" : 20, 1056 | "userSetupAllowed" : false, 1057 | "autheticatorFlow" : false 1058 | }, { 1059 | "requirement" : "ALTERNATIVE", 1060 | "priority" : 30, 1061 | "flowAlias" : "Verify Existing Account by Re-authentication", 1062 | "userSetupAllowed" : false, 1063 | "autheticatorFlow" : true 1064 | } ] 1065 | }, { 1066 | "id" : "0893a763-23a6-4d64-a463-5a3f9b3972fa", 1067 | "alias" : "Verify Existing Account by Re-authentication", 1068 | "description" : "Reauthentication of existing account", 1069 | "providerId" : "basic-flow", 1070 | "topLevel" : false, 1071 | "builtIn" : true, 1072 | "authenticationExecutions" : [ { 1073 | "authenticator" : "idp-username-password-form", 1074 | "requirement" : "REQUIRED", 1075 | "priority" : 10, 1076 | "userSetupAllowed" : false, 1077 | "autheticatorFlow" : false 1078 | }, { 1079 | "authenticator" : "auth-otp-form", 1080 | "requirement" : "OPTIONAL", 1081 | "priority" : 20, 1082 | "userSetupAllowed" : false, 1083 | "autheticatorFlow" : false 1084 | } ] 1085 | }, { 1086 | "id" : "148d5bf3-a217-4ddb-915d-4bcb3e9f52fc", 1087 | "alias" : "browser", 1088 | "description" : "browser based authentication", 1089 | "providerId" : "basic-flow", 1090 | "topLevel" : true, 1091 | "builtIn" : true, 1092 | "authenticationExecutions" : [ { 1093 | "authenticator" : "auth-cookie", 1094 | "requirement" : "ALTERNATIVE", 1095 | "priority" : 10, 1096 | "userSetupAllowed" : false, 1097 | "autheticatorFlow" : false 1098 | }, { 1099 | "authenticator" : "auth-spnego", 1100 | "requirement" : "DISABLED", 1101 | "priority" : 20, 1102 | "userSetupAllowed" : false, 1103 | "autheticatorFlow" : false 1104 | }, { 1105 | "requirement" : "ALTERNATIVE", 1106 | "priority" : 30, 1107 | "flowAlias" : "forms", 1108 | "userSetupAllowed" : false, 1109 | "autheticatorFlow" : true 1110 | } ] 1111 | }, { 1112 | "id" : "41cf3bf9-79ba-43b1-8be7-eaab4dfdc1ad", 1113 | "alias" : "clients", 1114 | "description" : "Base authentication for clients", 1115 | "providerId" : "client-flow", 1116 | "topLevel" : true, 1117 | "builtIn" : true, 1118 | "authenticationExecutions" : [ { 1119 | "authenticator" : "client-secret", 1120 | "requirement" : "ALTERNATIVE", 1121 | "priority" : 10, 1122 | "userSetupAllowed" : false, 1123 | "autheticatorFlow" : false 1124 | }, { 1125 | "authenticator" : "client-jwt", 1126 | "requirement" : "ALTERNATIVE", 1127 | "priority" : 20, 1128 | "userSetupAllowed" : false, 1129 | "autheticatorFlow" : false 1130 | } ] 1131 | }, { 1132 | "id" : "0cb46485-1217-4dd8-85fe-91f95d41c106", 1133 | "alias" : "direct grant", 1134 | "description" : "OpenID Connect Resource Owner Grant", 1135 | "providerId" : "basic-flow", 1136 | "topLevel" : true, 1137 | "builtIn" : true, 1138 | "authenticationExecutions" : [ { 1139 | "authenticator" : "direct-grant-validate-username", 1140 | "requirement" : "REQUIRED", 1141 | "priority" : 10, 1142 | "userSetupAllowed" : false, 1143 | "autheticatorFlow" : false 1144 | }, { 1145 | "authenticator" : "direct-grant-validate-password", 1146 | "requirement" : "REQUIRED", 1147 | "priority" : 20, 1148 | "userSetupAllowed" : false, 1149 | "autheticatorFlow" : false 1150 | }, { 1151 | "authenticator" : "direct-grant-validate-otp", 1152 | "requirement" : "OPTIONAL", 1153 | "priority" : 30, 1154 | "userSetupAllowed" : false, 1155 | "autheticatorFlow" : false 1156 | } ] 1157 | }, { 1158 | "id" : "45653de2-b59b-4fe3-b7d3-19f41f7a8d4d", 1159 | "alias" : "first broker login", 1160 | "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", 1161 | "providerId" : "basic-flow", 1162 | "topLevel" : true, 1163 | "builtIn" : true, 1164 | "authenticationExecutions" : [ { 1165 | "authenticatorConfig" : "review profile config", 1166 | "authenticator" : "idp-review-profile", 1167 | "requirement" : "REQUIRED", 1168 | "priority" : 10, 1169 | "userSetupAllowed" : false, 1170 | "autheticatorFlow" : false 1171 | }, { 1172 | "authenticatorConfig" : "create unique user config", 1173 | "authenticator" : "idp-create-user-if-unique", 1174 | "requirement" : "ALTERNATIVE", 1175 | "priority" : 20, 1176 | "userSetupAllowed" : false, 1177 | "autheticatorFlow" : false 1178 | }, { 1179 | "requirement" : "ALTERNATIVE", 1180 | "priority" : 30, 1181 | "flowAlias" : "Handle Existing Account", 1182 | "userSetupAllowed" : false, 1183 | "autheticatorFlow" : true 1184 | } ] 1185 | }, { 1186 | "id" : "b656b501-384c-4267-b398-ccc2d637a7bb", 1187 | "alias" : "forms", 1188 | "description" : "Username, password, otp and other auth forms.", 1189 | "providerId" : "basic-flow", 1190 | "topLevel" : false, 1191 | "builtIn" : true, 1192 | "authenticationExecutions" : [ { 1193 | "authenticator" : "auth-username-password-form", 1194 | "requirement" : "REQUIRED", 1195 | "priority" : 10, 1196 | "userSetupAllowed" : false, 1197 | "autheticatorFlow" : false 1198 | }, { 1199 | "authenticator" : "auth-otp-form", 1200 | "requirement" : "OPTIONAL", 1201 | "priority" : 20, 1202 | "userSetupAllowed" : false, 1203 | "autheticatorFlow" : false 1204 | } ] 1205 | }, { 1206 | "id" : "7b756cbb-9cba-4ebb-a3c1-b49c477c00b6", 1207 | "alias" : "registration", 1208 | "description" : "registration flow", 1209 | "providerId" : "basic-flow", 1210 | "topLevel" : true, 1211 | "builtIn" : true, 1212 | "authenticationExecutions" : [ { 1213 | "authenticator" : "registration-page-form", 1214 | "requirement" : "REQUIRED", 1215 | "priority" : 10, 1216 | "flowAlias" : "registration form", 1217 | "userSetupAllowed" : false, 1218 | "autheticatorFlow" : true 1219 | } ] 1220 | }, { 1221 | "id" : "e3b452a2-ccfa-434e-b16b-c5df3c3fae7c", 1222 | "alias" : "registration form", 1223 | "description" : "registration form", 1224 | "providerId" : "form-flow", 1225 | "topLevel" : false, 1226 | "builtIn" : true, 1227 | "authenticationExecutions" : [ { 1228 | "authenticator" : "registration-user-creation", 1229 | "requirement" : "REQUIRED", 1230 | "priority" : 20, 1231 | "userSetupAllowed" : false, 1232 | "autheticatorFlow" : false 1233 | }, { 1234 | "authenticator" : "registration-profile-action", 1235 | "requirement" : "REQUIRED", 1236 | "priority" : 40, 1237 | "userSetupAllowed" : false, 1238 | "autheticatorFlow" : false 1239 | }, { 1240 | "authenticator" : "registration-password-action", 1241 | "requirement" : "REQUIRED", 1242 | "priority" : 50, 1243 | "userSetupAllowed" : false, 1244 | "autheticatorFlow" : false 1245 | }, { 1246 | "authenticator" : "registration-recaptcha-action", 1247 | "requirement" : "DISABLED", 1248 | "priority" : 60, 1249 | "userSetupAllowed" : false, 1250 | "autheticatorFlow" : false 1251 | } ] 1252 | }, { 1253 | "id" : "27c50f4e-00d4-4e51-8a70-ff0dd770c8f1", 1254 | "alias" : "reset credentials", 1255 | "description" : "Reset credentials for a user if they forgot their password or something", 1256 | "providerId" : "basic-flow", 1257 | "topLevel" : true, 1258 | "builtIn" : true, 1259 | "authenticationExecutions" : [ { 1260 | "authenticator" : "reset-credentials-choose-user", 1261 | "requirement" : "REQUIRED", 1262 | "priority" : 10, 1263 | "userSetupAllowed" : false, 1264 | "autheticatorFlow" : false 1265 | }, { 1266 | "authenticator" : "reset-credential-email", 1267 | "requirement" : "REQUIRED", 1268 | "priority" : 20, 1269 | "userSetupAllowed" : false, 1270 | "autheticatorFlow" : false 1271 | }, { 1272 | "authenticator" : "reset-password", 1273 | "requirement" : "REQUIRED", 1274 | "priority" : 30, 1275 | "userSetupAllowed" : false, 1276 | "autheticatorFlow" : false 1277 | }, { 1278 | "authenticator" : "reset-otp", 1279 | "requirement" : "OPTIONAL", 1280 | "priority" : 40, 1281 | "userSetupAllowed" : false, 1282 | "autheticatorFlow" : false 1283 | } ] 1284 | }, { 1285 | "id" : "ec800528-42a9-4116-9af2-41fc5f5414a6", 1286 | "alias" : "saml ecp", 1287 | "description" : "SAML ECP Profile Authentication Flow", 1288 | "providerId" : "basic-flow", 1289 | "topLevel" : true, 1290 | "builtIn" : true, 1291 | "authenticationExecutions" : [ { 1292 | "authenticator" : "http-basic-authenticator", 1293 | "requirement" : "REQUIRED", 1294 | "priority" : 10, 1295 | "userSetupAllowed" : false, 1296 | "autheticatorFlow" : false 1297 | } ] 1298 | } ], 1299 | "authenticatorConfig" : [ { 1300 | "alias" : "create unique user config", 1301 | "config" : { 1302 | "require.password.update.after.registration" : "false" 1303 | } 1304 | }, { 1305 | "alias" : "review profile config", 1306 | "config" : { 1307 | "update.profile.on.first.login" : "missing" 1308 | } 1309 | } ], 1310 | "requiredActions" : [ { 1311 | "alias" : "CONFIGURE_TOTP", 1312 | "name" : "Configure Totp", 1313 | "providerId" : "CONFIGURE_TOTP", 1314 | "enabled" : true, 1315 | "defaultAction" : false, 1316 | "config" : { } 1317 | }, { 1318 | "alias" : "UPDATE_PASSWORD", 1319 | "name" : "Update Password", 1320 | "providerId" : "UPDATE_PASSWORD", 1321 | "enabled" : true, 1322 | "defaultAction" : false, 1323 | "config" : { } 1324 | }, { 1325 | "alias" : "UPDATE_PROFILE", 1326 | "name" : "Update Profile", 1327 | "providerId" : "UPDATE_PROFILE", 1328 | "enabled" : true, 1329 | "defaultAction" : false, 1330 | "config" : { } 1331 | }, { 1332 | "alias" : "VERIFY_EMAIL", 1333 | "name" : "Verify Email", 1334 | "providerId" : "VERIFY_EMAIL", 1335 | "enabled" : true, 1336 | "defaultAction" : false, 1337 | "config" : { } 1338 | }, { 1339 | "alias" : "terms_and_conditions", 1340 | "name" : "Terms and Conditions", 1341 | "providerId" : "terms_and_conditions", 1342 | "enabled" : false, 1343 | "defaultAction" : false, 1344 | "config" : { } 1345 | } ], 1346 | "browserFlow" : "browser", 1347 | "registrationFlow" : "registration", 1348 | "directGrantFlow" : "direct grant", 1349 | "resetCredentialsFlow" : "reset credentials", 1350 | "clientAuthenticationFlow" : "clients" 1351 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | de.slackspace.keycloaktutorial 6 | keycloak-tutorial 7 | 0.0.1-SNAPSHOT 8 | jar 9 | 10 | 11 | org.springframework.boot 12 | spring-boot-starter-parent 13 | 1.3.3.RELEASE 14 | 15 | 16 | 17 | 18 | UTF-8 19 | de.slackspace.keycloaktutorial.KeyCloakTutorial 20 | 1.9.1.Final 21 | 1.8 22 | 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-test 32 | test 33 | 34 | 35 | org.keycloak 36 | keycloak-tomcat8-adapter 37 | ${keycloak.version} 38 | 39 | 40 | org.keycloak 41 | keycloak-spring-boot-adapter 42 | ${keycloak.version} 43 | 44 | 45 | org.springframework.security 46 | spring-security-web 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-devtools 51 | 52 | 53 | 54 | 55 | 56 | 57 | maven-compiler-plugin 58 | 59 | ${java.version} 60 | ${java.version} 61 | 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-maven-plugin 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/main/java/de/slackspace/keycloaktutorial/KeyCloakTutorial.java: -------------------------------------------------------------------------------- 1 | package de.slackspace.keycloaktutorial; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class KeyCloakTutorial { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(KeyCloakTutorial.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/de/slackspace/keycloaktutorial/contract/domain/Contract.java: -------------------------------------------------------------------------------- 1 | package de.slackspace.keycloaktutorial.contract.domain; 2 | 3 | public class Contract { 4 | 5 | private String name; 6 | 7 | public Contract(String name) { 8 | this.setName(name); 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | public void setName(String name) { 16 | this.name = name; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/de/slackspace/keycloaktutorial/contract/web/ContractResource.java: -------------------------------------------------------------------------------- 1 | package de.slackspace.keycloaktutorial.contract.web; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestMethod; 8 | import org.springframework.web.bind.annotation.ResponseBody; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import de.slackspace.keycloaktutorial.contract.domain.Contract; 12 | import de.slackspace.keycloaktutorial.security.domain.CurrentUser; 13 | import de.slackspace.keycloaktutorial.security.domain.UserDetails; 14 | 15 | @RestController 16 | @RequestMapping(value = "/api/contracts") 17 | public class ContractResource { 18 | 19 | @RequestMapping(method = RequestMethod.GET) 20 | @ResponseBody 21 | public List getContracts(@CurrentUser UserDetails userDetails) { 22 | Contract a = new Contract("a - called by user: " + createUserAppendix(userDetails)); 23 | Contract b = new Contract("b - called by user: " + createUserAppendix(userDetails)); 24 | 25 | List contracts = new ArrayList<>(); 26 | contracts.add(a); 27 | contracts.add(b); 28 | 29 | return contracts; 30 | } 31 | 32 | private String createUserAppendix(UserDetails userDetails) { 33 | return userDetails.getFullName() + " (id= " + userDetails.getId() + ")"; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/de/slackspace/keycloaktutorial/security/domain/CurrentUser.java: -------------------------------------------------------------------------------- 1 | package de.slackspace.keycloaktutorial.security.domain; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | @Target(ElementType.PARAMETER) 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Documented 12 | public @interface CurrentUser {} 13 | -------------------------------------------------------------------------------- /src/main/java/de/slackspace/keycloaktutorial/security/domain/UserDetails.java: -------------------------------------------------------------------------------- 1 | package de.slackspace.keycloaktutorial.security.domain; 2 | 3 | import java.util.Set; 4 | 5 | public class UserDetails { 6 | 7 | private String id; 8 | 9 | private String email; 10 | 11 | private String firstName; 12 | 13 | private String lastName; 14 | 15 | private Set roles; 16 | 17 | public UserDetails(String id, String firstName, String lastName, String email, Set roles) { 18 | this.id = id; 19 | this.firstName = firstName; 20 | this.lastName = lastName; 21 | this.email = email; 22 | this.roles = roles; 23 | } 24 | 25 | public String getEmail() { 26 | return email; 27 | } 28 | 29 | public String getFirstName() { 30 | return firstName; 31 | } 32 | 33 | public String getLastName() { 34 | return lastName; 35 | } 36 | 37 | public Set getRoles() { 38 | return roles; 39 | } 40 | 41 | public String getFullName() { 42 | return getFirstName() + " " + getLastName(); 43 | } 44 | 45 | public String getId() { 46 | return id; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/de/slackspace/keycloaktutorial/security/web/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package de.slackspace.keycloaktutorial.security.web.config; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 9 | 10 | import de.slackspace.keycloaktutorial.security.web.resolver.UserDetailsArgumentResolver; 11 | 12 | @Configuration 13 | public class WebConfig extends WebMvcConfigurerAdapter { 14 | 15 | @Override 16 | public void addArgumentResolvers(List argumentResolvers) { 17 | argumentResolvers.add(createUserDetailsResolver()); 18 | } 19 | 20 | @Bean 21 | public UserDetailsArgumentResolver createUserDetailsResolver() { 22 | return new UserDetailsArgumentResolver(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/de/slackspace/keycloaktutorial/security/web/resolver/UserDetailsArgumentResolver.java: -------------------------------------------------------------------------------- 1 | package de.slackspace.keycloaktutorial.security.web.resolver; 2 | 3 | import org.keycloak.KeycloakPrincipal; 4 | import org.keycloak.adapters.RefreshableKeycloakSecurityContext; 5 | import org.keycloak.representations.AccessToken; 6 | import org.springframework.core.MethodParameter; 7 | import org.springframework.web.bind.support.WebArgumentResolver; 8 | import org.springframework.web.bind.support.WebDataBinderFactory; 9 | import org.springframework.web.context.request.NativeWebRequest; 10 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 11 | import org.springframework.web.method.support.ModelAndViewContainer; 12 | 13 | import de.slackspace.keycloaktutorial.security.domain.CurrentUser; 14 | import de.slackspace.keycloaktutorial.security.domain.UserDetails; 15 | 16 | public class UserDetailsArgumentResolver implements HandlerMethodArgumentResolver { 17 | 18 | @Override 19 | public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer, 20 | NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 21 | 22 | if (supportsParameter(methodParameter)) { 23 | return createUserDetails(webRequest); 24 | } 25 | else { 26 | return WebArgumentResolver.UNRESOLVED; 27 | } 28 | } 29 | 30 | @SuppressWarnings("unchecked") 31 | private Object createUserDetails(NativeWebRequest webRequest) { 32 | KeycloakPrincipal principal = 33 | (KeycloakPrincipal) webRequest.getUserPrincipal(); 34 | 35 | AccessToken token = principal.getKeycloakSecurityContext().getToken(); 36 | 37 | return new UserDetails(token.getId(), token.getGivenName(), token.getFamilyName(), token.getEmail(), 38 | token.getRealmAccess().getRoles()); 39 | } 40 | 41 | @Override 42 | public boolean supportsParameter(MethodParameter methodParameter) { 43 | return methodParameter.getParameterAnnotation(CurrentUser.class) != null 44 | && methodParameter.getParameterType().equals(UserDetails.class); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port = 8000 2 | 3 | keycloak.realm = Demo-Realm 4 | keycloak.realmKey = MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkRS35Z5lblNpvfHr8C6XEGi9vwYew737+LmcpDbxamLVSdNEWi01Gh2TXCWjDsONaUmvQBIVPvJM5dRmnR5lNtYFnRr6vrT/18I/+E/pU/5a9fj0hMyib1HEsKslI6P2os1vunIiHgkDJkePo9tTbRsG71WH7fXM7BSienZO0ZLV1TbgiLj+PSoFLzAHF0P3BXzHIGTqHzs/UHZ/7ub9Oc2vpSXi4IZKGVLQnc9VNZaMu0QWgnqjMCr7CQRORMQ+y4QGw+DFaZ1pBNpZ+bgy6wKSf17U/5Va/CWs/+UjGj50L/1QqertuMcFKqnnQDLhRmnQErLtdpXzop0YQxka/QIDAQAB 5 | keycloak.auth-server-url = http://localhost:8080/auth 6 | keycloak.ssl-required = external 7 | keycloak.resource = tutorial-backend 8 | keycloak.bearer-only = true 9 | keycloak.credentials.secret = 44c91fb2-d789-42ae-aa0b-d14a10fb1383 10 | 11 | keycloak.securityConstraints[0].securityCollections[0].name = spring secured api 12 | keycloak.securityConstraints[0].securityCollections[0].authRoles[0] = admin 13 | keycloak.securityConstraints[0].securityCollections[0].authRoles[1] = manager 14 | keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /api/* -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KeyCloak Tutorial 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | -------------------------------------------------------------------------------- /src/main/webapp/js/lib/angular-route-1.5.0.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.5.0 3 | (c) 2010-2016 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(r,d,C){'use strict';function x(s,h,g){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,c,b,f,y){function k(){n&&(g.cancel(n),n=null);l&&(l.$destroy(),l=null);m&&(n=g.leave(m),n.then(function(){n=null}),m=null)}function z(){var b=s.current&&s.current.locals;if(d.isDefined(b&&b.$template)){var b=a.$new(),f=s.current;m=y(b,function(b){g.enter(b,null,m||c).then(function(){!d.isDefined(u)||u&&!a.$eval(u)||h()});k()});l=f.scope=b;l.$emit("$viewContentLoaded"); 7 | l.$eval(v)}else k()}var l,m,n,u=b.autoscroll,v=b.onload||"";a.$on("$routeChangeSuccess",z);z()}}}function A(d,h,g){return{restrict:"ECA",priority:-400,link:function(a,c){var b=g.current,f=b.locals;c.html(f.$template);var y=d(c.contents());if(b.controller){f.$scope=a;var k=h(b.controller,f);b.controllerAs&&(a[b.controllerAs]=k);c.data("$ngControllerController",k);c.children().data("$ngControllerController",k)}a[b.resolveAs||"$resolve"]=f;y(a)}}}r=d.module("ngRoute",["ng"]).provider("$route",function(){function s(a, 8 | c){return d.extend(Object.create(a),c)}function h(a,d){var b=d.caseInsensitiveMatch,f={originalPath:a,regexp:a},g=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,d,b,c){a="?"===c?c:null;c="*"===c?c:null;g.push({name:b,optional:!!a});d=d||"";return""+(a?"":d)+"(?:"+(a?d:"")+(c&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=new RegExp("^"+a+"$",b?"i":"");return f}var g={};this.when=function(a,c){var b=d.copy(c);d.isUndefined(b.reloadOnSearch)&& 9 | (b.reloadOnSearch=!0);d.isUndefined(b.caseInsensitiveMatch)&&(b.caseInsensitiveMatch=this.caseInsensitiveMatch);g[a]=d.extend(b,a&&h(a,b));if(a){var f="/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";g[f]=d.extend({redirectTo:a},h(f,b))}return this};this.caseInsensitiveMatch=!1;this.otherwise=function(a){"string"===typeof a&&(a={redirectTo:a});this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$templateRequest","$sce",function(a,c,b,f,h,k,r){function l(b){var e= 10 | t.current;(x=(p=n())&&e&&p.$$route===e.$$route&&d.equals(p.pathParams,e.pathParams)&&!p.reloadOnSearch&&!v)||!e&&!p||a.$broadcast("$routeChangeStart",p,e).defaultPrevented&&b&&b.preventDefault()}function m(){var w=t.current,e=p;if(x)w.params=e.params,d.copy(w.params,b),a.$broadcast("$routeUpdate",w);else if(e||w)v=!1,(t.current=e)&&e.redirectTo&&(d.isString(e.redirectTo)?c.path(u(e.redirectTo,e.params)).search(e.params).replace():c.url(e.redirectTo(e.pathParams,c.path(),c.search())).replace()),f.when(e).then(function(){if(e){var a= 11 | d.extend({},e.resolve),b,c;d.forEach(a,function(b,e){a[e]=d.isString(b)?h.get(b):h.invoke(b,null,null,e)});d.isDefined(b=e.template)?d.isFunction(b)&&(b=b(e.params)):d.isDefined(c=e.templateUrl)&&(d.isFunction(c)&&(c=c(e.params)),d.isDefined(c)&&(e.loadedTemplateUrl=r.valueOf(c),b=k(c)));d.isDefined(b)&&(a.$template=b);return f.all(a)}}).then(function(c){e==t.current&&(e&&(e.locals=c,d.copy(e.params,b)),a.$broadcast("$routeChangeSuccess",e,w))},function(b){e==t.current&&a.$broadcast("$routeChangeError", 12 | e,w,b)})}function n(){var a,b;d.forEach(g,function(f,g){var q;if(q=!b){var h=c.path();q=f.keys;var l={};if(f.regexp)if(h=f.regexp.exec(h)){for(var k=1,n=h.length;k { 53 | window._keycloak = Keycloak('keycloak/keycloak.json'); 54 | 55 | window._keycloak.init({ 56 | onLoad: 'login-required' 57 | }) 58 | .success((authenticated) => { 59 | if(authenticated) { 60 | window._keycloak.loadUserProfile().success(function(profile){ 61 | angular.bootstrap(document, ['keycloak-tutorial']); // manually bootstrap Angular 62 | }); 63 | } 64 | else { 65 | window.location.reload(); 66 | } 67 | }) 68 | .error(function () { 69 | window.location.reload(); 70 | }); 71 | }); -------------------------------------------------------------------------------- /src/main/webapp/keycloak/keycloak.json: -------------------------------------------------------------------------------- 1 | { 2 | "realm": "Demo-Realm", 3 | "realm-public-key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkRS35Z5lblNpvfHr8C6XEGi9vwYew737+LmcpDbxamLVSdNEWi01Gh2TXCWjDsONaUmvQBIVPvJM5dRmnR5lNtYFnRr6vrT/18I/+E/pU/5a9fj0hMyib1HEsKslI6P2os1vunIiHgkDJkePo9tTbRsG71WH7fXM7BSienZO0ZLV1TbgiLj+PSoFLzAHF0P3BXzHIGTqHzs/UHZ/7ub9Oc2vpSXi4IZKGVLQnc9VNZaMu0QWgnqjMCr7CQRORMQ+y4QGw+DFaZ1pBNpZ+bgy6wKSf17U/5Va/CWs/+UjGj50L/1QqertuMcFKqnnQDLhRmnQErLtdpXzop0YQxka/QIDAQAB", 4 | "auth-server-url": "http://localhost:8080/auth", 5 | "ssl-required": "external", 6 | "resource": "tutorial-frontend", 7 | "public-client": true 8 | } -------------------------------------------------------------------------------- /src/main/webapp/views/main.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

Angular is {{status}}

5 |
You're logged in as:
6 | 7 | Id: {{profile.id}} 8 |

9 | Username: {{profile.username}} 10 |

11 | Email: {{profile.email}} 12 |

13 | Full Name: {{profile.firstName}} {{profile.lastName}} 14 | 15 |

16 | 17 | 18 | 19 |

20 |

Only managers can see this

21 | 22 |

23 | 24 | Contracts: 25 |

    26 |
  • {{contract.name}}
  • 27 |
28 |
29 | 30 |
31 |

Only admins can see this

32 | 33 |

34 | 35 | Contracts: 36 |

    37 |
  • {{contract.name}}
  • 38 |
39 |
40 |
41 |
42 | --------------------------------------------------------------------------------