I want to setup a simple OAuth2 provider based on spring-boot, spring-security and spring-oauth2.
I got everything working on a single instance machine: For an OAuth2 authorization, the user is sent to /oauth/authorize
. Most user's are not logged in so they are redirected to /login
by spring security and then back t /oauth/authorize
to finish the authorization.
In the default configuration, spring-security sets a cookie in the user's browser with a session-id and stores session data in-memory.
public static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.and()
.formLogin()
.loginPage("/login")
.permitAll();
}
[...]
In order to enable load-balancing and blue-green deployments without loosing user sessions, (I think) I have to perform the following steps:
/login
redirect at a different place
Does that approach make sense? What changes are necessary?
After the user makes a login to your provider, you generate an authorization code which is send to the client application (via the redirect (callback) url).
Later the client application makes a request to your server for obtaining access token. In this request it provides the autorization code.
At that point you need to be able to compare the authorization code sent by the client application with the one you generated on first place. This is where you need a shared memory.
If you look at this section of the protocol section-4.1 you need the shared memory between point C and D.
This can not be achieved with anything outside of your servers, because this is the point where you verify that the client application is authorized.
Similar is the case with the access and refresh tokens later in the process.
For the login step (point A and B) - it looks fine to have the redirect url (and client state - see section-4.1) inside the login form. If this is the only place where the session is used - you can get rid of it. But you will still need shared memory (shared database) for the authorization code.
This is a classic problem of distributed session storage. First of all, the concepts of "session" (session ids and cookies) combined with "stateless" is kind of a contradiction.
OAuth2 is supposed to be a "stateless" delegated authorization framework provided you persist the initial input request (including redirect url) at the server side before generating the access code.
Leaking those details to cookies before receiving credentials could be exposing you to security exploits. You could mitigate the risk by making sure that the cookie is HttpOnly (not accessible by JS) and secure (released over httpS only), but I would not recommend that approach anyways.
About your other point: Spring Security’s remember-me feature is designed to carry a reference to the authentication credentials only, not the details on the initial auth2 requests. Moreover, the persisted options (PersistentTokenBasedRememberMeServices) only supports memory (single node) and jdbc flavors by default.
Adjusting those for your needs will required considerable changes. Doable but requires a lot of effort.
In my experience, there are two alternatives that comes to mind:
Configure sticky sessions using a front-load balancer (e.g: haproxy, nginx, F5, etc…). The user session will be tied to the node where the credentials where submitted. The implication is that if that node goes down; the user will have to re-authenticate to create new access tokens, but the access tokens already given should be fine if used against other nodes.
Configure/implement a transparent distributed web session storage. Some distributed memory storage providers (e.g.: hazelcast ) offer plugins that be configured to the application servers to make this transparent for the user. There is some added overhead implied on this, but there is almost no additional code needed to satisfy your requirement.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With