{"id":17317411,"url":"https://github.com/justin-tay/keycloak-spring-boot-example","last_synced_at":"2025-03-27T02:18:52.203Z","repository":{"id":231077256,"uuid":"780305176","full_name":"justin-tay/keycloak-spring-boot-example","owner":"justin-tay","description":"Spring Boot example using Spring Security with Keycloak","archived":false,"fork":false,"pushed_at":"2024-04-12T08:45:27.000Z","size":81,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-12T15:36:18.659Z","etag":null,"topics":["java","keycloak","oidc","spring","spring-boot"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/justin-tay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-04-01T07:23:41.000Z","updated_at":"2024-04-15T05:37:34.689Z","dependencies_parsed_at":"2024-04-12T09:45:33.938Z","dependency_job_id":null,"html_url":"https://github.com/justin-tay/keycloak-spring-boot-example","commit_stats":null,"previous_names":["justin-tay/keycloak-spring-boot-example"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justin-tay%2Fkeycloak-spring-boot-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justin-tay%2Fkeycloak-spring-boot-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justin-tay%2Fkeycloak-spring-boot-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/justin-tay%2Fkeycloak-spring-boot-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/justin-tay","download_url":"https://codeload.github.com/justin-tay/keycloak-spring-boot-example/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245767360,"owners_count":20668827,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","keycloak","oidc","spring","spring-boot"],"created_at":"2024-10-15T13:16:33.161Z","updated_at":"2025-03-27T02:18:52.184Z","avatar_url":"https://github.com/justin-tay.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spring Boot Example OpenID Connect Relying Party for Keycloak\r\n\r\nThis project is an example implementation of a OpenID Connect Relying Party built using Spring Boot and Spring Security OAuth2 Client.\r\n\r\n[Keycloak](https://github.com/keycloak/keycloak) is used as the public Identity Provider for testing purposes\r\n\r\n## Quick Start\r\n\r\n### Configuring Keycloak as the Identity Provider\r\n\r\nThe Spring Boot application exposes its JWKS at http://localhost:8081/oauth2/jwks\r\n\r\nStart Keycloak\r\n\r\n```shell\r\nkc start-dev\r\n```\r\n\r\nAccess Keycloak at http://localhost:8080/\r\n\r\n* Create an administrative user\r\n* Login to the administrative console\r\n* Create the `test` realm\r\n  * Click on the `Keycloak` dropdown on the left navigation bar\r\n  * Select `Create Realm`\r\n  * Set `test` as the `Realm name` and click on `Create`\r\n* Create the `keycloak-spring-boot-example` client\r\n  * Click on `Clients` on the left navigation bar\r\n  * Click on `Create client`\r\n  * Set `keycloak-spring-boot-example` as the `Client ID` and click on `Next`\r\n  * Toggle `Client authentication` to `On` and click on `Next`\r\n  * Set `http://localhost:8081/*` as `Valid redirect URIs`, `Valid post logout redirect URIs` and `Web origins` and click on `Save`\r\n  * Under `Logout settings` \r\n    * Toggle `Front channel logout` to `Off`\r\n    * Set `http://localhost:8081/logout/connect/back-channel/keycloak` as `Backchannel logout URL` and click on `Save`\r\n  * Click on `Credentials`\r\n  * Click on the `Client Authenticator` dropdown and select `Signed Jwt` and click on `Save`\r\n  * Click on `Keys`\r\n  * Toggle `Use JWKS URL` to `On` and enter `http://localhost:8081/oauth2/jwks` as the `JWKS URL` and click on `Save`\r\n* Allow user registrations\r\n  * Click on `Realm settings` on the left navigation bar\r\n  * Click on `Login`\r\n  * Toggle `User registration` to `On`\r\n\r\n### Installing and running the Relying Party Spring Boot application\r\n\r\n```shell\r\nmvn spring-boot:run\r\n```\r\n\r\n### Testing the example Relying Party\r\n\r\n| Description                   | Endpoint\r\n|-------------------------------|-----------------------------------------------------\r\n| Access the application        | http://localhost:8081/login-user\r\n| Logout from the application   | http://localhost:8081/logout\r\n| View the public keys          | http://localhost:8081/oauth2/jwks\r\n| Access Keycloak user account  | http://localhost:8080/realms/test/account\r\n| Logout from Keycloak          | http://localhost:8080/realms/test/protocol/openid-connect/logout\r\n\r\n## Integration Details\r\n\r\nNote that this example does not securely store the private keys which are located in `src/main/resources/jwks.json`. This should be securely stored and rotated, for instance on AWS this should be stored in AWS Secrets Manager with a Secrets Manager Rotation Lambda. This can be configured in Spring using [Spring Cloud AWS Secrets Manager](https://github.com/awspring/spring-cloud-aws).\r\n\r\n### Json Web Key Sets\r\n\r\nKeycloak will call the endpoint http://localhost:8080/oauth2/jwks in order to get the public keys of the application\r\n* The public verification key used for Keycloak to verify the signature for the `private_key_jwt` client assertion\r\n\r\nThe `JWKSet` `@Bean` used by the application is produced in `WebSecurityConfiguration` and its public keys are exposed using the `@RestController` `JwksController`. Note that `JWKSet.toString()` removes the private components of the keys.\r\n\r\n### Client Authentication\r\n\r\nKeycloak was configured to use `private_key_jwt` client authentication by setting `Signed Jwt` as the `Client Authenticator`.\r\n\r\nSpring needs to be configured to use this client authentication method, and the token response client needs to be configured to use the appropriate private key to perform the signing.\r\n\r\n```yaml\r\nspring:\r\n  security:\r\n    oauth2:\r\n      client:\r\n        registration:\r\n          keycloak:\r\n            client-id: keycloak-spring-boot-example\r\n            client-authentication-method: private_key_jwt\r\n            authorization-grant-type: authorization_code\r\n            scope:\r\n            - openid\r\n```\r\n\r\nThe `DefaultAuthorizationCodeTokenResponseClient` is used with the `NimbusJwtClientAuthenticationParametersConverter` configured with the private key and `NimbusJwtClientAuthenticationParametersConverter` used to customize the claims and headers on the client assertion.\r\n\r\n### OpenID Connect Relying Party Initiated Logout\r\n\r\nSpring supports the use of the `end_session_endpoint` to inform Keycloak that the user is being logged out. This is done by configuring a `OidcClientInitiatedLogoutSuccessHandler` as the logout success handler. Upon logout success this will redirect the browser to the `end_session_endpoint` with the `id_token_hint` set. Keycloak will then redirect the browser back to the application using the `post_logout_redirect_uri`.\r\n\r\n### OpenID Connect Back Channel Logout\r\n\r\nSpring supports receiving back channel logout requests from Keycloak if the user logs out from Keycloak.\r\n\r\n```java\r\nhttp.oidcLogout(oidcLogout -\u003e oidcLogout.backChannel(withDefaults()));\r\n```\r\n\r\nThe client on Keycloak needs to be configured to\r\n* Toggle `Front channel logout` to `Off`\r\n* Set `http://localhost:8081/logout/connect/back-channel/keycloak` as `Backchannel logout URL`\r\n\r\nWhere `keycloak` corresponds to the client registration id.\r\n\r\n## Flow\r\n\r\n```mermaid\r\nsequenceDiagram\r\n    autonumber\r\n\r\n    participant User\r\n    participant Browser\r\n    participant Application Server\r\n    participant Keycloak\r\n\r\n    User-\u003e\u003eBrowser: Navigate to user information page\r\n\r\n    Browser-\u003e\u003eApplication Server: Send request for user information page (/login-user)\r\n\r\n    Application Server-\u003e\u003eApplication Server: Not authenticated, determine provider to redirect to\r\n\r\n    Application Server--\u003e\u003eBrowser: Redirect to Application for Keycloak provider\r\n\r\n    Browser-\u003e\u003eApplication Server: Send request for Keycloak provider (/oauth2/authorization/keycloak)\r\n\r\n    Application Server-\u003e\u003eApplication Server: Generate Authorization Request\r\n\r\n    Application Server--\u003e\u003eBrowser: Redirect to Keycloak with Authorization Request\r\n\r\n    Browser-\u003e\u003eKeycloak: Send Authorization Request (/realms/test/protocol/openid-connect/auth)\r\n\r\n    Keycloak-\u003e\u003eKeycloak: Generate login page\r\n\r\n    Keycloak--\u003e\u003eBrowser: Return login page\r\n\r\n    User-\u003e\u003eBrowser: Enter login credentials and sign in\r\n\r\n    Browser-\u003e\u003eKeycloak: Send login credentials \r\n\r\n    Keycloak-\u003e\u003eKeycloak: Process login credentials\r\n\r\n    Keycloak--\u003e\u003eBrowser: Redirect to Application with Authorization Response with code\r\n\r\n    Browser-\u003e\u003eApplication Server: Send Authorization Response with code (/login/oauth2/code/keycloak)\r\n\r\n    Application Server-\u003e\u003eApplication Server: Generate Token Request and client credentials\r\n\r\n    Application Server-\u003e\u003eKeycloak: Send Token Request with client credentials (/realms/test/protocol/openid-connect/token)\r\n\r\n    Keycloak-\u003e\u003eApplication Server: Send request for client json web key set (/oauth2/jwks)\r\n\r\n    Application Server--\u003e\u003eKeycloak: Return client json web key set\r\n\r\n    Keycloak-\u003e\u003eKeycloak: Process client credentials and create tokens\r\n\r\n    Keycloak--\u003e\u003eApplication Server: Return Token Response with access, refresh and id tokens\r\n\r\n    Application Server--\u003e\u003eBrowser: Redirect to user information page\r\n\r\n    Browser-\u003e\u003eApplication Server: Send request for user information page (/login-user)\r\n\r\n    Application Server-\u003e\u003eApplication Server: Authenticated\r\n\r\n    Application Server--\u003e\u003eBrowser: Return user information page\r\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjustin-tay%2Fkeycloak-spring-boot-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjustin-tay%2Fkeycloak-spring-boot-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjustin-tay%2Fkeycloak-spring-boot-example/lists"}