Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: support clerk as auth provider #2374

Merged
merged 15 commits into from
May 5, 2024
Prev Previous commit
Next Next commit
♻️ refactor: refactor the auth env config to s3-oss/env
  • Loading branch information
arvinxx committed May 5, 2024
commit e4d1bfad21f0a70fa517d9add536586a97855543
5 changes: 5 additions & 0 deletions src/app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { notFound } from 'next/navigation';
import { PropsWithChildren } from 'react';
import { Center, Flexbox } from 'react-layout-kit';

import { enableClerk } from '@/const/auth';

const Page = ({ children }: PropsWithChildren) => {
if (!enableClerk) return notFound();

return (
<Flexbox height={'100%'} width={'100%'}>
<Center height={'100%'} width={'100%'}>
Expand Down
2 changes: 1 addition & 1 deletion src/app/(main)/settings/common/features/Common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type SettingItemGroup = ItemGroup;

export interface SettingsCommonProps {
showAccessCodeConfig: boolean;
showOAuthLogin: boolean;
showOAuthLogin?: boolean;
}

const Common = memo<SettingsCommonProps>(({ showAccessCodeConfig, showOAuthLogin }) => {
Expand Down
10 changes: 7 additions & 3 deletions src/app/(main)/settings/common/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { authEnv } from '@/config/auth';
import { getServerConfig } from '@/config/server';

import Common from './features/Common';
import Theme from './features/Theme';

const { SHOW_ACCESS_CODE_CONFIG, ENABLE_OAUTH_SSO } = getServerConfig();

const Page = () => {
const { SHOW_ACCESS_CODE_CONFIG } = getServerConfig();

return (
<>
<Theme />
<Common showAccessCodeConfig={SHOW_ACCESS_CODE_CONFIG} showOAuthLogin={ENABLE_OAUTH_SSO} />
<Common
showAccessCodeConfig={SHOW_ACCESS_CODE_CONFIG}
showOAuthLogin={authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH}
/>
</>
);
};
Expand Down
6 changes: 3 additions & 3 deletions src/app/api/plugin/gateway/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createGatewayOnEdgeRuntime } from '@lobehub/chat-plugins-gateway';
import { getJWTPayload } from '@/app/api/chat/auth/utils';
import { createErrorResponse } from '@/app/api/errorResponse';
import { getServerConfig } from '@/config/server';
import { LOBE_CHAT_AUTH_HEADER, OAUTH_AUTHORIZED } from '@/const/auth';
import { LOBE_CHAT_AUTH_HEADER, OAUTH_AUTHORIZED, enableNextAuth } from '@/const/auth';
import { LOBE_CHAT_TRACE_ID, TraceNameMap } from '@/const/trace';
import { AgentRuntimeError } from '@/libs/agent-runtime';
import { TraceClient } from '@/libs/traces';
Expand All @@ -14,13 +14,13 @@ import { getTracePayload } from '@/utils/trace';
import { parserPluginSettings } from './settings';

const checkAuth = (accessCode: string | null, oauthAuthorized: boolean | null) => {
const { ACCESS_CODES, PLUGIN_SETTINGS, ENABLE_OAUTH_SSO } = getServerConfig();
const { ACCESS_CODES, PLUGIN_SETTINGS } = getServerConfig();

// if there is no plugin settings, just skip the auth
if (!PLUGIN_SETTINGS) return { auth: true };

// If authorized by oauth
if (oauthAuthorized && ENABLE_OAUTH_SSO) return { auth: true };
if (oauthAuthorized && enableNextAuth) return { auth: true };

// if accessCode doesn't exist
if (!ACCESS_CODES.length) return { auth: true };
Expand Down
133 changes: 133 additions & 0 deletions src/config/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/* eslint-disable sort-keys-fix/sort-keys-fix , typescript-sort-keys/interface */
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace NodeJS {
interface ProcessEnv {
// ===== Clerk ===== //
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY?: string;
CLERK_SECRET_KEY?: string;
CLERK_WEBHOOK_SECRET?: string;

// ===== Next Auth ===== //
/**
* @deprecated
*/
ENABLE_OAUTH_SSO?: string;
NEXT_AUTH_SECRET?: string;
/**
* @deprecated
*/
SSO_PROVIDERS?: string;
NEXT_AUTH_SSO_PROVIDERS?: string;

AUTH0_CLIENT_ID?: string;
AUTH0_CLIENT_SECRET?: string;
AUTH0_ISSUER?: string;

// Github
GITHUB_CLIENT_ID?: string;
GITHUB_CLIENT_SECRET?: string;

// Azure AD
AZURE_AD_CLIENT_ID?: string;
AZURE_AD_CLIENT_SECRET?: string;
AZURE_AD_TENANT_ID?: string;

// AUTHENTIK
AUTHENTIK_CLIENT_ID?: string;
AUTHENTIK_CLIENT_SECRET?: string;
AUTHENTIK_ISSUER?: string;

// ZITADEL
ZITADEL_CLIENT_ID?: string;
ZITADEL_CLIENT_SECRET?: string;
ZITADEL_ISSUER?: string;
}
}
}

export const getAuthConfig = () =>
createEnv({
client: {
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().optional(),
/**
* whether to enabled clerk
*/
NEXT_PUBLIC_ENABLE_CLERK_AUTH: z.boolean().optional(),

NEXT_PUBLIC_ENABLE_NEXT_AUTH: z.boolean().optional(),
},
server: {
// Clerk
CLERK_SECRET_KEY: z.string().optional(),

// NEXT-AUTH
NEXT_AUTH_SECRET: z.string().optional(),
NEXT_AUTH_SSO_PROVIDERS: z.string().optional().default('auth0'),

// Auth0
AUTH0_CLIENT_ID: z.string().optional(),
AUTH0_CLIENT_SECRET: z.string().optional(),
AUTH0_ISSUER: z.string().optional(),

// Github
GITHUB_CLIENT_ID: z.string().optional(),
GITHUB_CLIENT_SECRET: z.string().optional(),

// Azure AD
AZURE_AD_CLIENT_ID: z.string().optional(),
AZURE_AD_CLIENT_SECRET: z.string().optional(),
AZURE_AD_TENANT_ID: z.string().optional(),

// AUTHENTIK
AUTHENTIK_CLIENT_ID: z.string().optional(),
AUTHENTIK_CLIENT_SECRET: z.string().optional(),
AUTHENTIK_ISSUER: z.string().optional(),

// ZITADEL
ZITADEL_CLIENT_ID: z.string().optional(),
ZITADEL_CLIENT_SECRET: z.string().optional(),
ZITADEL_ISSUER: z.string().optional(),
},

runtimeEnv: {
// Clerk
NEXT_PUBLIC_ENABLE_CLERK_AUTH: !!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,
CLERK_SECRET_KEY: process.env.CLERK_SECRET_KEY,

// Next Auth
NEXT_PUBLIC_ENABLE_NEXT_AUTH: !!process.env.NEXT_AUTH_SECRET || process.env.ENABLE_OAUTH_SSO,
NEXT_AUTH_SSO_PROVIDERS: process.env.NEXT_AUTH_SSO_PROVIDERS || process.env.SSO_PROVIDERS,
NEXT_AUTH_SECRET: process.env.NEXT_AUTH_SECRET,

// Auth0
AUTH0_CLIENT_ID: process.env.AUTH0_CLIENT_ID,
AUTH0_CLIENT_SECRET: process.env.AUTH0_CLIENT_SECRET,
AUTH0_ISSUER: process.env.AUTH0_ISSUER,

// Github
GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID,
GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET,

// Azure AD
AZURE_AD_CLIENT_ID: process.env.AZURE_AD_CLIENT_ID,
AZURE_AD_CLIENT_SECRET: process.env.AZURE_AD_CLIENT_SECRET,
AZURE_AD_TENANT_ID: process.env.AZURE_AD_TENANT_ID,

// AUTHENTIK
AUTHENTIK_CLIENT_ID: process.env.AUTHENTIK_CLIENT_ID,
AUTHENTIK_CLIENT_SECRET: process.env.AUTHENTIK_CLIENT_SECRET,
AUTHENTIK_ISSUER: process.env.AUTHENTIK_ISSUER,

// ZITADEL
ZITADEL_CLIENT_ID: process.env.ZITADEL_CLIENT_ID,
ZITADEL_CLIENT_SECRET: process.env.ZITADEL_CLIENT_SECRET,
ZITADEL_ISSUER: process.env.ZITADEL_ISSUER,
},
});

export const authEnv = getAuthConfig();
4 changes: 0 additions & 4 deletions src/config/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,11 @@ declare global {
NEXT_PUBLIC_I18N_DEBUG_SERVER: string;

NEXT_PUBLIC_DEVELOPER_DEBUG: string;

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: string;
}
}
}

export const getClientConfig = () => ({
ENABLED_CLERK: !!process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY,

BASE_PATH: process.env.NEXT_PUBLIC_BASE_PATH || '',

// Plausible Analytics
Expand Down
79 changes: 0 additions & 79 deletions src/config/server/auth.ts

This file was deleted.

4 changes: 1 addition & 3 deletions src/config/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { getAnalyticsConfig } from './analytics';
import { getAppConfig } from './app';
import { getAuthConfig } from './auth';
import { getProviderConfig } from './provider';

export const getServerConfig = () => {
Expand All @@ -10,8 +9,7 @@ export const getServerConfig = () => {

const provider = getProviderConfig();
const app = getAppConfig();
const auth = getAuthConfig();
const analytics = getAnalyticsConfig();

return { ...provider, ...app, ...analytics, ...auth };
return { ...provider, ...app, ...analytics };
};
7 changes: 7 additions & 0 deletions src/const/auth.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import { authEnv } from '@/config/auth';

export const enableClerk = authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH;
export const enableNextAuth = authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH;
export const enableAuth =
authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH || authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH;

export const LOBE_CHAT_AUTH_HEADER = 'X-lobe-chat-auth';

export const OAUTH_AUTHORIZED = 'X-oauth-authorized';
Expand Down
3 changes: 0 additions & 3 deletions src/const/version.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import pkg from '@/../package.json';
import { getClientConfig } from '@/config/client';

export const CURRENT_VERSION = pkg.version;

export const enableAuth = getClientConfig().ENABLED_CLERK;
10 changes: 3 additions & 7 deletions src/layout/AuthProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { PropsWithChildren } from 'react';

import { getClientConfig } from '@/config/client';
import { getServerConfig } from '@/config/server';
import { authEnv } from '@/config/auth';

import Clerk from './Clerk';
import NextAuth from './NextAuth';

const { ENABLE_OAUTH_SSO = false } = getServerConfig();

const { ENABLED_CLERK } = getClientConfig();
const AuthProvider = ({ children }: PropsWithChildren) => {
if (ENABLED_CLERK) return <Clerk>{children}</Clerk>;
if (authEnv.NEXT_PUBLIC_ENABLE_CLERK_AUTH) return <Clerk>{children}</Clerk>;

if (ENABLE_OAUTH_SSO) return <NextAuth>{children}</NextAuth>;
if (authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH) return <NextAuth>{children}</NextAuth>;

return children;
};
Expand Down
10 changes: 4 additions & 6 deletions src/libs/next-auth/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import NextAuth from 'next-auth';

import { getServerConfig } from '@/config/server';
import { authEnv } from '@/config/auth';

import { ssoProviders } from './sso-providers';

const { NEXTAUTH_SECRET, ENABLE_OAUTH_SSO, SSO_PROVIDERS } = getServerConfig();

export const initSSOProviders = () => {
return ENABLE_OAUTH_SSO
? SSO_PROVIDERS.split(/[,,]/).map((provider) => {
return authEnv.NEXT_PUBLIC_ENABLE_NEXT_AUTH
? authEnv.NEXT_AUTH_SSO_PROVIDERS.split(/[,,]/).map((provider) => {
const validProvider = ssoProviders.find((item) => item.id === provider);

if (validProvider) return validProvider.provider;
Expand Down Expand Up @@ -38,7 +36,7 @@ const nextAuth = NextAuth({
},
},
providers: initSSOProviders(),
secret: NEXTAUTH_SECRET,
secret: authEnv.NEXT_AUTH_SECRET,
trustHost: true,
});

Expand Down
Loading