英文:
next-auth with EmailProvider - is it possible to assign a new user to a role based on their email domain?
问题
我正在使用NextAuth和EmailProvider来处理登录到Next.js应用程序的功能,并使用Prisma将新用户保存在PostgreSQL数据库中。模式包括一个名为isAdmin
的字段,默认值为false
。
我想要将该字段设置为true
,但只适用于从特定电子邮件域登录的新用户,但我不确定是否有可能实现这一点。目前,我需要手动进入数据库,一旦用户注册后,就设置标志,这显然非常不理想。
我非常感激任何指导,甚至确认我正在尝试的是否可能!
补充说明: 现在我认为我可能需要向NextAuth函数添加事件或回调,但文档侧重于重定向用户,而不是用户创建,因此我仍然不确定我正在尝试的是否可能实现(https://next-auth.js.org/configuration/events)。
以下是我的 pages>api>auth>[...nextauth].js
代码:
import NextAuth from "next-auth"
import EmailProvider from "next-auth/providers/email"
import { PrismaAdapter } from "@next-auth/prisma-adapter"
import prisma from "lib/prisma"
let data
if (process.env.NODE_ENV === "development") {
data = {
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}
} else {
data = {
server: {
host: process.env.EMAIL_SERVER_HOST,
port: process.env.EMAIL_SERVER_PORT,
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD,
},
},
from: process.env.EMAIL_FROM,
}
}
export default NextAuth({
providers: [EmailProvider(data)],
database: process.env.DATABASE_URL,
secret: process.env.SECRET,
session: {
jwt: true,
maxAge: 30 * 24 * 60 * 60, // 30 days
},
debug: true,
adapter: PrismaAdapter(prisma),
callbacks: {
session: async ({ session, user }) => {
session.user.id = user.id
session.user.isAdmin = user.isAdmin
return Promise.resolve(session)
},
},
})
以及我的 Prisma 模式:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
isAdmin Boolean @default(false)
accounts Account[]
sessions Session[]
}
// 其余模型略...
英文:
I'm using NextAuth and EmailProvider to handle log-in to a Next.js app, and new users are saved in a postgres database using Prisma. The schema includes a isAdmin
field that defaults to false
I'd like to set that field to true
for new users who sign in from a specific email domain but can't work out if that's possible - at the moment I need to go into the dB and set the flag manually once the user has signed up, which is obviously very much not ideal.
I'd very much appreciate any pointers, or even confirmation that what I'm trying to do is impossible!
ETA: I now think I may need to add either an Event or a Callback to the NextAuth function, but the documentation focuses on redirecting users rather than on user creation, so I'm still not sure if what I'm trying to do is even possible (https://next-auth.js.org/configuration/events).
Below is my pages>api>auth>[...nextauth].js
code.
import NextAuth from "next-auth"
import EmailProvider from "next-auth/providers/email"
import { PrismaAdapter } from "@next-auth/prisma-adapter"
import prisma from "lib/prisma"
let data
if (process.env.NODE_ENV === "development") {
data = {
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}
} else {
data = {
server: {
host: process.env.EMAIL_SERVER_HOST,
port: process.env.EMAIL_SERVER_PORT,
auth: {
user: process.env.EMAIL_SERVER_USER,
pass: process.env.EMAIL_SERVER_PASSWORD,
},
},
from: process.env.EMAIL_FROM,
}
}
export default NextAuth({
providers: [EmailProvider(data)],
database: process.env.DATABASE_URL,
secret: process.env.SECRET,
session: {
jwt: true,
maxAge: 30 * 24 * 60 * 60, // 30 days
},
debug: true,
adapter: PrismaAdapter(prisma),
callbacks: {
session: async ({ session, user }) => {
session.user.id = user.id
session.user.isAdmin = user.isAdmin
return Promise.resolve(session)
},
},
})
and my Prisma schema
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
isAdmin Boolean @default(false)
accounts Account[]
sessions Session[]
}
model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
oauth_token_secret String?
oauth_token String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
答案1
得分: 3
我已解决了我的问题,答案是"不过...但"。如果有可能劫持next-auth用户创建过程,我还没有找到方法 - 但现在我怀疑这可能涉及分叉next-auth代码,而且总体而言都不是一个好主意。所以"不,你不能在创建时分配角色给用户"。
但是,正如我在编辑我的问题时提到的,next-auth有事件
的概念,允许您在响应next-auth生命周期事件时执行自己的代码,例如signIn
事件每次成功登录时触发。
所以我使用了signIn
事件,它接收用户对象和一个isNewUser
布尔值作为参数。我检查用户是否是新创建的,如果是,他们是否拥有指定的电子邮件域?如果是,然后我在用户创建后更新数据库。
由于所有这些都是在登录期间完成的,对用户来说看起来像一个单一的步骤过程。
具体步骤:
(1) 在.env
文件中添加所需的电子邮件域:
ADMIN_EMAIL_DOMAIN="whitelist.domain.com"
(2) 在[...nextauth].js
中的NextAuth
函数中添加一个事件:
//...与上述代码相同
import { updateUserToAdmin } from "lib/data.js";
//...与上述代码相同
export default NextAuth({
//...与上述代码相同
events: {
signIn: async ({ user, isNewUser }) => {
if (isNewUser) {
const userEmail = user.email;
const isAdminEmail =
userEmail.split("@")[1] === process.env.ADMIN_EMAIL_DOMAIN;
isAdminEmail
? await updateUserToAdmin(user.id, prisma, isAdminEmail)
: console.log("非管理员域");
}
},
},
});
(3) 在lib>data.js
中添加updateUserToAdmin
查询:
export const updateUserToAdmin = async (userId, prisma, isAdminEmail) => {
return await prisma.user.update({
where: {
id: userId,
},
data: {
isAdmin: isAdminEmail,
},
});
}
希望其他人会发现这对他们有帮助。如果您想提出任何改进意见,请随时评论。
英文:
I have solved my own problem and the answer is "no ...but". If it is possible to hijack the next-auth user creation process, I haven't found out how - but I now suspect that it would involve forking the next-auth code and would all round be a Really Bad Idea anyway. So "no, you can't assign a role to a user at the point of creation"
However, as I mentioned in my edit to my question, next-auth has the concept of Events
, which allow you to execute your own code in response to next-auth life-cycle occurrences e.g. the signIn
event is triggered every time a successful log in occurs.
So I used the signIn
event, which receives the user object and an isNewUser
boolean as params. I check if the user is newly-created and if so, do they have the specified email domain? If yes, then I update the database after the user has been created.
Since this is all done during sign in, it looks like a single-step process to the user.
In detail:
(1) add the required email domain to the .env
file:
ADMIN_EMAIL_DOMAIN="whitelist.domain.com"
(2) add an event to the NextAuth
function in [...nextauth].js
:
//...code as above
import { updateUserToAdmin } from "lib/data.js"
//...code as above
export default NextAuth({
//...code as above
events: {
signIn: async ({ user, isNewUser }) => {
if (isNewUser) {
const userEmail = user.email
const isAdminEmail =
userEmail.split("@")[1] === process.env.ADMIN_EMAIL_DOMAIN
isAdminEmail
? await updateUserToAdmin(user.id, prisma, isAdminEmail)
: console.log(`non-Admin domain`)
}
},
},
})
(3) add the updateUserToAdmin
query to lib>data.js
:
return await prisma.user.update({
where: {
id: userId,
},
data: {
isAdmin: isAdminEmail,
},
})
}
I hope someone else finds this helpful. Do feel free to comment if you want to suggest any improvements.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论