Hook for domain validation after OAuth profile is fetched but before session is created #1244

Closed
opened 2026-03-13 08:29:50 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @scobbe on GitHub (May 22, 2025).

Is this suited for github?

  • Yes, this is suited for github

I need a point in the OAuth flow after the provider has returned a verified email but before BetterAuth writes any user / account / session rows. That gap would let me reject personal-address log-ins (eg gmail.com) without first creating—and then deleting—a session. I could not find such a hook in the docs.

Describe the solution you'd like

Expose a beforeCreateSession (or similar) hook that receives the provider profile and allows returning false | APIError to abort the sign-in cleanly—similar to how returning null from getUserInfo skips user creation.

Describe alternatives you've considered

After-hook rollback – works but incurs an extra DB write/delete and feels brittle:

hooks: {
  after: createAuthMiddleware(async (ctx) => {
    if (!ctx.context.newSession) return;
    const email = ctx.context.newSession.user.email;
    const domain = email?.split("@").pop()?.toLowerCase();
    if (!domain || !ALLOWED_EMAIL_DOMAINS.includes(domain)) {
      await ctx.context.adapter.delete<"session">({
        model: "session",
        where: [{ field: "id", value: ctx.context.newSession.session.id }],
      });
      const loginUrl = new URL("/login", env.BETTER_AUTH_URL);
      loginUrl.searchParams.set("error", "unauthorized_email");        
      return Response.redirect(loginUrl.toString(), 302);
    }
  }),
}

I'm also unable to get the redirect logic to respect the redirect URL I pass. Is there anyway to get BetterAuth to redirect with the search params I set?

Additional context

No response

Originally created by @scobbe on GitHub (May 22, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. I need a point in the OAuth flow after the provider has returned a verified email but before BetterAuth writes any user / account / session rows. That gap would let me reject personal-address log-ins (eg gmail.com) without first creating—and then deleting—a session. I could not find such a hook in the docs. ### Describe the solution you'd like Expose a beforeCreateSession (or similar) hook that receives the provider profile and allows returning false | APIError to abort the sign-in cleanly—similar to how returning null from getUserInfo skips user creation. ### Describe alternatives you've considered After-hook rollback – works but incurs an extra DB write/delete and feels brittle: ``` hooks: { after: createAuthMiddleware(async (ctx) => { if (!ctx.context.newSession) return; const email = ctx.context.newSession.user.email; const domain = email?.split("@").pop()?.toLowerCase(); if (!domain || !ALLOWED_EMAIL_DOMAINS.includes(domain)) { await ctx.context.adapter.delete<"session">({ model: "session", where: [{ field: "id", value: ctx.context.newSession.session.id }], }); const loginUrl = new URL("/login", env.BETTER_AUTH_URL); loginUrl.searchParams.set("error", "unauthorized_email"); return Response.redirect(loginUrl.toString(), 302); } }), } ``` I'm also unable to get the redirect logic to respect the redirect URL I pass. Is there anyway to get BetterAuth to redirect with the search params I set? ### Additional context _No response_
Author
Owner

@werkamsus commented on GitHub (May 22, 2025):

+1

@werkamsus commented on GitHub (May 22, 2025): +1
Author
Owner

@body20002 commented on GitHub (May 28, 2025):

I found a way but it's not by no means great. I can't find a way to do a redirect with the social sign in
but basically, you have to set ctx.context.returned.redirect = true and specify which url to redirect in ctx.context.returned.url
here's what I use.

  hooks: {
    after: createAuthMiddleware(async (ctx) => {
      if (ctx.path.includes("social")) {
        return;
      }
      if (ctx.path.startsWith("/sign-in") || ctx.path.startsWith("/sign-up")) {
        const session = ctx.context.newSession;
        const baseURL = ctx.context.options.baseURL;
        // @ts-ignore
        ctx.context.returned.redirect = true;
        if (
          session?.user.role === "admin" ||
          session?.user.role === "superadmin"
        ) {
          // @ts-ignore
          ctx.context.returned.url = `${baseURL}/dashboard`;
          return;
        }
        // @ts-ignore
        ctx.context.returned.url = `${baseURL}/profile`;
        return;
      }
    }),
  },

using throw ctx.redirect("/dashboard") doesn't work it only sends redirect in the server and doesn't redirect the client application.

@body20002 commented on GitHub (May 28, 2025): I found a way but it's not by no means great. I can't find a way to do a redirect with the social sign in but basically, you have to set `ctx.context.returned.redirect = true` and specify which url to redirect in `ctx.context.returned.url` here's what I use. ```ts hooks: { after: createAuthMiddleware(async (ctx) => { if (ctx.path.includes("social")) { return; } if (ctx.path.startsWith("/sign-in") || ctx.path.startsWith("/sign-up")) { const session = ctx.context.newSession; const baseURL = ctx.context.options.baseURL; // @ts-ignore ctx.context.returned.redirect = true; if ( session?.user.role === "admin" || session?.user.role === "superadmin" ) { // @ts-ignore ctx.context.returned.url = `${baseURL}/dashboard`; return; } // @ts-ignore ctx.context.returned.url = `${baseURL}/profile`; return; } }), }, ``` using `throw ctx.redirect("/dashboard")` doesn't work it only sends redirect in the server and doesn't redirect the client application.
Author
Owner

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

Hi, @scobbe. 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 new OAuth hook that triggers after email verification but before session creation to allow domain validation without creating and deleting sessions.
  • You also inquired about handling custom redirect URLs with search parameters after sign-in rejection.
  • Community members like werkamsus support this feature, while body20002 shared a partial workaround using context flags for server-side redirects.
  • The issue underscores the need for cleaner client-aware redirect handling and pre-session validation hooks.
  • No official resolution or implementation has been made yet.

Next Steps:

  • Please let me know if this issue is still relevant to the latest version of better-auth by commenting below to keep the discussion open.
  • Otherwise, I will automatically close this issue in 7 days.

Thank you for your understanding and contribution!

@dosubot[bot] commented on GitHub (Aug 27, 2025): Hi, @scobbe. 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 new OAuth hook that triggers after email verification but before session creation to allow domain validation without creating and deleting sessions. - You also inquired about handling custom redirect URLs with search parameters after sign-in rejection. - Community members like werkamsus support this feature, while body20002 shared a partial workaround using context flags for server-side redirects. - The issue underscores the need for cleaner client-aware redirect handling and pre-session validation hooks. - No official resolution or implementation has been made yet. **Next Steps:** - Please let me know if this issue is still relevant to the latest version of better-auth by commenting below to keep the discussion open. - Otherwise, I will automatically close this issue in 7 days. Thank you for your understanding and contribution!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1244