Peter Jacxsens
Posted on April 1, 2024
There are some issues that we left unhandled, let's quickly go over them.
Automatically signing in on reset password in NextAuth
In our auth flow, we added the forgot/reset password option for credentials. But, as our flow stands now, we prompt the user to sign in after resetting the password. This is acceptable but not ideal.
A successful password reset in Strapi
actually returns the user object + a Strapi token
. In other words, everything we need to sign the user in automatically. I delayed talking talking about this because we didn't have all the necessary pieces just yet! But we do now.
We can now use what we learned in the change username and change password functionality and apply it to reset password. That is, stop using useFormState
. Call our server action in a submitHandler function, await the response from this server action and on success call the useSession.update
function. What would we pass it? Everything you need to update the token.
Note: I didn't actually try this out so you may run into some problems. But, I'm pretty sure it can be done.
Automatically signing in on email confirmation in NextAuth
When first signing up, the user receives an email confirmation request. Upon clicking the confirmation link the user gets a success message and is asked to sign in. Can we do this automatically?
Maybe, but we would have to seriously rework our component. First on the actual first sign up we would have to integrate NextAuth
. Our Strapi
endpoint for signing up returns (on success) the user object but no token. So, we would have to put this user into the NextAuth token
. I think you can listen for the trigger 'signup' inside the jwt callback
for this.
It would also be necessary to use a second CredentialsProvider for sign up and give it a unique ID. You can then call the signIn
function with this ID, f.e.
// not
signIn('credentials', ...)
// use ID
signIn('signin', ...)
signIn('signup', ...)
Inside this second CredentialsProvider (with id signup), you can then write an authorize
function that calls the Strapi
signup endpoint + handle errors and everything else that needs to be done.
The issue now is how to get the Strapi token
. On email confirmation, the Strapi
endpoint will not return the token but redirect the user. Also, we don't have a password to sign in with. (DO NOT send the password along any requests!)
And that is where I run aground. Maybe it can be done but I don't know how because I can't get to the Strapi token
.
Also note that you would have to update your guarded components. No longer only checking if(session)
but also something like && session.user.authorized
.
Tokens and expiry in Strapi and NextAuth
We used a lot of tokens in our auth flow: Strapi sign in token
, request reset token, reset password token, NextAuth
token,... All of these tokens have expiry dates. This means that they are only valid within a certain period. I'm pretty sure the expiry date is encoded inside the token.
On top of that, tokens also can be invalidated but not expired. A good example of this is the password reset token. Once you've used it to reset a password, Strapi
will invalidate it. This token will then be invalid even though the expiry date may not have been reached yet.
Note, I don't know how this works yet. I'm yet to study it. I only know bits and pieces but I do know it's a problem we have to deal with.
When our NextAuth
token expires, the user will simply be signed out and will have to sign in again. No problems there. There are ways to automate this with f.e. GoogleProvider. On sign in, we leverage some NextAuth
callbacks to request a new NextAuth
token ... somehow. Not sure how but it is possible.
A more immediate problem is when our NextAuth
token is still valid or hasn't expired but the Strapi token
has. This would lead to a cascade of problems and very confusing error message. The user would still be logged in, yet Strapi
would throw errors like 'invalid credentials' or something. That is a problem and I don't know how to solve this yet.
We already came across another problem. Upon changing our password, Strapi
returned us a new Strapi token
. We then updated the NextAuth token
via the useSession.update
method. But, what happened to the old token? Spoilers, it is still valid. This means that it could be used to access our app. So, again, something we would have to manage.
Strapi
also has a block user option. You would have to look into this to find out what this means for the token.
That is pretty much all I know about this for the moment. Takeaway: handle token expiry.
Final conclusion
We setup a complete auth using credentials and GoogleProvider in Next
and Strapi
using NextAuth
. Contrary to some articles in the wild, this wasn't easy and it wasn't quick - if done right.
On top of that, we took some shortcuts. We avoided some problems by simplifying our app or choosing a basic auth flow. Building a production level auth flow will complicate things. Meeting design specs will complicate things even more.
There will be things you can't do. Partly due to NextAuth
's insufficient server code support but also just hard restrictions. Things NextAuth
just can't do.
That being said, we did do a lot. We do having a fully functioning auth flow. One I hope I was able to explain clearly. It's important to get a feel for how NextAuth
works. Feel the flow. Especially inside the callbacks
. Besides this I tried to guide you through countless problems and how I managed to solve them. In the end this took up a lot of our time and I hope you learned from it.
Don't be shy to critique this. If you catch a mistake or a better solution, feel free to let me know in the comments.
Anymore on the way? As mentioned above, I still need to figure out how to handle token. Besides this I may do this series over with NextAuth v5
once it comes out of beta. Not making any promises.
Hope you enjoyed this series. Comment and share if you liked it.
If you want to support my writing, you can donate with paypal.
Posted on April 1, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.