LobeChat 使用 Auth.js v5 作为外部身份验证服务。Auth.js 是一个开源的身份验证库,它提供了一种简单的方式来实现身份验证和授权功能。本文档将介绍如何使用 Auth.js 来实现新的身份验证方式。
为了在 LobeChat 中添加新的身份验证提供者(例如添加 Okta),你需要完成以下步骤:
首先,你需要查阅 Auth.js 提供者列表 来了解是否你的提供者已经被支持。如果你的提供者已经被支持,你可以直接使用 Auth.js 提供的 SDK 来实现身份验证功能。
接下来我会以 Okta 为例来介绍如何添加新的身份验证提供者
打开 src/app/api/auth/next-auth.ts
文件,引入 next-auth/providers/okta
import { NextAuth } from 'next-auth'; import Auth0 from 'next-auth/providers/auth0'; import Okta from 'next-auth/providers/okta'; // 引入 Okta 提供者
新增预定义的服务端配置
// 导入服务器配置 const { OKTA_CLIENT_ID, OKTA_CLIENT_SECRET, OKTA_ISSUER } = getServerConfig(); const nextAuth = NextAuth({ providers: [ // ... 其他提供者 Okta({ clientId: OKTA_CLIENT_ID, clientSecret: OKTA_CLIENT_SECRET, issuer: OKTA_ISSUER, }), ], });
打开 src/config/server/app.ts
文件,在 getAppConfig
函数中新增 Okta 相关的环境变量
export const getAppConfig = () => { // ... 其他代码 return { // ... 其他环境变量 OKTA_CLIENT_ID: process.env.OKTA_CLIENT_ID || '', OKTA_CLIENT_SECRET: process.env.OKTA_CLIENT_SECRET || '', OKTA_ISSUER: process.env.OKTA_ISSUER || '', }; };
修改在 src/features/Conversation/Error/OAuthForm.tsx
及 src/app/settings/common/Common.tsx
中的 signIn
函数参数
默认为 auth0
,你可以将其修改为 okta
以切换到 Okta 提供者,或删除该参数以支持所有已添加的身份验证服务
该值为 Auth.js 提供者 的 id,你可以阅读相应的 next-auth/providers
模块源码以读取默认 ID
在部署时新增 Okta 相关的环境变量 OKTA_CLIENT_ID
、OKTA_CLIENT_SECRET
、OKTA_ISSUER
,并填入相应的值,即可使用
在前端页面中使用 useOAuthSession()
方法获取后端返回的用户信息 user
:
import { useOAuthSession } from '@/hooks/useOAuthSession'; const { user, isOAuthLoggedIn } = useOAuthSession();
默认的 user
类型为 User
,类型定义为:
interface User { id?: string; name?: string | null; email?: string | null; image?: string | null; }
id
处理逻辑user.id
用于标识用户。当引入新身份 OAuth 提供者后,您需要在 src/app/api/auth/next-auth.ts
中处理 OAuth 回调所携带的信息。您需要从中选取用户的 id
。在此之前,我们需要了解 Auth.js
的数据处理顺序:
authorize --> jwt --> session
默认情况下,在 jwt --> session
过程中,Auth.js
会自动根据登陆类型将用户 id
赋值到 account.providerAccountId
中。 如果您需要选取其他值作为用户 id
,您需要实现以下处理逻辑。
callbacks: { async jwt({ token, account, profile }) { if (account) { // 您可以从 `account` 或 `profile` 中选取其他值 token.userId = account.providerAccountId; } return token; }, },
session
返回如果您想在 session
中携带更多关于 profile
及 account
的信息,根据上面提到的 Auth.js
数据处理顺序,那必须先将该信息复制到 token
上。
示例:把用户头像 URL:profile.picture
添加到session
中:
callbacks: { async jwt({ token, profile, account }) { if (profile && account) { token.userId = account.providerAccountId; + token.avatar = profile.picture; } return token; }, async session({ session, token }) { if (session.user) { session.user.id = token.userId ?? session.user.id; + session.user.avatar = token.avatar; } return session; }, },
然后补充对新增参数的类型定义:
declare module '@auth/core/jwt' { interface JWT { // ... avatar?: string; } } declare module 'next-auth' { interface User { avatar?: string; } }
如果您配置了多个身份验证提供者,并且他们的 userId
映射各不相同,可以在 jwt
方法中的 account.provider
参数获取身份提供者的默认 id ,从而进入不同的处理逻辑。
callbacks: { async jwt({ token, profile, account }) { if (profile && account) { if (account.provider === 'Authing') token.userId = account.providerAccountId ?? token.sub; else if (acount.provider === 'Okta') token.userId = profile.sub ?? token.sub; else // other providers } return token; }, }