@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
@@ -1,167 +0,0 @@
1
- 'use client';
2
-
3
- import { BRANDING_NAME } from '@lobechat/business-const';
4
- import { DOCUMENTS_REFER_URL, PRIVACY_URL, TERMS_URL } from '@lobechat/const';
5
- import { Button, Skeleton, Text } from '@lobehub/ui';
6
- import { LobeHub } from '@lobehub/ui/brand';
7
- import { Col, Flex, Row } from 'antd';
8
- import { createStaticStyles } from 'antd-style';
9
- import { AuthError } from 'next-auth';
10
- import { signIn } from 'next-auth/react';
11
- import { useRouter, useSearchParams } from '@/libs/next/navigation';
12
- import { memo, useState } from 'react';
13
- import { useTranslation } from 'react-i18next';
14
-
15
- import BrandWatermark from '@/components/BrandWatermark';
16
- import AuthIcons from '@/components/NextAuth/AuthIcons';
17
- import { useUserStore } from '@/store/user';
18
-
19
- const styles = createStaticStyles(({ css, cssVar }) => ({
20
- button: css`
21
- text-transform: capitalize;
22
- `,
23
- container: css`
24
- min-width: 360px;
25
- border: 1px solid ${cssVar.colorBorder};
26
- border-radius: ${cssVar.borderRadiusLG}px;
27
- background: ${cssVar.colorBgContainer};
28
- `,
29
- contentCard: css`
30
- padding-block: 2.5rem;
31
- padding-inline: 2rem;
32
- `,
33
- description: css`
34
- margin: 0;
35
- color: ${cssVar.colorTextSecondary};
36
- `,
37
- footer: css`
38
- padding: 1rem;
39
- border-block-start: 1px solid ${cssVar.colorBorder};
40
- border-radius: 0 0 8px 8px;
41
-
42
- color: ${cssVar.colorTextDescription};
43
-
44
- background: ${cssVar.colorBgElevated};
45
- `,
46
- text: css`
47
- text-align: center;
48
- `,
49
- title: css`
50
- margin: 0;
51
- color: ${cssVar.colorTextHeading};
52
- `,
53
- }));
54
-
55
- const BtnListLoading = memo(() => {
56
- return (
57
- <Flex gap={'small'} vertical>
58
- <Skeleton.Button active style={{ minWidth: 300 }} />
59
- <Skeleton.Button active style={{ minWidth: 300 }} />
60
- <Skeleton.Button active style={{ minWidth: 300 }} />
61
- </Flex>
62
- );
63
- });
64
-
65
- /**
66
- * Follow the implementation from AuthJS official documentation,
67
- * but using client components.
68
- * ref: https://authjs.dev/guides/pages/signin
69
- */
70
- export default memo(() => {
71
- const { t } = useTranslation('auth');
72
- const { t: tCommon } = useTranslation('common');
73
- const router = useRouter();
74
- const [loadingProvider, setLoadingProvider] = useState<string | null>(null);
75
-
76
- const oAuthSSOProviders = useUserStore((s) => s.oAuthSSOProviders);
77
-
78
- const searchParams = useSearchParams();
79
-
80
- // Redirect back to the page url, fallback to '/' if failed
81
- const callbackUrl = searchParams.get('callbackUrl') ?? '/';
82
-
83
- const handleSignIn = async (provider: string) => {
84
- setLoadingProvider(provider);
85
- try {
86
- await signIn(provider, { redirectTo: callbackUrl });
87
- } catch (error) {
88
- setLoadingProvider(null);
89
- // Signin can fail for a number of reasons, such as the user
90
- // not existing, or the user not having the correct role.
91
- // In some cases, you may want to redirect to a custom error
92
- if (error instanceof AuthError) {
93
- return router.push(`/next-auth/?error=${error.type}`);
94
- }
95
-
96
- // Otherwise if a redirects happens Next.js can handle it
97
- // so you can just re-thrown the error and let Next.js handle it.
98
- // Docs: https://nextjs.org/docs/app/api-reference/functions/redirect#server-component
99
- throw error;
100
- }
101
- };
102
-
103
- const footerBtns = [
104
- { href: DOCUMENTS_REFER_URL, id: 0, label: tCommon('document') },
105
- { href: PRIVACY_URL, id: 1, label: t('footer.privacy') },
106
- { href: TERMS_URL, id: 2, label: t('footer.terms') },
107
- ];
108
-
109
- return (
110
- <div className={styles.container}>
111
- <div className={styles.contentCard}>
112
- {/* Card Body */}
113
- <Flex gap="large" vertical>
114
- {/* Header */}
115
- <div className={styles.text}>
116
- <Text as={'h4'} className={styles.title}>
117
- <div>
118
- <LobeHub size={48} />
119
- </div>
120
- {t('signin.title')}
121
- </Text>
122
- <Text as={'p'} className={styles.description}>
123
- {t('signin.subtitle', { appName: BRANDING_NAME })}
124
- </Text>
125
- </div>
126
- {/* Content */}
127
- <Flex gap="small" vertical>
128
- {oAuthSSOProviders ? (
129
- oAuthSSOProviders.map((provider) => (
130
- <Button
131
- className={styles.button}
132
- icon={AuthIcons(provider, 16)}
133
- key={provider}
134
- loading={loadingProvider === provider}
135
- onClick={() => handleSignIn(provider)}
136
- >
137
- {provider}
138
- </Button>
139
- ))
140
- ) : (
141
- <BtnListLoading />
142
- )}
143
- </Flex>
144
- </Flex>
145
- </div>
146
- <div className={styles.footer}>
147
- {/* Footer */}
148
- <Row>
149
- <Col span={12}>
150
- <Flex justify="left" style={{ height: '100%' }}>
151
- <BrandWatermark />
152
- </Flex>
153
- </Col>
154
- <Col offset={4} span={8}>
155
- <Flex justify="right">
156
- {footerBtns.map((btn) => (
157
- <Button key={btn.id} onClick={() => router.push(btn.href)} size="small" type="text">
158
- {btn.label}
159
- </Button>
160
- ))}
161
- </Flex>
162
- </Col>
163
- </Row>
164
- </div>
165
- </div>
166
- );
167
- });
@@ -1,11 +0,0 @@
1
- import { Suspense } from 'react';
2
-
3
- import Loading from '@/components/Loading/BrandTextLoading';
4
-
5
- import AuthSignInBox from './AuthSignInBox';
6
-
7
- export default () => (
8
- <Suspense fallback={<Loading debugId="Auth > SignIn" />}>
9
- <AuthSignInBox />
10
- </Suspense>
11
- );
@@ -1,12 +0,0 @@
1
- import { notFound } from '@/libs/next/navigation';
2
- import { type PropsWithChildren } from 'react';
3
-
4
- import { enableBetterAuth } from '@/envs/auth';
5
-
6
- const Layout = ({ children }: PropsWithChildren) => {
7
- if (!enableBetterAuth) return notFound();
8
-
9
- return children;
10
- };
11
-
12
- export default Layout;
@@ -1,12 +0,0 @@
1
- import { notFound } from '@/libs/next/navigation';
2
- import { type PropsWithChildren } from 'react';
3
-
4
- import { enableBetterAuth } from '@/envs/auth';
5
-
6
- const Layout = ({ children }: PropsWithChildren) => {
7
- if (!enableBetterAuth) return notFound();
8
-
9
- return children;
10
- };
11
-
12
- export default Layout;
@@ -1,12 +0,0 @@
1
- import { notFound } from '@/libs/next/navigation';
2
- import { type PropsWithChildren } from 'react';
3
-
4
- import { enableBetterAuth } from '@/envs/auth';
5
-
6
- const Layout = ({ children }: PropsWithChildren) => {
7
- if (!enableBetterAuth) return notFound();
8
-
9
- return children;
10
- };
11
-
12
- export default Layout;
@@ -1,47 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
2
-
3
- import { getAuthConfig } from './auth';
4
-
5
- const ORIGINAL_ENV = { ...process.env };
6
- const ORIGINAL_WINDOW = globalThis.window;
7
-
8
- describe('getAuthConfig fallbacks', () => {
9
- beforeEach(() => {
10
- // reset env to a clean clone before each test
11
- process.env = { ...ORIGINAL_ENV };
12
- globalThis.window = ORIGINAL_WINDOW;
13
- });
14
-
15
- afterEach(() => {
16
- process.env = { ...ORIGINAL_ENV };
17
- globalThis.window = ORIGINAL_WINDOW;
18
- });
19
-
20
- it('should fall back to NEXT_AUTH_SSO_PROVIDERS when AUTH_SSO_PROVIDERS is empty string', () => {
21
- process.env.AUTH_SSO_PROVIDERS = '';
22
- process.env.NEXT_AUTH_SSO_PROVIDERS = 'logto,github';
23
-
24
- // Simulate server runtime so @t3-oss/env treats this as server-side access
25
- // (happy-dom sets window by default in Vitest)
26
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
27
- // @ts-expect-error - allow overriding for test
28
- globalThis.window = undefined;
29
-
30
- const config = getAuthConfig();
31
-
32
- expect(config.AUTH_SSO_PROVIDERS).toBe('logto,github');
33
- });
34
-
35
- it('should fall back to NEXT_AUTH_SECRET when AUTH_SECRET is empty string', () => {
36
- process.env.AUTH_SECRET = '';
37
- process.env.NEXT_AUTH_SECRET = 'nextauth-secret';
38
-
39
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
40
- // @ts-expect-error - allow overriding for test
41
- globalThis.window = undefined;
42
-
43
- const config = getAuthConfig();
44
-
45
- expect(config.AUTH_SECRET).toBe('nextauth-secret');
46
- });
47
- });
@@ -1,44 +0,0 @@
1
- 'use client';
2
-
3
- import { useSession } from 'next-auth/react';
4
- import { memo, useEffect } from 'react';
5
- import { createStoreUpdater } from 'zustand-utils';
6
-
7
- import { useUserStore } from '@/store/user';
8
- import { type LobeUser } from '@/types/user';
9
-
10
- // update the user data into the context
11
- const UserUpdater = memo(() => {
12
- const { data: session, status } = useSession();
13
- const isLoaded = status !== 'loading';
14
-
15
- const isSignedIn = (status === 'authenticated' && session && !!session.user) || false;
16
-
17
- const nextUser = session?.user;
18
- const useStoreUpdater = createStoreUpdater(useUserStore);
19
-
20
- useStoreUpdater('isLoaded', isLoaded);
21
- useStoreUpdater('isSignedIn', isSignedIn);
22
- useStoreUpdater('nextSession', session!);
23
-
24
- // 使用 useEffect 处理需要保持同步的用户数据
25
- useEffect(() => {
26
- if (nextUser) {
27
- const userAvatar = useUserStore.getState().user?.avatar;
28
-
29
- const lobeUser = {
30
- // 头像使用设置的,而不是从 next-auth 中获取
31
- avatar: userAvatar || '',
32
- email: nextUser.email,
33
- fullName: nextUser.name,
34
- id: nextUser.id,
35
- } as LobeUser;
36
-
37
- // 更新用户相关数据
38
- useUserStore.setState({ nextUser: nextUser, user: lobeUser });
39
- }
40
- }, [nextUser]);
41
- return null;
42
- });
43
-
44
- export default UserUpdater;
@@ -1,17 +0,0 @@
1
- import { SessionProvider } from 'next-auth/react';
2
- import { type PropsWithChildren } from 'react';
3
-
4
- import { API_ENDPOINTS } from '@/services/_url';
5
-
6
- import UserUpdater from './UserUpdater';
7
-
8
- const NextAuth = ({ children }: PropsWithChildren) => {
9
- return (
10
- <SessionProvider basePath={API_ENDPOINTS.oauth}>
11
- {children}
12
- <UserUpdater />
13
- </SessionProvider>
14
- );
15
- };
16
-
17
- export default NextAuth;
@@ -1,177 +0,0 @@
1
- import type {
2
- AdapterAuthenticator,
3
- AdapterSession,
4
- AdapterUser,
5
- VerificationToken,
6
- } from '@auth/core/adapters';
7
- import debug from 'debug';
8
- import { type Adapter, type AdapterAccount } from 'next-auth/adapters';
9
- import urlJoin from 'url-join';
10
-
11
- import { serverDBEnv } from '@/config/db';
12
- import { appEnv } from '@/envs/app';
13
-
14
- const log = debug('lobe-next-auth:adapter');
15
-
16
- interface BackendAdapterResponse {
17
- data?: any;
18
- error?: string;
19
- success: boolean;
20
- }
21
-
22
- // Due to use direct HTTP Post, the date string cannot parse automatically
23
- export const dateKeys = ['expires', 'emailVerified'];
24
-
25
- /**
26
- * @description LobeNextAuthDbAdapter is implemented to handle the database operations for NextAuth
27
- * @returns {Adapter}
28
- */
29
- export function LobeNextAuthDbAdapter(): Adapter {
30
- const baseUrl = appEnv.APP_URL;
31
-
32
- // Ensure the baseUrl is set, otherwise throw an error
33
- if (!baseUrl) {
34
- throw new Error('LobeNextAuthDbAdapter: APP_URL is not set in environment variables');
35
- }
36
- const interactionUrl = urlJoin(baseUrl, '/api/auth/adapter');
37
- log(`LobeNextAuthDbAdapter initialized with url: ${interactionUrl}`);
38
-
39
- // Ensure serverDBEnv.KEY_VAULTS_SECRET is set, otherwise throw an error
40
- if (!serverDBEnv.KEY_VAULTS_SECRET) {
41
- throw new Error('LobeNextAuthDbAdapter: KEY_VAULTS_SECRET is not set in environment variables');
42
- }
43
-
44
- const fetcher = (action: string, data: any) =>
45
- fetch(interactionUrl, {
46
- body: JSON.stringify({ action, data }),
47
- headers: {
48
- 'Authorization': `Bearer ${serverDBEnv.KEY_VAULTS_SECRET}`,
49
- 'Content-Type': 'application/json',
50
- },
51
- method: 'POST',
52
- });
53
- const postProcessor = async (res: Response) => {
54
- const data = (await res.json()) as BackendAdapterResponse;
55
- log('LobeNextAuthDbAdapter: postProcessor called with data:', data);
56
- if (!data.success) {
57
- log('LobeNextAuthDbAdapter: Error in postProcessor:');
58
- log(data);
59
- throw new Error(`LobeNextAuthDbAdapter: ${data.error}`);
60
- }
61
- if (data?.data) {
62
- for (const key of dateKeys) {
63
- if (data.data[key]) {
64
- data.data[key] = new Date(data.data[key]);
65
- continue;
66
- }
67
- }
68
- }
69
- return data.data;
70
- };
71
-
72
- return {
73
- async createAuthenticator(authenticator): Promise<AdapterAuthenticator> {
74
- const data = await fetcher('createAuthenticator', authenticator);
75
- return await postProcessor(data);
76
- },
77
- async createSession(session): Promise<AdapterSession> {
78
- const data = await fetcher('createSession', session);
79
- return await postProcessor(data);
80
- },
81
- async createUser(user): Promise<AdapterUser> {
82
- const data = await fetcher('createUser', user);
83
- return await postProcessor(data);
84
- },
85
- async createVerificationToken(data): Promise<VerificationToken | null | undefined> {
86
- const result = await fetcher('createVerificationToken', data);
87
- return await postProcessor(result);
88
- },
89
- async deleteSession(sessionToken): Promise<AdapterSession | null | undefined> {
90
- const result = await fetcher('deleteSession', sessionToken);
91
- await postProcessor(result);
92
- return;
93
- },
94
- async deleteUser(id): Promise<AdapterUser | null | undefined> {
95
- const result = await fetcher('deleteUser', id);
96
- await postProcessor(result);
97
- return;
98
- },
99
-
100
- async getAccount(providerAccountId, provider): Promise<AdapterAccount | null> {
101
- const data = await fetcher('getAccount', {
102
- provider,
103
- providerAccountId,
104
- });
105
- return await postProcessor(data);
106
- },
107
-
108
- async getAuthenticator(credentialID): Promise<AdapterAuthenticator | null> {
109
- const result = await fetcher('getAuthenticator', credentialID);
110
- return await postProcessor(result);
111
- },
112
-
113
- async getSessionAndUser(sessionToken): Promise<{
114
- session: AdapterSession;
115
- user: AdapterUser;
116
- } | null> {
117
- const result = await fetcher('getSessionAndUser', sessionToken);
118
- return await postProcessor(result);
119
- },
120
-
121
- async getUser(id): Promise<AdapterUser | null> {
122
- log('getUser called with id:', id);
123
- const result = await fetcher('getUser', id);
124
- return await postProcessor(result);
125
- },
126
-
127
- async getUserByAccount(account): Promise<AdapterUser | null> {
128
- const data = await fetcher('getUserByAccount', account);
129
- return await postProcessor(data);
130
- },
131
-
132
- async getUserByEmail(email): Promise<AdapterUser | null> {
133
- const data = await fetcher('getUserByEmail', email);
134
- return await postProcessor(data);
135
- },
136
-
137
- async linkAccount(data): Promise<AdapterAccount | null | undefined> {
138
- const result = await fetcher('linkAccount', data);
139
- return await postProcessor(result);
140
- },
141
-
142
- async listAuthenticatorsByUserId(userId): Promise<AdapterAuthenticator[]> {
143
- const result = await fetcher('listAuthenticatorsByUserId', userId);
144
- return await postProcessor(result);
145
- },
146
-
147
- // @ts-ignore: The return type is {Promise<void> | Awaitable<AdapterAccount | undefined>}
148
- async unlinkAccount(account): Promise<void | AdapterAccount | undefined> {
149
- const result = await fetcher('unlinkAccount', account);
150
- await postProcessor(result);
151
- return;
152
- },
153
-
154
- async updateAuthenticatorCounter(credentialID, counter): Promise<AdapterAuthenticator> {
155
- const result = await fetcher('updateAuthenticatorCounter', {
156
- counter,
157
- credentialID,
158
- });
159
- return await postProcessor(result);
160
- },
161
-
162
- async updateSession(data): Promise<AdapterSession | null | undefined> {
163
- const result = await fetcher('updateSession', data);
164
- return await postProcessor(result);
165
- },
166
-
167
- async updateUser(user): Promise<AdapterUser> {
168
- const result = await fetcher('updateUser', user);
169
- return await postProcessor(result);
170
- },
171
-
172
- async useVerificationToken(identifier_token): Promise<VerificationToken | null> {
173
- const result = await fetcher('useVerificationToken', identifier_token);
174
- return await postProcessor(result);
175
- },
176
- };
177
- }
@@ -1,64 +0,0 @@
1
- import type { NextAuthConfig } from 'next-auth';
2
-
3
- import { getAuthConfig } from '@/envs/auth';
4
-
5
- import { LobeNextAuthDbAdapter } from './adapter';
6
- import { ssoProviders } from './sso-providers';
7
-
8
- const {
9
- NEXT_AUTH_DEBUG,
10
- NEXT_AUTH_SECRET,
11
- NEXT_AUTH_SSO_SESSION_STRATEGY,
12
- NEXT_AUTH_SSO_PROVIDERS,
13
- NEXT_PUBLIC_ENABLE_NEXT_AUTH,
14
- } = getAuthConfig();
15
-
16
- export const initSSOProviders = () => {
17
- return NEXT_PUBLIC_ENABLE_NEXT_AUTH
18
- ? NEXT_AUTH_SSO_PROVIDERS.split(/[,,]/).map((provider) => {
19
- const validProvider = ssoProviders.find((item) => item.id === provider.trim());
20
-
21
- if (validProvider) return validProvider.provider;
22
-
23
- throw new Error(`[NextAuth] provider ${provider} is not supported`);
24
- })
25
- : [];
26
- };
27
-
28
- // Notice this is only an object, not a full Auth.js instance
29
- export default {
30
- adapter: NEXT_PUBLIC_ENABLE_NEXT_AUTH ? LobeNextAuthDbAdapter() : undefined,
31
- callbacks: {
32
- // Note: Data processing order of callback: authorize --> jwt --> session
33
- async jwt({ token, user }) {
34
- // ref: https://authjs.dev/guides/extending-the-session#with-jwt
35
- if (user?.id) {
36
- token.userId = user?.id;
37
- }
38
- return token;
39
- },
40
- async session({ session, token, user }) {
41
- if (session.user) {
42
- // ref: https://authjs.dev/guides/extending-the-session#with-database
43
- if (user) {
44
- session.user.id = user.id;
45
- } else {
46
- session.user.id = (token.userId ?? session.user.id) as string;
47
- }
48
- }
49
- return session;
50
- },
51
- },
52
- debug: NEXT_AUTH_DEBUG,
53
- pages: {
54
- error: '/next-auth/error',
55
- signIn: '/next-auth/signin',
56
- },
57
- providers: initSSOProviders(),
58
- secret: NEXT_AUTH_SECRET ?? process.env.AUTH_SECRET,
59
- session: {
60
- // Force use JWT if server service is disabled
61
- strategy: NEXT_AUTH_SSO_SESSION_STRATEGY,
62
- },
63
- trustHost: process.env?.AUTH_TRUST_HOST ? process.env.AUTH_TRUST_HOST === 'true' : true,
64
- } satisfies NextAuthConfig;
@@ -1,20 +0,0 @@
1
- import NextAuth from 'next-auth';
2
-
3
- import authConfig from './auth.config';
4
-
5
- /**
6
- * NextAuth initialization without Database adapter
7
- *
8
- * @note
9
- * We currently use `jwt` strategy for session management.
10
- * So you don't need to import `signIn` or `signOut` from
11
- * this module, just import from `next-auth` directly.
12
- *
13
- * Inside react component
14
- * @example
15
- * ```ts
16
- * import { signOut } from 'next-auth/react';
17
- * signOut();
18
- * ```
19
- */
20
- export default NextAuth(authConfig);
@@ -1,24 +0,0 @@
1
- import Auth0 from 'next-auth/providers/auth0';
2
-
3
- import { CommonProviderConfig } from './sso.config';
4
-
5
- const provider = {
6
- id: 'auth0',
7
- provider: Auth0({
8
- ...CommonProviderConfig,
9
- // Specify auth scope, at least include 'openid email'
10
- // all scopes in Auth0 ref: https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims
11
- authorization: { params: { scope: 'openid email profile' } },
12
- profile(profile) {
13
- return {
14
- email: profile.email,
15
- id: profile.sub,
16
- image: profile.picture,
17
- name: profile.name,
18
- providerAccountId: profile.sub,
19
- };
20
- },
21
- }),
22
- };
23
-
24
- export default provider;
@@ -1,39 +0,0 @@
1
- import type { OIDCConfig } from '@auth/core/providers';
2
-
3
- import { CommonProviderConfig } from './sso.config';
4
-
5
- export type AutheliaProfile = {
6
- // The users display name
7
- email: string;
8
- // The users email
9
- groups: string[];
10
- // The username the user used to login with
11
- name: string;
12
- preferred_username: string; // The users groups
13
- sub: string; // The users id
14
- };
15
-
16
- const provider = {
17
- id: 'authelia',
18
- provider: {
19
- ...CommonProviderConfig,
20
- authorization: { params: { scope: 'openid email profile' } },
21
- checks: ['state', 'pkce'],
22
- clientId: process.env.AUTH_AUTHELIA_ID,
23
- clientSecret: process.env.AUTH_AUTHELIA_SECRET,
24
- id: 'authelia',
25
- issuer: process.env.AUTH_AUTHELIA_ISSUER,
26
- name: 'Authelia',
27
- profile(profile) {
28
- return {
29
- email: profile.email,
30
- id: profile.sub,
31
- name: profile.name,
32
- providerAccountId: profile.sub,
33
- };
34
- },
35
- type: 'oidc',
36
- } satisfies OIDCConfig<AutheliaProfile>,
37
- };
38
-
39
- export default provider;
@@ -1,25 +0,0 @@
1
- import Authentik from 'next-auth/providers/authentik';
2
-
3
- import { CommonProviderConfig } from './sso.config';
4
-
5
- const provider = {
6
- id: 'authentik',
7
- provider: Authentik({
8
- ...CommonProviderConfig,
9
- // Specify auth scope, at least include 'openid email'
10
- // all scopes in Authentik ref: https://goauthentik.io/docs/providers/oauth2
11
- authorization: { params: { scope: 'openid email profile' } },
12
- // TODO(NextAuth): map unique user id to `providerAccountId` field
13
- // profile(profile) {
14
- // return {
15
- // email: profile.email,
16
- // image: profile.picture,
17
- // name: profile.name,
18
- // providerAccountId: profile.user_id,
19
- // id: profile.user_id,
20
- // };
21
- // },
22
- }),
23
- };
24
-
25
- export default provider;