Implement the OAuth 2.0 Authorization Code with PKCE Flow

Implement the OAuth 2.0 Authorization Code with PKCE Flow

This tutorial shows you how to migrate from the OAuth 2.0 Implicit flow to the more secure Authorization Code with PKCE flow.

Vimal Prakash

Hi Micah Silverman , Great post,…
I have 1 doubt. Normally when it used in the native client we also send the refresh_token (if it is enabled) in the token response, are you recommending to allow refresh_token here also or ?

Micah

It’s up to the implementation as to whether or not to grant refresh tokens to a SPA. Okta chooses not to as you could have a potentially unlimited refresh token leak from a SPA.

Okta does provide a mechanism to get a new access token, but it’s outside the spec. By default, the okta-auth-js library will fetch a new access token by leveraging the okta session cookie that’s already set when you authenticated. This is the default. It’s also configurable, if you don’t want this behavior.

Vimal Prakash

ok got that.

Himanshu Arora

Is there an article pointing of achieving the PKCE implementation without using the okta-auth-js library?

M Khan

What IDE is this?

aaronpk

Yes! Here’s a post that describes how to do it in plain Javascript, no libraries required. https://developer.okta.com/…

Matt Raible

There is no IDE used in this tutorial. Maybe you’re referring to the screenshot of developer tools in Chrome?

David Curtis

Is there any reason why Okta doesn’t provide a PKCE option with non SPA web apps ?

aaronpk

You can do PKCE with a regular web app too in Okta, it just doesn’t show up as an option to require it in the admin UI. Try starting an OAuth flow with a pkce_challenge in the authorization request, and you’ll see that the pkce_verifier parameter is needed on the token request.

David Curtis

Thanks, It is a .net MVC app with the Okta libraries for OWIN integration. Would the pkce_challenge and pkce_verifier work with that ?

aaronpk

I don’t know if the libraries support it, but the server supports it directly anyway.

Hieu Ho Van

Can I implement in a way that when SPA receive the code and state, it forwards to the backend (Spring boot) to validate and exchange for the access token?

Currently, I got this error when trying to doing that way: "error=“invalid_request”, error_description=“Possible CSRF detected - state parameter was required but no state could be found”"

Appreciated your advise on this issue

Matt Raible

You can get the access token in the SPA itself and have your Spring Boot app validate it. Or you can package your Angular app in your Spring Boot app and let Spring Security do all the work.
The flow you’re asking for would require you to have an Authorization Server on your backend. This is possible, but not recommended. Friends don’t let friends write authentication. :slight_smile:

Hieu Ho Van

Hi Matt,

Thanks for you response. My original intention was letting Springboot doing all of the authentication. After authenticated successfully with Okta, Springboot will continue with existing authentication process to log the user into the system. Then finally return the AuthDTO back to Angular to initialize the App.

The configuration for approach #1 as below:
Angular:
- Issuer: xxx.okta.com/oauth2/default
- redirectUri: localhost:8080/oauth/okta/authorize
- Used OktaAuthService for the authentication part

Springboot:
- Use okta-spring-boot-starter with @EnableOAuth2Sso to handle the SSO authentication in ApiGateway service (using Zuul). Client-secret is storing here.
- Also implemented the CustomSuccessHandler.onAuthenticationSuccess to redirect to Auth service with the 8081 port. From there, it will call to several other services to complete the authentication

However, with that config, Angular could not capture the AuthDTO from springboot after succeed authentication. So I change the configuration a little bit. I still want to keep the client-secret from springboot side hence, need to get the access_token from springboot. The changed configuration for approach #2 as below (changed the redirectUri):

Angular:
Issuer: xxx.okta.com/oauth2/default
redirectUri: localhost:4200/oauth/okta/authorize

Spingboot: Still the same as above.

In Angular, implement the callback handler that obtain the code and state from Okta. Then call to ApiGateway localhost:8080/oauth/okta/authorize, manually passing the code and state. The purpose is to capture the AuthDTO returning from the ApiGateway.

However, I faced an issue with the error: “Possible CSRF detected - state parameter was required but no state could be found”. Debugged through the AuthorizationCodeAccessTokenProvider at getParametersForTokenRequest and can see that preservedState is null.

Could you suggest which is the safe way to perform the authentication that follows above requirement? I guess both approachs are having roadblocks which direct to following questions:

Approach 1: How Angular can handle the AuthDTO after the springboot fully doing the authentication with okta and itself.

Approach 2: How to overcome the issue with CSRF ?

Any finally, is there any better approach to achive this kind of authentication ?

Appreciate your help on this.

Matt Raible

To overcome the issue with CSRF, you have to configure Spring Security to send the token in a cookie that Angular can read. By default, it’s in an HTTP-only cookie and JavaScript can’t read it.


@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}

If you’re using Angular, that should be all you need to do. It supports XSRF Protection in its HttpClient.

I’m guessing your Spring Boot app is version 1.5.x? The reason I ask is because @EnableOAuth2Sso is no longer recommended in Spring Boot 2.x. See Migrate Your Spring Boot App to the Latest and Greatest Spring Security and OAuth 2.0.

You might be interested in my Java Microservices with Spring Boot and Spring Cloud tutorial too. It doesn’t have an Angular client, but it shows how to configure the backend for OAuth with our latest bits and Spring Boot 2.1.5.

Hieu Ho Van

I debugged and can see that the reason spring complains about the CSRF is this piece of code in AuthorizationCodeAccessTokenProvider

private MultiValueMap<string, string=“”> getParametersForTokenRequest(AuthorizationCodeResourceDetails resource, AccessTokenRequest request) {
MultiValueMap<string, string=“”> form = new LinkedMultiValueMap();
form.set(“grant_type”, “authorization_code”);
form.set(“code”, request.getAuthorizationCode());
Object preservedState = request.getPreservedState();
if ((request.getStateKey() != null || this.stateMandatory) && preservedState == null) {
throw new InvalidRequestException(“Possible CSRF detected - state parameter was required but no state could be found”);
} else {

In approach 1, if spring does the complete authentication flow, the preservedState is set before obtaining the accessToken. The whole flow as below:


1. Angular checks if user is logged in or not. If not kick user to Okta login page
2. User logged in successfully, Okta redirect to Spring with url: /api/oauth/okta/authorize
3. Spring Redirecting to 'https://dev-xxx.okta.com/oa…. At this point, Spring set the preservedState as “http://localhost:8080/api/oauth/okta/authorize
4. Spring receive the redirect from Okta with url: “/api/oauth/okta/authorize?code=ZZZ”
5. Spring obtains access_token from Okta through endpoint: https://dev-XXX.okta.com/oa…

However, in approach 2, step #1, #2 and #3 are handled by Angular, then I guess Spring complains about CSRF because it does not trust the request from Angular with url: “/api/oauth/okta/authorize?code=ZZZ”

I’m in a middle of mixing solutions that none of them is working. Could you help with below questions:

1. If I want to have Spring handle all authentication work (approach 1). How Angular can handle the AuthDTO after the springboot fully doing the authentication with okta and itself.?
2. If I want to have Angular do all authentication, do you have example Angular code that using PKCE flow?
3. Is there any better approach to achieve this kind of authentication ?

To answer you questions, I’m using springboot 2.0. I will change the implementation follow your suggestion. Thanks for that.

Matt Raible

It seems to me that you’re complicating things and making it harder than it needs to be.
I recommend packaging your Angular app in your Spring Boot app. That’s the most secure way as you can use Auth Code Flow on the server. Here’s an older blog post I wrote on how to do that. https://developer.okta.com/…
This is what JHipster uses for its OAuth and OIDC implementation.

Hieu Ho Van

Thanks Matt, however, packaging Angular app into Springboot app requires the redesign and seems to affect a lot. Do you have any other suggestion? If possible, can Angular handle all authentication using PKCE? If so, can you direct me to the sample ?

Thanks

Matt Raible

Yes, you can use PKCE with our Angular SDK. You just need to have pkce: true in your config.

You can create a new project with Angular CLI, commit it to Git, then use OktaDev Schematics to integrate our Angular SDK. It uses PKCE by default.

https://github.com/oktadeve…