Authorization Code Flow Implementation: Access Token vs Cookie-Based Authentication from Browser

I have a regular web app, with a browser client and a database that lives on the server, with the backend written in Go. The database can save access tokens.

I have tried to implement Authorization Code Flow (Authorization Code Flow). I’m confused about whether or not I should be using the access token and exposing it in the browser React code. I have seen recommendations from Auth0 that the access token should be stored in the server if possible (for example, see here: Which is the best way to store the auth0 token for a web app); but does this mean that I may allow the client React code to use the access token?

In order to avoid exposing the access token to the browser and React client code at all, I have implemented a cookie-based authentication mechanism. Upon logging in, a cookie is stored in the browser, and then this cookie (sent via HTTP request) is used in the backend to retrieve an access token from my server-side database. This access token is then validated and used upon every API request to my server.

Is this implementation reasonable?

An alternative (which I have not implemented) is to have the server send the access token (after authorization code flow completes) directly to the browser, where it will be used (and not stored) in the React Javascript code. Then upon every API request to my server, the access token would be attached to the ‘Bearer’ header of the HTTP request. If the user has a valid cookie, they would receive a token from the server (not just when code flow completes).

Let me know which implementation is preferred.

1 Like

facing a similar situation. any preferred method you found?

In my opinion the very first paragraphs of the JWT.io websites are contradictory.
Link: JSON Web Token Introduction - jwt.io
You can read:

"
You also [hould not store sensitive session data in browser storage due to lack of security

Whenever the user wants to access a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema. The content of the header should look like the following:

Authorization: Bearer <token>

"

Yet the only way to setup a header is by using JavaScript, which is compatible with HTTP-only cookies.

  • The header pattern is meant for server to server communication, where the token may be stored in memory
  • The valid pattern in browsers is to use HTTP-only cookies

This means setting up CORS properly, which is most often absurdly complex, because Set-Cookie header will never work like you want, especially during dev with localhost using HTTP instead of HTTPS.

If you are interested, I’ve started a documentation for proper patterns for web apps, in the context of Next: research/auth.md at main · lbke/research · GitHub

I lack the skills and time to finish it properly but at least I try to be as clear as possible about the context where different architectures apply.