Lessons learnt: Of Spring Boot + OAuth2 + redirect URIs

TL;DR: make sure NGINX is setup correctly (proxy_set_header) before messing around with your code.

Scenario: Deploying a Spring Boot micro-service behind an NGINX reverse proxy gave us issues when using default Google OAuth2 configuration as described here , basically showing the “Redirect URI Mismatch” mentioned at the very end of the linked article

Trying the solution based on security.oauth2.client.pre-established-redirect-uri as mentioned in this article didn’t make any difference, looking at the debug spring security logs showed the framework was still redirecting requests to the default redirect URL

First Attempt (an interesting detour but ultimately failed attempt)

What did make a difference was the following application property we found from the spring boot documentation:

spring.security.oauth2.client.registration.google.redirect-uri-template: http://abc.example.com/login/oauth2/callback/google

However, it is important to set the redirectionEndpoint baseURI as part of the configuration, for example:

@Configuration
class OAuth2LoginSecurityConfig : WebSecurityConfigurerAdapter() {
    @Throws(Exception::class)
    override fun configure(http: HttpSecurity) {
        http
                .authorizeRequests()
                    .antMatchers("/login.html", "/favicon.ico")
                        .permitAll()
                    .anyRequest().authenticated()
                    .and()
                        .oauth2Login()
                            .redirectionEndpoint().baseUri("/login/callback/code/*")
    }
}

Note how the baseUri is set to only the path portion of the redirect-uri-template, and does not include the hostname.

Second Attempt (correct solution)

The above almost got us to the correct solution, however Spring Boot threw up the error of “invalid redirect uri“. Looking at the code, it transpires that Spring Oauth2 checks the redirect URI returned to it from the authentication provider (Google in our case) with a redirect URI it builds on the fly – using the hostname it detects from the original incoming HTTP Request (i.e. the request coming from the front-end).

Since this HTTP Request passes through an NGINX reverse proxy, Spring Boot was actually seeing “localhost” in the HTTP Host Header, and since localhost does not match the redirect-uri-template we set above (abc.example.com in our particular example), then Spring Boot throws up the invalid redirect uri error.

So it turns out the solution was really rather simple:

  1. Remove the changes introduced in the first attempt (i.e. the custom redirectionEndpoint settings and the redirect-uri-template)
  2. Instruct NGINX to copy the HTTP host header it received from the original client, into the HTTP hos header that it sends to the server, thereby preserving the HTTP Host header, like so:
proxy_set_header Host $http_host;

This is equivalent to Apache’s ProxyPreserveHost setting

Advertisements