OIDC Authorization Code Grant Type#
- In the Keycloak Setup, we learned how to set upÂ
Keycloak
 authorization server forÂOIDC Authorization Code Grant Type
 . Now, in this section, we will continue to configure the Spring Boot application to be asClient
and alsoResource Server
for connecting to theKeycloak
to get token and also verifying in coming token requests with before accessing the data.
Prepare#
- Firstly, we need to review the simple diagram that we are going to do as in the image below.
- The user access
Browser
and then call the API/v1/secure/messages
for getting the protected data. - The
Spring Boot application
checks the credential and realize that, the user has not logged in yet then the it will save this APIURL
into a request cache currently the APIURL
is stored under aJESSIONID
and it will be used in steps later. Then the user will be responded with a redirect URL to trigger authorization flow. - The
Browser
calls the the responded API for triggering the authorization flow. - When the API
/oauth2/authorization/keycloak
is called then theSpring Boot application
will prepare an authorization redirect URL and it will also save theOAuth2AuthorizationRequest
under theJESSIONID
on the step above. Then the authorization redirect URL will be responded to the browser. - The
Browser
will redirect to the responded URL to theKeycloak Authorization Server
page for login with username and password to prove the user identity. - If the username and password are correct then the
Keycloak Authorization Server
will respond a redirect URL together with anauthorization code
to theBrowser
. This redirect URL is theclient registered redirect
that we configured inKeycloak Authorization Server
in Keycloak Setup. - The
Browser
call theclient registered redirect
URL together with theAuthorization Code
andJESSIONID
- When the
URL
is called the filterOAuth2LoginAuthenticationFilter
ofSpring Boot application
will be triggered and it will load theOAuth2AuthorizationRequest
which is stored in the session to extract some information and repair the request with theauthorization code
and call to theKeycloak Authorization Server
to verify theauhthorization code
and exchange theAccess Token
andID token
if theauthorization code
is correct. - If the
authorization code
is correct then theAccess Token
,ID Token
and other information will be responded. - The
Spring Boot application
will extract those information and build theOAuth2AuthenticationToken
. Then thisAuthentication
will set into a new session and this session will be responded as a newJSESSIONID
cookie. ThisAuthentication
also be set into theSpring Security Context
. Finally, theJESSIONID
cookie with theredirect URL
which is the first URL that thebrowser
called/v1/secure/messages
without the authorization will be responded to the Browser, thisredirect URL
is got from the request cache at step 2. - When
Browser
received theJESSIONID
cookie and redirect URL. Then it will call the redirect URL and theBrowser
will also set theJESSIONID
into the request automatically. -
The
Spring Boot application
received the request with theJESSIONID
(processed in theSecurityContextHolderFilter
) and it look up the corresponding session in the session store by the value ofJSESSIONID
. If the session exists and theSecurityContext
in the session is valid also then theSecurityContext
is set in theSecurityContextHolder
and finally the data will be response to theBrowser
. -
For setting up
Keycloak authorization server
please view Keycloak Setup with Client Credentials Grant Type.
Spring Boot Server Setup#
Dependencies#
- Let's add some dependencies as below.
pom.xml | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
|
- In which for setting up
OIDC Authorization Code Grant Type
with Spring Boot we need the dependencyspring-boot-starter-oauth2-client
.
Controller#
- Then let's create 2 simple controllers with 2 simple APIs for testing. One controller is public and the other one is secure controller which need the authentication to access.
PublicController.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
SecuredController.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Config#
- Next, let's create a
SecurityConfig.java
for configuring the security as below.
SecurityConfig.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
-
As you can see that, in this config:
- We just simply disable the csrf by using
http.csrf(AbstractHttpConfigurer::disable)
. - Then we config public APIs which don't need the authorization by
1 2 3 4 5
.authorizeHttpRequests(auth -> auth.requestMatchers("/v1/public/**") .permitAll() .anyRequest() .authenticated())
- Finally, we will configure authentication for OIDC by using
.oauth2Login(withDefaults());
- We just simply disable the csrf by using
-
To use the built-in default
OIDC
configuration we have to provide some client information in theapplication.yaml
as below.
application.yaml | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
-
So we have the definitions for those configurations as below:
-
Spring Security OAuth2 Client Configuration
- spring.security.oauth2.client.registration.keycloak: Defines the client registration details for Keycloak.
- provider: Specifies the provider for the OAuth2 client, which is Keycloak.
- client-id: The client ID registered with Keycloak.
- client-secret: The client secret associated with the client ID.
- authorization-grant-type: The type of authorization grant to be used, here it is
authorization_code
. - scope: The scope of the access request (e.g., openid, profile, email).
- redirect-uri: The URI where the authorization server will redirect to with the authorization code.
- spring.security.oauth2.client.provider.keycloak: Defines the provider details for Keycloak.
- issuer-uri: The issuer URI for the Keycloak realm.
- authorization-uri: The URI to initiate the authorization request.
- token-uri: The URI to obtain the access token.
- user-info-uri: The URI to obtain user information.
- jwk-set-uri: The URI to obtain the JSON Web Key Set (JWKS) for verifying tokens.
- spring.security.oauth2.client.registration.keycloak: Defines the client registration details for Keycloak.
Configuration | Value | Description |
---|---|---|
server.port |
7070 |
The port number on which the server runs. |
spring.security.oauth2.client.registration.keycloak.provider |
keycloak |
The OAuth2 provider name. |
spring.security.oauth2.client.registration.keycloak.client-id |
auth-code-grant-type |
The client ID registered with Keycloak. |
spring.security.oauth2.client.registration.keycloak.client-secret |
QXHFcSiFLF96KassAQppb9HJtZ45Lbbf |
The client secret associated with the client ID. |
spring.security.oauth2.client.registration.keycloak.authorization-grant-type |
authorization_code |
The type of authorization grant. |
spring.security.oauth2.client.registration.keycloak.scope |
openid, profile, email |
The scope of the access request. |
spring.security.oauth2.client.registration.keycloak.redirect-uri |
http://localhost:7070/login/oauth2/code/keycloak |
The URI where the authorization server will redirect with the authorization code. |
spring.security.oauth2.client.provider.keycloak.issuer-uri |
http://localhost:8080/auth/realms/myrealm |
The issuer URI for the Keycloak realm. |
spring.security.oauth2.client.provider.keycloak.authorization-uri |
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/auth |
The URI to initiate the authorization request. |
spring.security.oauth2.client.provider.keycloak.token-uri |
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token |
The URI to obtain the access token. |
spring.security.oauth2.client.provider.keycloak.user-info-uri |
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/userinfo |
The URI to obtain user information. |
spring.security.oauth2.client.provider.keycloak.jwk-set-uri |
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/certs |
The URI to obtain the JSON Web Key Set (JWKS) for verifying tokens. |
Testing#
- Now, let's start the Spring Boot application, open browser and go to
http://localhost:7070/v1/public/messages
then we can see the message immediately without login.
- Then let's go to
http://localhost:7070/v1/secure/messages
then you will be navigated to the Keycloak login page for authentication as below.
- Then after login successfully we can see the secure message as below.
Deep Dive Into Details#
- After we testing successfully, now let's open the network and open debug mode for Spring Boot application, and we will check step by step the OIDC Authorization Code Grant Type flow in Spring Security.
- So for the first step, when we access the protected API
http://localhost:7070/v1/secure/messages
. Then theSpring Boot application
checks the credential and realize that, the user has not logged in yet then the it will save this APIURL
into a request cache, currently the request cache will be stored under aJESSIONID
(E6730EA2590274BC5EE3AC11472B29F2
) and it will be used in steps later. Then the user will be responded with a redirect URLhttp://localhost:7070/oauth2/authorization/keycloak
to trigger authorization flow.
- Now, The
Browser
calls the the responded API for triggering the authorization flow. When the API/oauth2/authorization/keycloak
is called then theSpring Boot application
will prepare an authorization redirect URL and it will also save theOAuth2AuthorizationRequest
into a session (theJESSIONID
-E6730EA2590274BC5EE3AC11472B29F2
that we saw in the step above). Then the authorization redirect URL will be responded to the browser. For example:http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/auth?response_type=code&client_id=auth-code-grant-type&scope=openid%20profile%20email&state=Ho4AIPgBfVSA8tykxnsfklRUQo2mRL7cOX5LWe_ex_8%3D&redirect_uri=http://localhost:7070/login/oauth2/code/keycloak&nonce=gugi6sOh-muBFRb9JZFvd0D6Qkmz83MGpEPHk1ESsP4
- The
Browser
will redirect to the responded URL to theKeycloak Authorization Server
page for login with username and password to prove the user identity.
- After the user click button login then the API in the image above will be called and if the username and password are correct then the
Keycloak Authorization Server
will respond a redirect URL together with anauthorization code
to theBrowser
. This redirect URL is theclient registered redirect
that we configured inKeycloak Authorization Server
in Keycloak Setup.
- The
Browser
call theclient registered redirect
URL together with theAuthorization Code
andJESSIONID
. When theURL
is called the filterOAuth2LoginAuthenticationFilter
which extends theAbstractAuthenticationProcessingFilter
ofSpring Boot application
will be triggered and it will load theOAuth2AuthorizationRequest
which is stored in the session to extract some information and repair the request with theauthorization code
and call to theKeycloak Authorization Server
to verify theauhthorization code
and exchange theAccess Token
andID token
if theauthorization code
is correct. - The method
attemptAuthentication
will be responsible for extractingOAuth2AuthorizationRequest
in theOAuth2LoginAuthenticationFilter
and call to theKeyCloak
for getting theAccess Token
andID Token
.
OAuth2LoginAuthenticationFilter.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
- We have the code
OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository.removeAuthorizationRequest(request, response);
is used for getting theOAuth2AuthorizationRequest
which is stored in the session. It will call the methodremoveAuthorizationRequest
under theHttpSessionOAuth2AuthorizationRequestRepository
class. The method will loadOAuth2AuthorizationRequest
and remove that attribute in the Session also.
HttpSessionOAuth2AuthorizationRequestRepository.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
- Go back to the
OAuth2LoginAuthenticationFilter
at lineOAuth2LoginAuthenticationToken authenticationResult = (OAuth2LoginAuthenticationToken) this.getAuthenticationManager().authenticate(authenticationRequest);
we will call to theKeyCloak
Provider to get theAccess Token
andID Token
. All these information will be stored in theOAuth2LoginAuthenticationToken
and the Spring Security will extract them to continue the process in the `AbstractAuthenticationProcessingFilter.
- The
Spring Boot application
will extractOAuth2LoginAuthenticationToken
and build theOAuth2AuthenticationToken
. Then thisOAuth2AuthenticationToken
will be saved in an attribute of the current Session by supported of theHttpSessionOAuth2AuthorizedClientRepository
.
HttpSessionOAuth2AuthorizedClientRepository.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
- Then this
OAuth2AuthenticationToken
will be responded to theAbstractAuthenticationProcessingFilter
. Then theAbstractAuthenticationProcessingFilter
will set it into a new session and all the session attributes of old session will be copied to it also. This new session will be responded as a newJSESSIONID
. See the methoddoFilter
inAbstractAuthenticationProcessingFilter
as below.
AbstractAuthenticationProcessingFilter.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
- At line
this.sessionStrategy.onAuthentication(authenticationResult, request, response);
in thedoFilter
we will call toCompositeSessionAuthenticationStrategy.java
to create a new session and copy all information of old session into it.
CompositeSessionAuthenticationStrategy.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
- At line
delegate.onAuthentication(authentication, request, response);
, theCompositeSessionAuthenticationStrategy
will delegate creating and copying information from old session to new session forAbstractSessionFixationProtectionStrategy
. See the code below.
AbstractSessionFixationProtectionStrategy.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
- The line
session = applySessionFixation(request);
will call the methodapplySessionFixation
ofChangeSessionIdAuthenticationStrategy
which will return a new session which contains all information of the old one.
ChangeSessionIdAuthenticationStrategy.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
- Finally, let's go back to the
AbstractAuthenticationProcessingFilter
, the methodsuccessfulAuthentication(request, response, chain, authenticationResult);
in methoddoFilter
will be called and theAuthentication
will be set into theSpring Security Context
.
AbstractAuthenticationProcessingFilter.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
- After that, The line
this.successHandler.onAuthenticationSuccess(request, response, authResult)
will trigger the methodonAuthenticationSuccess
method inSavedRequestAwareAuthenticationSuccessHandler
to continue the process.
SavedRequestAwareAuthenticationSuccessHandler.java | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
-
In side the method
onAuthenticationSuccess
of theSavedRequestAwareAuthenticationSuccessHandler
. At lineSavedRequest savedRequest = this.requestCache.getRequest(request, response);
, it will load theSavedRequest
from theHttpSessionRequestCache
and now the new session with copied information from old session from the first step will be loaded. Then the saved request URL will be responded. -
Finally, the new
JESSIONID
cookie together with theredirect URL
which is the first URL that thebrowser
called/v1/secure/messages
without the authorization, thisredirect URL
is got from the request cache from firstJESSIONID
will be responded as in the image above.
- Then now, the browser will call the
redirect URL
and it will add the newJESSIONID
automatically and we can get the secure message successfully.