migrate auth from next-auth to better-auth with magic link support

Replace next-auth with better-auth, adding magic link email login as
the primary auth method and GitHub OAuth as an optional social provider.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Thomas Ruoff
2026-03-03 21:07:23 +01:00
parent bc34a05d2b
commit d63ded8c6a
11 changed files with 1506 additions and 427 deletions

View File

@@ -0,0 +1,37 @@
import { auth } from "../../../lib/auth"
import type { NextApiRequest, NextApiResponse } from "next"
export const config = {
api: {
bodyParser: false,
},
}
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const baseUrl = process.env.BETTER_AUTH_URL || "http://localhost:3000"
const url = new URL(req.url!, baseUrl)
let body: Buffer | undefined
if (req.method !== "GET" && req.method !== "HEAD") {
body = await new Promise<Buffer>((resolve, reject) => {
const chunks: Buffer[] = []
req.on("data", (chunk: Buffer) => chunks.push(chunk))
req.on("end", () => resolve(Buffer.concat(chunks)))
req.on("error", reject)
})
}
const webRequest = new Request(url.toString(), {
method: req.method,
headers: req.headers as HeadersInit,
body: body,
})
const response = await auth.handler(webRequest)
res.status(response.status)
response.headers.forEach((value, key) => {
res.setHeader(key, value)
})
res.end(await response.text())
}

View File

@@ -1,73 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import NextAuth from 'next-auth'
import EmailProvider from 'next-auth/providers/email'
import GitHubProvider from 'next-auth/providers/github'
import { MongoDBAdapter } from '@next-auth/mongodb-adapter'
import { MONGO_URI } from '../../../db'
import { MongoClient, ServerApiVersion } from 'mongodb'
let client: MongoClient
const ADMIN_EMAIL = process.env.ADMIN_EMAIL
const GITHUB_USERS_GRANTED = ['111471']
async function getMongoClient() {
if (!client) {
client = new MongoClient(MONGO_URI, {
serverApi: {
version: ServerApiVersion.v1,
strict: true,
deprecationErrors: true,
}
})
await client.connect()
}
return client
}
export default async function auth(req: NextApiRequest, res: NextApiResponse) {
return await NextAuth(req, res, {
secret: process.env.NEXTAUTH_SECRET,
adapter: MongoDBAdapter(getMongoClient()),
providers: [
GitHubProvider({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
}),
EmailProvider({
server: {
host: "wirtanen.uberspace.de",
port: 465,
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
logger: true,
debug: true,
},
from: process.env.FROM_EMAIL,
}),
],
callbacks: {
async signIn({ account, email }) {
// if user sigin requested magic link via EmailProvider
if (account.provider === 'email') {
if (email?.verificationRequest) {
// only allow admins by email entered
return account.providerAccountId === ADMIN_EMAIL
}
// if user accesses with magic link, also only allow admin
return account.providerAccountId === ADMIN_EMAIL
} else if (account.provider === 'github') {
// only one and only one user
return GITHUB_USERS_GRANTED.includes(account.providerAccountId)
}
return false
},
},
})
}