This article describes how to create Spring Boot application with oauth2 authorization using password grant type. This grant type is appropriate for internal clients which we trust to get username and password from the user, like for example internal web UI or native mobile app.

Spring Security OAuth2: glitchy, obscure (dozen of different points to touch to configure everything together), badly documented (no single comment on configuration classes, very poor docs) and unalterable (private, final, they even don’t use spring dependency injection concepts and prefer instantiation of key classes using their constructors in private methods). This is the second worst project from Spring I’ve seen after Spring Webflow.

Furthermore there’s just lack of good examples. There’s a huge development in Spring Boot recently (2.0.0 released) and the most of examples I’ve found about Spring Boot + OAuth2 are already outdated and don’t work. Moreover people focus on things apparently different from those I need. I’ve found tons of examples how to authenticate to your app with google or facebook. Another huge package of examples are authorization server implementations going through the access code flow with approval screen. Apparently almost nobody wants to create application with standard web UI authenticated to oauth2 server with standard username and password with only optional access code flow for external clients.

Honorable mentions

Before begin I’d like to mention the only two resources worthwhile reading I’ve found. One is OAuth 2 Developers Guide which explains not much but you can treat this as a short list of hook points. The second one is very good Spring Security and Angular article. However if you print this article to PDF you’ll see it has over 100 pages, while OAuth2 is a really simple flow. The capacity of this article in my opinion show how difficult to explain is Spring OAuth2 implementation, which is meant to be simple as possible.

The goal

In our journey we will try to go step by step through configuring Spring OAuth2 for the following case: we want to have an application with (possibly) multiple internal clients for which it’s safe for user to enter his password, what should be accomplished by password oauth2 grant type. These clients are our applications, like standard web UI or mobile clients. For these client, especialy the web UI, we want the application to behave like standard session-based application which users are used to. This means in particular automatic logout after 30 min.

Building authorization server

Let’s start with the book of building authorization server from scratch:

In userDetailsService() we create example user details service providing users and their passwords. This bean usually uses some database, but in this example we use in-memory implementation. We create here a single user with ROLE_USER authority.

With the internal_secret password required to access /oauth/token endpoint (with username: internal taken from client name).

This client allows to access some hypothetical oauth2 scope indicated by scopes() method.

This client supports two grant types in authorizedGrantTypes():

password - returns the access token after explicit username and password is given.

refresh_token - allows to get new access token using refresh token.

This client won’t display approval screen to get access to selected oauth2 scopes, but autoApprove-s all his scopes.

The access token will expire after 10 mins (accessTokenValiditySeconds) while the refresh token will expire after 30 mins (refreshTokenValiditySeconds). The refreshTokenValiditySeconds reflects standard session cookie expiration time for session-based web apps.

In configure(AuthorizationServerEndpointsConfigurer) we just configure previously created UserDetailsService to be used with authorization endpoint.

After reading some docs it turns out configure(AuthorizationServerEndpointsConfigurer endpoints) metod needs authentication manager setup to support password grant type. However, AuthenticationManager is no longer accessible as a bean in Spring Security. This is really obscure as I discussed here. Anyway, to get it working we need to create yet another security configuration providing this bean:

And check if it works again with tha same CURL, and we get … the login page.

This is the point at which I got stuck for a longer time, but finally decided to do one trick, which is using @EnableResourceServer on authorization server configuration and creating simple HttpSecurity config. So, it looks the authorization server cannot work properly without resource server…

However you can spend hundreds of hours on providing various configuration here, beat your head against the wall etc, and nothing works. The Spring team decided to provide a trap here, only for persevere people.

The second thing making it really difficult to jump over is that it still doesn’t work after this fix, because our previous CURL shows the following:

After another iteration of digging in the code, this time with trace debug level it turned out that Spring OAuth2 expects getting a password in encoded form of DelegatingPasswordEncoder, which consists of the encoder string and then the password encoded (for example: {bcrypt}$2a$10$Y[...]). This is weird because I’d need to encode this password in javascript on client side. Fortunately we can change it to default plain password (which is safe to be sent using SSL) with yet another security config (this is another AuthorizationServerConfigurer interface method):

At this exact moment you’re so happy you’ve got it, so “why haven’t I implemented whole this stuff myself in 2 hours or so” thought doesn’t even come to you mind.

Refreshing the token

When you implement the client for oauth2 authorization server you use the access token in Authorization: Bearer [token] header to authorize your requests. But you also always need to consider the access token expiration time. If it expires (in this examples after 10 mins) you need to get the new access token using refresh token:

The problem here is that refresh token (04e1f577-8ab2-4504-887b-90c14b1ab5b7 in the example) is still the same. This is default behavior for external application access scenarios, where refresh token has a very long lifetime, for example 14 days, and once every 14 days you need to authorize the external application again. However, if you want to authorize native web client and simulate standard session cookie behavior, which is getting logged out 30 mins after last click, the constant refresh token will be a problem. Consider the user who logs in and then is using the app more than 30 mins: he will be unconditionally logged out after some time (30 min in the example) even if he is actively clicking in the app, because this is the lifetime of refresh token.

Better approach here is to use non-reusable refresh token, what can be configured in AuthorizationServerEndpointsConfigurer using reuseRefreshTokens() method:

Now, after obtaining a new access token with the refresh token, the returning refresh token will be different and its timeout starts again. This way we can simulate standard web application behavior, and user will be logged out some time after his last click.

Of course there’s no simple solutions in Spring OAuth2 because in configure(ClientDetailsServiceConfigurer) we can for example configure another client (or even multiple clients dynamically loaded from db) which is appropriate to handle external client application accomplishing access_token grant type, and for which we may want to apply long-lived and non-reusable refresh token. Spring OAuth2 won’t allow that because it encapsulated refresh token policy in one configure(AuthorizationServerEndpointsConfigurer) method applied for all oauth2 clients.

Have a lot of fun time hacking this :)

Getting the data from the local resource server

Having such a great thing like forced resource server in my authorization server I wouldn’t be myself to not to check how does it work to get the secured resource (this is the local invocation of the service which also implements the authorization server). I’m checking this with this simple controller:

The remote solution is still to be done here, this is why I mark this subject with [todo]. For the purpose of this article I wanted to figure out something different than my solution in the current project (see below). The first step is the standard implementation doing calls from a remote service to the authorization server confirming access tokens. The second one is to use JWT-based implementation.