Created
June 7, 2023 16:02
-
-
Save IzumiSy/1c1a68709d6e590f08274fd76e2e23f2 to your computer and use it in GitHub Desktop.
PrismaAdapter for NextAuth with all camelCase naming convention
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 { Adapter, AdapterSession, AdapterUser } from 'next-auth/adapters' | |
import { PrismaClient } from '@prisma/client' | |
import { PrismaClientKnownRequestError } from '@prisma/client/runtime' | |
import { User, Session } from '@prisma/client' | |
// PrismaAdapter customized to have more cleaner naming consistency | |
// (From: https://github.com/nextauthjs/next-auth/blob/main/packages/adapter-prisma/src/index.ts) | |
export function PrismaAdapter(p: PrismaClient): Adapter { | |
const toAdapterUser = (user: User): AdapterUser => { | |
return { | |
id: user.id, | |
name: user.name, | |
email: user.email || '', | |
emailVerified: user.emailVerified, | |
image: user.image, | |
} | |
} | |
const toAdapterSession = (session: Session): AdapterSession => { | |
return { | |
sessionToken: session.sessionToken, | |
userId: session.userID, | |
expires: session.expires, | |
} | |
} | |
return { | |
// users | |
createUser: (data) => p.user.create({ data }).then(toAdapterUser), | |
async getUser(id) { | |
const user = await p.user.findUnique({ where: { id } }) | |
if (user === null) return null | |
return toAdapterUser(user) | |
}, | |
async getUserByEmail(email) { | |
const user = await p.user.findUnique({ where: { email } }) | |
if (user === null) return null | |
return toAdapterUser(user) | |
}, | |
async getUserByAccount({ provider, providerAccountId }) { | |
const account = await p.account.findUnique({ | |
include: { user: true }, | |
where: { | |
provider_providerAccountID: { | |
provider, | |
providerAccountID: providerAccountId, | |
}, | |
}, | |
}) | |
if (account === null) return null | |
return toAdapterUser(account.user) | |
}, | |
async updateUser({ id, ...data }) { | |
return await p.user.update({ where: { id }, data }).then(toAdapterUser) | |
}, | |
async deleteUser(id) { | |
await p.user.delete({ where: { id } }) | |
}, | |
// accounts | |
async linkAccount(data) { | |
await p.account.create({ | |
data: { | |
userID: data.userId, | |
type: data.type, | |
provider: data.provider, | |
providerAccountID: data.providerAccountId, | |
refreshToken: data.refresh_token, | |
accessToken: data.access_token, | |
expiresAt: data.expires_at, | |
tokenType: data.token_type, | |
scope: data.scope, | |
idToken: data.id_token, | |
sessionState: data.session_state, | |
}, | |
}) | |
return data | |
}, | |
async unlinkAccount({ provider, providerAccountId }) { | |
await p.account.delete({ | |
where: { | |
provider_providerAccountID: { | |
provider, | |
providerAccountID: providerAccountId, | |
}, | |
}, | |
}) | |
}, | |
// sessions | |
async getSessionAndUser(sessionToken) { | |
const userAndSession = await p.session.findUnique({ | |
where: { sessionToken }, | |
include: { user: true }, | |
}) | |
if (!userAndSession) return null | |
const { user, ...session } = userAndSession | |
return { | |
user: toAdapterUser(user), | |
session: toAdapterSession(session), | |
} | |
}, | |
createSession: (data) => | |
p.session | |
.create({ | |
data: { | |
sessionToken: data.sessionToken, | |
userID: data.userId, | |
expires: data.expires, | |
}, | |
}) | |
.then(toAdapterSession), | |
updateSession: (data) => | |
p.session | |
.update({ | |
where: { sessionToken: data.sessionToken }, | |
data, | |
}) | |
.then(toAdapterSession), | |
async deleteSession(sessionToken) { | |
await p.session.delete({ where: { sessionToken } }) | |
}, | |
async createVerificationToken(data) { | |
const verificationToken = await p.verificationToken.create({ data }) | |
// @ts-expect-errors // MongoDB needs an ID, but we don't | |
if (verificationToken.id) delete verificationToken.id | |
return verificationToken | |
}, | |
async useVerificationToken(identifier_token) { | |
try { | |
const verificationToken = await p.verificationToken.delete({ | |
where: { identifier_token }, | |
}) | |
// @ts-expect-errors // MongoDB needs an ID, but we don't | |
if (verificationToken.id) delete verificationToken.id | |
return verificationToken | |
} catch (error) { | |
// If token already used/deleted, just return null | |
// https://www.prisma.io/docs/reference/api-reference/error-reference#p2025 | |
if ((error as PrismaClientKnownRequestError).code === 'P2025') | |
return null | |
throw error | |
} | |
}, | |
} | |
} |
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
model Account { | |
id String @id @default(cuid()) | |
userID String @map("user_id") | |
type String | |
provider String | |
providerAccountID String @map("provider_account_id") | |
refreshToken String? @map("refresh_token") @db.String() | |
accessToken String? @map("access_token") @db.String() | |
expiresAt Int? @map("expires_at") | |
tokenType String? @map("token_type") | |
scope String? | |
idToken String? @map("id_token") @db.String() | |
sessionState String? @map("session_state") | |
user User @relation(fields: [userID], references: [id], onDelete: Cascade) | |
@@unique([provider, providerAccountID]) | |
@@map("accounts") | |
} | |
model Session { | |
id String @id @default(cuid()) | |
sessionToken String @unique @map("session_token") | |
userID String @map("user_id") | |
expires DateTime | |
user User @relation(fields: [userID], references: [id], onDelete: Cascade) | |
@@map("sessions") | |
} | |
model User { | |
id String @id @default(cuid()) | |
name String? | |
email String? @unique | |
emailVerified DateTime? @map("email_verified") | |
image String? | |
Session Session[] | |
Account Account[] | |
@@map("users") | |
} | |
model VerificationToken { | |
identifier String | |
token String @unique | |
expires DateTime | |
@@unique([identifier, token]) | |
@@map("verificationTokens") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment