Open henrikwirth opened 4 years ago
@jasonbahl mentioned this tweet: https://twitter.com/ryanflorence/status/1060361144701833216?s=21
So one possible approach for login, is that it is just a component and not a Route itself. So for this example, if someone navigates to the Dashboard and the user is not logged in, the Login/SignUp form will be shown, instead of redirecting to login/signup routes.
I'm still not sure about it. I somehow like to have "/login" and "/signup" routes to be able to reference too. I'll leave that open for discussion.
@jasonbahl I tried the Cors plugin but didn't get it to work as expected. The login mutation they offer, breaks the login mutation, as there are 2 registered at that time. And the normal login mutation doesn't seem to send the credentials back.
I would love to see that in the JWT Plugin, so that we can set HttpOnly Cookies on Login and maybe on successful registration.
Any thoughts to that would be highly appreciated :)
Hey @henrikwirth - This looks pretty good. Some feedback:
If this repo is meant to be a community resource to show folks how to set up auth with Gatsby, Apollo Client and wp-graphql-jwt-authentication, then I think these things could be tweaked:
Trying to boot up the app without yarn installed results in errors due to "postinstall": "yarn run createTypes"
. This could be changed to only rely on npm and not assume that yarn is available. Or this script could be removed altogether, since it's not required for basic use of Apollo Client.
BatchHttpLink is being used in apollo.js, and I see that there is a HttpLink that is commented out. Having HttpLink as the default would probably make more sense if this is meant to be a basic community resource, though.
Using https://www.npmjs.com/package/uuid for generating UUIDs may be preferable to using my homebrew getUuid()
function, just to extract that custom code out of the project and lean on an npm package to own that minor bit of complexity.
There's a lot going on in the auth.js and useAuth.js files. I think some of that code could be simplified.
When a user logs in, the JWT sent back contains the auth token, refresh token and any user data that was in the body of the query. It looks like you're storing the refresh token only as REFRESH_TOKEN
in localStorage, though. I don't believe that's necessary – we can store the entire JWT in localStorage and pull all three of those pieces of data out of it (auth token, refresh token & any user data) whenever they're needed without storing them separately/individually.
Handling token refreshes in apollo/client.js
using apollo-link-token-refresh
and also in src/hooks/useAuth.js
seems redundant. It seems like we should have a single source of truth for doing refreshes rather than doing it in two separate places in the app. I'm still thinking through the best way to do that, myself.
-- If this repo is NOT meant to be a basic example for the community to follow though and is a starter for yourself/your company to reference for future projects though, then please disregard some of my comments above :)
I'm working on a Codesandbox that is meant to be a bare bones example of how to set up Gatsby + Apollo Client + wp-graphql-jwt-authentication. I'm trying to make it as simple as possible, with token refreshes only being handled in one place I'll share it when I have it in working order. I'd love to compare our approaches and get your feedback on it. Thanks again for your work here! 👍🏼
@kellenmace
I agree, that some things should probably be simplified with more starter friendly things.
The Silent Refresh is not working for me properly with the apollo-link-token-refresh
if you have any idea on how to fix it, that would be great. I certainly only want one place to do it.
Good point about npm over yarn.
For the AuthToken in localStorage I have to disagree. See the first article I linked. From my understanding that makes sense and it saver than in localStorage.
@henrikwirth I’ll try to get the refresh flow working with that package and the newest versions of all plugins and let you know how it goes.
I missed the “Auth Token only in Memory” item on your checklist above, and the linked blog post, and didn’t realize that you were intentionally not saving the JWT auth token to localStorage. That code makes a lot of sense now :).
@kellenmace I just added some changes as of you suggestion:
Add uuid module. Remove BatchHttpLink and use normal HttpLink. Use npm instead of yarn.
Not sure what you mean with newest plugins, but if you mean WPGraphQL, then there is still some fixes needed. Also the RefreshToken is the same as the AuthToken at the moment. There was a fix I think Jason forgot to merge here: https://github.com/wp-graphql/wp-graphql-jwt-authentication/pull/64
@henrikwirth This looks great. Thanks for the resources as well. Can't wait to refactor some of my projects.
Based on what I read, I have a question: Does the wp-graphql-jwt-authentication
plugin already implement setting the jwtRefreshToken
as a HttpOnly
cookie?
I didn't get the HttpOnly cookie to work yet. I'll try to figure out a solution for it though. I'll add it to the todos of this epic to keep track.
Intro
This epic is to track possible solutions on how to implement the JWT User Authentication workflow.
Philosophy
Checkout this post: https://blog.hasura.io/best-practices-of-using-jwt-with-graphql
I want to use that post as a base for the implementation.
Here is also a good reference: https://flaviocopes.com/graphql-auth-apollo-jwt-cookies/
Auth Flow Strategy
Register/SignUp
User registers through from with following mutation:
Same as Login 2. - 6.
Login
jwtAuthToken
(5min Expiration) andjwtRefreshToken
(long lived: how long?)jwtAuthToken
andjwtRefreshToken
are returned back to the client as a JSON payload.jwtAuthToken
is stored in memory, thejwtRefreshToken
in localStorage.On Page Visit
jwtAuthToken
is in Memory, if not check ifjwtRefreshToken
is stored in localStoragejwtRefreshToken
is stored, then start Silent Refresh to get newjwtAuthToken
to store it in MemorySilent Refresh
Note: There is no new
jwtAuthExpiration
andisJwtAuthSecretRevoked
given and authToken is not calledjwtAuthToken
=> Issue for JWT pluginjwtRefreshToken
and if it is valid (or hasn't been revoked)jwtRefreshToken
jwtAuthToken
andjwtAuthExpiration
to the client and also sets a newjwtRefreshToken
cookie via Set-Cookie header.jwtAuthToken
in Payload and save in Memory.jwtAuthToken
is expired.Scope
Starter
HttpOnly
NPM Module
The idea is to use all the implemented above and abstract it in a way, it is easy for users to plugin to their projects, with less boilerplate.
wrapRootElement