convex-workos
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseConvex + WorkOS AuthKit
Convex + WorkOS AuthKit
Provider-specific patterns for integrating WorkOS AuthKit with Convex.
适用于 Convex 与 WorkOS AuthKit 集成的专属提供商模式。
Required Configuration
必要配置
1. auth.config.ts
1. auth.config.ts
typescript
// convex/auth.config.ts
const clientId = process.env.WORKOS_CLIENT_ID;
export default {
providers: [
{
type: 'customJwt',
issuer: 'https://api.workos.com/',
algorithm: 'RS256',
applicationID: clientId,
jwks: `https://api.workos.com/sso/jwks/${clientId}`
},
{
type: 'customJwt',
issuer: `https://api.workos.com/user_management/${clientId}`,
algorithm: 'RS256',
jwks: `https://api.workos.com/sso/jwks/${clientId}`
}
]
};Note: WorkOS requires TWO provider entries for different JWT issuers.
typescript
// convex/auth.config.ts
const clientId = process.env.WORKOS_CLIENT_ID;
export default {
providers: [
{
type: 'customJwt',
issuer: 'https://api.workos.com/',
algorithm: 'RS256',
applicationID: clientId,
jwks: `https://api.workos.com/sso/jwks/${clientId}`
},
{
type: 'customJwt',
issuer: `https://api.workos.com/user_management/${clientId}`,
algorithm: 'RS256',
jwks: `https://api.workos.com/sso/jwks/${clientId}`
}
]
};注意: WorkOS 需要两个提供商条目,对应不同的 JWT 颁发者。
2. Environment Variables
2. 环境变量
bash
undefinedbash
undefined.env.local (Vite/React)
.env.local (Vite/React)
VITE_WORKOS_CLIENT_ID=client_01...
VITE_WORKOS_REDIRECT_URI=http://localhost:5173/callback
VITE_WORKOS_CLIENT_ID=client_01...
VITE_WORKOS_REDIRECT_URI=http://localhost:5173/callback
.env.local (Next.js)
.env.local (Next.js)
WORKOS_CLIENT_ID=client_01...
WORKOS_API_KEY=sk_test_...
WORKOS_COOKIE_PASSWORD=your_32_char_minimum_password_here
NEXT_PUBLIC_WORKOS_REDIRECT_URI=http://localhost:3000/callback
WORKOS_CLIENT_ID=client_01...
WORKOS_API_KEY=sk_test_...
WORKOS_COOKIE_PASSWORD=your_32_char_minimum_password_here
NEXT_PUBLIC_WORKOS_REDIRECT_URI=http://localhost:3000/callback
Convex Dashboard Environment Variables
Convex 控制台环境变量
WORKOS_CLIENT_ID=client_01...
undefinedWORKOS_CLIENT_ID=client_01...
undefinedClient Setup
客户端设置
React (Vite)
React(Vite)
typescript
// src/main.tsx
import { AuthKitProvider, useAuth } from "@workos-inc/authkit-react";
import { ConvexProviderWithAuthKit } from "@convex-dev/workos";
import { ConvexReactClient } from "convex/react";
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
ReactDOM.createRoot(document.getElementById("root")!).render(
<AuthKitProvider
clientId={import.meta.env.VITE_WORKOS_CLIENT_ID}
redirectUri={import.meta.env.VITE_WORKOS_REDIRECT_URI}
>
<ConvexProviderWithAuthKit client={convex} useAuth={useAuth}>
<App />
</ConvexProviderWithAuthKit>
</AuthKitProvider>
);Install:
npm install @workos-inc/authkit-react @convex-dev/workostypescript
// src/main.tsx
import { AuthKitProvider, useAuth } from "@workos-inc/authkit-react";
import { ConvexProviderWithAuthKit } from "@convex-dev/workos";
import { ConvexReactClient } from "convex/react";
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
ReactDOM.createRoot(document.getElementById("root")!).render(
<AuthKitProvider
clientId={import.meta.env.VITE_WORKOS_CLIENT_ID}
redirectUri={import.meta.env.VITE_WORKOS_REDIRECT_URI}
>
<ConvexProviderWithAuthKit client={convex} useAuth={useAuth}>
<App />
</ConvexProviderWithAuthKit>
</AuthKitProvider>
);安装命令:
npm install @workos-inc/authkit-react @convex-dev/workosNext.js App Router
Next.js App 路由
typescript
// components/ConvexClientProvider.tsx
'use client';
import { ReactNode, useCallback, useRef } from 'react';
import { ConvexReactClient, ConvexProviderWithAuth } from 'convex/react';
import { AuthKitProvider, useAuth, useAccessToken } from '@workos-inc/authkit-nextjs/components';
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
export function ConvexClientProvider({ children }: { children: ReactNode }) {
return (
<AuthKitProvider>
<ConvexProviderWithAuth client={convex} useAuth={useAuthFromAuthKit}>
{children}
</ConvexProviderWithAuth>
</AuthKitProvider>
);
}
function useAuthFromAuthKit() {
const { user, loading: isLoading } = useAuth();
const { accessToken, loading: tokenLoading, error: tokenError } = useAccessToken();
const loading = (isLoading ?? false) || (tokenLoading ?? false);
const authenticated = !!user && !!accessToken && !loading;
const stableAccessToken = useRef<string | null>(null);
if (accessToken && !tokenError) {
stableAccessToken.current = accessToken;
}
const fetchAccessToken = useCallback(async () => {
if (stableAccessToken.current && !tokenError) {
return stableAccessToken.current;
}
return null;
}, [tokenError]);
return {
isLoading: loading,
isAuthenticated: authenticated,
fetchAccessToken,
};
}Install:
npm install @workos-inc/authkit-nextjs @convex-dev/workostypescript
// components/ConvexClientProvider.tsx
'use client';
import { ReactNode, useCallback, useRef } from 'react';
import { ConvexReactClient, ConvexProviderWithAuth } from 'convex/react';
import { AuthKitProvider, useAuth, useAccessToken } from '@workos-inc/authkit-nextjs/components';
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
export function ConvexClientProvider({ children }: { children: ReactNode }) {
return (
<AuthKitProvider>
<ConvexProviderWithAuth client={convex} useAuth={useAuthFromAuthKit}>
{children}
</ConvexProviderWithAuth>
</AuthKitProvider>
);
}
function useAuthFromAuthKit() {
const { user, loading: isLoading } = useAuth();
const { accessToken, loading: tokenLoading, error: tokenError } = useAccessToken();
const loading = (isLoading ?? false) || (tokenLoading ?? false);
const authenticated = !!user && !!accessToken && !loading;
const stableAccessToken = useRef<string | null>(null);
if (accessToken && !tokenError) {
stableAccessToken.current = accessToken;
}
const fetchAccessToken = useCallback(async () => {
if (stableAccessToken.current && !tokenError) {
return stableAccessToken.current;
}
return null;
}, [tokenError]);
return {
isLoading: loading,
isAuthenticated: authenticated,
fetchAccessToken,
};
}安装命令:
npm install @workos-inc/authkit-nextjs @convex-dev/workosNext.js Middleware
Next.js 中间件
typescript
// middleware.ts
import { authkitMiddleware } from '@workos-inc/authkit-nextjs';
export default authkitMiddleware({
middlewareAuth: {
enabled: true,
unauthenticatedPaths: ['/', '/sign-in', '/sign-up']
}
});
export const config = {
matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)']
};typescript
// middleware.ts
import { authkitMiddleware } from '@workos-inc/authkit-nextjs';
export default authkitMiddleware({
middlewareAuth: {
enabled: true,
unauthenticatedPaths: ['/', '/sign-in', '/sign-up']
}
});
export const config = {
matcher: ['/((?!.*\\\\..*|_next).*)', '/', '/(api|trpc)(.*)']
};Next.js Auth Routes
Next.js 认证路由
typescript
// app/callback/route.ts
import { handleAuth } from '@workos-inc/authkit-nextjs';
export const GET = handleAuth();
// app/sign-in/route.ts
import { redirect } from 'next/navigation';
import { getSignInUrl } from '@workos-inc/authkit-nextjs';
export async function GET() {
return redirect(await getSignInUrl());
}
// app/sign-up/route.ts
import { redirect } from 'next/navigation';
import { getSignUpUrl } from '@workos-inc/authkit-nextjs';
export async function GET() {
return redirect(await getSignUpUrl());
}typescript
// app/callback/route.ts
import { handleAuth } from '@workos-inc/authkit-nextjs';
export const GET = handleAuth();
// app/sign-in/route.ts
import { redirect } from 'next/navigation';
import { getSignInUrl } from '@workos-inc/authkit-nextjs';
export async function GET() {
return redirect(await getSignInUrl());
}
// app/sign-up/route.ts
import { redirect } from 'next/navigation';
import { getSignUpUrl } from '@workos-inc/authkit-nextjs';
export async function GET() {
return redirect(await getSignUpUrl());
}CORS Configuration (React/Vite only)
CORS 配置(仅适用于 React/Vite)
For React apps, configure CORS in WorkOS Dashboard:
- Authentication > Sessions > Cross-Origin Resource Sharing (CORS)
- Click Manage
- Add your dev domain:
http://localhost:5173 - Add your prod domain when deploying
对于 React 应用,需在 WorkOS 控制台配置 CORS:
- 进入 Authentication > Sessions > Cross-Origin Resource Sharing (CORS)
- 点击 Manage
- 添加开发域名:
http://localhost:5173 - 部署到生产环境时添加生产域名
UI Components
UI 组件
typescript
import { useAuth } from "@workos-inc/authkit-react"; // or authkit-nextjs/components
import { Authenticated, Unauthenticated } from "convex/react";
function App() {
const { user, signIn, signOut } = useAuth();
return (
<>
<Authenticated>
<button onClick={() => signOut()}>Sign out</button>
<Content />
</Authenticated>
<Unauthenticated>
<button onClick={() => signIn()}>Sign in</button>
</Unauthenticated>
</>
);
}typescript
import { useAuth } from "@workos-inc/authkit-react"; // 或 authkit-nextjs/components
import { Authenticated, Unauthenticated } from "convex/react";
function App() {
const { user, signIn, signOut } = useAuth();
return (
<>
<Authenticated>
<button onClick={() => signOut()}>Sign out</button>
<Content />
</Authenticated>
<Unauthenticated>
<button onClick={() => signIn()}>Sign in</button>
</Unauthenticated>
</>
);
}Auto-Provisioning (Development)
自动配置(开发环境)
Convex can auto-create WorkOS environments for development:
- Run template:
npm create convex@latest -- -t react-vite-authkit - Follow prompts to link Convex team with WorkOS
- Dev deployments auto-provision WorkOS environments
Configured automatically:
- Redirect URI
- CORS origin
- Local environment variables in
.env.local
Limitations:
- Only works for dev deployments
- Production must be manually configured
Convex 可自动为开发环境创建 WorkOS 环境:
- 运行模板命令:
npm create convex@latest -- -t react-vite-authkit - 按照提示将 Convex 团队与 WorkOS 关联
- 开发部署会自动配置 WorkOS 环境
自动配置内容:
- 重定向 URI
- CORS 源
- 中的本地环境变量
.env.local
限制:
- 仅适用于开发部署
- 生产环境需手动配置
Dev vs Prod Configuration
开发与生产环境配置对比
| Environment | API Key | Redirect URI |
|---|---|---|
| Development | | |
| Production | | |
Set different WORKOS_CLIENT_ID in Convex Dashboard for dev vs prod deployments.
| 环境 | API 密钥 | 重定向 URI |
|---|---|---|
| 开发环境 | | |
| 生产环境 | | |
在 Convex 控制台中为开发和生产部署设置不同的 WORKOS_CLIENT_ID。
WorkOS-Specific Troubleshooting
WorkOS 专属问题排查
| Issue | Cause | Fix |
|---|---|---|
| CORS error | Domain not added | Add domain in WorkOS Dashboard > Sessions > CORS |
| Token validation fails | Wrong issuer | Check BOTH providers in auth.config.ts |
Missing | JWT config | Check WorkOS JWT configuration |
| "Platform not authorized" | Workspace unlinked | Run |
| 问题 | 原因 | 解决方案 |
|---|---|---|
| CORS 错误 | 未添加域名 | 在 WorkOS 控制台 > Sessions > CORS 中添加域名 |
| 令牌验证失败 | 颁发者配置错误 | 检查 auth.config.ts 中的两个提供商条目 |
缺少 | JWT 配置问题 | 检查 WorkOS JWT 配置 |
| "Platform not authorized"(平台未授权) | 工作区未关联 | 运行 |
"Platform not authorized" Error
"平台未授权" 错误
bash
npx convex integration workos disconnect-team
npx convex integration workos provision-teamNote: Use a different email if creating new WorkOS workspace.
bash
npx convex integration workos disconnect-team
npx convex integration workos provision-team注意:创建新的 WorkOS 工作区时请使用不同的邮箱。
DO ✅
正确做法 ✅
- Include BOTH provider entries in auth.config.ts (different issuers)
- Configure CORS for React/Vite apps
- Use not WorkOS's
useConvexAuth()for auth stateuseAuth() - Set WORKOS_CLIENT_ID in Convex Dashboard
- Use 32+ char WORKOS_COOKIE_PASSWORD for Next.js
- 在 auth.config.ts 中包含两个提供商条目(对应不同的颁发者)
- 为 React/Vite 应用配置 CORS
- 使用 而非 WorkOS 的
useConvexAuth()来获取认证状态useAuth() - 在 Convex 控制台中设置 WORKOS_CLIENT_ID
- 为 Next.js 使用 32 位以上的 WORKOS_COOKIE_PASSWORD
DON'T ❌
错误做法 ❌
- Forget the second provider entry (user_management issuer)
- Skip CORS configuration for browser-based apps
- Use WorkOS auth hooks to gate Convex queries
- Hardcode the client ID (use env var)
- Use same WorkOS env for dev and prod
- 遗漏第二个提供商条目(user_management 颁发者)
- 跳过浏览器端应用的 CORS 配置
- 使用 WorkOS 认证钩子来限制 Convex 查询
- 硬编码客户端 ID(使用环境变量)
- 为开发和生产环境使用同一个 WorkOS 环境",