Nextjs-auth0: Passwordless email link authentication is not working

Created on 23 Mar 2021  路  17Comments  路  Source: auth0/nextjs-auth0

Description

Passwordless email link authentication is not working.

Reproduction

  1. Created Auth0 SPA Application
  2. Set the allowed callback URL to http://localhost:3000/api/auth/callback
  3. Set allowed logout URL to http://localhost:3000/
  4. Configured our nextjs application, including env vars, to use nextjs-auth0 default setup for /pages/api/auth/[...auth0].js

    import { handleAuth } from '@auth0/nextjs-auth0';
    
    export default handleAuth();
    
  5. Went to the auth0 management dashboard
  6. Navigated to the Passwordless Connection for email and opened the modal
  7. Click the applications tab
  8. Make sure the newly created SPA application from step 1 was checked
  9. Clicked on try tab in modal
  10. Selected application created earlier in step 1
  11. Changed email recipient to my test email
  12. Changed mode to link
  13. Clicked Try
  14. Clicked link in email

Obviously, the repeatable steps are 9-14. Which I expected to be authenticated into the nextjs application without issue. However, I get the following error

Cannot read property 'toString' of undefined

This happens because the decodeState function for the get-login-state hook tries decoding a state that is undefined. The transientcookiehandler goes to read a cookie called state with the following function transientCookieHandler.read('state', req, res).

While actually debugging the only set cookies that it claims are there from the request are session cookies. When I check the callback request in chrome dev tools network tab I see the state cookie listed under request cookies. The response cookies for that callback request have a state cookie with an empty value.

Any assistance is appreciated.

Environment

Please provide the following:

  • **Version of this library used: 1.1.0
  • **Version of the platform or framework used, if applicable: nextjs 10.0.7
  • **Other relevant versions (language, server software, OS, browser): using js in VS Code with Chrome browser
  • **Other modules/plugins/libraries that might be involved: N/A
question

All 17 comments

Hey @benhamiltonpro - thanks for raising this

First off - that's a bad error, apologies, it should say something like "Missing state cookie / missing code/state params", I'll raise some work internally to fix.

Secondly, I wouldn't expect the "Try" link to be able to complete login - I think that button is designed more to just test what the email will look like.

In order to login, passwordless or otherwise, the login has to be initiated from the application. When you start login in your Next.js application (by visiting /api/auth/login) the SDK will leave a state cookie, which it will then verify using the state parameter from the callback url when the user is returned to your app (That's why the transientCookieHandler couldn;t find the state cookie). If you test Passwordless by logging in through the application, it should work fine.

Created Auth0 SPA Application

Also, you need to create a "Regular Web Application", the authentication is handled serverside so this is not an SPA app in terms of login.

Thank you for the quick response and the detailed explanation. I will first look at switching the app to a Regular Web Application instead of SPA.

So there is no way to send a magic link to a user with a verified email via the auth0 management API (say with an automated job) and have them click that link to get into the app? They have to first come to the app, hit the passwordless lock, and enter their email to get a link that will have the state?

Hi @benhamiltonpro

So there is no way to send a magic link to a user with a verified email via the auth0 management API (say with an automated job) and have them click that link to get into the app?

You can do this with embedded passwordless login, see how this works for SPAs (Using Auth0.js and Lock.js) and for Web Apps (we don't have an SDK for this, you'd use the node-auth0 client)

This SDK uses Universal Login, which is the preferred way to implement Passwordless Authentication because of the security implications of embedded auth.

To get approximately the flow you want for this SDK and Universal Login, you would do:

  • configure the universal login to use passwordless https://auth0.com/docs/connections/passwordless/guides/universal-login
  • prompt user to visit a page on your app that requires login (use withPageAuthRequired or /api/auth/login)
  • They will visit the Universal Login Page and do the Passwordless flow, either code or link
  • They'll be sent back to your app (where there will be a state cookie for the SDK to verify against the callback request)

Got passwordless to work when starting the auth from our app. Thanks.

@adamjmcgrath, we are using the auth0 authentication api to generate a magic link and would like to allow it to log users into our nextjs app. The email link generation is successful, but when clicking the link in the email we are seeing this error in the browser: state missing from the response.

From your discussion above I'm assuming that the next-auth0 sdk is what sets that state on a redirect to api/login.

That said, we have configured universal login to use passwordless, but, optimally we would trigger the generation of the email link from outside of our nextjs application entirely, if this is possible.

We do not want to have a user visit the Universal login page to get a code or link. The goal is that the link is generated purely via a call to the auth0 authentication api.

Currently, the link is generated from postman (so outside of the application), but we've also attempted to send it via server side or from the next.js client side, but nothing has had an effect on the outcome.

Is there some other way than the SDK to generate the state the callback url needs?

Do you know what might be going wrong here? Thanks so much for any advice!

I'm noticing that when we click on the magic link that we're getting redirected to the callback url with a # in the url where I'd expect a ? where query params start.

If I manually replace the # with a ? and instead I get a state mismatch error.

If I manually edit the state to match I get a TypeError: id_token not present in TokenSet error message.

Okay so I've been able to successfully login using a magic link I generate via the authentication api by using the state generated by the library.

However, this involved me just copying and pasting into my api request the state from the browser URL that exists once the library redirects back to the auth0 universal login page.

I would like to programmatically obtain this state and use it to automatically hit the authentication api to generate a magic link. All of the state generation occurs within the library and a redirect will happen before I'm able to obtain this state.

Is there a way to pre generate and set this state to a value that I pass to the library?

So to be more specific, the state that works for me appears to be generated with the nonce that is created by the login handler in the auth0/nextjs library, but it's generated when that handler redirects to the authorize endpoint at the auth0 domain, and it does not look like the base64 encoded nonce state object that is generated by the library.

Hi @jaredgalanis

That said, we have configured universal login to use passwordless, but, optimally we would trigger the generation of the email link from outside of our nextjs application entirely, if this is possible

In order to login using this SDK, the login must start from the application.

Have a look at my suggestion in https://github.com/auth0/nextjs-auth0/issues/346#issuecomment-805855526 for how to approximate the flow you are trying to achieve using this SDK

Thanks @adamjmcgrath!

I was starting to get that this is what you meant in your comments above. We are currently trying to see if we can devise a solution where the user hits the application which redirects to universal login page and to send the state query param that is generated for that page to an api that we control which generates the link.

So far I have been successful in doing that, but I can't also know the email address since the the authorization URL that the callback handler in this library redirects us to seems to go through a series of redirects.

Are you aware of a way for the universal login page to receive params coming from the application? If could do this we could either generate the link ourselves automatically or we could at least fill in the email field on the passwordless lock page automatically.

Also, there doesn't seem to be documentation about having embedded password approaches work with auth0/nextjs. Am I missing something?

Hi @jaredgalanis,

We are currently trying to see if we can devise a solution where the user hits the application which redirects to universal login page and to send the state query param that is generated for that page to an api that we control which generates the link.

If you visit the Passwordless Universal Login page confgured for email, the user will be prompted to enter their email (which you can prefill if you know the email - see custom_param in the next comment) - to which, Auth0 will send a passwordless link, see https://auth0.com/docs/universal-login/configure-universal-login-with-passwordless#universal-login-lock-passwordless-

Are you aware of a way for the universal login page to receive params coming from the application?

Yep - there's an example here https://github.com/auth0/nextjs-auth0/blob/main/EXAMPLES.md#customize-handlers-behavior (see the custom_param bit)

Also, there doesn't seem to be documentation about having embedded password approaches work with auth0/nextjs. Am I missing something?

This is intentional, nextjs-auth0 only support the Universal Login Page

Thanks @adamjmcgrath! I think we're almost there after your help.

One last thing I'm trying to figure out is how to get the current version of the sdk to provide an access token that is a jwt and not opaque. In past versions of the sdk I was able to pass an audience parameter to the config for the sdk's client, but this doesn't seem to be valid in the new version of the sdk.

Hi @jaredgalanis - you should be able to pass the audience in the authorizationParams config, in the handleLogin options or use the AUTH0_AUDIENCE environment variable.

See the Access an External API from an API Route Example for more info

@adamjmcgrath is there a way to pass params to the passwordless/start endpoint that would be available in a pre or post registration hook or a rule to be able to set them as metadata for a user?

@jaredgalanis - I don't know to be honest. That's probably one for the community forums

@jaredgalanis - I don't know to be honest. That's probably one for the community forums

Thanks, @adamjmcgrath.

Leaving this here just in case someone else lands here. I had found this on the community forums last week after I pinged you. It sounds like as of today's date, the answer is that hooks/rules aren't available in the passwordless flow.

That being said, we did find a way to engineer a solution without them. Thanks for all your help!!!

In case a search finds someone here maybe this will help:

I experienced this recently and had nothing to do with this lib. I followed the in-dashboard setup instructions and could not get login to work, giving me the error the OP referenced.

It turns out that I was using (old) custom HTML for my login page in the dashboard which was not compatible with the passwordless setup.

I simply reset with the default HTML for passwordless and was able to further customize from there, to get everything working.

Was this page helpful?
0 / 5 - 0 ratings