/**
*
*
Official Sequilize adapter for Auth.js / NextAuth.js.
*
*
*
*
*
* ## Installation
*
* ```bash npm2yarn
* npm install next-auth @auth/sequelize-adapter sequelize
* ```
*
* @module @auth/sequelize-adapter
*/
import type {
Adapter,
AdapterUser,
AdapterAccount,
AdapterSession,
VerificationToken,
} from "@auth/core/adapters"
import { Sequelize, Model, ModelCtor } from "sequelize"
import * as defaultModels from "./models.js"
export { defaultModels as models }
// @see https://sequelize.org/master/manual/typescript.html
//@ts-expect-error
interface AccountInstance
extends Model>,
AdapterAccount {}
interface UserInstance
extends Model>,
AdapterUser {}
interface SessionInstance
extends Model>,
AdapterSession {}
interface VerificationTokenInstance
extends Model>,
VerificationToken {}
/** This is the interface of the Sequelize adapter options. */
export interface SequelizeAdapterOptions {
/**
* Whether to {@link https://sequelize.org/docs/v6/core-concepts/model-basics/#model-synchronization synchronize} the models or not.
*/
synchronize?: boolean
/**
* The {@link https://sequelize.org/docs/v6/core-concepts/model-basics/ Sequelize Models} related to Auth.js that will be created in your database.
*/
models?: Partial<{
User: ModelCtor
Account: ModelCtor
Session: ModelCtor
VerificationToken: ModelCtor
}>
}
export default function SequelizeAdapter(
client: Sequelize,
options?: SequelizeAdapterOptions
): Adapter {
const { models, synchronize = true } = options ?? {}
const defaultModelOptions = { underscored: true, timestamps: false }
const { User, Account, Session, VerificationToken } = {
User:
models?.User ??
client.define(
"user",
defaultModels.User,
defaultModelOptions
),
Account:
models?.Account ??
client.define(
"account",
defaultModels.Account,
defaultModelOptions
),
Session:
models?.Session ??
client.define(
"session",
defaultModels.Session,
defaultModelOptions
),
VerificationToken:
models?.VerificationToken ??
client.define(
"verificationToken",
defaultModels.VerificationToken,
defaultModelOptions
),
}
let _synced = false
const sync = async () => {
if (process.env.NODE_ENV !== "production" && synchronize && !_synced) {
const syncOptions =
typeof synchronize === "object" ? synchronize : undefined
await Promise.all([
User.sync(syncOptions),
Account.sync(syncOptions),
Session.sync(syncOptions),
VerificationToken.sync(syncOptions),
])
_synced = true
}
}
Account.belongsTo(User, { onDelete: "cascade" })
Session.belongsTo(User, { onDelete: "cascade" })
return {
async createUser(user) {
await sync()
return await User.create(user)
},
async getUser(id) {
await sync()
const userInstance = await User.findByPk(id)
return userInstance?.get({ plain: true }) ?? null
},
async getUserByEmail(email) {
await sync()
const userInstance = await User.findOne({
where: { email },
})
return userInstance?.get({ plain: true }) ?? null
},
async getUserByAccount({ provider, providerAccountId }) {
await sync()
const accountInstance = await Account.findOne({
// @ts-expect-error
where: { provider, providerAccountId },
})
if (!accountInstance) {
return null
}
const userInstance = await User.findByPk(accountInstance.userId)
return userInstance?.get({ plain: true }) ?? null
},
async updateUser(user) {
await sync()
await User.update(user, { where: { id: user.id } })
const userInstance = await User.findByPk(user.id)
return userInstance!
},
async deleteUser(userId) {
await sync()
const userInstance = await User.findByPk(userId)
await User.destroy({ where: { id: userId } })
return userInstance
},
async linkAccount(account) {
await sync()
await Account.create(account)
},
async unlinkAccount({ provider, providerAccountId }) {
await sync()
await Account.destroy({
where: { provider, providerAccountId },
})
},
async createSession(session) {
await sync()
return await Session.create(session)
},
async getSessionAndUser(sessionToken) {
await sync()
const sessionInstance = await Session.findOne({
where: { sessionToken },
})
if (!sessionInstance) {
return null
}
const userInstance = await User.findByPk(sessionInstance.userId)
if (!userInstance) {
return null
}
return {
session: sessionInstance?.get({ plain: true }),
user: userInstance?.get({ plain: true }),
}
},
async updateSession({ sessionToken, expires }) {
await sync()
await Session.update(
{ expires, sessionToken },
{ where: { sessionToken } }
)
return await Session.findOne({ where: { sessionToken } })
},
async deleteSession(sessionToken) {
await sync()
const session = await Session.findOne({ where: { sessionToken } })
await Session.destroy({ where: { sessionToken } })
return session?.get({ plain: true })
},
async createVerificationToken(token) {
await sync()
return await VerificationToken.create(token)
},
async useVerificationToken({ identifier, token }) {
await sync()
const tokenInstance = await VerificationToken.findOne({
where: { identifier, token },
})
await VerificationToken.destroy({ where: { identifier } })
return tokenInstance?.get({ plain: true }) ?? null
},
}
}