9

Since PKCE is now the recommended method of authorisation over the implicit flow, I'm looking for best practice on handling code verifier and recommendations on how this might be done. On high level PKCE Authorisation flow consist of:

  1. Generate code_verifier on client side
  2. Generate code_challenge from (1)
  3. hit /authorise with code_challenge which redirect to select idp and in callback there's a code
  4. use code from (3) along with code_verifier to exchange for access token

Question is, in step 3, before the application redirect to authorisation server and then the idp, one has to store the code_verifier somewhere. Where is that somewhere?

Seems like libraries like okta-oidc-js store the code_verifier in sessionStorage. Doesn't that expose you to XSS attack? i.e. if I was store the code_verifier in sessionStorage before the application goes into the Authorisation flow and redirects, on the callback, what stops some rouge extension from reading the code from the URL and code_verifier from sessionStorage? Combination of which can be used to exchange for a access token.

Dale K
  • 25,246
  • 15
  • 42
  • 71
ke3pup
  • 1,835
  • 4
  • 36
  • 66

2 Answers2

6

What you describe is the standard SPA way of doing things - it could potentially be abused by malicious code, but there is some protection in the fact that an authorization code can only be used once and that the verifier is not stored for long.

A related XSS attack is to run a complete OAuth authorization redirect + code exchange on a hidden iframe - there is no protection against that, regardless of whether a back end or client secret is involved.

If you want to be strict about security, the emerging trend is more of a back end for front end approach, where the back end is a 'Proxy API' running at https://api.mywebdomain.com

  • The result of OAuth authorization is a same site cookie issued by the API, to prevent the above iframe attack

  • The SPA can then either use the auth cookie to get an access token or double hop API requests via the proxy API.

There is a good recent video on SPA security here that discusses these threats in further depth. The browser is a difficult place to implement security and redirects come with risks.

It is still recommended to separate Web and API concerns however - eg the above proxy API should not get in the way of a company wanting to deploy their SPA via a content delivery network.

LOGIN DANCE

In my opinion the preferred approach is summarized below, for full control and no issues with recent browser changes:

  • SPA calls a URL such as https://api.mywebdomain.com/login/start, which writes an HTTP only encrypted cookie for .mywebdomain.com containing the state and code_verifier, and also returns the authorization request URL

  • SPA then does the redirect itself, and saves page location / state to session storage beforehand if needed

  • SPA then receives the response URL with code and state, then POSTs them to a URL such as https://api.mywebdomain.com/login/end. Afterwards the SPA can restore its page location and state, so that usability is good.

  • API completes the login by verifying the state against that in the state cookie, then using the code_verifier from the state cookie. The result of all of this is to write an auth cookie (containing a refresh token) that could not be abused on an iframe.

Gary Archer
  • 22,534
  • 2
  • 12
  • 24
  • So is the suggestion to generate the code_verifier on server side stored as same site cookie (lax) and have the server do the code for token exchange once the client has done the redirect...? – ke3pup May 13 '21 at 14:13
  • Also on that, why is the recommended oauth flow for website (spa or otherwise) PKCE? The spec doesn't mention or recommend where to store code_verifier when client is responsible on creating and using it – ke3pup May 13 '21 at 14:15
  • 1
    The video was very useful – ke3pup May 13 '21 at 14:54
  • Just to finish up - PKCE is fine since it is the OAuth 2.1 recommendation. It can be generated server side and stored in a state cookie, then used later. I added the above section with my current thoughts on how I think the flow should work. I think it's important to not lose the good points of an SPA architecture when solving this security problem. – Gary Archer May 13 '21 at 15:38
1

I agree with Gary's approach, with one change. The response url with code and state does not need to be intercepted by SPA and converted into another POST call to the BFF (backend for frontend). If secure cookies were set on the browser at the beginning of the flow containing state and code verifier, the response url can land directly on the BFF which will then have all the parameters available for performing the code exchange (state and code as url parameters, code_verifier from cookie)