You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

194 lines
6.6 KiB
Markdown

# New Authentication Provider Guide
LobeChat uses [Auth.js v5](https://authjs.dev/) as the external authentication service. Auth.js is an open-source authentication library that provides a simple way to implement authentication and authorization features. This document will introduce how to use Auth.js to implement a new authentication provider.
### TOC
- [Add New Authentication Provider](#add-new-authentication-provider)
- [Pre-requisites: Check the Official Provider List](#pre-requisites-check-the-official-provider-list)
- [Step 1: Add Authenticator Core Code](#step-1-add-authenticator-core-code)
- [Step 2: Update Server Configuration Code](#step-2-update-server-configuration-code)
- [Step 3: Change Frontend Pages](#step-3-change-frontend-pages)
- [Step 4: Configure the Environment Variables](#step-4-configure-the-environment-variables)
- [Step 5: Modify server-side user information processing logic](#step-5-modify-server-side-user-information-processing-logic)
## Add New Authentication Provider
To add a new authentication provider in LobeChat (for example, adding Okta), you need to follow the steps below:
### Pre-requisites: Check the Official Provider List
First, you need to check the [Auth.js Provider List](https://authjs.dev/reference/core/providers) to see if your provider is already supported. If yes, you can directly use the SDK provided by Auth.js to implement the authentication feature.
Next, I will use [Okta](https://authjs.dev/reference/core/providers/okta) as an example to introduce how to add a new authentication provider.
### Step 1: Add Authenticator Core Code
Open the `src/app/api/auth/next-auth.ts` file and import `next-auth/providers/okta`.
```ts
import { NextAuth } from 'next-auth';
import Auth0 from 'next-auth/providers/auth0';
import Okta from 'next-auth/providers/okta';
// Import Okta provider
```
Add the predefined server configuration.
```ts
// Import server configuration
const { OKTA_CLIENT_ID, OKTA_CLIENT_SECRET, OKTA_ISSUER } = getServerConfig();
const nextAuth = NextAuth({
providers: [
// ... Other providers
Okta({
clientId: OKTA_CLIENT_ID,
clientSecret: OKTA_CLIENT_SECRET,
issuer: OKTA_ISSUER,
}),
],
});
```
### Step 2: Update Server Configuration Code
Open the `src/config/server/app.ts` file and add Okta-related environment variables in the `getAppConfig` function.
```ts
export const getAppConfig = () => {
// ... Other code
return {
// ... Other environment variables
OKTA_CLIENT_ID: process.env.OKTA_CLIENT_ID || '',
OKTA_CLIENT_SECRET: process.env.OKTA_CLIENT_SECRET || '',
OKTA_ISSUER: process.env.OKTA_ISSUER || '',
};
};
```
### Step 3: Change Frontend Pages
Modify the `signIn` function parameter in `src/Features/Conversation/Error/OAuthForm.tsx` and \`src/app/settings/common/Common.tsx
The default is `auth0`, which you can change to `okta` to switch to the Okta provider, or remove this parameter to support all added authentication services
This value is the id of the Auth.js provider, and you can read the source code of the corresponding `next-auth/providers` module to read the default ID.
### Step 4: Configure the Environment Variables
Add `OKTA_CLIENT_ID`、`OKTA_CLIENT_SECRET`、`OKTA_ISSUER` environment variables when you deploy.
### Step 5: Modify server-side user information processing logic
#### Get user information in the frontend
Use the `useOAuthSession()` method in the frontend page to get the user information `user` returned by the backend:
```ts
import { useOAuthSession } from '@/hooks/useOAuthSession';
const { user, isOAuthLoggedIn } = useOAuthSession();
```
The default type of `user` is `User`, and the type definition is:
```ts
interface User {
id?: string;
name?: string | null;
email?: string | null;
image?: string | null;
}
```
#### Modify user `id` handling logic
The `user.id` is used to identify users. When introducing a new OAuth identity provider, you need to handle the information carried in the OAuth callback in `src/app/api/auth/next-auth.ts`. You need to select the user's `id` from this information. Before that, we need to understand the data processing sequence of `Auth.js`:
```txt
authorize --> jwt --> session
```
By default, in the `jwt --> session` process, `Auth.js` will [automatically assign the user `id` to `account.providerAccountId` based on the login type](https://authjs.dev/reference/core/types#provideraccountid). If you need to select a different value as the user `id`, you need to implement the following handling logic:
```ts
callbacks: {
async jwt({ token, account, profile }) {
if (account) {
// You can select a different value from `account` or `profile`
token.userId = account.providerAccountId;
}
return token;
},
},
```
#### Customize `session` return
If you want to carry more information about `profile` and `account` in the `session`, according to the data processing order mentioned above in `Auth.js`, you must first copy this information to the `token`. For example, add the user avatar URL `profile.picture` to the `session`:
```diff
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;
},
},
```
Then supplement the type definition for the new parameters:
```ts
declare module '@auth/core/jwt' {
interface JWT {
// ...
avatar?: string;
}
}
declare module 'next-auth' {
interface User {
avatar?: string;
}
}
```
> [More built-in type extensions in Auth.js](https://authjs.dev/getting-started/typescript#module-augmentation)
#### Differentiate multiple authentication providers in the processing logic
If you have configured multiple authentication providers and their `userId` mappings are different, you can use the `account.provider` parameter in the `jwt` method to get the default id of the identity provider and enter different processing logic.
```ts
callbacks: {
async jwt({ token, profile, account }) {
if (profile && account) {
if (account.provider === 'authing')
token.userId = account.providerAccountId ?? token.sub;
else if (acount.provider === 'auth0')
token.userId = profile.sub ?? token.sub;
else
// other providers
}
return token;
},
}
```
Now, you can use Okta as your provider to implement the authentication feature in LobeChat.