2025-01-21 22:50:16 +00:00
|
|
|
import 'next-auth/jwt';
|
2025-02-12 21:51:44 +00:00
|
|
|
import NextAuth, { DefaultSession } from "next-auth"
|
2025-01-16 23:24:13 +00:00
|
|
|
import GitHub from "next-auth/providers/github"
|
2025-02-10 22:31:38 +00:00
|
|
|
import Google from "next-auth/providers/google"
|
2025-02-15 05:00:45 +00:00
|
|
|
import Credentials from "next-auth/providers/credentials"
|
2025-01-16 23:24:13 +00:00
|
|
|
import { PrismaAdapter } from "@auth/prisma-adapter"
|
|
|
|
|
import { prisma } from "@/prisma";
|
2025-02-12 21:51:44 +00:00
|
|
|
import { AUTH_GITHUB_CLIENT_ID, AUTH_GITHUB_CLIENT_SECRET, AUTH_GOOGLE_CLIENT_ID, AUTH_GOOGLE_CLIENT_SECRET, AUTH_SECRET, AUTH_URL } from "./lib/environment";
|
2025-01-21 22:50:16 +00:00
|
|
|
import { User } from '@sourcebot/db';
|
2025-02-12 21:51:44 +00:00
|
|
|
import 'next-auth/jwt';
|
|
|
|
|
import type { Provider } from "next-auth/providers";
|
2025-02-15 05:00:45 +00:00
|
|
|
import { verifyCredentialsRequestSchema, verifyCredentialsResponseSchema } from './lib/schemas';
|
|
|
|
|
|
|
|
|
|
export const runtime = 'nodejs';
|
2025-01-21 22:50:16 +00:00
|
|
|
|
|
|
|
|
declare module 'next-auth' {
|
|
|
|
|
interface Session {
|
|
|
|
|
user: {
|
|
|
|
|
id: string;
|
|
|
|
|
} & DefaultSession['user'];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
declare module 'next-auth/jwt' {
|
|
|
|
|
interface JWT {
|
2025-02-12 21:51:44 +00:00
|
|
|
userId: string
|
2025-01-21 22:50:16 +00:00
|
|
|
}
|
2025-02-12 21:51:44 +00:00
|
|
|
}
|
2025-01-16 23:24:13 +00:00
|
|
|
|
|
|
|
|
const providers: Provider[] = [
|
2025-02-14 00:17:03 +00:00
|
|
|
GitHub({
|
|
|
|
|
clientId: AUTH_GITHUB_CLIENT_ID,
|
|
|
|
|
clientSecret: AUTH_GITHUB_CLIENT_SECRET,
|
|
|
|
|
}),
|
|
|
|
|
Google({
|
|
|
|
|
clientId: AUTH_GOOGLE_CLIENT_ID,
|
|
|
|
|
clientSecret: AUTH_GOOGLE_CLIENT_SECRET,
|
|
|
|
|
}),
|
2025-02-15 05:00:45 +00:00
|
|
|
Credentials({
|
|
|
|
|
credentials: {
|
|
|
|
|
email: {},
|
|
|
|
|
password: {}
|
|
|
|
|
},
|
|
|
|
|
type: "credentials",
|
|
|
|
|
authorize: async (credentials) => {
|
|
|
|
|
const body = verifyCredentialsRequestSchema.safeParse(credentials);
|
|
|
|
|
if (!body.success) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
const { email, password } = body.data;
|
|
|
|
|
|
|
|
|
|
// authorize runs in the edge runtime (where we cannot make DB calls / access environment variables),
|
|
|
|
|
// so we need to make a request to the server to verify the credentials.
|
|
|
|
|
const response = await fetch(new URL('/api/auth/verifyCredentials', AUTH_URL), {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: JSON.stringify({ email, password }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const user = verifyCredentialsResponseSchema.parse(await response.json());
|
|
|
|
|
return {
|
|
|
|
|
id: user.id,
|
|
|
|
|
email: user.email,
|
|
|
|
|
name: user.name,
|
|
|
|
|
image: user.image,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
2025-01-16 23:24:13 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// @see: https://authjs.dev/guides/pages/signin
|
|
|
|
|
export const providerMap = providers
|
|
|
|
|
.map((provider) => {
|
|
|
|
|
if (typeof provider === "function") {
|
|
|
|
|
const providerData = provider()
|
|
|
|
|
return { id: providerData.id, name: providerData.name }
|
|
|
|
|
} else {
|
|
|
|
|
return { id: provider.id, name: provider.name }
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.filter((provider) => provider.id !== "credentials");
|
|
|
|
|
|
2025-01-21 22:50:16 +00:00
|
|
|
|
2025-02-12 22:17:49 +00:00
|
|
|
const useSecureCookies = AUTH_URL?.startsWith("https://") ?? false;
|
|
|
|
|
const hostName = AUTH_URL ? new URL(AUTH_URL).hostname : "localhost";
|
2025-01-16 23:24:13 +00:00
|
|
|
|
|
|
|
|
export const { handlers, signIn, signOut, auth } = NextAuth({
|
|
|
|
|
secret: AUTH_SECRET,
|
|
|
|
|
adapter: PrismaAdapter(prisma),
|
|
|
|
|
session: {
|
|
|
|
|
strategy: "jwt",
|
|
|
|
|
},
|
2025-02-13 21:23:30 +00:00
|
|
|
trustHost: true,
|
2025-01-21 22:50:16 +00:00
|
|
|
callbacks: {
|
|
|
|
|
async jwt({ token, user: _user }) {
|
|
|
|
|
const user = _user as User | undefined;
|
|
|
|
|
// @note: `user` will be available on signUp or signIn triggers.
|
|
|
|
|
// Cache the userId in the JWT for later use.
|
|
|
|
|
if (user) {
|
|
|
|
|
token.userId = user.id;
|
|
|
|
|
}
|
|
|
|
|
return token;
|
|
|
|
|
},
|
|
|
|
|
async session({ session, token }) {
|
|
|
|
|
// @WARNING: Anything stored in the session will be sent over
|
|
|
|
|
// to the client.
|
|
|
|
|
session.user = {
|
|
|
|
|
...session.user,
|
|
|
|
|
// Propogate the userId to the session.
|
|
|
|
|
id: token.userId,
|
|
|
|
|
}
|
|
|
|
|
return session;
|
2025-02-12 21:51:44 +00:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
cookies: {
|
|
|
|
|
sessionToken: {
|
|
|
|
|
name: `${useSecureCookies ? '__Secure-' : ''}authjs.session-token`,
|
|
|
|
|
options: {
|
|
|
|
|
httpOnly: true,
|
|
|
|
|
sameSite: 'lax',
|
|
|
|
|
path: '/',
|
|
|
|
|
secure: useSecureCookies,
|
|
|
|
|
domain: `.${hostName}`
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
callbackUrl: {
|
|
|
|
|
name: `${useSecureCookies ? '__Secure-' : ''}authjs.callback-url`,
|
|
|
|
|
options: {
|
|
|
|
|
sameSite: 'lax',
|
|
|
|
|
path: '/',
|
|
|
|
|
secure: useSecureCookies,
|
|
|
|
|
domain: `.${hostName}`
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
csrfToken: {
|
2025-02-14 20:32:36 +00:00
|
|
|
name: `${useSecureCookies ? '__Secure-' : ''}authjs.csrf-token`,
|
2025-02-12 21:51:44 +00:00
|
|
|
options: {
|
|
|
|
|
httpOnly: true,
|
|
|
|
|
sameSite: 'lax',
|
|
|
|
|
path: '/',
|
|
|
|
|
secure: useSecureCookies,
|
|
|
|
|
domain: `.${hostName}`
|
|
|
|
|
}
|
2025-01-21 22:50:16 +00:00
|
|
|
}
|
|
|
|
|
},
|
2025-01-16 23:24:13 +00:00
|
|
|
providers: providers,
|
|
|
|
|
pages: {
|
|
|
|
|
signIn: "/login"
|
|
|
|
|
}
|
2025-01-21 22:50:16 +00:00
|
|
|
});
|