Spring

 Problems using Spring Security with an Okta OIDC JWT

Dave Westerman's profile image
Dave Westerman posted Nov 09, 2019 12:51 AM

I am trying to set up a Spring Boot REST application using Spring Security. Our front-end is authenticating our users with OIDC in Okta, and is passing the JWT in the Authorization Bearer token.

 

I am referencing the following example, and it seems to be a very simple setup:

https://docs.spring.io/spring-security-oauth2-boot/docs/current/reference/html/boot-features-security-oauth2-resource-server.html

 

Our Spring Boot application is only going to be a Resource Server, so I think I only need to follow the directions in sections 2.1 to 2.2.3. I have `@EnableResourceServer` in my application, and I have the following in my `application.yml`:

spring: security: oauth2: resource: jwt: key-set-uri: https://dcsg-hub-dev.okta.com/oauth2/v1/keys

 

 

When I call my app using Postman, I am getting a 401 error:

{ "error": "invalid_token", "error_description": "Invalid access token: eyJraWQ..." }

 

 

It shows the valid token, which I can verify by calling the Okta `userinfo` endpoint. Unfortunately, it does not give me any other information.

 

I ended up setting breakpoints in the `org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter` class. Stepping into the code called from this location, the error is being caused at code in `org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager`:

public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (authentication == null) { throw new InvalidTokenException("Invalid token (token not found)"); } else { String token = (String)authentication.getPrincipal(); OAuth2Authentication auth = this.tokenServices.loadAuthentication(token); if (auth == null) { throw new InvalidTokenException("Invalid token: " + token);

 

 

Which calls the following code in the same class:

public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException, InvalidTokenException { OAuth2AccessToken accessToken = this.tokenStore.readAccessToken(accessTokenValue); if (accessToken == null) { throw new InvalidTokenException("Invalid access token: " + accessTokenValue);

 

It seems that the code is using the token itself as a key to retrieve the token from a hash map. As you can see above, it is using `getPrincipal()` to get the 'token'. It seems to me it thinks the token should be a user ID, which it is trying to use to find the user's token. It seems odd that it is using the token as a key to get the value, when the value is already there.

 

Any ideas?

 

Daniel Mikusa's profile image
Daniel Mikusa

It must think you are using opaque tokens. That code you referenced looks like the DefaultTokenServices class, which from the Javadocs is a `Base implementation for token services using random UUID values for the access token and refresh token values`.

 

https://github.com/spring-projects/spring-security-oauth/blob/7bfe08d8f95b2fec035de484068f7907851b27d0/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/token/DefaultTokenServices.java#L227

 

Do you have the spring-security-jwt dependency included?

 

>For JWT support, you also need spring-security-jwt.

 

https://docs.spring.io/spring-security-oauth2-boot/docs/current/reference/html/boot-features-security-oauth2-resource-server.html#dependencies-2

 

You also have `jwt` on line 5 of your properties snippet above, but the doc says `jwk`. That might cause it to miss that config property.

spring: security: oauth2: resource: jwk: key-set-uri: https://idp.example.com/.well-known/jwks.json

If that doesn't help, it might be good to turn up the log level for `org.springframework.security` to DEBUG and see if you see anything else. In particular, around start up and how it's configured things. The Actuator beans endpoint might be helpful too, you could see which beans got configured, or `--debug` start argument.

 

Hope that helps!

Daniel Mikusa's profile image
Daniel Mikusa

The idea with the log level is to look at how security is being configured in your app and try to follow along and make sure that it's being configured as expected. It can also help to trace into the code if you're seeing an unexpected log entry. You can look that up and see why it's taking that code path.

 

Looking at the `--debug` output or the beans Actuator endpoint can help give you a state of how Spring Boot has auto configured your app, which can also help understand the state of things when an app isn't configured as expected.

 

Sorry for being vague. It's tricky without seeing your app.

Daniel Mikusa's profile image
Daniel Mikusa

Oh, I also noticed that the docs you're referencing is for a deprecated project. With Spring Boot 2+, it's recommended to go with Spring Security's built-in support. Sorry, the naming is a little confusing, but this explains the difference.

 

https://docs.spring.io/spring-security-oauth2-boot/docs/current-SNAPSHOT/reference/htmlsingle/#d5e5

 

There are several example projects for setting up a resource server with Boot 2 + Spring Security, see here.

 

https://github.com/spring-projects/spring-security/tree/5.2.1.RELEASE/samples/boot

 

Hope that helps!

Dave Westerman's profile image
Dave Westerman

I forgot to add that I am using PKCE.

Dave Westerman's profile image
Dave Westerman

Thanks, Daniel! I do have `spring-security-jwt` in my dependencies. I did change that config key from `jwt` to `jwk`, but it doesn't seem to have made a difference. I also removed the `@EnableResourceServer` annotation, since I saw elsewhere that it's not needed for Spring 2.x. When I got rid of that, I just get a regular 401 error. I am running with log level set to `TRACE`, but I need to wade through all the data, I'm not quite sure what . I'm looking for yet.

Dave Westerman's profile image
Dave Westerman

Thanks again, Daniel!

 

So, I followed the doc that you linked to, and now I have this in my `application.yml`:

 

spring: security: oauth2: resourceserver: jwt: issuer-uri: https://dcsg-hub-dev.okta.com jwk-set-uri: https://dcsg-hub-dev.okta.com/oauth2/v1/keys

 

This is now definitely calling the JWK URI, whereas before it wasn't. However, now I am getting the following error:

 

Caused by: com.nimbusds.jose.proc.BadJOSEException: Signed JWT rejected: Another algorithm expected, or no matching key(s) found

 

I set breakpoints in that code, and for whatever reason, the `kid` that comes back in the token/JWT from Okta does not match any of the keys that are in the Okta `jwk-set-uri`. I think I need to talk to Okta about this.

 

Dave Westerman's profile image
Dave Westerman

Never mind, I found out from Okta that this feature does not work unless you set up your own authorization server on Okta with API Access Management.

Daniel Mikusa's profile image
Daniel Mikusa

Thanks for following up and sharing what you found!