Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stateless spring security oauth2 provider

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:

  • Disable server side sessions - for an API that is only responsible for OAuth2 authorization I don't think it is necessary to have a shared database for sessions.
  • Instead, Enable a remember-me cookie containing the user authentication, temporariliy during authorization.
  • Store the redirect url for the /login redirect at a different place
    • Is it possible to store this in the login form or user cookie? Or what would be an "sessionless" alternative?
  • Disable CSRF (I know how to do that and oauth2 has auth_codes which I think have a similar purpose. Just for completeness.)

Does that approach make sense? What changes are necessary?

like image 1000
Jan Avatar asked Jul 07 '16 13:07

Jan


2 Answers

  1. 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).

  2. Later the client application makes a request to your server for obtaining access token. In this request it provides the autorization code.

  3. 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.

like image 87
Alexandar Mitsev Avatar answered Sep 18 '22 18:09

Alexandar Mitsev


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:

  1. 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.

  2. 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.

like image 22
danielgpm Avatar answered Sep 18 '22 18:09

danielgpm