For a better understanding of spring security OAuth2 internals, We will be building this application with a very basic OAUTH2 integration with default configurations provided by spring security using oauth2Login() element and then customize it to a greater extent by adding custom login page, custom redirect-uri and UserService, token endpoint etc to extract user details such as name, email and profile image and use this information to generate JWT token out of it to authorize user for future server calls. We will also build an option available for a new user registration using email and password along with google oauth.
In my previous articles, we have discussed a lot about OAuth2 and it's principles and hence I will encourage you to go through those tutorials - Spring Security OAuth2 first to understand the basics of OAuth2 and then continue here.
Project Structure
Head over to start.spring.io and download a sample spring boot app. Below is the snapshot of mine.
Maven Depenedencies
Apart from the default dependencies that were included during project initialization, we also require below dependencies. pom.xml
Let us first use the default support provided by spring security 5 for OAuth2 and make our google OAuth sign in process successful as it requires bare minimum configurations. Doing so, we will not get confused after overriding the default implementation and build the final project.
Below is the security config that we require to get started with the OAuth in spring security. oauth2Login() in the below configuration enables all default configurations and makes all the resources protected. Only parameters it requires is the client-id and client-secret which we will be generating on google console in next section.
Now let us implement a test page - index.html. As we have protected all the resources, whenever we try to access index.html, we will be redirected to Google OAuth page and after successful authentication the index.html page will be served by spring boot. index.html
Let us register our app on google developer console to get the client-id and cient-secret. As by default spring security 5 redirects user to {baseUrl}/{action}/oauth2/code/google post authentication, we have added
http://localhost:8080/login/oauth2/code/google
as authorised redirect URI on google console.
Once we have the client-id and client-secret, let us configure the same in our application.properties. Below entries in the application.properties is enough to run our application with default configuration. application.properties
Testing Spring Security 5 OAuth2 Default Configuration
Whatever we have implemented above is enough to get started with a spring boot OAuth2 application. Spring boot has really made OAuth2 implementation very simple in a spring MVC app. Now, we can run SpringBootGoogleOauthApplication.java as a java application to test the application.
1. Hit localhost:8080/login or localhost:8080 and you will be forwarded to google sign in.
2. Now you can login to google(provider) and authorise this app.
3. Now you can see the index.html page rendered in your browser.
Spring Security Google OAuth2 Internals
The authentication process has been done now and the spring boot configuration looks very simple and straight forward but there are many things that happened in the background. Let us discuss each of them now.
When a request is made to localhost:8080 to access secured resource, spring security does not find any authenticated object in the context and redirects to
http://localhost:8080/oauth2/authorization/google
for authentication against oauth2Login() element. This GET request is intercepted by OAuth2AuthorizationRequestRedirectFilter and matches against '/oauth2/authorization/{registrationId} where registrationId in this case is google.
If you inspect the URL, the url contains the client-id, client-secret and redirect-uri that we configured in the application.properties.
If the redirect_uri that we have configured in application.proprties does not match with the one that we have configured in google console, then you may see an error as Error: redirect_uri_mismatch
After a successful authentication with google and authorization of our app, the user will be redirected to '/login/oauth2/code/google with the authentication code as request param and this request will be intercepted by OAuth2LoginAuthenticationFilter. With this auth code, a POST request will be made to
https://www.googleapis.com/oauth2/v4/token
to access the user info such as email, picture, name, family_name. Once the response is received by our app, following authentication object will be set in spring security context with ROLE as user and the user will served the secured page index.html page against "/".
The Redirection Endpoint is used by the Authorization Server for returning the Authorization Response to the client. As we discussed above, we are using default redirection endpoint -
http://localhost:8080/login/oauth2/code/google
till now. We can customize it to any other URL of our choice(/oauth2/callback/). Below is the spring security configuration for it. Make sure to update this property in google console and application.properties.
Our custom user info endpoint will make request to the provider user info endpoint and retrieve the user info such as name, email, image etc. To do so, we need to implement OAuth2UserService. Spring boot provides two different implementation for this. We will be implementing OidcUserService for OpenID Connect (Google) providers.
For Facebook, we need to implement DefaultOAuth2UserService which we will implement in next article. The implenentaion will fetch the user info and creates entry in our local database. And we have already configured our MySql DB support for this app. Below is the implementation.
A call to super() will amke the REST call to fetch the userinfo from the external provider(Google). After a success response, the user entry will be created in our local system. CustomOidcUserService.java
oauth2Login().authorizationEndpoint() allows configuring the Authorization Endpoint. It is used by the client to obtain authorization from the resource owner via user-agent redirection.
oauth2Login()
.authorizationEndpoint()
.baseUri("/oauth2/authorize")
.authorizationRequestRepository(customAuthorizationRequestRepository());
@Bean
public AuthorizationRequestRepository customAuthorizationRequestRepository() {
return new HttpSessionOAuth2AuthorizationRequestRepository();
}
At this point of time, if you try to run this application and follow above steps, you can see a new user entry in the DB.
Customizing Login Endpoint
By default, the OAuth 2.0 Login Page is auto-generated by the DefaultLoginPageGeneratingFilter. To override the default login page, configure oauth2Login().loginPage() and to start authentication with Google then you can redirect user to /oauth2/authorize/google. Here, /oauth2/authorize is the authorization endpoint that is configured above. You can visit this article for a Spring Security OAuth2 User Registration
To generate a custom JWT (Json Web Token) token, we require to overide the default AuthenticationSuccessHandler in spring security with our custom success handler. So far, our login process is done and now let us define our success handler. Inside the success handler we will be generating our JWT token.
While generating the JWT token, we will be re-using the features that we had developed in our previous article - Spring Boot JWT Auth. CustomAuthenticationSuccessHandler.java
Below is the implementation for generating JWT token. The implementation of JWT authentication in spring security is very much similar to this - . In next article, we will build the complete implementation of custom login with email and password and JWT token validation.
@Component
public class JwtTokenUtil implements Serializable {
public static final long ACCESS_TOKEN_VALIDITY_SECONDS = 5*60*60;
public static final String SIGNING_KEY = "devglan123r";
public static String generateToken(User user) {
Claims claims = Jwts.claims().setSubject(user.getEmail());
return Jwts.builder()
.setClaims(claims)
.setIssuer("https://devglan.com")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_VALIDITY_SECONDS*1000))
.signWith(SignatureAlgorithm.HS256, SIGNING_KEY)
.compact();
}
}
Conclusion
In this article, we implemented spring boot security google oauth and integrated JWT with it.Also, we looked into different ways we can customize the default behaviour of spring security 5 OAuth implementation. In the next article, we will be overriding the default login page and add support for custom login with email and password alng with JWT token validation.