@lobehub/lobehub 2.0.0-next.355 → 2.0.0-next.356

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/.env.desktop +0 -1
  2. package/.env.example +16 -20
  3. package/.env.example.development +1 -4
  4. package/.github/workflows/e2e.yml +10 -11
  5. package/CHANGELOG.md +33 -0
  6. package/Dockerfile +28 -4
  7. package/changelog/v1.json +9 -0
  8. package/docker-compose/local/docker-compose.yml +2 -2
  9. package/docker-compose/local/grafana/docker-compose.yml +2 -2
  10. package/docker-compose/local/logto/docker-compose.yml +2 -2
  11. package/docker-compose/local/zitadel/.env.example +2 -2
  12. package/docker-compose/local/zitadel/.env.zh-CN.example +2 -2
  13. package/docker-compose/production/grafana/docker-compose.yml +2 -2
  14. package/docker-compose/production/logto/.env.example +2 -2
  15. package/docker-compose/production/logto/.env.zh-CN.example +2 -2
  16. package/docker-compose/production/zitadel/.env.example +2 -2
  17. package/docker-compose/production/zitadel/.env.zh-CN.example +2 -2
  18. package/docs/development/basic/add-new-authentication-providers.mdx +144 -136
  19. package/docs/development/basic/add-new-authentication-providers.zh-CN.mdx +146 -136
  20. package/docs/self-hosting/advanced/auth/legacy.mdx +4 -0
  21. package/docs/self-hosting/advanced/auth/legacy.zh-CN.mdx +4 -0
  22. package/docs/self-hosting/advanced/auth/nextauth-to-betterauth.mdx +326 -0
  23. package/docs/self-hosting/advanced/auth/nextauth-to-betterauth.zh-CN.mdx +323 -0
  24. package/docs/self-hosting/advanced/auth.mdx +43 -16
  25. package/docs/self-hosting/advanced/auth.zh-CN.mdx +44 -16
  26. package/docs/self-hosting/advanced/redis/upstash.mdx +69 -0
  27. package/docs/self-hosting/advanced/redis/upstash.zh-CN.mdx +69 -0
  28. package/docs/self-hosting/advanced/redis.mdx +128 -0
  29. package/docs/self-hosting/advanced/redis.zh-CN.mdx +126 -0
  30. package/docs/self-hosting/environment-variables/auth.mdx +15 -1
  31. package/docs/self-hosting/environment-variables/auth.zh-CN.mdx +15 -1
  32. package/docs/self-hosting/environment-variables/basic.mdx +13 -0
  33. package/docs/self-hosting/environment-variables/basic.zh-CN.mdx +13 -0
  34. package/docs/self-hosting/environment-variables/redis.mdx +68 -0
  35. package/docs/self-hosting/environment-variables/redis.zh-CN.mdx +67 -0
  36. package/docs/self-hosting/migration/v2/breaking-changes.mdx +23 -23
  37. package/docs/self-hosting/migration/v2/breaking-changes.zh-CN.mdx +23 -23
  38. package/docs/self-hosting/server-database/docker-compose.mdx +4 -4
  39. package/docs/self-hosting/server-database/docker-compose.zh-CN.mdx +4 -4
  40. package/e2e/CLAUDE.md +5 -6
  41. package/e2e/docs/local-setup.md +9 -12
  42. package/e2e/scripts/setup.ts +9 -15
  43. package/e2e/src/support/webServer.ts +6 -5
  44. package/package.json +4 -6
  45. package/packages/database/src/schemas/nextauth.ts +7 -2
  46. package/packages/utils/src/server/__tests__/auth.test.ts +1 -63
  47. package/packages/utils/src/server/auth.ts +8 -24
  48. package/scripts/_shared/checkDeprecatedAuth.js +99 -0
  49. package/scripts/clerk-to-betterauth/index.ts +8 -3
  50. package/scripts/nextauth-to-betterauth/_internal/config.ts +41 -0
  51. package/scripts/nextauth-to-betterauth/_internal/db.ts +32 -0
  52. package/scripts/nextauth-to-betterauth/_internal/env.ts +6 -0
  53. package/scripts/nextauth-to-betterauth/index.ts +226 -0
  54. package/scripts/nextauth-to-betterauth/verify.ts +188 -0
  55. package/scripts/prebuild.mts +66 -13
  56. package/scripts/serverLauncher/startServer.js +5 -5
  57. package/src/app/(backend)/api/auth/[...all]/route.ts +5 -23
  58. package/src/app/(backend)/api/webhooks/casdoor/route.ts +5 -5
  59. package/src/app/(backend)/api/webhooks/logto/route.ts +8 -8
  60. package/src/app/(backend)/middleware/auth/index.test.ts +8 -1
  61. package/src/app/(backend)/middleware/auth/index.ts +6 -15
  62. package/src/app/(backend)/middleware/auth/utils.test.ts +0 -32
  63. package/src/app/(backend)/middleware/auth/utils.ts +3 -8
  64. package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +8 -1
  65. package/src/app/(backend)/webapi/create-image/comfyui/route.ts +0 -1
  66. package/src/app/(backend)/webapi/models/[provider]/route.test.ts +8 -1
  67. package/src/app/[variants]/(auth)/signin/SignInEmailStep.tsx +1 -1
  68. package/src/app/[variants]/(auth)/signup/[[...signup]]/page.tsx +4 -17
  69. package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobContentEditor.tsx +34 -21
  70. package/src/app/[variants]/(main)/settings/profile/features/SSOProvidersList/index.tsx +12 -19
  71. package/src/app/[variants]/(main)/settings/profile/index.tsx +8 -14
  72. package/src/components/{NextAuth/AuthIcons.tsx → AuthIcons.tsx} +8 -10
  73. package/src/envs/auth.ts +12 -51
  74. package/src/envs/email.ts +3 -0
  75. package/src/envs/redis.ts +12 -54
  76. package/src/features/ChatInput/ChatInputProvider.tsx +22 -2
  77. package/src/features/ChatInput/InputEditor/index.tsx +14 -3
  78. package/src/features/ChatInput/store/initialState.ts +2 -0
  79. package/src/features/User/__tests__/PanelContent.test.tsx +0 -11
  80. package/src/features/User/__tests__/UserAvatar.test.tsx +1 -16
  81. package/src/layout/AuthProvider/index.tsx +1 -6
  82. package/src/layout/GlobalProvider/StoreInitialization.tsx +2 -4
  83. package/src/libs/better-auth/define-config.ts +2 -0
  84. package/src/libs/better-auth/plugins/email-whitelist.test.ts +120 -0
  85. package/src/libs/better-auth/plugins/email-whitelist.ts +62 -0
  86. package/src/libs/next/config/define-config.ts +13 -1
  87. package/src/libs/next/proxy/define-config.ts +2 -75
  88. package/src/libs/oidc-provider/provider.test.ts +0 -4
  89. package/src/libs/redis/index.ts +0 -1
  90. package/src/libs/redis/manager.test.ts +9 -45
  91. package/src/libs/redis/manager.ts +2 -16
  92. package/src/libs/redis/redis.test.ts +2 -4
  93. package/src/libs/redis/redis.ts +2 -4
  94. package/src/libs/redis/types.ts +2 -24
  95. package/src/libs/redis/utils.test.ts +0 -10
  96. package/src/libs/redis/utils.ts +0 -19
  97. package/src/libs/trpc/lambda/context.test.ts +0 -13
  98. package/src/libs/trpc/lambda/context.ts +21 -59
  99. package/src/libs/trpc/middleware/userAuth.ts +1 -7
  100. package/src/libs/trusted-client/getSessionUser.ts +15 -35
  101. package/src/server/globalConfig/index.ts +1 -3
  102. package/src/server/routers/lambda/__tests__/user.test.ts +0 -48
  103. package/src/server/routers/lambda/user.ts +1 -12
  104. package/src/server/services/email/impls/nodemailer/index.ts +2 -2
  105. package/src/server/services/webhookUser/index.ts +88 -0
  106. package/src/services/user/index.test.ts +0 -14
  107. package/src/services/user/index.ts +0 -4
  108. package/src/store/user/slices/auth/action.test.ts +22 -126
  109. package/src/store/user/slices/auth/action.ts +32 -65
  110. package/src/store/user/slices/auth/initialState.ts +0 -3
  111. package/src/store/user/slices/auth/selectors.ts +0 -3
  112. package/tests/setup.ts +10 -0
  113. package/scripts/_shared/checkDeprecatedClerkEnv.js +0 -42
  114. package/src/app/(backend)/api/auth/adapter/route.ts +0 -137
  115. package/src/app/[variants]/(auth)/next-auth/error/AuthErrorPage.tsx +0 -40
  116. package/src/app/[variants]/(auth)/next-auth/error/page.tsx +0 -11
  117. package/src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx +0 -167
  118. package/src/app/[variants]/(auth)/next-auth/signin/page.tsx +0 -11
  119. package/src/app/[variants]/(auth)/reset-password/layout.tsx +0 -12
  120. package/src/app/[variants]/(auth)/signin/layout.tsx +0 -12
  121. package/src/app/[variants]/(auth)/verify-email/layout.tsx +0 -12
  122. package/src/envs/auth.test.ts +0 -47
  123. package/src/layout/AuthProvider/NextAuth/UserUpdater.tsx +0 -44
  124. package/src/layout/AuthProvider/NextAuth/index.tsx +0 -17
  125. package/src/libs/next-auth/adapter/index.ts +0 -177
  126. package/src/libs/next-auth/auth.config.ts +0 -64
  127. package/src/libs/next-auth/index.ts +0 -20
  128. package/src/libs/next-auth/sso-providers/auth0.ts +0 -24
  129. package/src/libs/next-auth/sso-providers/authelia.ts +0 -39
  130. package/src/libs/next-auth/sso-providers/authentik.ts +0 -25
  131. package/src/libs/next-auth/sso-providers/casdoor.ts +0 -50
  132. package/src/libs/next-auth/sso-providers/cloudflare-zero-trust.ts +0 -34
  133. package/src/libs/next-auth/sso-providers/cognito.ts +0 -8
  134. package/src/libs/next-auth/sso-providers/feishu.ts +0 -83
  135. package/src/libs/next-auth/sso-providers/generic-oidc.ts +0 -38
  136. package/src/libs/next-auth/sso-providers/github.ts +0 -23
  137. package/src/libs/next-auth/sso-providers/google.ts +0 -18
  138. package/src/libs/next-auth/sso-providers/index.ts +0 -35
  139. package/src/libs/next-auth/sso-providers/keycloak.ts +0 -22
  140. package/src/libs/next-auth/sso-providers/logto.ts +0 -48
  141. package/src/libs/next-auth/sso-providers/microsoft-entra-id-helper.ts +0 -29
  142. package/src/libs/next-auth/sso-providers/microsoft-entra-id.ts +0 -19
  143. package/src/libs/next-auth/sso-providers/okta.ts +0 -22
  144. package/src/libs/next-auth/sso-providers/sso.config.ts +0 -8
  145. package/src/libs/next-auth/sso-providers/wechat.ts +0 -36
  146. package/src/libs/next-auth/sso-providers/zitadel.ts +0 -21
  147. package/src/libs/redis/upstash.test.ts +0 -158
  148. package/src/libs/redis/upstash.ts +0 -136
  149. package/src/server/services/nextAuthUser/index.ts +0 -318
  150. package/src/server/services/nextAuthUser/utils.ts +0 -62
  151. package/src/types/next-auth.d.ts +0 -26
@@ -4,184 +4,192 @@ title: New Authentication Provider Guide
4
4
 
5
5
  # New Authentication Provider Guide
6
6
 
7
- 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.
7
+ LobeChat uses [Better Auth](https://www.better-auth.com) as its authentication service. This document explains how to add new SSO authentication providers.
8
8
 
9
- ## Add New Authentication Provider
9
+ ## Architecture Overview
10
10
 
11
- To add a new authentication provider in LobeChat (for example, adding Okta), you need to follow the steps below:
11
+ Better Auth SSO providers fall into two categories:
12
12
 
13
- ### Pre-requisites: Check the Official Provider List
13
+ | Type | Description | Examples |
14
+ | --------- | ------------------------------------------- | -------------------------------- |
15
+ | `builtin` | Providers natively supported by Better Auth | Google, GitHub, Microsoft, Apple |
16
+ | `generic` | Implemented via Generic OIDC/OAuth plugin | Okta, Auth0, Keycloak, etc. |
14
17
 
15
- 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.
18
+ ## Adding a New SSO Provider
16
19
 
17
- Next, I will use [Okta](https://authjs.dev/reference/core/providers/okta) as an example to introduce how to add a new authentication provider.
20
+ Using **Okta** as an example, here's how to add a `generic` type provider.
18
21
 
19
- ### Step 1: Add Authenticator Core Code
22
+ ### Step 1: Create Provider Definition File
20
23
 
21
- Open the `src/app/api/auth/next-auth.ts` file and import `next-auth/providers/okta`.
24
+ Create `okta.ts` in `src/libs/better-auth/sso/providers/`:
22
25
 
23
26
  ```ts
24
- import { NextAuth } from 'next-auth';
25
- import Auth0 from 'next-auth/providers/auth0';
26
- import Okta from 'next-auth/providers/okta';
27
-
28
- // Import Okta provider
29
- ```
30
-
31
- Add the predefined server configuration.
27
+ import { authEnv } from '@/envs/auth';
28
+
29
+ import { buildOidcConfig } from '../helpers';
30
+ import type { GenericProviderDefinition } from '../types';
31
+
32
+ const provider: GenericProviderDefinition<{
33
+ AUTH_OKTA_ID: string;
34
+ AUTH_OKTA_ISSUER: string;
35
+ AUTH_OKTA_SECRET: string;
36
+ }> = {
37
+ // Build OIDC configuration
38
+ build: (env) =>
39
+ buildOidcConfig({
40
+ clientId: env.AUTH_OKTA_ID,
41
+ clientSecret: env.AUTH_OKTA_SECRET,
42
+ issuer: env.AUTH_OKTA_ISSUER,
43
+ overrides: {
44
+ // Optional: customize user profile mapping
45
+ mapProfileToUser: (profile) => ({
46
+ email: profile.email,
47
+ name: profile.name ?? profile.preferred_username ?? profile.email ?? profile.sub,
48
+ }),
49
+ },
50
+ providerId: 'okta',
51
+ }),
32
52
 
33
- ```ts
34
- // Import server configuration
35
- const { OKTA_CLIENT_ID, OKTA_CLIENT_SECRET, OKTA_ISSUER } = getServerConfig();
53
+ // Environment variable validation
54
+ checkEnvs: () => {
55
+ return !!(authEnv.AUTH_OKTA_ID && authEnv.AUTH_OKTA_SECRET && authEnv.AUTH_OKTA_ISSUER)
56
+ ? {
57
+ AUTH_OKTA_ID: authEnv.AUTH_OKTA_ID,
58
+ AUTH_OKTA_ISSUER: authEnv.AUTH_OKTA_ISSUER,
59
+ AUTH_OKTA_SECRET: authEnv.AUTH_OKTA_SECRET,
60
+ }
61
+ : false;
62
+ },
36
63
 
37
- const nextAuth = NextAuth({
38
- providers: [
39
- // ... Other providers
64
+ // Provider ID (used in AUTH_SSO_PROVIDERS)
65
+ id: 'okta',
66
+ type: 'generic',
67
+ };
40
68
 
41
- Okta({
42
- clientId: OKTA_CLIENT_ID,
43
- clientSecret: OKTA_CLIENT_SECRET,
44
- issuer: OKTA_ISSUER,
45
- }),
46
- ],
47
- });
69
+ export default provider;
48
70
  ```
49
71
 
50
- ### Step 2: Update Server Configuration Code
72
+ ### Step 2: Register the Provider
51
73
 
52
- Open the `src/config/server/app.ts` file and add Okta-related environment variables in the `getAppConfig` function.
74
+ Import and register in `src/libs/better-auth/sso/index.ts`:
53
75
 
54
76
  ```ts
55
- export const getAppConfig = () => {
56
- // ... Other code
57
-
58
- return {
59
- // ... Other environment variables
60
-
61
- OKTA_CLIENT_ID: process.env.OKTA_CLIENT_ID || '',
62
- OKTA_CLIENT_SECRET: process.env.OKTA_CLIENT_SECRET || '',
63
- OKTA_ISSUER: process.env.OKTA_ISSUER || '',
64
- };
65
- };
77
+ // Import provider
78
+ import Okta from './providers/okta';
79
+
80
+ // Add to providerDefinitions array
81
+ const providerDefinitions = [
82
+ // ... other providers
83
+ Okta,
84
+ ] as const;
66
85
  ```
67
86
 
68
- ### Step 3: Change Frontend Pages
69
-
70
- Modify the `signIn` function parameter in `src/Features/Conversation/Error/OAuthForm.tsx` and \`src/app/settings/common/Common.tsx
87
+ ### Step 3: Add Environment Variable Types
71
88
 
72
- 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
89
+ Add type declarations in `src/envs/auth.ts`:
73
90
 
74
- 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.
91
+ ```ts
92
+ // Add to ProcessEnv interface
93
+ AUTH_OKTA_ID?: string;
94
+ AUTH_OKTA_SECRET?: string;
95
+ AUTH_OKTA_ISSUER?: string;
96
+
97
+ // Add to getAuthConfig server schema
98
+ AUTH_OKTA_ID: z.string().optional(),
99
+ AUTH_OKTA_SECRET: z.string().optional(),
100
+ AUTH_OKTA_ISSUER: z.string().optional(),
101
+
102
+ // Add to runtimeEnv
103
+ AUTH_OKTA_ID: process.env.AUTH_OKTA_ID,
104
+ AUTH_OKTA_SECRET: process.env.AUTH_OKTA_SECRET,
105
+ AUTH_OKTA_ISSUER: process.env.AUTH_OKTA_ISSUER,
106
+ ```
75
107
 
76
- ### Step 4: Configure the Environment Variables
108
+ ### Step 4: Update Documentation (Optional)
77
109
 
78
- Add `OKTA_CLIENT_ID`、`OKTA_CLIENT_SECRET`、`OKTA_ISSUER` environment variables when you deploy.
110
+ Add provider documentation in `docs/self-hosting/advanced/auth.mdx` and `docs/self-hosting/advanced/auth.zh-CN.mdx`.
79
111
 
80
- ### Step 5: Modify server-side user information processing logic
112
+ ## Adding a Built-in Provider
81
113
 
82
- #### Get user information in the frontend
114
+ For providers natively supported by Better Auth (e.g., Discord), the steps differ slightly:
83
115
 
84
- Use the `useOAuthSession()` method in the frontend page to get the user information `user` returned by the backend:
116
+ ### Step 1: Create Provider Definition File
85
117
 
86
118
  ```ts
87
- import { useOAuthSession } from '@/hooks/useOAuthSession';
119
+ import { authEnv } from '@/envs/auth';
120
+
121
+ import type { BuiltinProviderDefinition } from '../types';
122
+
123
+ const provider: BuiltinProviderDefinition<{
124
+ AUTH_DISCORD_ID: string;
125
+ AUTH_DISCORD_SECRET: string;
126
+ }> = {
127
+ build: (env) => ({
128
+ clientId: env.AUTH_DISCORD_ID,
129
+ clientSecret: env.AUTH_DISCORD_SECRET,
130
+ }),
131
+ checkEnvs: () => {
132
+ return !!(authEnv.AUTH_DISCORD_ID && authEnv.AUTH_DISCORD_SECRET)
133
+ ? {
134
+ AUTH_DISCORD_ID: authEnv.AUTH_DISCORD_ID,
135
+ AUTH_DISCORD_SECRET: authEnv.AUTH_DISCORD_SECRET,
136
+ }
137
+ : false;
138
+ },
139
+ id: 'discord',
140
+ type: 'builtin',
141
+ };
88
142
 
89
- const { user, isOAuthLoggedIn } = useOAuthSession();
143
+ export default provider;
90
144
  ```
91
145
 
92
- The default type of `user` is `User`, and the type definition is:
146
+ ### Step 2: Update Constants File
147
+
148
+ Add to `src/libs/better-auth/constants.ts`:
93
149
 
94
150
  ```ts
95
- interface User {
96
- id?: string;
97
- name?: string | null;
98
- email?: string | null;
99
- image?: string | null;
100
- }
151
+ export const BUILTIN_BETTER_AUTH_PROVIDERS = [
152
+ 'apple',
153
+ 'google',
154
+ 'github',
155
+ 'cognito',
156
+ 'microsoft',
157
+ 'discord', // Add new provider
158
+ ] as const;
101
159
  ```
102
160
 
103
- #### Modify user `id` handling logic
161
+ ## Callback URL Format
104
162
 
105
- 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`:
106
-
107
- ```txt
108
- authorize --> jwt --> session
109
- ```
163
+ When configuring OAuth applications, use these callback URL formats:
110
164
 
111
- 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:
112
-
113
- ```ts
114
- callbacks: {
115
- async jwt({ token, account, profile }) {
116
- if (account) {
117
- // You can select a different value from `account` or `profile`
118
- token.userId = account.providerAccountId;
119
- }
120
- return token;
121
- },
122
- },
123
- ```
165
+ - **Built-in providers**: `https://yourdomain.com/api/auth/callback/{providerId}`
166
+ - **Generic OIDC**: `https://yourdomain.com/api/auth/callback/{providerId}`
124
167
 
125
- #### Customize `session` return
126
-
127
- 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`:
128
-
129
- ```diff
130
- callbacks: {
131
- async jwt({ token, profile, account }) {
132
- if (profile && account) {
133
- token.userId = account.providerAccountId;
134
- + token.avatar = profile.picture;
135
- }
136
- return token;
137
- },
138
- async session({ session, token }) {
139
- if (session.user) {
140
- session.user.id = token.userId ?? session.user.id;
141
- + session.user.avatar = token.avatar;
142
- }
143
- return session;
144
- },
145
- },
146
- ```
168
+ ## Using the New Provider
147
169
 
148
- Then supplement the type definition for the new parameters:
170
+ After configuring environment variables, enable in `AUTH_SSO_PROVIDERS`:
149
171
 
150
- ```ts
151
- declare module '@auth/core/jwt' {
152
- interface JWT {
153
- // ...
154
- avatar?: string;
155
- }
156
- }
157
-
158
- declare module 'next-auth' {
159
- interface User {
160
- avatar?: string;
161
- }
162
- }
172
+ ```bash
173
+ AUTH_SSO_PROVIDERS=google,github,okta
174
+ AUTH_OKTA_ID=your-client-id
175
+ AUTH_OKTA_SECRET=your-client-secret
176
+ AUTH_OKTA_ISSUER=https://your-domain.okta.com
163
177
  ```
164
178
 
165
- > [More built-in type extensions in Auth.js](https://authjs.dev/getting-started/typescript#module-augmentation)
179
+ ## Debugging Tips
166
180
 
167
- #### Differentiate multiple authentication providers in the processing logic
181
+ 1. **Environment variable check fails**: Ensure all required environment variables are set
182
+ 2. **Callback URL errors**: Verify the callback URL configured in your OAuth application
183
+ 3. **User profile mapping**: Use `mapProfileToUser` to customize the mapping from OAuth profile to user info
168
184
 
169
- 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.
170
-
171
- ```ts
172
- callbacks: {
173
- async jwt({ token, profile, account }) {
174
- if (profile && account) {
175
- if (account.provider === 'authing')
176
- token.userId = account.providerAccountId ?? token.sub;
177
- else if (acount.provider === 'auth0')
178
- token.userId = profile.sub ?? token.sub;
179
- else
180
- // other providers
181
- }
182
- return token;
183
- },
184
- }
185
- ```
185
+ ## Related Files
186
186
 
187
- Now, you can use Okta as your provider to implement the authentication feature in LobeChat.
187
+ | File | Description |
188
+ | ----------------------------------------- | -------------------------------- |
189
+ | `src/libs/better-auth/sso/providers/*.ts` | Provider definitions |
190
+ | `src/libs/better-auth/sso/index.ts` | Provider registration |
191
+ | `src/libs/better-auth/sso/types.ts` | Type definitions |
192
+ | `src/libs/better-auth/sso/helpers.ts` | Helper functions |
193
+ | `src/libs/better-auth/constants.ts` | Built-in provider constants |
194
+ | `src/envs/auth.ts` | Environment variable definitions |
195
+ | `src/libs/better-auth/define-config.ts` | Better Auth configuration |
@@ -1,185 +1,195 @@
1
1
  ---
2
- title: 新身份验证方式开发指南
2
+ title: 新身份验证提供商开发指南
3
3
  ---
4
4
 
5
- # 新身份验证方式开发指南
5
+ # 新身份验证提供商开发指南
6
6
 
7
- LobeChat 使用 [Auth.js v5](https://authjs.dev/) 作为外部身份验证服务。Auth.js 是一个开源的身份验证库,它提供了一种简单的方式来实现身份验证和授权功能。本文档将介绍如何使用 Auth.js 来实现新的身份验证方式。
7
+ LobeChat 使用 [Better Auth](https://www.better-auth.com) 作为身份验证服务。本文档介绍如何添加新的 SSO 身份验证提供商。
8
8
 
9
- ## 添加新的身份验证提供者
9
+ ## 架构概述
10
10
 
11
- 为了在 LobeChat 中添加新的身份验证提供者(例如添加 Okta),你需要完成以下步骤:
11
+ Better Auth SSO 提供商分为两类:
12
12
 
13
- ### 准备工作:查阅官方的提供者列表
13
+ | 类型 | 说明 | 示例 |
14
+ | --------- | -------------------------- | ----------------------------- |
15
+ | `builtin` | Better Auth 内置支持的提供商 | Google、GitHub、Microsoft、Apple |
16
+ | `generic` | 通过 Generic OIDC/OAuth 插件实现 | Okta、Auth0、Keycloak 等 |
14
17
 
15
- 首先,你需要查阅 [Auth.js 提供者列表](https://authjs.dev/reference/core/providers) 来了解是否你的提供者已经被支持。如果你的提供者已经被支持,你可以直接使用 Auth.js 提供的 SDK 来实现身份验证功能。
18
+ ## 添加新的 SSO 提供商
16
19
 
17
- 接下来我会以 [Okta](https://authjs.dev/reference/core/providers/okta) 为例来介绍如何添加新的身份验证提供者
20
+ 以添加 **Okta** 为例,介绍添加 `generic` 类型提供商的完整步骤。
18
21
 
19
- ### 步骤 1: 新增关键代码
22
+ ### 步骤 1: 创建提供商定义文件
20
23
 
21
- 打开 `src/app/api/auth/next-auth.ts` 文件,引入 `next-auth/providers/okta`
24
+ `src/libs/better-auth/sso/providers/` 目录下创建 `okta.ts`:
22
25
 
23
26
  ```ts
24
- import { NextAuth } from 'next-auth';
25
- import Auth0 from 'next-auth/providers/auth0';
26
- import Okta from 'next-auth/providers/okta';
27
-
28
- // 引入 Okta 提供者
29
- ```
30
-
31
- 新增预定义的服务端配置
27
+ import { authEnv } from '@/envs/auth';
28
+
29
+ import { buildOidcConfig } from '../helpers';
30
+ import type { GenericProviderDefinition } from '../types';
31
+
32
+ const provider: GenericProviderDefinition<{
33
+ AUTH_OKTA_ID: string;
34
+ AUTH_OKTA_ISSUER: string;
35
+ AUTH_OKTA_SECRET: string;
36
+ }> = {
37
+ // 构建 OIDC 配置
38
+ build: (env) =>
39
+ buildOidcConfig({
40
+ clientId: env.AUTH_OKTA_ID,
41
+ clientSecret: env.AUTH_OKTA_SECRET,
42
+ issuer: env.AUTH_OKTA_ISSUER,
43
+ overrides: {
44
+ // 可选:自定义用户信息映射
45
+ mapProfileToUser: (profile) => ({
46
+ email: profile.email,
47
+ name: profile.name ?? profile.preferred_username ?? profile.email ?? profile.sub,
48
+ }),
49
+ },
50
+ providerId: 'okta',
51
+ }),
32
52
 
33
- ```ts
34
- // 导入服务器配置
35
- const { OKTA_CLIENT_ID, OKTA_CLIENT_SECRET, OKTA_ISSUER } = getServerConfig();
53
+ // 环境变量检查
54
+ checkEnvs: () => {
55
+ return !!(authEnv.AUTH_OKTA_ID && authEnv.AUTH_OKTA_SECRET && authEnv.AUTH_OKTA_ISSUER)
56
+ ? {
57
+ AUTH_OKTA_ID: authEnv.AUTH_OKTA_ID,
58
+ AUTH_OKTA_ISSUER: authEnv.AUTH_OKTA_ISSUER,
59
+ AUTH_OKTA_SECRET: authEnv.AUTH_OKTA_SECRET,
60
+ }
61
+ : false;
62
+ },
36
63
 
37
- const nextAuth = NextAuth({
38
- providers: [
39
- // ... 其他提供者
64
+ // 提供商 ID(用于 AUTH_SSO_PROVIDERS 配置)
65
+ id: 'okta',
66
+ type: 'generic',
67
+ };
40
68
 
41
- Okta({
42
- clientId: OKTA_CLIENT_ID,
43
- clientSecret: OKTA_CLIENT_SECRET,
44
- issuer: OKTA_ISSUER,
45
- }),
46
- ],
47
- });
69
+ export default provider;
48
70
  ```
49
71
 
50
- ### 步骤 2: 更新服务端配置代码
72
+ ### 步骤 2: 注册提供商
51
73
 
52
- 打开 `src/config/server/app.ts` 文件,在 `getAppConfig` 函数中新增 Okta 相关的环境变量
74
+ `src/libs/better-auth/sso/index.ts` 中导入并注册:
53
75
 
54
76
  ```ts
55
- export const getAppConfig = () => {
56
- // ... 其他代码
57
-
58
- return {
59
- // ... 其他环境变量
60
-
61
- OKTA_CLIENT_ID: process.env.OKTA_CLIENT_ID || '',
62
- OKTA_CLIENT_SECRET: process.env.OKTA_CLIENT_SECRET || '',
63
- OKTA_ISSUER: process.env.OKTA_ISSUER || '',
64
- };
65
- };
77
+ // 导入提供商
78
+ import Okta from './providers/okta';
79
+
80
+ // 添加到 providerDefinitions 数组
81
+ const providerDefinitions = [
82
+ // ... 其他提供商
83
+ Okta,
84
+ ] as const;
66
85
  ```
67
86
 
68
- ### 步骤 3: 修改前端页面
87
+ ### 步骤 3: 添加环境变量类型声明
69
88
 
70
- 修改在 `src/features/Conversation/Error/OAuthForm.tsx` 及 `src/app/settings/common/Common.tsx` 中的 `signIn` 函数参数
89
+ `src/envs/auth.ts` 中添加类型声明:
71
90
 
72
- 默认为 `auth0`,你可以将其修改为 `okta` 以切换到 Okta 提供者,或删除该参数以支持所有已添加的身份验证服务
73
-
74
- 该值为 Auth.js 提供者 的 id,你可以阅读相应的 `next-auth/providers` 模块源码以读取默认 ID
91
+ ```ts
92
+ // ProcessEnv 接口中添加
93
+ AUTH_OKTA_ID?: string;
94
+ AUTH_OKTA_SECRET?: string;
95
+ AUTH_OKTA_ISSUER?: string;
96
+
97
+ // getAuthConfig server schema 中添加
98
+ AUTH_OKTA_ID: z.string().optional(),
99
+ AUTH_OKTA_SECRET: z.string().optional(),
100
+ AUTH_OKTA_ISSUER: z.string().optional(),
101
+
102
+ // runtimeEnv 中添加
103
+ AUTH_OKTA_ID: process.env.AUTH_OKTA_ID,
104
+ AUTH_OKTA_SECRET: process.env.AUTH_OKTA_SECRET,
105
+ AUTH_OKTA_ISSUER: process.env.AUTH_OKTA_ISSUER,
106
+ ```
75
107
 
76
- ### 步骤 4: 配置环境变量
108
+ ### 步骤 4: 更新文档(可选)
77
109
 
78
- 在部署时新增 Okta 相关的环境变量 `OKTA_CLIENT_ID`、`OKTA_CLIENT_SECRET`、`OKTA_ISSUER`,并填入相应的值,即可使用
110
+ `docs/self-hosting/advanced/auth.mdx` `docs/self-hosting/advanced/auth.zh-CN.mdx` 中添加提供商文档。
79
111
 
80
- ### 步骤 5: 修改服务端用户信息处理逻辑
112
+ ## 添加内置提供商
81
113
 
82
- #### 在前端获取用户信息
114
+ 如果要添加 Better Auth 内置支持的提供商(如 Discord),步骤略有不同:
83
115
 
84
- 在前端页面中使用 `useOAuthSession()` 方法获取后端返回的用户信息 `user`:
116
+ ### 步骤 1: 创建提供商定义文件
85
117
 
86
118
  ```ts
87
- import { useOAuthSession } from '@/hooks/useOAuthSession';
119
+ import { authEnv } from '@/envs/auth';
120
+
121
+ import type { BuiltinProviderDefinition } from '../types';
122
+
123
+ const provider: BuiltinProviderDefinition<{
124
+ AUTH_DISCORD_ID: string;
125
+ AUTH_DISCORD_SECRET: string;
126
+ }> = {
127
+ build: (env) => ({
128
+ clientId: env.AUTH_DISCORD_ID,
129
+ clientSecret: env.AUTH_DISCORD_SECRET,
130
+ }),
131
+ checkEnvs: () => {
132
+ return !!(authEnv.AUTH_DISCORD_ID && authEnv.AUTH_DISCORD_SECRET)
133
+ ? {
134
+ AUTH_DISCORD_ID: authEnv.AUTH_DISCORD_ID,
135
+ AUTH_DISCORD_SECRET: authEnv.AUTH_DISCORD_SECRET,
136
+ }
137
+ : false;
138
+ },
139
+ id: 'discord',
140
+ type: 'builtin',
141
+ };
88
142
 
89
- const { user, isOAuthLoggedIn } = useOAuthSession();
143
+ export default provider;
90
144
  ```
91
145
 
92
- 默认的 `user` 类型为 `User`,类型定义为:
146
+ ### 步骤 2: 更新常量文件
147
+
148
+ 在 `src/libs/better-auth/constants.ts` 中添加:
93
149
 
94
150
  ```ts
95
- interface User {
96
- id?: string;
97
- name?: string | null;
98
- email?: string | null;
99
- image?: string | null;
100
- }
151
+ export const BUILTIN_BETTER_AUTH_PROVIDERS = [
152
+ 'apple',
153
+ 'google',
154
+ 'github',
155
+ 'cognito',
156
+ 'microsoft',
157
+ 'discord', // 新增
158
+ ] as const;
101
159
  ```
102
160
 
103
- #### 修改用户 `id` 处理逻辑
161
+ ## 回调 URL 格式
104
162
 
105
- `user.id` 用于标识用户。当引入新身份 OAuth 提供者后,您需要在 `src/app/api/auth/next-auth.ts` 中处理 OAuth 回调所携带的信息。您需要从中选取用户的 `id`。在此之前,我们需要了解 `Auth.js` 的数据处理顺序:
163
+ 配置 OAuth 应用时,回调 URL 格式为:
106
164
 
107
- ```txt
108
- authorize --> jwt --> session
109
- ```
110
-
111
- 默认情况下,在 `jwt --> session` 过程中,`Auth.js` 会[自动根据登陆类型](https://authjs.dev/reference/core/types#provideraccountid)将用户 `id` 赋值到 `account.providerAccountId` 中。 如果您需要选取其他值作为用户 `id` ,您需要实现以下处理逻辑。
112
-
113
- ```ts
114
- callbacks: {
115
- async jwt({ token, account, profile }) {
116
- if (account) {
117
- // 您可以从 `account` 或 `profile` 中选取其他值
118
- token.userId = account.providerAccountId;
119
- }
120
- return token;
121
- },
122
- },
123
- ```
165
+ - **内置提供商**:`https://yourdomain.com/api/auth/callback/{providerId}`
166
+ - **Generic OIDC**:`https://yourdomain.com/api/auth/callback/{providerId}`
124
167
 
125
- #### 自定义 `session` 返回
126
-
127
- 如果您想在 `session` 中携带更多关于 `profile` 及 `account` 的信息,根据上面提到的 `Auth.js` 数据处理顺序,那必须先将该信息复制到 `token` 上。示例:把用户头像 URL:`profile.picture` 添加到`session` 中:
128
-
129
- ```diff
130
- callbacks: {
131
- async jwt({ token, profile, account }) {
132
- if (profile && account) {
133
- token.userId = account.providerAccountId;
134
- + token.avatar = profile.picture;
135
- }
136
- return token;
137
- },
138
- async session({ session, token }) {
139
- if (session.user) {
140
- session.user.id = token.userId ?? session.user.id;
141
- + session.user.avatar = token.avatar;
142
- }
143
- return session;
144
- },
145
- },
146
- ```
168
+ ## 使用新提供商
147
169
 
148
- 然后补充对新增参数的类型定义:
170
+ 配置环境变量后,在 `AUTH_SSO_PROVIDERS` 中启用:
149
171
 
150
- ```ts
151
- declare module '@auth/core/jwt' {
152
- interface JWT {
153
- // ...
154
- avatar?: string;
155
- }
156
- }
157
-
158
- declare module 'next-auth' {
159
- interface User {
160
- avatar?: string;
161
- }
162
- }
172
+ ```bash
173
+ AUTH_SSO_PROVIDERS=google,github,okta
174
+ AUTH_OKTA_ID=your-client-id
175
+ AUTH_OKTA_SECRET=your-client-secret
176
+ AUTH_OKTA_ISSUER=https://your-domain.okta.com
163
177
  ```
164
178
 
165
- > [更多`Auth.js`内置类型拓展](https://authjs.dev/getting-started/typescript#module-augmentation)
179
+ ## 调试技巧
166
180
 
167
- #### 在处理逻辑中区分多个身份验证提供者
181
+ 1. **环境变量检查失败**:确保所有必需的环境变量都已设置
182
+ 2. **回调 URL 错误**:检查 OAuth 应用配置的回调 URL 是否正确
183
+ 3. **用户信息映射**:通过 `mapProfileToUser` 自定义从 OAuth profile 到用户信息的映射
168
184
 
169
- 如果您配置了多个身份验证提供者,并且他们的 `userId` 映射各不相同,可以在 `jwt` 方法中的 `account.provider` 参数获取身份提供者的默认 id ,从而进入不同的处理逻辑。
185
+ ## 相关文件
170
186
 
171
- ```ts
172
- callbacks: {
173
- async jwt({ token, profile, account }) {
174
- if (profile && account) {
175
- if (account.provider === 'Authing')
176
- token.userId = account.providerAccountId ?? token.sub;
177
- else if (acount.provider === 'Okta')
178
- token.userId = profile.sub ?? token.sub;
179
- else
180
- // other providers
181
- }
182
- return token;
183
- },
184
- }
185
- ```
187
+ | 文件 | 说明 |
188
+ | ----------------------------------------- | -------------- |
189
+ | `src/libs/better-auth/sso/providers/*.ts` | 提供商定义 |
190
+ | `src/libs/better-auth/sso/index.ts` | 提供商注册 |
191
+ | `src/libs/better-auth/sso/types.ts` | 类型定义 |
192
+ | `src/libs/better-auth/sso/helpers.ts` | 辅助函数 |
193
+ | `src/libs/better-auth/constants.ts` | 内置提供商常量 |
194
+ | `src/envs/auth.ts` | 环境变量定义 |
195
+ | `src/libs/better-auth/define-config.ts` | Better Auth 配置 |