Created
March 18, 2024 20:54
-
-
Save adriangalilea/6601f0fcde1a2225977054d79d2a0d4e to your computer and use it in GitHub Desktop.
NextAuth Auth.js module augmentation + custom adapter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import NextAuth from "next-auth"; | |
import GitHub from "next-auth/providers/github"; | |
import { DrizzleAdapter } from "@auth/drizzle-adapter"; | |
import { db } from "./db"; | |
import { type AdapterUser, Adapter } from "@auth/core/adapters"; | |
import { users, accounts, verificationTokens, sessions } from "@/db/schema"; | |
import { eq, and } from "drizzle-orm"; | |
declare module "next-auth" { | |
interface Profile { | |
gh_id: string; | |
gh_image: string; | |
gh_username: string; | |
} | |
interface User { | |
gh_id: string; | |
gh_image: string; | |
gh_username: string; | |
} | |
} | |
function customAdapter(): Adapter { | |
const adapter = DrizzleAdapter(db); | |
// Overwrite createUser method on adapter | |
adapter.createUser = async (data): Promise<AdapterUser> => { | |
// console.log("Creating user", data); | |
const dataEntered = await db | |
.insert(users) | |
.values({ | |
...data, | |
id: crypto.randomUUID(), | |
// @ts-ignore | |
gh_id: data.gh_id, | |
// @ts-ignore | |
gh_username: data.gh_username, | |
// @ts-ignore | |
gh_image: data.gh_image, | |
}) | |
.returning() | |
.then((res) => res[0] ?? null); | |
if (!dataEntered) { | |
throw new Error("User Creation Failed"); | |
} | |
// @ts-ignore | |
return dataEntered; | |
}; | |
// @ts-ignore | |
adapter.getUser = async (data) => { | |
console.log("Getting user", data); | |
const result = await db | |
.select() | |
.from(users) | |
.where(eq(users.id, data)) | |
.get(); | |
return result ?? null; | |
}; | |
// @ts-ignore | |
adapter.getUserByEmail = async (data) => { | |
console.log("Getting user by email", data); | |
const result = await db | |
.select() | |
.from(users) | |
.where(eq(users.email, data)) | |
.get(); | |
return result ?? null; | |
}; | |
// @ts-ignore | |
adapter.createSession = (data) => { | |
console.log("Creating session", data); | |
return db.insert(sessions).values(data).returning().get(); | |
}; | |
// @ts-ignore | |
adapter.getSessionAndUser = async (data) => { | |
console.log("Getting session and user", data); | |
const result = await db | |
.select({ session: sessions, user: users }) | |
.from(sessions) | |
.where(eq(sessions.sessionToken, data)) | |
.innerJoin(users, eq(users.id, sessions.userId)) | |
.get(); | |
return result ?? null; | |
}; | |
// @ts-ignore | |
adapter.updateUser = async (data) => { | |
if (!data.id) { | |
throw new Error("No user id."); | |
} | |
const result = await db | |
.update(users) | |
.set(data) | |
.where(eq(users.id, data.id)) | |
.returning() | |
.get(); | |
return result ?? null; | |
}; | |
// @ts-ignore | |
adapter.updateSession = async (data) => { | |
const result = await db | |
.update(sessions) | |
.set(data) | |
.where(eq(sessions.sessionToken, data.sessionToken)) | |
.returning() | |
.get(); | |
return result ?? null; | |
}; | |
// @ts-ignore | |
adapter.getUserByAccount = async (account) => { | |
console.log("Getting user by account", account); | |
const results = await db | |
.select() | |
.from(accounts) | |
.leftJoin(users, eq(users.id, accounts.userId)) | |
.where( | |
and( | |
eq(accounts.provider, account.provider), | |
eq(accounts.providerAccountId, account.providerAccountId) | |
) | |
) | |
.get(); | |
if (!results) { | |
return null; | |
} | |
return Promise.resolve(results).then((results) => results.user); | |
}; | |
// @ts-ignore | |
adapter.deleteSession = async (sessionToken) => { | |
const result = await db | |
.delete(sessions) | |
.where(eq(sessions.sessionToken, sessionToken)) | |
.returning() | |
.get(); | |
return result ?? null; | |
}; | |
// @ts-ignore | |
adapter.createVerificationToken = async (token) => { | |
const result = await db | |
.insert(verificationTokens) | |
.values(token) | |
.returning() | |
.get(); | |
return result ?? null; | |
}; | |
// @ts-ignore | |
adapter.useVerificationToken = async (token) => { | |
try { | |
const result = await db | |
.delete(verificationTokens) | |
.where( | |
and( | |
eq(verificationTokens.identifier, token.identifier), | |
eq(verificationTokens.token, token.token) | |
) | |
) | |
.returning() | |
.get(); | |
return result ?? null; | |
} catch (err) { | |
throw new Error("No verification token found."); | |
} | |
}; | |
// @ts-ignore | |
adapter.deleteUser = async (id) => { | |
const result = await db | |
.delete(users) | |
.where(eq(users.id, id)) | |
.returning() | |
.get(); | |
return result ?? null; | |
}; | |
// @ts-ignore | |
adapter.unlinkAccount = async (account) => { | |
await db | |
.delete(accounts) | |
.where( | |
and( | |
eq(accounts.providerAccountId, account.providerAccountId), | |
eq(accounts.provider, account.provider) | |
) | |
) | |
.run(); | |
}; | |
return { | |
...adapter, | |
}; | |
} | |
export const { | |
handlers: { GET, POST }, | |
auth, | |
signIn, | |
signOut, | |
} = NextAuth({ | |
debug: true, | |
basePath: "/auth", | |
adapter: customAdapter(), | |
providers: [ | |
GitHub({ | |
profile(profile) { | |
return { | |
id: profile.id.toString(), | |
gh_id: profile.id.toString(), | |
name: profile.name ?? profile.login, | |
email: profile.email, | |
gh_image: profile.avatar_url, | |
gh_username: profile.login, | |
}; | |
}, | |
}), | |
], | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment