3

I am adding oauth into an application and I'm running into the following error:

[invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 401 Unauthorized: [no body]

The project has a Spring Boot backend and an Eclipse rcp frontend. I'm attempting to authenticate using azure active directory as an authorization server. So far, I'm able to launch a browser widget in on startup of the eclipse application and successfully complete the authorization code request by pointing the browser to http://localhost:8080/oauth2/authorization/azure. After completing the authorization code request, the browser gets redirected to http://localhost:8080/login?error and displays the error above.

Dependencies from pom.xml

Built using spring boot with the following relevant dependencies:

  • spring-boot-starter-web v2.2.4
  • azure-active-directory-spring-boot-starter v2.2.1
  • spring-security-oauth2-client v5.2.1
  • spring-security-oauth2-jose v5.2.1
  • spring-security-oauth2-resource-server v5.2.1

Config from application.yml

We support multiple authorization servers, here is the fully configured azure client:

spring:
  security:
    oauth2:
      client:
        azure:
          client-id: XXX
          client-secret: XXX
          client-name: Microsoft
          scope: openid, https://graph.microsoft.com/user.read, profile
          authorization-grant-type: authorization_code
          redirect-uri: http://localhost:8080/login/oauth2/code/azure
          client-authentication-method: basic
          authentication-method: post
      provider:
        authorization-uri: https://login.microsoftonline.com/XXX/oauth2/v2.0/authorize
        token-uri: https://login.microsoftonline.com/XXX/oauth2/v2.0/token
        user-info-uri: https://graph.microsoft.com/oidc/userinfo
        jwt-set-uri: https://login.microsoftonline.com/dXXX/discovery/v2.0/keys

azure:
   activedirectory:
      tenant-id: XXX
      active-directory-groups: XXX
      allow-telemetry: false

websecurityconfig.java

@Configuration
@EnableConfigurationProperties
@EnableWebSecurity
@Order(1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                    [...]
                    .anyRequest().authenticated()
                    .and()
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                .oauth2Login();
    }

    [...]
}

Spring logs

Here is the complete stack trace from the moment I attempt to authenticate with my azure AD user credentials (shortened to fit body length requirements & with the authorization code censored ofc):

2020-02-19 16:10:33.925 DEBUG 19564 --- [qtp148813381-16] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@2dd6e039
2020-02-19 16:10:33.925 DEBUG 19564 --- [qtp148813381-16] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-02-19 16:10:33.925 DEBUG 19564 --- [qtp148813381-16] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-02-19 16:10:33.928 DEBUG 19564 --- [qtp148813381-20] o.s.security.web.FilterChainProxy        : /login/oauth2/code/azure?code=CODE&state=nqsFqxkkNzHJE5knQVdqFLjoPxg1MT_bcn7KzjKSFfU%3d&session_state=3ebe517e-d450-4d49-b8db-8afafe1fa37e at position 1 of 16 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.security.web.FilterChainProxy        : /login/oauth2/code/azure?code=CODE&state=nqsFqxkkNzHJE5knQVdqFLjoPxg1MT_bcn7KzjKSFfU%3d&session_state=3ebe517e-d450-4d49-b8db-8afafe1fa37e at position 2 of 16 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_SECURITY_CONTEXT
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: Session@3a690b15{id=node01xqnw7l82ne041bil2flqsn3vr0,x=node01xqnw7l82ne041bil2flqsn3vr0.node0,req=1,res=true}. A new one will be created.
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.security.web.FilterChainProxy        : /login/oauth2/code/azure?code=CODE&state=nqsFqxkkNzHJE5knQVdqFLjoPxg1MT_bcn7KzjKSFfU%3d&session_state=3ebe517e-d450-4d49-b8db-8afafe1fa37e at position 3 of 16 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.security.web.FilterChainProxy        : /login/oauth2/code/azure?code=CODE&state=nqsFqxkkNzHJE5knQVdqFLjoPxg1MT_bcn7KzjKSFfU%3d&session_state=3ebe517e-d450-4d49-b8db-8afafe1fa37e at position 4 of 16 in additional filter chain; firing Filter: 'LogoutFilter'
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', GET]
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/login/oauth2/code/azure'; against '/logout'
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', POST]
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /login/oauth2/code/azure' doesn't match 'POST /logout'
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', PUT]
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /login/oauth2/code/azure' doesn't match 'PUT /logout'
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.s.web.util.matcher.OrRequestMatcher  : Trying to match using Ant [pattern='/logout', DELETE]
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.s.w.u.matcher.AntPathRequestMatcher  : Request 'GET /login/oauth2/code/azure' doesn't match 'DELETE /logout'
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.s.web.util.matcher.OrRequestMatcher  : No matches found
2020-02-19 16:10:33.929 DEBUG 19564 --- [qtp148813381-20] o.s.security.web.FilterChainProxy        : /login/oauth2/code/azure?code=CODE&state=nqsFqxkkNzHJE5knQVdqFLjoPxg1MT_bcn7KzjKSFfU%3d&session_state=3ebe517e-d450-4d49-b8db-8afafe1fa37e at position 5 of 16 in additional filter chain; firing Filter: 'OAuth2AuthorizationRequestRedirectFilter'
2020-02-19 16:10:33.930 DEBUG 19564 --- [qtp148813381-20] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/login/oauth2/code/azure'; against '/oauth2/authorization/{registrationId}'
2020-02-19 16:10:33.930 DEBUG 19564 --- [qtp148813381-20] o.s.security.web.FilterChainProxy        : /login/oauth2/code/azure?code=CODE&state=nqsFqxkkNzHJE5knQVdqFLjoPxg1MT_bcn7KzjKSFfU%3d&session_state=3ebe517e-d450-4d49-b8db-8afafe1fa37e at position 6 of 16 in additional filter chain; firing Filter: 'OAuth2LoginAuthenticationFilter'
2020-02-19 16:10:33.930 DEBUG 19564 --- [qtp148813381-20] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/login/oauth2/code/azure'; against '/login/oauth2/code/*'
2020-02-19 16:10:33.930 DEBUG 19564 --- [qtp148813381-20] .s.o.c.w.OAuth2LoginAuthenticationFilter : Request is to process authentication
2020-02-19 16:10:33.930 DEBUG 19564 --- [qtp148813381-20] o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider
2020-02-19 16:10:33.931 DEBUG 19564 --- [qtp148813381-20] o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider
2020-02-19 16:10:34.273 DEBUG 19564 --- [qtp148813381-20] .s.a.DefaultAuthenticationEventPublisher : No event was found for the exception org.springframework.security.oauth2.core.OAuth2AuthenticationException
2020-02-19 16:10:34.275 DEBUG 19564 --- [qtp148813381-20] .s.o.c.w.OAuth2LoginAuthenticationFilter : Authentication request failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 401 Unauthorized: [no body]

org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 401 Unauthorized: [no body]
    at org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider.authenticate(OidcAuthorizationCodeAuthenticationProvider.java:148) ~[spring-security-o

2020-02-19 16:10:34.277 DEBUG 19564 --- [qtp148813381-20] .s.o.c.w.OAuth2LoginAuthenticationFilter : Updated SecurityContextHolder to contain null Authentication
2020-02-19 16:10:34.277 DEBUG 19564 --- [qtp148813381-20] .s.o.c.w.OAuth2LoginAuthenticationFilter : Delegating to authentication failure handler org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@2b0d857b
2020-02-19 16:10:34.277 DEBUG 19564 --- [qtp148813381-20] .a.SimpleUrlAuthenticationFailureHandler : Redirecting to /login?error
2020-02-19 16:10:34.277 DEBUG 19564 --- [qtp148813381-20] o.s.s.web.DefaultRedirectStrategy        : Redirecting to '/login?error'
2020-02-19 16:10:34.277 DEBUG 19564 --- [qtp148813381-20] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@2dd6e039
2020-02-19 16:10:34.277 DEBUG 19564 --- [qtp148813381-20] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-02-19 16:10:34.278 DEBUG 19564 --- [qtp148813381-20] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
[...]

Attemps to fix this error

I have tried all the solutions from this open issue: https://github.com/microsoft/azure-spring-boot/issues/526, including enabling oauth2AllowImplicitFlow in the azure portal manifest, to no avail.

If I print the authorization code on from the eclipse browser and create a token request to azure AD (with the azure postman collection) I get a successfull response with an bearer token.

So why am I getting a 401 Unauthorized when making my token request?

I would appreciate any suggestions on how to approach this problem. I'm desperatly looking for a solution and my next step is to try log the spring token request or inspect it with wireshark (going to have to decrypt the TLS connection since the endpoint for azure is https)

Thanks if you've read this far :)

phil
  • 313
  • 1
  • 4
  • 13
  • When exchanging the code for the token, what does the response look like? – NatFar Feb 19 '20 at 15:59
  • The response I get in postman looks like this: { "token_type": "Bearer", "scope": "profile openid email https://graph.microsoft.com/User.Read", "expires_in": 3599, "ext_expires_in": 3599, "access_token": "ACCESS_TOKEN", "id_token": "ID_TOKEN" } – phil Feb 19 '20 at 16:01
  • Any ideas why there's no HttpSession found? – NatFar Feb 19 '20 at 16:13
  • I don't know. Looking into it – phil Feb 19 '20 at 16:42
  • Have you referred to https://github.com/microsoft/azure-spring-boot/tree/master/azure-spring-boot-samples/azure-active-directory-v2-spring-boot-backend-sample? – Jim Xu Feb 21 '20 at 01:36
  • Yes I have read the sample project and the documentation for microsoft identity https://learn.microsoft.com/en-us/azure/active-directory/develop/) but still can't find the root cause of this error – phil Feb 21 '20 at 14:29
  • @phil Do you have any other concerns? – Jim Xu Feb 27 '20 at 02:30

3 Answers3

1

As mentionned in a comment on @Jim Xu's answer, I resolved this issue by changing the azure endpoints from v2 to v1. This is done by changing the endpoints e.g. http://login.microsoft.com/common/oauth2/v2.0/authorize becomes http://login.microsoft.com/common/oauth2/authorize as indicated in the v1 & v2 comparison.

For more info about v1 have a look at the docs

phil
  • 313
  • 1
  • 4
  • 13
1

How to get oauth2 V2 to work

First, note that microsoft V1 login does not work with private accounts! Thus, downgrading may not be an option for everyone.

For successful V2 configuration: I suggest using the discovery document - even if you dont use automated discovery, you can copy-paste the values: https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

In your case, the jwk-set-uri is set different than the json from above suggests, and should instead be: https://login.microsoftonline.com/common/discovery/v2.0/keys

Below is the full spring provider.azure configuration key which I use:

authorization-uri: "https://login.microsoftonline.com/<tenant>/oauth2/v2.0/authorize"
token-uri: "https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token"
user-info-uri: "https://graph.microsoft.com/oidc/userinfo"
jwk-set-uri: "https://login.microsoftonline.com/<tenant>/v2.0/keys"
user-name-attribute: "name"
user-info-authentication-method: "header"

Tested with spring-boot v2.1.2 and spring security 5.1.3.

tosh007
  • 111
  • 1
  • 5
0

According to my test, we can use the following code

My configuration file

spring:
  security:
    oauth2:
      client:
        registration:
           azure:
             client-id: xxx
             client-secret: xxx
             client-name: Azure
             client-authentication-method: basic
             provider: azure-oauth-provider
             scope: openid, https://graph.microsoft.com/user.read, profile
             redirect-uri: http://localhost:8080/login/oauth2/code/azure
             authorization-grant-type: authorization_code

        provider:
            azure-oauth-provider:
              authorization-uri: https://login.microsoftonline.com/<tenant id>/oauth2/v2.0/authorize
              user-info-uri: https://graph.microsoft.com/oidc/userinfo 
              token-uri: https://login.microsoftonline.com/<tenant id>/oauth2/v2.0/token
              jwk-set-uri: https://login.microsoftonline.com/<tenant id>/v2.0/keys
              user-name-attribute: name

azure:
  activedirectory:
    tenant-id: xxx
    active-directory-groups: ***

WebSecurityConfig.java

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .oauth2Login()
            .userInfoEndpoint()
            .oidcUserService(oidcUserService);
    }
}

enter image description here

enter image description here

Jim Xu
  • 21,610
  • 2
  • 19
  • 39