Skip to content
This repository has been archived by the owner on Oct 24, 2024. It is now read-only.

Okta-react support for Auth Flow with PKCE #348

Closed
4 of 9 tasks
arrghh1 opened this issue Nov 14, 2018 · 36 comments
Closed
4 of 9 tasks

Okta-react support for Auth Flow with PKCE #348

arrghh1 opened this issue Nov 14, 2018 · 36 comments

Comments

@arrghh1
Copy link

arrghh1 commented Nov 14, 2018

I'm submitting this issue for the package(s):

  • jwt-verifier
  • okta-angular
  • oidc-middleware
  • okta-react
  • okta-react-native
  • okta-vue

I'm submitting a:

  • Bug report
  • Feature request
  • Other (Describe below)

Current behavior

The Okta-react library currently supports OAuth 2.0 Implicit Grant authorisation flow.

Expected behavior

The Okta-React library should support OAuth 2.0 Authorisation with PKCE as recommended here: https://tools.ietf.org/html/draft-ietf-oauth-security-topics-09#section-2.1.2

Extra information about the use case/user story you are trying to implement

Our organisation's security architects are mandating Auth Code with PKCE, due to the potential known attacks on implicit grant.

@jmelberg-okta
Copy link
Contributor

This is currently a platform feature gap, as the OAuth 2.0 /token endpoint doesn't allow requests from browser clients due to a number of known attack vectors.

Our API teams are actively working on opening up this endpoint, however there is no ETA on when we can expect to see this feature gap addressed.

@reste85
Copy link

reste85 commented Jan 7, 2019

Hi all,
how is this related with the following issue?
okta/okta-auth-js#78

thank you!

@mraible
Copy link

mraible commented Jan 7, 2019

@reste85 It's the same issue. The /token endpoint needs to allow CORS to do PKCE in a browser.

@ericfurspan
Copy link

Hello,
In the meantime, until this feature gap is implemented, I'm wondering what Okta recommends for implementing Auth code flow with PKCE within a React app (SPA)?

Is the only way to forfeit Okta's JS/React SDK's and instead configure the app in Okta dashboard as a "Native app" along with manually hit the /authorize endpoint (without any SDK)?

@nbarbettini
Copy link
Contributor

@quanda That's correct (for now). Here's what we plan to do:

  1. Roll out server support for PKCE on browser apps (making progress).
  2. Update our JS SDKs to support authorization code + PKCE as an option alongside Implicit.

This issue is the place to watch for updates.

@rcollette
Copy link

It seems that CORS/PKCE is now supported.

Are there plans to start active work on this?

https://developer.okta.com/blog/2019/05/01/is-the-oauth-implicit-flow-dead

@swiftone
Copy link
Contributor

swiftone commented Jun 18, 2019

Are there plans to start active work on this?

Active work is already underway. PKCE support has been added to AuthJS (though an npm publish is still awaiting some coordination): https://github.com/okta/okta-auth-js/blob/master/README.md#pkce-oauth-20-flow

Updating the various Front-end frameworks will also follow Soon(:tm:)

@rcollette
Copy link

I see that there is active work for React, is there anything active going on for the angular client?

@swiftone
Copy link
Contributor

@rcollette - Yes, the Angular SDK is slated to add PKCE options following the React SDK.

@swiftone
Copy link
Contributor

Okta React SDK PKCE Support is released in v1.2.2 (credit to @aarongranick-okta who implemented it). The other SDKs and related samples are up next.

@kkmathigir
Copy link

All, we have a React.js based SPA and we want to do SSO using OKTA. This capability "Auth flow with PKCE" seem to be the best approach and advanced and appropriate than the implicit flow. However, I have a question on how to handle the resource server authentication of the resource along with the SPA. Any suggestions. My scenario is:
A) React based app (SPA) hosted as static content along with okta react support for Auth Flow with PKCE.
B) React app invokes APIs exposed through APIGEE API Gateway.
C) API's are hosted on springboot as microservices.
In this scenario, should i configure my springboot as well with Okta security and my react app send the access token to springboot to validate/authenticate the resource access?
Apologies, if this is not the right issue to post this on, but would need some help on my scenario.

@mraible
Copy link

mraible commented Aug 29, 2019

Hello @kkmathigir. Yes, you can configure your Spring Boot app with Okta and have React send the access token. Our Spring Boot Starter makes that pretty easy to do. I've written a couple blog posts about it on https://developer.okta.com/blog. For example:

@kkmathigir
Copy link

kkmathigir commented Aug 29, 2019

Hello @kkmathigir. Yes, you can configure your Spring Boot app with Okta and have React send the access token. Our Spring Boot Starter makes that pretty easy to do. I've written a couple blog posts about it on https://developer.okta.com/blog. For example:

Appreciate your response @mraible . We will try this out and get back. Let the React do the 2 legged auth with authorization flow and PKCE and get the JWT/access token. the same access token is expected to be passed on to spring boot app. the spring boot app need to be configured with okta security as well. Spring boot app will validate the JWT and allow resource access. - Please confirm if my understanding is correct. Thanks!
Note: My react app will be hosted outside of springboot in a AWS S3 and springboot app on a EC2 container.

@mraible
Copy link

mraible commented Aug 29, 2019

Yes, your understanding is correct. The only thing more secure is to package your React app with your Spring Boot app and use auth code flow with Spring Boot. That's how we do it in JHipster (a React + Spring Boot application generator).

The 2nd blog post I referenced shows how to do that, in the Configure Maven to Build and Package React with Spring Boot section.

@kkmathigir
Copy link

ding is correct. The only thing more secure is to package your React app with your Spring Boot app and use auth code flow with Spring Boot. That's how we do it in JHipster (a React + Spring Boot application generator).

Thanks @mraible . Can you clarify more on what you mean by "more secure". What will we lack with following #348 approach of React with auth flow with pkce vs packaging react app with springboot app (api). As such, I thought auth flow with pkce in a static react app will be secure enough than the previous implicit flow and i no more have to bundle the react app with spring boot. As such, we are already bundling react app with spring boot app, but has overhead in terms of making releases for react and api and everytime both of these components gets deployed vs i want to have a separate release cycles for react changes and api/springboot changes.

@swiftone
Copy link
Contributor

@rcollette et al -

PKCE flow support has been added to the the various Okta JS front end SDKs (okta-angular 1.2.2, okta-react 1.2.3 , okta-vue 1.2.3) and their associated sample repos ( Credit and props to @aarongranick-okta )

I'm closing this ticket as the issue is resolved.

@mraible
Copy link

mraible commented Sep 5, 2019

Can you clarify more on what you mean by "more secure".

@kkmathigir Packaging the apps in the same artifact is more secure because you can use Spring Security's OAuth support with a client secret and the communication to your authorization server happens on the back channel. This means the tokens never get passed over the front channel and the end user never sees the exchange happening.

@kkmathigir
Copy link

Can you clarify more on what you mean by "more secure".

@kkmathigir Packaging the apps in the same artifact is more secure because you can use Spring Security's OAuth support with a client secret and the communication to your authorization server happens on the back channel. This means the tokens never get passed over the front channel and the end user never sees the exchange happening.

Thanks for your response @mraible . It makes sense.

@jasonrberk
Copy link

@mraible I'm reading all your posts, but none of them seem to answer the questions around using Okta with a pure Spring Boot resource server. it all basically boils down to a simple fact:

I DO NOT HAVE A CLIENT SECRET

I have a vanilla JS UI served from S3 that uses https://www.npmjs.com/package/@okta/okta-auth-js to make use of PKCE and auth my user at our corporate Okta instance.

My UI works as expected and I'm getting both an ID and Access Token

now, in my spring boot app that hosts REST API endpoints, I want to authenticate all requests have a Bearer token in the Authorization header.

what Okta deps should I use?
what configuration do I need?

I've spent three days trying to make sense of these different Git repos:

I can't find a single example that doesn't expect a client secret at some point.

is okta/okta-spring-boot#132 still an issue...cause it sure seems like it is?

how about okta/okta-spring-boot#147

Please provide some sanity to this mess of code samples and out of date documentation.

@mraible
Copy link

mraible commented May 21, 2020

You don't need a client ID or secret with Spring Boot if you just want JWT validation. All you need is the Okta Spring Boot starter and an issuer defined. See an example in our Java REST API blog post.

As far as PKCE support in our starter, that's an open issue.

@jasonrberk
Copy link

As far as PKCE support in our starter, that's an open issue.

Does that mean I won't be able to actually do the authentication? Or does that mean if I'm serving my client from my server I wouldn't be able to use PKCE?

@mraible
Copy link

mraible commented May 21, 2020

There's two different concepts: authentication and authorization. Authentication means when you hit your secured endpoint, you're redirected to Okta to log in, then back to your endpoint. Authorization is simply validation of the bearer token to make sure it's valid. If you hit the endpoint with curl or your browser, you'll get a 401 and won't be redirected.

PKCE is all about authentication. If you're setting up a resource server, you only need authorization, not authentication.

@jasonrberk
Copy link

Yes, my JS client is what issues the redirect to okta, handles the return with the code and ultimately stores tokens in browser storage.

My API simply needs to validate the token passed in is valid and was issued for the client that was used by the UI. I don't want tokens issued by okta for other applications to be allowed to access my applications resource server. I read on one of the numerous blog posts that by providing okta spring boot the client ID it would perform that check

@jasonrberk
Copy link

And just to be clear I don't want my resource server to ever redirect me to okta.... It should simply return the 401.

@aarongranick-okta
Copy link
Contributor

@jasonrberk
Copy link

Thanks @aarongranick-okta that looks helpful. Now I just need to figure out the correct version of your starter to use with my app as it appears there are compatibility issues at certain versions based on the open issues mentioned above

@mraible
Copy link

mraible commented May 22, 2020 via email

@aarongranick-okta
Copy link
Contributor

@jasonrberk The access token is in a standard "JWT" format and it can be verified using any library which understands it. So you don't necessarily need to work with the springboot, although it probably offers some conveniences. I think (but am not 100% sure) that our SDK is using jjwt under the covers: https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt

To verify the token you will need to cryptographically verify the signature. This is not the 'clientSecret' that is used by server-side OAuth applications (You are using PKCE in a SPA/client-side application, which is totally fine).

The (RSA) key will come from a "well known" endpoint. For example: https://aarongranick.oktapreview.com/.well-known/openid-configuration gives the "jwks_url":
https://aarongranick.oktapreview.com/oauth2/default/v1/keys

These details are handled for you by the higher level SDK, but may be helpful to understand, especially if you decide to use a lower-level SDK

@jasonrberk
Copy link

@mraible

org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE

@aarongranick-okta

I thought the whole idea was to add the okta SB starter???

What's the difference between the starter and the SDK and why would I choose to use one over the other.

Surely the idea of protecting a resource server with an OAuth 2 token can't possibly be this nuanced or difficult. as I shared with Matt I've yet to see an example that assumes pure oauth2 oidc protected resource server. most of the tutorials I've seen thus far spent more time setting up react or graphQL or some other external framework or dependency and very little time actually showing you how to use the okta tooling.

I'll be digging into this pretty much all day tommorow and I do greatly appreciate everyone's help

@jasonrberk
Copy link

I keep getting this error:

2020-05-22 10:57:50.252 user:[] DEBUG 36265 --- [nio-8080-exec-1] .o.s.r.w.BearerTokenAuthenticationFilter : Authentication request for failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: An error occurred while attempting to decode the Jwt: Couldn't retrieve remote JWK set: org.springframework.web.client.HttpClientErrorException$NotFound: 404 Not Found

@aarongranick-okta
Copy link
Contributor

Sounds like the issuer url may not be set correctly. It should be the url for your authorization server (okta org)/oauth2/(auth server id)

@jasonrberk
Copy link

jasonrberk commented May 22, 2020

in bootstrap.yml:

okta:
  oauth2:
    issuer: https://dev-225831.okta.com/oauth2/default

here's my okta account:

Screen Shot 2020-05-22 at 11 14 01 AM

and here's the error I'm getting:

Authentication request for failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found

and here's my decoded token

{
  "ver": 1,
  "jti": "AT.DhW7_5RtvHlSxw_m_WQEK-2R0qfmCkYcZTLGq2pR5Eo",
  "iss": "https://dev-225831.okta.com",
  "aud": "https://dev-225831.okta.com",
  "sub": "[email protected]",
  "iat": 1590159909,
  "exp": 1590163509,
  "cid": "0oa3wrjdl999qKeTP357",
  "uid": "..........",
  "scp": [
    "openid",
    "profile",
    "email"
  ]
}

@jasonrberk
Copy link

I created a fresh spring boot project following this guide:

https://developer.okta.com/docs/guides/protect-your-api/springboot/before-you-begin/

I turned up the spring security logs and see this:

2020-05-22 11:51:36.610 DEBUG 37122 --- [nio-8080-exec-1] .o.s.r.w.BearerTokenAuthenticationFilter : Authentication request for failed!

org.springframework.security.oauth2.server.resource.InvalidBearerTokenException: An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found
	at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.authenticate(JwtAuthenticationProvider.java:86) ~[spring-security-oauth2-resource-server-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter.doFilterInternal(BearerTokenAuthenticationFilter.java:124) ~[spring-security-oauth2-resource-server-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:92) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
Caused by: org.springframework.security.oauth2.jwt.BadJwtException: An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found
	at org.springframework.security.oauth2.jwt.NimbusJwtDecoder.createJwt(NimbusJwtDecoder.java:162) ~[spring-security-oauth2-jose-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.oauth2.jwt.NimbusJwtDecoder.decode(NimbusJwtDecoder.java:126) ~[spring-security-oauth2-jose-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport.decode(NimbusJwtDecoderJwkSupport.java:92) ~[spring-security-oauth2-jose-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.authenticate(JwtAuthenticationProvider.java:84) ~[spring-security-oauth2-resource-server-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	... 55 common frames omitted
Caused by: com.nimbusds.jose.proc.BadJOSEException: Signed JWT rejected: Another algorithm expected, or no matching key(s) found
	at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:384) ~[nimbus-jose-jwt-8.9.jar:8.9]
	at com.nimbusds.jwt.proc.DefaultJWTProcessor.process(DefaultJWTProcessor.java:330) ~[nimbus-jose-jwt-8.9.jar:8.9]
	at org.springframework.security.oauth2.jwt.NimbusJwtDecoder.createJwt(NimbusJwtDecoder.java:141) ~[spring-security-oauth2-jose-5.3.2.RELEASE.jar:5.3.2.RELEASE]
	... 58 common frames omitted

2020-05-22 11:51:36.611 DEBUG 37122 --- [nio-8080-exec-1] 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@63f90e63
2020-05-22 11:51:36.612 DEBUG 37122 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-05-22 11:51:36.612 DEBUG 37122 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

@aarongranick-okta
Copy link
Contributor

@jasonrberk Thanks for posting the detailed information. Your access token shows the issuer that minted it:

"iss": "https://dev-225831.okta.com",

I think this might be the problem. Looks like the frontend app is configured to use the okta org as the issuer, rather than the custom authorization server. The frontend SPA app should be configured with the same exact issuer as the backend springboot project. So the issuer should be set to https://dev-225831.okta.com/oauth2/default in the frontend app config.

@jasonrberk
Copy link

jasonrberk commented May 22, 2020

ok, now how do I get the authenticated user information.... It appears to have auth'd me, but now I get this error.... seems the injected OidcUser is NULL????

2020-05-22 12:31:10.001 DEBUG 37984 --- [nio-8080-exec-1] o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/private/test'; against '/api/private/**'
2020-05-22 12:31:10.002 DEBUG 37984 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Secure object: FilterInvocation: URL: /api/private/test; Attributes: [authenticated]
2020-05-22 12:31:10.002 DEBUG 37984 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Previously Authenticated: org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken@284ceebf: Principal: org.springframework.security.oauth2.jwt.Jwt@b4f5e112; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: SCOPE_openid, SCOPE_email, SCOPE_profile
2020-05-22 12:31:10.006 DEBUG 37984 --- [nio-8080-exec-1] o.s.s.access.vote.AffirmativeBased       : Voter: org.springframework.security.web.access.expression.WebExpressionVoter@330e1c5f, returned: 1
2020-05-22 12:31:10.006 DEBUG 37984 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : Authorization successful
2020-05-22 12:31:10.006 DEBUG 37984 --- [nio-8080-exec-1] o.s.s.w.a.i.FilterSecurityInterceptor    : RunAsManager did not change Authentication object
2020-05-22 12:31:10.006 DEBUG 37984 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : /api/private/test reached end of additional filter chain; proceeding with original chain
2020-05-22 12:31:10.018  INFO 37984 --- [nio-8080-exec-1] c.n.pulse.SampleController               : auth user: null
2020-05-22 12:31:10.025 DEBUG 37984 --- [nio-8080-exec-1] 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@10f49e83
2020-05-22 12:31:10.025 DEBUG 37984 --- [nio-8080-exec-1] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
2020-05-22 12:31:10.031 ERROR 37984 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
	at com.nextgearcapital.pulse.SampleController.testPublic(SampleController.java:23) ~[classes/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.6.RELEASE.jar:5.2.6.RELEASE]

my application.yml is

okta:
  oauth2:
    issuer: https://dev-225831.okta.com/oauth2/default
    audience: api://default
logging:
  level:
    ROOT: info
    org.springframework.security: debug

and my sample controller is

@Slf4j
@RestController
@RequestMapping(value = "/api")
public class SampleController {

    @GetMapping("/private/test")
    public String testPublic(@AuthenticationPrincipal OidcUser oidcUser) {
        log.info("auth user: {}", oidcUser);
        return "welcome " + oidcUser.getEmail();
    }
}

and my security config is

@Configuration
public class OktaOAuth2WebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // Require authentication for all requests under /api/private
                .antMatchers("/api/private/**").authenticated()
                .and()
                .oauth2ResourceServer().jwt();

        // process CORS annotations
        http.cors();

        // force a non-empty response body for 401's to make the response more browser friendly
        Okta.configureResourceServer401ResponseBody(http);
    }
}

@jasonrberk
Copy link

the discussion in okta/okta-spring-boot#132 leads me to think I need to setup users, groups, apps and Auth Servers in my okta instance to get what I want....

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests