Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Support magic links in SignInPage #4085

Open
wants to merge 41 commits into
base: master
Choose a base branch
from

Conversation

bharatkashyap
Copy link
Member

@bharatkashyap bharatkashyap commented Sep 13, 2024

@bharatkashyap bharatkashyap added the enhancement This is not a bug, nor a new feature label Sep 13, 2024
@bharatkashyap bharatkashyap changed the title [feat] Add nodemailer provider to SignInPage [feat] Support magic links in SignInPage Sep 13, 2024
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Sep 15, 2024
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Sep 17, 2024
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Sep 22, 2024
@github-actions github-actions bot added PR: out-of-date The pull request has merge conflicts and can't be merged and removed PR: out-of-date The pull request has merge conflicts and can't be merged labels Sep 30, 2024
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Oct 10, 2024
Copy link
Member

@apedroferreira apedroferreira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, seems to work well, the demos look clean!
I wrote down some thoughts and ideas.


Find details on how to set up each provider in the [Auth.js documentation](https://authjs.dev/getting-started/authentication/oauth).
:::

## Magic Link
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a default success alert that would already show in this demo?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a separate demo on the Alerts just below this one, so I think this one is okay? If you think strongly that we should merge them, I can do that

Copy link
Member

@apedroferreira apedroferreira Oct 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a huge deal, it's just that the first demo doesn't provide any feedback when you press the submit button so maybe that will feel a bit weird if it's the first thing a user tries.
But I just noticed that other demos in the same page work the same... maybe they could always show a standard browser alert or something (instead of a console log that most people will miss), but I guess that's something you can add at any time, doesn't need to block this PR.


{{"demo": "MagicLinkAlertSignInPage.js", "iframe": true, "height": 500}}

To get the magic link working, you need to add the following code to your custom sign-in page:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess they don't really "need" to but it's a way that users can do it, right? So maybe we can say they "can" add that code instead, as this is an example of how they can do it with Auth.js?

if (provider.id === 'nodemailer' &&
(error as any).digest?.includes('verify-request')) {
return {
success: 'Check your email for a verification link.',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would a more specific name like successAlert or successMessage be better?

Copy link
Member Author

@bharatkashyap bharatkashyap Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, but that would mean we also rename the error prop in AuthResponse to errorMessage which – while better – would be a breaking change on the AuthResponse interface. What do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, just a thought, I think error shouldn't be renamed so we can keep success if it's more consistent with that.

// For the nodemailer provider, we want to return a success message
// if the error is a 'verify-request' error, instead of redirecting
// to a `verify-request` page
if (provider.id === 'nodemailer' && (error as any).digest?.includes('verify-request')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess they don't provide types that we can use to type this error with the digest?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this is quite fragile, but I couldn't find any way to detect exclusively that the redirect is to verify-request using an Error type. I'll investigate a bit more

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, no big deal if you can't find it, was just wondering.

redirectTo: callbackUrl ?? '/',
});
} catch (error) {
if (error instanceof Error && error.message === 'NEXT_REDIRECT') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not super clean that this is how "success" works (by catching an error) but if that's just how Auth.js does it I guess there's nothing we can do?
Ideally this code would be easier to follow.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how Next.js + Auth.js server compoents redirects work - throwing a NEXT_REDIRECT which we must catch, I'm basing this on vercel/next.js#49298 (comment)

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Oct 10, 2024
@github-actions github-actions bot added PR: out-of-date The pull request has merge conflicts and can't be merged and removed PR: out-of-date The pull request has merge conflicts and can't be merged labels Oct 11, 2024
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Oct 16, 2024
@github-actions github-actions bot added PR: out-of-date The pull request has merge conflicts and can't be merged and removed PR: out-of-date The pull request has merge conflicts and can't be merged labels Oct 16, 2024
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Oct 18, 2024
@bharatkashyap bharatkashyap changed the title [feat] Support magic links in SignInPage [core] Support magic links in SignInPage Oct 18, 2024
@bharatkashyap bharatkashyap requested review from Janpot and removed request for Janpot October 18, 2024 18:06
@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Oct 18, 2024
@jakobmerrild
Copy link

@bharatkashyap I was looking for any open issues w.r.t. supporting one-time passwords when using the credentials provider and found your issue. However, looking at the code in this PR it doesn't seem to solve that part of the issue.
Do you have any plans to include support for one-time passwords in the credentials provider with this PR?

@bharatkashyap
Copy link
Member Author

@bharatkashyap I was looking for any open issues w.r.t. supporting one-time passwords when using the credentials provider and found your issue. However, looking at the code in this PR it doesn't seem to solve that part of the issue. Do you have any plans to include support for one-time passwords in the credentials provider with this PR?

@jakobmerrild Not with this PR (I should edit the title of that issue) but the plan is to implement support for OTPs as an immediate follow up of this PR, including adding an example app. I'll create a separate issue: #4292

@jakobmerrild
Copy link

Sounds great!

@jakobmerrild
Copy link

Actually, looking at the new issue it sounds like you are planning to support one time codes sent to the user's email.

What I'm looking for is 2FA support via one-time passwords, basically an additional optional input field on the SignInPage.

@bharatkashyap
Copy link
Member Author

Actually, looking at the new issue it sounds like you are planning to support one time codes sent to the user's email.

What I'm looking for is 2FA support via one-time passwords, basically an additional optional input field on the SignInPage.

Understood, that isn't part of the roadmap yet but feel free to open an issue/comment with your requirements on that issue!

@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Oct 21, 2024
@jakobmerrild
Copy link

Thanks for letting me know. I have opened an issue. In the mean time is there a way for me to create my own sign-in page and have it integrate with the Account component?

@bharatkashyap
Copy link
Member Author

Thanks for letting me know. I have opened an issue. In the mean time is there a way for me to create my own sign-in page and have it integrate with the Account component?

Thanks for the detailed issue, I'll add it to the consideration for our future versions.

For the meantime, you could use the Account component separately like the demos in this page: https://mui.com/toolpad/core/react-account/ while building your own component for the sign in page.

@jakobmerrild
Copy link

Thanks! I managed to get it to work using your example and the basic page from authjs. Should then be able to just create a new custom page and pass it into the pages prop for the NextAuth I believe.
Though I am curious why your example uses the signIn and signOut functions from next-auth/react instead the ones exported from auth.ts.

@bharatkashyap
Copy link
Member Author

Though I am curious why your example uses the signIn and signOut functions from next-auth/react instead the ones exported from auth.ts.

This is because SignInPage is a client component - see https://authjs.dev/getting-started/migrating-to-v5#details

@jakobmerrild
Copy link

Gotcha! Thanks for all the help and sorry for spamming your PR 🙈

Copy link
Member

@apedroferreira apedroferreira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No major changes since last time I approved, right? Let me know if there's anything I should check more in specific.
LGTM, added some comments that sound worth doing before merging, but nothing blocking!

await userEvent.click(signInButton);

expect(signIn).toHaveBeenCalled();
expect(signIn.mock.calls[0][0]).toHaveProperty('id', 'nodemailer');
Copy link
Member

@apedroferreira apedroferreira Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that none of those usual methods like toHaveBeenLastCalledWith works for these checks? Or would it? They're just more readable.

fullWidth
slotProps={{
htmlInput: {
sx: { paddingTop: '12px', paddingBottom: '12px' },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe try to use theme units if possible unless we really need the 12px specifically here?

<Alert severity="error">{error}</Alert>
) : null}
{Object.values(providers ?? {}).map((provider) => {
if (provider.id === 'credentials' || provider.id === 'passkey') {
if (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A .filter here would probably be cleaner, before using .map?

<Alert severity="error">{error}</Alert>
) : null}
{Object.values(providers ?? {}).map((provider) => {
if (provider.id === 'credentials' || provider.id === 'passkey') {
if (
provider.id === 'credentials' ||
Copy link
Member

@apedroferreira apedroferreira Oct 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also just create a single boolean variable for these provider id checks, might end up being useful for other situations later too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement This is not a bug, nor a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[SignInPage] Add magic link support
4 participants