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
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.
|