[GH-ISSUE #810] sending magic link returns 400 due to content type text/plain #8449

Closed
opened 2026-04-13 03:31:09 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @rhyek on GitHub (Dec 8, 2024).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/810

lib version 1.0.10. Some example code:

remix client

import { useState } from 'react';
import { createAuthClient } from 'better-auth/client';
import { magicLinkClient } from 'better-auth/client/plugins';
import { useEnvVars } from '~/hooks/use-env-vars';

export function useBetterAuthClient() {
  const envs = useEnvVars();
  const [client] = useState(() =>
    createAuthClient({
      baseURL: `${envs.AUTH_API_PUBLIC_BASE_URL}/auth`,
      fetchOptions: {
        mode: 'no-cors',
        timeout: 5000,
      },
      plugins: [magicLinkClient()],
    }),
  );
  return client;
}

express server

import { toNodeHandler } from 'better-auth/node';
import express from 'express';
import { auth } from './auth';

const app = express();

// Add JSON parsing middleware
app.use(express.json());

const handler = toNodeHandler(auth);
app.all('/auth/*', (req, res) => {
  void handler(req, res);
});

app.listen(process.env.PORT, () => {
  console.log(`auth api listening on port ${process.env.PORT}`);
});

// ./auth.ts

import { betterAuth } from 'better-auth';
import { prismaAdapter } from 'better-auth/adapters/prisma';
import { magicLink } from 'better-auth/plugins';
import { sendNoReplyEmail } from '@convivia/backend/mail';
import { prisma } from '../../@backend/prisma';
import { envs } from './envs';

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: 'postgresql',
  }),
  socialProviders: {
    google: {
      clientId: envs.GOOGLE_OAUTH_WEB_CLIENT_ID,
      clientSecret: envs.GOOGLE_OAUTH_WEB_CLIENT_SECRET,
    },
  },
  // baseURL: `http://localhost:${envs.PORT}`,
  basePath: '/auth',
  trustedOrigins: [envs.BROWSER_APP_PUBLIC_BASE_URL],
  plugins: [
    magicLink({
      disableSignUp: true,
      expiresIn: 5 * 60,
      sendMagicLink: async ({ email, url, token }) => {
        console.log('url', url);
        await sendNoReplyEmail({
          to: email,
          subject: 'Link de acceso',
          html: `Has click <a href="${url}">aquí</a> para abrir sesión en el sitio.`,
        });
      },
    }),
  ],
});

With this basic setup, calling:

        const { data, error } = await betterAuthClient.signIn.magicLink({
          email,
          callbackURL: redirectUri,
        });

throws with a 400 and no response body whatsoever.

Making this small change:

app.all('/auth/*', (req, res) => {
  req.headers['content-type'] = 'application/json'; // ADDED THIS
  void handler(req, res);
});

It now works. The email is sent. However, even though on the client the response status code is 200, the method call returns { data: null, error: { status: 0, statusText: '' }}, which appears to be a connection error, but there was no connection error. The magic link was sent.

Originally created by @rhyek on GitHub (Dec 8, 2024). Original GitHub issue: https://github.com/better-auth/better-auth/issues/810 lib version `1.0.10`. Some example code: remix client ```typescript import { useState } from 'react'; import { createAuthClient } from 'better-auth/client'; import { magicLinkClient } from 'better-auth/client/plugins'; import { useEnvVars } from '~/hooks/use-env-vars'; export function useBetterAuthClient() { const envs = useEnvVars(); const [client] = useState(() => createAuthClient({ baseURL: `${envs.AUTH_API_PUBLIC_BASE_URL}/auth`, fetchOptions: { mode: 'no-cors', timeout: 5000, }, plugins: [magicLinkClient()], }), ); return client; } ``` express server ```ts import { toNodeHandler } from 'better-auth/node'; import express from 'express'; import { auth } from './auth'; const app = express(); // Add JSON parsing middleware app.use(express.json()); const handler = toNodeHandler(auth); app.all('/auth/*', (req, res) => { void handler(req, res); }); app.listen(process.env.PORT, () => { console.log(`auth api listening on port ${process.env.PORT}`); }); // ./auth.ts import { betterAuth } from 'better-auth'; import { prismaAdapter } from 'better-auth/adapters/prisma'; import { magicLink } from 'better-auth/plugins'; import { sendNoReplyEmail } from '@convivia/backend/mail'; import { prisma } from '../../@backend/prisma'; import { envs } from './envs'; export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: 'postgresql', }), socialProviders: { google: { clientId: envs.GOOGLE_OAUTH_WEB_CLIENT_ID, clientSecret: envs.GOOGLE_OAUTH_WEB_CLIENT_SECRET, }, }, // baseURL: `http://localhost:${envs.PORT}`, basePath: '/auth', trustedOrigins: [envs.BROWSER_APP_PUBLIC_BASE_URL], plugins: [ magicLink({ disableSignUp: true, expiresIn: 5 * 60, sendMagicLink: async ({ email, url, token }) => { console.log('url', url); await sendNoReplyEmail({ to: email, subject: 'Link de acceso', html: `Has click <a href="${url}">aquí</a> para abrir sesión en el sitio.`, }); }, }), ], }); ``` With this basic setup, calling: ```ts const { data, error } = await betterAuthClient.signIn.magicLink({ email, callbackURL: redirectUri, }); ``` throws with a 400 and no response body whatsoever. Making this small change: ```ts app.all('/auth/*', (req, res) => { req.headers['content-type'] = 'application/json'; // ADDED THIS void handler(req, res); }); ``` It now works. The email is sent. However, even though on the client the response status code is 200, the method call returns `{ data: null, error: { status: 0, statusText: '' }}`, which appears to be a connection error, but there was no connection error. The magic link was sent.
GiteaMirror added the locked label 2026-04-13 03:31:09 -05:00
Author
Owner

@Bekacru commented on GitHub (Dec 8, 2024):

Make the json middleware exclude Better Auth routes, or mount it after the handler.

<!-- gh-comment-id:2525426170 --> @Bekacru commented on GitHub (Dec 8, 2024): Make the `json` middleware exclude Better Auth routes, or mount it after the handler.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#8449