[GH-ISSUE #4262] Expose function to generate verification URL without BetterAuth emailing it #27199

Open
opened 2026-04-17 18:04:15 -05:00 by GiteaMirror · 9 comments
Owner

Originally created by @Asguho on GitHub (Aug 27, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/4262

Originally assigned to: @ping-maxwell on GitHub.

Problem

Currently, we use magic links and OAuth for authentication, which generally works fine. However, sometimes we send out action-emails asking users to perform certain actions (like accepting reqeust to some file).

When they click the link, they are not authenticated, so they get redirected to the auth screen. There, they have to enter their email again, receive a new magic link, go back to their inbox, click that new link, and only then get redirected back to the original action URL.

This creates a frustrating user experience with unnecessary steps.

Proposed Solution

It would be much cleaner if the generate verification URL function was exposed, for example:

const data = await auth.api.magicLink.generateVerificationUrl({
    body: {
        email: "user@email.com", // required
        callbackURL: "/some-action-url",
        newUserCallbackURL: "/welcome",
        errorCallbackURL: "/error",
    },
});

With this, we could send out emails containing action links that already embed the magic-link verification.
That way, the link would look like this:
https://[domain]/api/auth/magic-link/verify?token=UCKAulYOZyqrupvkvBMLugiseYuAvWTD&callbackURL=[some-action-url]&errorCallbackURL=%2Ferror

This would let users go directly to the intended action without having to re-authenticate separately.

Originally created by @Asguho on GitHub (Aug 27, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/4262 Originally assigned to: @ping-maxwell on GitHub. ## Problem Currently, we use magic links and OAuth for authentication, which generally works fine. However, sometimes we send out action-emails asking users to perform certain actions (like accepting reqeust to some file). When they click the link, they are not authenticated, so they get redirected to the auth screen. There, they have to enter their email again, receive a new magic link, go back to their inbox, click that new link, and only then get redirected back to the original action URL. This creates a frustrating user experience with unnecessary steps. ## Proposed Solution It would be much cleaner if the generate verification URL function was exposed, for example: ```js const data = await auth.api.magicLink.generateVerificationUrl({ body: { email: "user@email.com", // required callbackURL: "/some-action-url", newUserCallbackURL: "/welcome", errorCallbackURL: "/error", }, }); ```` With this, we could send out emails containing action links that already embed the magic-link verification. That way, the link would look like this: `https://[domain]/api/auth/magic-link/verify?token=UCKAulYOZyqrupvkvBMLugiseYuAvWTD&callbackURL=[some-action-url]&errorCallbackURL=%2Ferror` This would let users go directly to the intended action without having to re-authenticate separately.
GiteaMirror added the credentials label 2026-04-17 18:04:15 -05:00
Author
Owner

@Asguho commented on GitHub (Aug 27, 2025):

If somebody points me in the right direction it would be possible for me to make a pull request

<!-- gh-comment-id:3227946523 --> @Asguho commented on GitHub (Aug 27, 2025): If somebody points me in the right direction it would be possible for me to make a pull request
Author
Owner

@dosubot[bot] commented on GitHub (Aug 27, 2025):

Hi @Asguho! I'm Dosu and I’m helping the better-auth team.

The code for generating magic link verification URLs is in packages/better-auth/src/plugins/magic-link/index.ts, specifically inside the signInMagicLink endpoint. To expose this functionality, you can extract the URL construction logic into a reusable function (for example, generateVerificationUrl) within the magic-link plugin. This function should take parameters like email, callbackURL, newUserCallbackURL, and errorCallbackURL, and handle token generation and storage just like the current endpoint does.

You can then expose this function either as part of the plugin's public API or as a new authenticated endpoint, following the existing architectural patterns. Make sure to include input validation and session/authentication checks, and preserve security measures such as rate limiting and token expiration. Updating type definitions and documentation to reflect the new function/endpoint will help others use it correctly.

The main things to look for and refactor are the token generation, storage, and URL construction logic currently embedded in the endpoint. You can see how it's done here. This approach will let you generate verification URLs for custom emails or flows, without sending the email through BetterAuth.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3227960329 --> @dosubot[bot] commented on GitHub (Aug 27, 2025): <!-- Greeting --> Hi @Asguho! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The code for generating magic link verification URLs is in `packages/better-auth/src/plugins/magic-link/index.ts`, specifically inside the `signInMagicLink` endpoint. To expose this functionality, you can extract the URL construction logic into a reusable function (for example, `generateVerificationUrl`) within the magic-link plugin. This function should take parameters like `email`, `callbackURL`, `newUserCallbackURL`, and `errorCallbackURL`, and handle token generation and storage just like the current endpoint does. You can then expose this function either as part of the plugin's public API or as a new authenticated endpoint, following the existing architectural patterns. Make sure to include input validation and session/authentication checks, and preserve security measures such as rate limiting and token expiration. Updating type definitions and documentation to reflect the new function/endpoint will help others use it correctly. The main things to look for and refactor are the token generation, storage, and URL construction logic currently embedded in the endpoint. You can see how it's done [here](https://github.com/better-auth/better-auth/blob/f978fe9a983c336356364e2392ffcdd99e551b2b/packages/better-auth/src/plugins/magic-link/index.ts). This approach will let you generate verification URLs for custom emails or flows, without sending the email through BetterAuth. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/2e6885cd-0285-43ad-a648-c6804d3becc7?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/2e6885cd-0285-43ad-a648-c6804d3becc7?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/2e6885cd-0285-43ad-a648-c6804d3becc7?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/2e6885cd-0285-43ad-a648-c6804d3becc7?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/2e6885cd-0285-43ad-a648-c6804d3becc7?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/2e6885cd-0285-43ad-a648-c6804d3becc7?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/2e6885cd-0285-43ad-a648-c6804d3becc7?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4262)
Author
Owner

@maelp commented on GitHub (Aug 28, 2025):

I'm also interested, BTW @Asguho @dosu I'm not sure why we need to set headers in that call when doing it from the backend? since the user is not yet logged in (and we're actually doing a backend call, so the user doesn't have "a session"), why should we add some kind of session headers there? they would be empty?

<!-- gh-comment-id:3232730306 --> @maelp commented on GitHub (Aug 28, 2025): I'm also interested, BTW @Asguho @dosu I'm not sure why we need to set `headers` in that call when doing it from the backend? since the user is not yet logged in (and we're actually doing a backend call, so the user doesn't have "a session"), why should we add some kind of session headers there? they would be empty?
Author
Owner

@Asguho commented on GitHub (Aug 28, 2025):

Your absolutely right. I copy pasted the Magiclink code and forgot to delete that part

<!-- gh-comment-id:3232806684 --> @Asguho commented on GitHub (Aug 28, 2025): Your absolutely right. I copy pasted the Magiclink code and forgot to delete that part
Author
Owner

@dvanmali commented on GitHub (Sep 24, 2025):

@maelp A session is needed to understand the user that is trying to log in, hence requireHeaders: true. If we don't have these headers, anyone can request a sign-in by supplying an email.

<!-- gh-comment-id:3331098830 --> @dvanmali commented on GitHub (Sep 24, 2025): @maelp A session is needed to understand the user that is trying to log in, hence `requireHeaders: true`. If we don't have these headers, anyone can request a sign-in by supplying an email.
Author
Owner

@ravnit-lotay commented on GitHub (Sep 30, 2025):

Also interested in this @dvanmali! Our use case is similar to @Asguho's, and we'd prefer if it worked as follows:

  1. We generate a magic link url without better-auth sending an email
  2. We send a user the magic link url via email ourselves
  3. The user simply presses the link and proceeds to be signed in

Essentially, we want to enable passwordless authentication for any email address, where we control the email delivery. This should work server-side without requiring user session headers

<!-- gh-comment-id:3353293236 --> @ravnit-lotay commented on GitHub (Sep 30, 2025): Also interested in this @dvanmali! Our use case is similar to @Asguho's, and we'd prefer if it worked as follows: 1) We generate a magic link url **_without_** better-auth sending an email 2) We send a user the magic link url via email ourselves 3) The user simply presses the link and proceeds to be signed in Essentially, we want to enable passwordless authentication for any email address, where we control the email delivery. This should work server-side without requiring user session headers
Author
Owner

@dosubot[bot] commented on GitHub (Dec 30, 2025):

Hi, @Asguho. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary

  • You requested a feature to generate magic link verification URLs without sending emails, allowing custom email delivery.
  • I provided guidance on refactoring the magic link endpoint to separate URL generation logic with security and validation.
  • Other users showed interest, discussing session header requirements and server-side generation without user session headers.
  • Maintainers explained session headers help prevent abuse but acknowledged this as a desired enhancement for flexible passwordless authentication.

Next Steps

  • Please let me know if this feature request is still relevant to the latest version of better-auth by commenting on this issue.
  • If I don’t hear back within 7 days, this issue will be automatically closed.

Thank you for your understanding and contribution!

<!-- gh-comment-id:3699824700 --> @dosubot[bot] commented on GitHub (Dec 30, 2025): Hi, @Asguho. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary** - You requested a feature to generate magic link verification URLs without sending emails, allowing custom email delivery. - I provided guidance on refactoring the magic link endpoint to separate URL generation logic with security and validation. - Other users showed interest, discussing session header requirements and server-side generation without user session headers. - Maintainers explained session headers help prevent abuse but acknowledged this as a desired enhancement for flexible passwordless authentication. **Next Steps** - Please let me know if this feature request is still relevant to the latest version of better-auth by commenting on this issue. - If I don’t hear back within 7 days, this issue will be automatically closed. Thank you for your understanding and contribution!
Author
Owner

@himself65 commented on GitHub (Jan 9, 2026):

Hi, we have sendMagicLink. I think you can use this to obtain the magic link URL and perform the same action. It's the replacement of the API generateVerificationUrl.

<!-- gh-comment-id:3729951064 --> @himself65 commented on GitHub (Jan 9, 2026): Hi, we have `sendMagicLink`. I think you can use this to obtain the magic link URL and perform the same action. It's the replacement of the API `generateVerificationUrl`.
Author
Owner

@Benzo-Fury commented on GitHub (Mar 3, 2026):

Hi, we have sendMagicLink. I think you can use this to obtain the magic link URL and perform the same action. It's the replacement of the API generateVerificationUrl.

Hello,
I think you mean by just obtaining the token via the sendMagicLink method and not actually sending an email. If this is what you mean... I personally feel like it's a bit hacky. Not to mention, users may wan't to still use the sendMagicLink method normally.

Would it be possible just to expose the method to create a magic link without sending one?

<!-- gh-comment-id:3990698276 --> @Benzo-Fury commented on GitHub (Mar 3, 2026): > Hi, we have `sendMagicLink`. I think you can use this to obtain the magic link URL and perform the same action. It's the replacement of the API `generateVerificationUrl`. Hello, I think you mean by just obtaining the token via the `sendMagicLink` method and not actually sending an email. If this is what you mean... I personally feel like it's a bit hacky. Not to mention, users may wan't to still use the `sendMagicLink` method normally. Would it be possible just to expose the method to create a magic link without sending one?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#27199