So far I have only dealt with server-rendered apps, where after a user logs in via username/password or using an OAuth provider (Facebook etc.), the server just sets a session cookie while redirecting to the relevant page.
However now I’m attempting to build an app using a more ‘modern’ approach, with React on the frontend and a JSON API backend. Apparently the standard choice for this is to use a JSON web token for authentication, however I’m having trouble working out how I’m meant to provide the JWT to the client so it can be stored in session/local storage or wherever.
Example to illustrate better:
-
User clicks link (
/auth/facebook
) to log in via Facebook -
User is redirected and shown Facebook login form and/or permission dialog (if necessary)
-
Facebook redirects user back to
/auth/facebook/callback
with an authorization code in tow, the server exchanges this for an access token and some information about the user -
Server finds or creates the user in the DB using the info, then creates a JWT containing a relevant subset of the user data (e.g. ID)
-
???
At this point I just want the user to be redirected to the main page for the React app (let’s say /app
) with the JWT in tow, so the frontend can take over. But I can’t think of an (elegant) way to do that without losing the JWT along the way, other than to put it in the query string for the redirect (/app?authtoken=...
) – but that will display in the address bar until I remove it manually using replaceState()
or whatever, and seems a little weird to me.
Really I’m just wondering how this is typically done, and I’m almost sure I’m missing something here. The server is Node (Koa with Passport), if that helps.
Edit: To be clear, I’m asking what the best way is to provide a token to the client (so it can be saved) after an OAuth redirect flow using Passport.
4
Answers
When you get a token from any passport authentication sites you have to save the token in your browser’s
localStorage
. The Dispatch is Redux’s Middleware. Ignoredispatch
if you don’t use redux in your app. you can just usesetState
here (A bit weird without redux).Client-side:
Here’s something similar API of mine, which returns token.
saving tokens
So When you make some authenticated requests,you have to attach the token with the request in this form.
authenticated requests
Here’s my index.js:
The token is checked each and everytime, so even if the browser got refreshed, you can still set the state.
checks if the user is authenticated
All that dispach actions does is it sets the state.
my reducer file(Redux only) else you can just use setState() in your index route file to provide the state to the whole application. Every time the dispatch is called, it runs a similar reducer file like this which sets the state.
setting the state
Delete the token from your localStorage to logout.
caution: Use any different name rather than
token
to save the token in your browser’slocalStorage
Server-Side:
considering your passport services file. You must set the header search.
Here’s passport.js
In my router.js
here is a login request from the server side. it’s storing the token in the header:
here is the login request on the react side, where I am grabbing the token from the header and setting the token in local storage once the username and password pass authentication:
Client: Parse the token and save it to Local Storage for subsequent use after page reload.
Log out
I recently ran across this same issue, and, not finding a solution here or elsewhere, wrote this blog post with my in-depth thoughts.
TL;DR: I came up with 3 possible approaches to send the JWT to the client after OAuth logins/redirects:
<script>
tag that:localStorage
(Since logging in with JWTs is essentially equivalent to “saving the JWT to
localStorage
, my favorite option was #3, but it’s possible there are downsides I haven’t considered. I’m interested in hearing what others think here.)Hope that helps!