chub-dev 0.1.0 → 0.1.2-beta.0
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.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,1220 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: auth
|
|
3
|
+
description: "Clerk JavaScript SDK for user authentication, session management, and identity workflows"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "javascript"
|
|
6
|
+
versions: "5.1.6"
|
|
7
|
+
updated-on: "2026-03-01"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "clerk,auth,authentication,user-management,session"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Clerk JavaScript SDK Coding Guidelines
|
|
13
|
+
|
|
14
|
+
You are a Clerk authentication expert. Help me with writing code using the Clerk JavaScript SDK and its official libraries for user authentication and management.
|
|
15
|
+
|
|
16
|
+
This guide covers both vanilla JavaScript implementations using `@clerk/clerk-js` and framework-specific implementations using packages like `@clerk/nextjs` and `@clerk/react`.
|
|
17
|
+
|
|
18
|
+
Please follow the following guidelines when generating code.
|
|
19
|
+
|
|
20
|
+
You can find the official documentation and code samples here:
|
|
21
|
+
https://clerk.com/docs
|
|
22
|
+
|
|
23
|
+
## Important Context About This SDK
|
|
24
|
+
|
|
25
|
+
Clerk provides **two main approaches** for JavaScript authentication:
|
|
26
|
+
1. **Vanilla JavaScript (`@clerk/clerk-js`)**: Uses the browser SDK loaded via CDN or NPM, ideal for standalone HTML/JS apps
|
|
27
|
+
2. **Framework SDKs**: Specialized packages for Next.js, React, Vue, etc. with framework-specific optimizations
|
|
28
|
+
|
|
29
|
+
This context guide covers both approaches comprehensively.
|
|
30
|
+
|
|
31
|
+
## Golden Rule: Use the Correct Clerk Package for Your Framework
|
|
32
|
+
|
|
33
|
+
Always use the appropriate Clerk SDK for your specific framework or environment. Clerk provides specialized packages for different platforms that include framework-specific optimizations and integrations.
|
|
34
|
+
|
|
35
|
+
**Available Packages:**
|
|
36
|
+
- **Next.js:** `@clerk/nextjs`
|
|
37
|
+
- **React:** `@clerk/react`
|
|
38
|
+
- **Vanilla JavaScript:** `@clerk/clerk-js`
|
|
39
|
+
- **Vue:** `@clerk/vue`
|
|
40
|
+
- **Astro:** `@clerk/astro`
|
|
41
|
+
- **Remix:** `@clerk/remix`
|
|
42
|
+
- **Express:** `@clerk/express`
|
|
43
|
+
- **Fastify:** `@clerk/fastify`
|
|
44
|
+
- **Expo:** `@clerk/expo`
|
|
45
|
+
|
|
46
|
+
**Installation Examples:**
|
|
47
|
+
```bash
|
|
48
|
+
# For Next.js applications
|
|
49
|
+
npm install @clerk/nextjs
|
|
50
|
+
|
|
51
|
+
# For React applications
|
|
52
|
+
npm install @clerk/react
|
|
53
|
+
|
|
54
|
+
# For vanilla JavaScript
|
|
55
|
+
npm install @clerk/clerk-js
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Prerequisites and Setup
|
|
59
|
+
|
|
60
|
+
- An existing Clerk application from the [Clerk Dashboard](https://dashboard.clerk.com/sign-up)
|
|
61
|
+
- For Next.js: Next.js 13.0.4 or later, React 18 or later, Node.js >=18.17.0
|
|
62
|
+
- For vanilla JavaScript: Any modern browser with ES6+ support
|
|
63
|
+
|
|
64
|
+
## Environment Variables
|
|
65
|
+
|
|
66
|
+
### For Framework Apps (Next.js, React, etc.)
|
|
67
|
+
Set up your environment variables in `.env.local`:
|
|
68
|
+
```
|
|
69
|
+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
|
|
70
|
+
CLERK_SECRET_KEY=sk_test_...
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### For Vanilla JavaScript Apps
|
|
74
|
+
Store your publishable key in a `.env` file and inject it into your HTML:
|
|
75
|
+
```
|
|
76
|
+
CLERK_PUBLISHABLE_KEY=pk_test_...
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Important**: Only the publishable key is needed for client-side authentication. Never expose your secret key in browser code.
|
|
80
|
+
|
|
81
|
+
## Next.js Integration
|
|
82
|
+
|
|
83
|
+
### ClerkProvider Setup
|
|
84
|
+
|
|
85
|
+
Wrap your application with ClerkProvider:
|
|
86
|
+
|
|
87
|
+
```jsx
|
|
88
|
+
import { ClerkProvider } from '@clerk/nextjs'
|
|
89
|
+
|
|
90
|
+
export default function RootLayout({ children }) {
|
|
91
|
+
return (
|
|
92
|
+
<ClerkProvider>
|
|
93
|
+
<html lang="en">
|
|
94
|
+
<body>{children}</body>
|
|
95
|
+
</html>
|
|
96
|
+
</ClerkProvider>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Middleware Configuration
|
|
102
|
+
|
|
103
|
+
Use `clerkMiddleware` for route protection:
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
|
|
107
|
+
|
|
108
|
+
const isProtectedRoute = createRouteMatcher(['/protected(.*)']);
|
|
109
|
+
const isAdminRoute = createRouteMatcher(['/admin(.*)']);
|
|
110
|
+
|
|
111
|
+
export default clerkMiddleware(async (auth, req) => {
|
|
112
|
+
if (isProtectedRoute(req)) {
|
|
113
|
+
await auth.protect();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (isAdminRoute(req)) {
|
|
117
|
+
await auth.protect({ role: 'org:admin' });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Important:** Do not use the deprecated `authMiddleware`. Always use `clerkMiddleware` for new implementations.
|
|
123
|
+
|
|
124
|
+
## Client-Side Authentication Hooks
|
|
125
|
+
|
|
126
|
+
### useUser Hook
|
|
127
|
+
|
|
128
|
+
Access the current user information:
|
|
129
|
+
|
|
130
|
+
```jsx
|
|
131
|
+
import { useUser } from '@clerk/nextjs';
|
|
132
|
+
|
|
133
|
+
function UserProfile() {
|
|
134
|
+
const { isLoaded, isSignedIn, user } = useUser();
|
|
135
|
+
|
|
136
|
+
if (!isLoaded || !isSignedIn) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return <div>Hello, {user.firstName}!</div>;
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### useAuth Hook
|
|
145
|
+
|
|
146
|
+
Access authentication state and methods:
|
|
147
|
+
|
|
148
|
+
```jsx
|
|
149
|
+
import { useAuth } from '@clerk/nextjs';
|
|
150
|
+
|
|
151
|
+
function AuthComponent() {
|
|
152
|
+
const { isLoaded, userId, sessionId, getToken } = useAuth();
|
|
153
|
+
|
|
154
|
+
// Use authentication state
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### useClerk Hook
|
|
159
|
+
|
|
160
|
+
Access the Clerk instance for advanced operations:
|
|
161
|
+
|
|
162
|
+
```jsx
|
|
163
|
+
import { useClerk } from '@clerk/nextjs';
|
|
164
|
+
|
|
165
|
+
function SignOutButton() {
|
|
166
|
+
const { signOut } = useClerk();
|
|
167
|
+
|
|
168
|
+
return <button onClick={() => signOut()}>Sign out</button>;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Server-Side Authentication
|
|
173
|
+
|
|
174
|
+
### auth() Helper (App Router)
|
|
175
|
+
|
|
176
|
+
Use the `auth()` helper in Server Components, Route Handlers, and Server Actions:
|
|
177
|
+
|
|
178
|
+
```jsx
|
|
179
|
+
import { auth } from '@clerk/nextjs/server';
|
|
180
|
+
|
|
181
|
+
export default async function Page() {
|
|
182
|
+
const { userId } = await auth();
|
|
183
|
+
|
|
184
|
+
if (!userId) {
|
|
185
|
+
return <div>Please sign in</div>;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return <div>User ID: {userId}</div>;
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### currentUser() Helper
|
|
193
|
+
|
|
194
|
+
Get the full user object on the server side:
|
|
195
|
+
|
|
196
|
+
```jsx
|
|
197
|
+
import { currentUser } from '@clerk/nextjs/server';
|
|
198
|
+
|
|
199
|
+
export default async function Page() {
|
|
200
|
+
const user = await currentUser();
|
|
201
|
+
|
|
202
|
+
if (!user) return <div>Not signed in</div>;
|
|
203
|
+
|
|
204
|
+
return <div>Hello {user?.firstName}</div>;
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### auth.protect() Method
|
|
209
|
+
|
|
210
|
+
Use `auth.protect()` for authentication and authorization checks:
|
|
211
|
+
|
|
212
|
+
```jsx
|
|
213
|
+
import { auth } from '@clerk/nextjs/server';
|
|
214
|
+
|
|
215
|
+
export default async function AdminPage() {
|
|
216
|
+
// Protect route - redirects unauthenticated users
|
|
217
|
+
await auth.protect();
|
|
218
|
+
|
|
219
|
+
// Or protect with role check
|
|
220
|
+
await auth.protect({ role: 'org:admin' });
|
|
221
|
+
|
|
222
|
+
return <div>Protected content</div>;
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Authentication Components
|
|
227
|
+
|
|
228
|
+
### Sign-In and Sign-Up Buttons
|
|
229
|
+
|
|
230
|
+
Use pre-built buttons for authentication flows:
|
|
231
|
+
|
|
232
|
+
```jsx
|
|
233
|
+
import { SignInButton, SignUpButton } from '@clerk/nextjs';
|
|
234
|
+
|
|
235
|
+
export default function Home() {
|
|
236
|
+
return (
|
|
237
|
+
<main>
|
|
238
|
+
<SignInButton mode='modal' forceRedirectUrl='/protected'>
|
|
239
|
+
Sign in button
|
|
240
|
+
</SignInButton>
|
|
241
|
+
|
|
242
|
+
<SignUpButton mode='modal' forceRedirectUrl='/protected'>
|
|
243
|
+
Sign up button
|
|
244
|
+
</SignUpButton>
|
|
245
|
+
</main>
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Pre-built Components
|
|
251
|
+
|
|
252
|
+
```jsx
|
|
253
|
+
import {
|
|
254
|
+
SignIn,
|
|
255
|
+
SignUp,
|
|
256
|
+
UserButton,
|
|
257
|
+
UserProfile
|
|
258
|
+
} from '@clerk/nextjs';
|
|
259
|
+
|
|
260
|
+
// Drop-in authentication components
|
|
261
|
+
<SignIn />
|
|
262
|
+
<SignUp />
|
|
263
|
+
<UserButton />
|
|
264
|
+
<UserProfile />
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Organization Management
|
|
268
|
+
|
|
269
|
+
### Organization Roles and Permissions
|
|
270
|
+
|
|
271
|
+
Clerk supports role-based access control within organizations:
|
|
272
|
+
|
|
273
|
+
```jsx
|
|
274
|
+
// Check organization permissions
|
|
275
|
+
await auth.protect({
|
|
276
|
+
role: 'org:admin'
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Custom permissions
|
|
280
|
+
await auth.protect({
|
|
281
|
+
permission: 'org:sys_memberships:manage'
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Organization Hooks
|
|
286
|
+
|
|
287
|
+
```jsx
|
|
288
|
+
import {
|
|
289
|
+
useOrganization,
|
|
290
|
+
useOrganizationList,
|
|
291
|
+
useUser
|
|
292
|
+
} from '@clerk/nextjs';
|
|
293
|
+
|
|
294
|
+
function OrganizationComponent() {
|
|
295
|
+
const { organization } = useOrganization();
|
|
296
|
+
const { organizationList } = useOrganizationList();
|
|
297
|
+
|
|
298
|
+
// Organization management logic
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Common Patterns
|
|
303
|
+
|
|
304
|
+
### Conditional Rendering Based on Auth State
|
|
305
|
+
|
|
306
|
+
```jsx
|
|
307
|
+
import { useUser } from '@clerk/nextjs';
|
|
308
|
+
|
|
309
|
+
function App() {
|
|
310
|
+
const { isSignedIn } = useUser();
|
|
311
|
+
|
|
312
|
+
return (
|
|
313
|
+
<div>
|
|
314
|
+
{isSignedIn ? (
|
|
315
|
+
<AuthenticatedApp />
|
|
316
|
+
) : (
|
|
317
|
+
<UnauthenticatedApp />
|
|
318
|
+
)}
|
|
319
|
+
</div>
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Route Protection with Redirects
|
|
325
|
+
|
|
326
|
+
```jsx
|
|
327
|
+
import { auth } from '@clerk/nextjs/server';
|
|
328
|
+
|
|
329
|
+
export default async function ProtectedPage() {
|
|
330
|
+
const { userId } = await auth();
|
|
331
|
+
|
|
332
|
+
if (!userId) {
|
|
333
|
+
redirect('/sign-in');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return <div>Protected content</div>;
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Vanilla JavaScript Integration (`@clerk/clerk-js`)
|
|
341
|
+
|
|
342
|
+
### Loading Clerk via CDN
|
|
343
|
+
|
|
344
|
+
The most common approach for vanilla JavaScript apps is loading Clerk from a CDN:
|
|
345
|
+
|
|
346
|
+
```html
|
|
347
|
+
<!DOCTYPE html>
|
|
348
|
+
<html>
|
|
349
|
+
<head>
|
|
350
|
+
<title>My App</title>
|
|
351
|
+
</head>
|
|
352
|
+
<body>
|
|
353
|
+
<!-- Your content here -->
|
|
354
|
+
|
|
355
|
+
<!-- Load Clerk SDK -->
|
|
356
|
+
<script
|
|
357
|
+
async
|
|
358
|
+
crossorigin="anonymous"
|
|
359
|
+
data-clerk-publishable-key="pk_test_..."
|
|
360
|
+
src="https://[your-clerk-domain].clerk.accounts.dev/npm/@clerk/clerk-js@latest/dist/clerk.browser.js"
|
|
361
|
+
type="text/javascript"
|
|
362
|
+
></script>
|
|
363
|
+
|
|
364
|
+
<script src="app.js"></script>
|
|
365
|
+
</body>
|
|
366
|
+
</html>
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Important Implementation Details:**
|
|
370
|
+
- The Clerk SDK loads **asynchronously**, so you must wait for it to be ready
|
|
371
|
+
- Access the Clerk instance via `window.Clerk`
|
|
372
|
+
- Always call `await clerk.load()` before using any Clerk methods
|
|
373
|
+
- Listen for the `clerk:loaded` event as a fallback if the script hasn't loaded yet
|
|
374
|
+
|
|
375
|
+
### Initializing Clerk Properly
|
|
376
|
+
|
|
377
|
+
```javascript
|
|
378
|
+
// Wait for page to load
|
|
379
|
+
window.addEventListener('load', async () => {
|
|
380
|
+
if (window.Clerk) {
|
|
381
|
+
// Clerk is already available
|
|
382
|
+
await initializeClerk(window.Clerk);
|
|
383
|
+
} else {
|
|
384
|
+
// Wait for Clerk to load
|
|
385
|
+
window.addEventListener('clerk:loaded', async (event) => {
|
|
386
|
+
await initializeClerk(event.detail);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
async function initializeClerk(clerk) {
|
|
392
|
+
try {
|
|
393
|
+
// CRITICAL: Always call load() first
|
|
394
|
+
await clerk.load();
|
|
395
|
+
|
|
396
|
+
// Now you can safely use clerk methods
|
|
397
|
+
const user = clerk.user;
|
|
398
|
+
|
|
399
|
+
// Your authentication logic here
|
|
400
|
+
} catch (error) {
|
|
401
|
+
console.error('Error initializing Clerk:', error);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Accessing User Information
|
|
407
|
+
|
|
408
|
+
After Clerk is loaded, access the current user via `clerk.user`:
|
|
409
|
+
|
|
410
|
+
```javascript
|
|
411
|
+
async function initializeClerk(clerk) {
|
|
412
|
+
await clerk.load();
|
|
413
|
+
|
|
414
|
+
const user = clerk.user;
|
|
415
|
+
|
|
416
|
+
if (user) {
|
|
417
|
+
// User is authenticated
|
|
418
|
+
console.log('User ID:', user.id);
|
|
419
|
+
console.log('Name:', user.firstName, user.lastName);
|
|
420
|
+
console.log('Email:', user.primaryEmailAddress?.emailAddress);
|
|
421
|
+
console.log('Created:', new Date(user.createdAt));
|
|
422
|
+
|
|
423
|
+
// Access other user properties
|
|
424
|
+
console.log('Phone:', user.primaryPhoneNumber?.phoneNumber);
|
|
425
|
+
console.log('Profile Image:', user.imageUrl);
|
|
426
|
+
} else {
|
|
427
|
+
// User is not signed in
|
|
428
|
+
console.log('No user signed in');
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Mounting Authentication Components
|
|
434
|
+
|
|
435
|
+
Clerk provides pre-built UI components that can be mounted into your HTML:
|
|
436
|
+
|
|
437
|
+
```javascript
|
|
438
|
+
async function initializeClerk(clerk) {
|
|
439
|
+
await clerk.load();
|
|
440
|
+
|
|
441
|
+
if (!clerk.user) {
|
|
442
|
+
// Mount sign-in component for unauthenticated users
|
|
443
|
+
clerk.mountSignIn(document.getElementById('sign-in-container'));
|
|
444
|
+
} else {
|
|
445
|
+
// Mount user button for authenticated users
|
|
446
|
+
clerk.mountUserButton(document.getElementById('user-button-container'));
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
**Available mount methods:**
|
|
452
|
+
- `clerk.mountSignIn(element)` - Full sign-in flow
|
|
453
|
+
- `clerk.mountSignUp(element)` - Full sign-up flow
|
|
454
|
+
- `clerk.mountUserButton(element)` - User profile dropdown button
|
|
455
|
+
- `clerk.mountUserProfile(element)` - Complete user profile management
|
|
456
|
+
|
|
457
|
+
### Listening to Authentication State Changes
|
|
458
|
+
|
|
459
|
+
Use `clerk.addListener()` to react to authentication changes:
|
|
460
|
+
|
|
461
|
+
```javascript
|
|
462
|
+
async function initializeClerk(clerk) {
|
|
463
|
+
await clerk.load();
|
|
464
|
+
|
|
465
|
+
// Initial UI update
|
|
466
|
+
updateUI(clerk.user);
|
|
467
|
+
|
|
468
|
+
// Listen for changes
|
|
469
|
+
clerk.addListener((emission) => {
|
|
470
|
+
// The emission object contains the changed properties
|
|
471
|
+
if (emission.user !== undefined) {
|
|
472
|
+
// User state changed (sign in, sign out, or profile update)
|
|
473
|
+
updateUI(emission.user);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (emission.session !== undefined) {
|
|
477
|
+
// Session changed
|
|
478
|
+
console.log('Session updated:', emission.session);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (emission.organization !== undefined) {
|
|
482
|
+
// Organization changed
|
|
483
|
+
console.log('Organization updated:', emission.organization);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function updateUI(user) {
|
|
489
|
+
const authenticatedView = document.getElementById('authenticated-view');
|
|
490
|
+
const unauthenticatedView = document.getElementById('unauthenticated-view');
|
|
491
|
+
|
|
492
|
+
if (user) {
|
|
493
|
+
unauthenticatedView.classList.add('hidden');
|
|
494
|
+
authenticatedView.classList.remove('hidden');
|
|
495
|
+
} else {
|
|
496
|
+
authenticatedView.classList.add('hidden');
|
|
497
|
+
unauthenticatedView.classList.remove('hidden');
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Implementing Sign Out
|
|
503
|
+
|
|
504
|
+
```javascript
|
|
505
|
+
async function handleSignOut(clerk) {
|
|
506
|
+
try {
|
|
507
|
+
await clerk.signOut();
|
|
508
|
+
// UI will update automatically via listener
|
|
509
|
+
console.log('Signed out successfully');
|
|
510
|
+
} catch (error) {
|
|
511
|
+
console.error('Sign out failed:', error);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Usage in your HTML
|
|
516
|
+
document.getElementById('sign-out-btn').addEventListener('click', () => {
|
|
517
|
+
handleSignOut(window.Clerk);
|
|
518
|
+
});
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
### Complete Vanilla JavaScript Example
|
|
522
|
+
|
|
523
|
+
Here's a complete working example:
|
|
524
|
+
|
|
525
|
+
```javascript
|
|
526
|
+
// app.js
|
|
527
|
+
window.addEventListener('load', async () => {
|
|
528
|
+
if (window.Clerk) {
|
|
529
|
+
await initializeClerk(window.Clerk);
|
|
530
|
+
} else {
|
|
531
|
+
window.addEventListener('clerk:loaded', async (event) => {
|
|
532
|
+
await initializeClerk(event.detail);
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
async function initializeClerk(clerk) {
|
|
538
|
+
try {
|
|
539
|
+
await clerk.load();
|
|
540
|
+
|
|
541
|
+
const loadingView = document.getElementById('loading-view');
|
|
542
|
+
const authenticatedView = document.getElementById('authenticated-view');
|
|
543
|
+
const unauthenticatedView = document.getElementById('unauthenticated-view');
|
|
544
|
+
const userInfo = document.getElementById('user-info');
|
|
545
|
+
const signOutBtn = document.getElementById('sign-out-btn');
|
|
546
|
+
|
|
547
|
+
function updateUI() {
|
|
548
|
+
const user = clerk.user;
|
|
549
|
+
loadingView.classList.add('hidden');
|
|
550
|
+
|
|
551
|
+
if (user) {
|
|
552
|
+
unauthenticatedView.classList.add('hidden');
|
|
553
|
+
authenticatedView.classList.remove('hidden');
|
|
554
|
+
|
|
555
|
+
userInfo.innerHTML = `
|
|
556
|
+
<h3>User Information</h3>
|
|
557
|
+
<p><strong>Name:</strong> ${user.firstName || ''} ${user.lastName || ''}</p>
|
|
558
|
+
<p><strong>Email:</strong> ${user.primaryEmailAddress?.emailAddress || 'N/A'}</p>
|
|
559
|
+
<p><strong>User ID:</strong> ${user.id}</p>
|
|
560
|
+
<p><strong>Created:</strong> ${new Date(user.createdAt).toLocaleDateString()}</p>
|
|
561
|
+
`;
|
|
562
|
+
} else {
|
|
563
|
+
authenticatedView.classList.add('hidden');
|
|
564
|
+
unauthenticatedView.classList.remove('hidden');
|
|
565
|
+
clerk.mountSignIn(document.getElementById('clerk-sign-in'));
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
updateUI();
|
|
570
|
+
|
|
571
|
+
clerk.addListener((emission) => {
|
|
572
|
+
if (emission.user !== undefined) {
|
|
573
|
+
updateUI();
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
signOutBtn.addEventListener('click', async () => {
|
|
578
|
+
await clerk.signOut();
|
|
579
|
+
updateUI();
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
clerk.mountUserButton(document.getElementById('clerk-user-button'));
|
|
583
|
+
|
|
584
|
+
} catch (error) {
|
|
585
|
+
console.error('Error initializing Clerk:', error);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Server-Side Key Injection Pattern
|
|
591
|
+
|
|
592
|
+
For production apps, inject the publishable key server-side to keep it out of version control:
|
|
593
|
+
|
|
594
|
+
```javascript
|
|
595
|
+
// server.js (Node.js example)
|
|
596
|
+
const http = require('http');
|
|
597
|
+
const fs = require('fs');
|
|
598
|
+
require('dotenv').config();
|
|
599
|
+
|
|
600
|
+
const server = http.createServer((req, res) => {
|
|
601
|
+
if (req.url === '/' || req.url === '/index.html') {
|
|
602
|
+
let html = fs.readFileSync('./index.html', 'utf-8');
|
|
603
|
+
|
|
604
|
+
// Replace placeholder with actual key
|
|
605
|
+
html = html.replace(
|
|
606
|
+
'CLERK_PUBLISHABLE_KEY_PLACEHOLDER',
|
|
607
|
+
process.env.CLERK_PUBLISHABLE_KEY
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
611
|
+
res.end(html);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
server.listen(3000);
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
```html
|
|
619
|
+
<!-- index.html -->
|
|
620
|
+
<script
|
|
621
|
+
data-clerk-publishable-key="CLERK_PUBLISHABLE_KEY_PLACEHOLDER"
|
|
622
|
+
src="https://[your-domain].clerk.accounts.dev/npm/@clerk/clerk-js@latest/dist/clerk.browser.js"
|
|
623
|
+
></script>
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
### Getting Authentication Tokens for API Calls
|
|
627
|
+
|
|
628
|
+
```javascript
|
|
629
|
+
async function makeAuthenticatedRequest(clerk) {
|
|
630
|
+
try {
|
|
631
|
+
// Get the current session token
|
|
632
|
+
const token = await clerk.session.getToken();
|
|
633
|
+
|
|
634
|
+
const response = await fetch('/api/protected-endpoint', {
|
|
635
|
+
headers: {
|
|
636
|
+
'Authorization': `Bearer ${token}`,
|
|
637
|
+
'Content-Type': 'application/json'
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
const data = await response.json();
|
|
642
|
+
return data;
|
|
643
|
+
} catch (error) {
|
|
644
|
+
console.error('API request failed:', error);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
### Handling Session Lifecycle
|
|
650
|
+
|
|
651
|
+
```javascript
|
|
652
|
+
async function initializeClerk(clerk) {
|
|
653
|
+
await clerk.load();
|
|
654
|
+
|
|
655
|
+
// Check if session exists
|
|
656
|
+
if (clerk.session) {
|
|
657
|
+
console.log('Active session:', clerk.session.id);
|
|
658
|
+
console.log('Session expires:', new Date(clerk.session.expireAt));
|
|
659
|
+
|
|
660
|
+
// Get session token
|
|
661
|
+
const token = await clerk.session.getToken();
|
|
662
|
+
console.log('Session token:', token);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Listen for session changes
|
|
666
|
+
clerk.addListener((emission) => {
|
|
667
|
+
if (emission.session !== undefined) {
|
|
668
|
+
if (emission.session) {
|
|
669
|
+
console.log('New session created');
|
|
670
|
+
} else {
|
|
671
|
+
console.log('Session ended');
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
## API Endpoints and Backend Integration
|
|
679
|
+
|
|
680
|
+
### Backend Package
|
|
681
|
+
|
|
682
|
+
For backend-only applications, use `@clerk/backend`:
|
|
683
|
+
|
|
684
|
+
```javascript
|
|
685
|
+
import { clerkClient } from '@clerk/backend';
|
|
686
|
+
|
|
687
|
+
// Server-side user management
|
|
688
|
+
const user = await clerkClient.users.getUser(userId);
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
### Verifying Tokens on Your Backend
|
|
692
|
+
|
|
693
|
+
```javascript
|
|
694
|
+
// Node.js/Express example
|
|
695
|
+
const { verifyToken } = require('@clerk/backend');
|
|
696
|
+
|
|
697
|
+
app.get('/api/protected', async (req, res) => {
|
|
698
|
+
try {
|
|
699
|
+
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
700
|
+
const claims = await verifyToken(token, {
|
|
701
|
+
secretKey: process.env.CLERK_SECRET_KEY
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
// Token is valid, proceed with request
|
|
705
|
+
res.json({ userId: claims.sub });
|
|
706
|
+
} catch (error) {
|
|
707
|
+
res.status(401).json({ error: 'Unauthorized' });
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
## Common Pitfalls and Best Practices
|
|
713
|
+
|
|
714
|
+
### Vanilla JavaScript Pitfalls
|
|
715
|
+
|
|
716
|
+
**Pitfall 1: Not waiting for Clerk to load**
|
|
717
|
+
```javascript
|
|
718
|
+
// WRONG - Clerk may not be loaded yet
|
|
719
|
+
window.Clerk.user; // May be undefined
|
|
720
|
+
|
|
721
|
+
// CORRECT - Wait for load event
|
|
722
|
+
window.addEventListener('load', async () => {
|
|
723
|
+
if (window.Clerk) {
|
|
724
|
+
await window.Clerk.load();
|
|
725
|
+
const user = window.Clerk.user; // Now safe to use
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
**Pitfall 2: Forgetting to call clerk.load()**
|
|
731
|
+
```javascript
|
|
732
|
+
// WRONG
|
|
733
|
+
async function initializeClerk(clerk) {
|
|
734
|
+
const user = clerk.user; // May not be initialized yet
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// CORRECT
|
|
738
|
+
async function initializeClerk(clerk) {
|
|
739
|
+
await clerk.load(); // CRITICAL - must call first
|
|
740
|
+
const user = clerk.user;
|
|
741
|
+
}
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
**Pitfall 3: Not handling both load scenarios**
|
|
745
|
+
```javascript
|
|
746
|
+
// INCOMPLETE - only handles one scenario
|
|
747
|
+
window.addEventListener('load', async () => {
|
|
748
|
+
await initializeClerk(window.Clerk); // What if Clerk isn't loaded yet?
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
// COMPLETE - handles both scenarios
|
|
752
|
+
window.addEventListener('load', async () => {
|
|
753
|
+
if (window.Clerk) {
|
|
754
|
+
await initializeClerk(window.Clerk);
|
|
755
|
+
} else {
|
|
756
|
+
window.addEventListener('clerk:loaded', async (event) => {
|
|
757
|
+
await initializeClerk(event.detail);
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### Framework Best Practices
|
|
764
|
+
|
|
765
|
+
**Use loading states properly:**
|
|
766
|
+
```javascript
|
|
767
|
+
const { isLoaded, isSignedIn, user } = useUser();
|
|
768
|
+
|
|
769
|
+
// Always check isLoaded first
|
|
770
|
+
if (!isLoaded) {
|
|
771
|
+
return <LoadingSpinner />;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (!isSignedIn) {
|
|
775
|
+
return <SignInPrompt />;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// Now safe to use user
|
|
779
|
+
return <UserProfile user={user} />;
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
**Avoid conditional hooks:**
|
|
783
|
+
```javascript
|
|
784
|
+
// WRONG
|
|
785
|
+
if (someCondition) {
|
|
786
|
+
const { user } = useUser(); // Breaks rules of hooks
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// CORRECT
|
|
790
|
+
const { user } = useUser();
|
|
791
|
+
if (someCondition && user) {
|
|
792
|
+
// Use user here
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
### Security Best Practices
|
|
797
|
+
|
|
798
|
+
1. **Never expose secret keys** - Only use publishable keys in client-side code
|
|
799
|
+
2. **Verify tokens on the backend** - Don't trust client-side authentication alone
|
|
800
|
+
3. **Use HTTPS** - Always serve your app over HTTPS in production
|
|
801
|
+
4. **Implement CORS properly** - Configure allowed origins in Clerk Dashboard
|
|
802
|
+
5. **Handle token expiration** - Sessions expire and need refresh
|
|
803
|
+
|
|
804
|
+
### Performance Optimization
|
|
805
|
+
|
|
806
|
+
**For Next.js:**
|
|
807
|
+
- Use `auth()` instead of `currentUser()` when you only need the user ID
|
|
808
|
+
- `currentUser()` makes an API call while `auth()` reads from the request
|
|
809
|
+
- Implement proper caching strategies for user data
|
|
810
|
+
|
|
811
|
+
**For Vanilla JS:**
|
|
812
|
+
- Load Clerk SDK asynchronously (use `async` attribute)
|
|
813
|
+
- Cache user data in memory to avoid repeated property access
|
|
814
|
+
- Use event listeners instead of polling for auth state changes
|
|
815
|
+
|
|
816
|
+
## Important Notes
|
|
817
|
+
|
|
818
|
+
### Framework-Specific Notes
|
|
819
|
+
|
|
820
|
+
- **Next.js Middleware Requirement:** `clerkMiddleware()` is required for server-side authentication helpers in Next.js App Router
|
|
821
|
+
- **Server-Only Functions:** Functions like `auth()` and `currentUser()` only work on the server side
|
|
822
|
+
- **Rate Limits:** Server-side user fetching counts towards Backend API rate limits
|
|
823
|
+
- **Deprecated APIs:** Do not use `authMiddleware` - use `clerkMiddleware` instead
|
|
824
|
+
|
|
825
|
+
### Vanilla JavaScript Notes
|
|
826
|
+
|
|
827
|
+
- **Asynchronous Loading:** The Clerk SDK loads asynchronously - always wait for it to be ready
|
|
828
|
+
- **Critical Load Step:** Always call `await clerk.load()` before accessing user/session data
|
|
829
|
+
- **Event-Driven Updates:** Use `clerk.addListener()` for reactive UI updates
|
|
830
|
+
- **Session Tokens:** Access tokens via `clerk.session.getToken()` for API authentication
|
|
831
|
+
- **Component Mounting:** Use mount methods (`mountSignIn`, etc.) for pre-built UI components
|
|
832
|
+
|
|
833
|
+
## Useful Links
|
|
834
|
+
|
|
835
|
+
- **Documentation:** https://clerk.com/docs
|
|
836
|
+
- **Quickstart Guides:** https://clerk.com/docs/quickstarts/overview
|
|
837
|
+
- **Discord Community:** https://clerk.com/discord
|
|
838
|
+
- **GitHub Repository:** https://github.com/clerk/javascript
|
|
839
|
+
|
|
840
|
+
## Quick Reference: Choosing Your Implementation
|
|
841
|
+
|
|
842
|
+
### Use Vanilla JavaScript (`@clerk/clerk-js`) when:
|
|
843
|
+
- Building standalone HTML/JS applications
|
|
844
|
+
- Working without a framework
|
|
845
|
+
- Need simple drop-in authentication
|
|
846
|
+
- Want to use CDN-hosted SDK
|
|
847
|
+
- Building static sites with authentication
|
|
848
|
+
|
|
849
|
+
**Key Pattern:**
|
|
850
|
+
```javascript
|
|
851
|
+
window.addEventListener('load', async () => {
|
|
852
|
+
if (window.Clerk) {
|
|
853
|
+
await window.Clerk.load();
|
|
854
|
+
// Use window.Clerk.user
|
|
855
|
+
}
|
|
856
|
+
});
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
### Use Next.js SDK (`@clerk/nextjs`) when:
|
|
860
|
+
- Building Next.js applications (App Router or Pages Router)
|
|
861
|
+
- Need server-side authentication
|
|
862
|
+
- Want middleware-based route protection
|
|
863
|
+
- Building full-stack applications with Next.js
|
|
864
|
+
|
|
865
|
+
**Key Pattern:**
|
|
866
|
+
```javascript
|
|
867
|
+
// Client: useUser(), useAuth(), useClerk()
|
|
868
|
+
// Server: auth(), currentUser()
|
|
869
|
+
// Middleware: clerkMiddleware()
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
### Use React SDK (`@clerk/react`) when:
|
|
873
|
+
- Building React applications (not Next.js)
|
|
874
|
+
- Using Create React App or Vite
|
|
875
|
+
- Need client-side authentication hooks
|
|
876
|
+
- Working with React Router
|
|
877
|
+
|
|
878
|
+
**Key Pattern:**
|
|
879
|
+
```javascript
|
|
880
|
+
import { ClerkProvider, useUser } from '@clerk/react';
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Use Backend SDK (`@clerk/backend`) when:
|
|
884
|
+
- Building backend-only APIs
|
|
885
|
+
- Need to verify tokens server-side
|
|
886
|
+
- Managing users from your backend
|
|
887
|
+
- Integrating with Express, Fastify, etc.
|
|
888
|
+
|
|
889
|
+
**Key Pattern:**
|
|
890
|
+
```javascript
|
|
891
|
+
import { clerkClient, verifyToken } from '@clerk/backend';
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
## Summary
|
|
895
|
+
|
|
896
|
+
This guide covers the core Clerk JavaScript SDK patterns across multiple implementation approaches:
|
|
897
|
+
|
|
898
|
+
### Key Concepts
|
|
899
|
+
- **Clerk is framework-agnostic** - Choose the right package for your stack
|
|
900
|
+
- **Publishable keys are safe for client-side** - Secret keys are for backend only
|
|
901
|
+
- **Authentication is session-based** - Clerk manages sessions automatically
|
|
902
|
+
- **Pre-built components** - Drop-in UI for authentication flows
|
|
903
|
+
- **Event-driven updates** - React to auth state changes in real-time
|
|
904
|
+
|
|
905
|
+
### Critical Implementation Details
|
|
906
|
+
|
|
907
|
+
**For Vanilla JavaScript:**
|
|
908
|
+
1. Always wait for Clerk to load (`window.Clerk` + `clerk:loaded` event)
|
|
909
|
+
2. Call `await clerk.load()` before accessing user/session data
|
|
910
|
+
3. Use `clerk.addListener()` for reactive UI updates
|
|
911
|
+
4. Mount components with `clerk.mountSignIn()`, `clerk.mountUserButton()`, etc.
|
|
912
|
+
|
|
913
|
+
**For Next.js:**
|
|
914
|
+
1. Wrap app with `<ClerkProvider>`
|
|
915
|
+
2. Use `clerkMiddleware()` for route protection
|
|
916
|
+
3. Use hooks (`useUser()`, `useAuth()`) in client components
|
|
917
|
+
4. Use `auth()` and `currentUser()` in server components
|
|
918
|
+
|
|
919
|
+
**For Backend:**
|
|
920
|
+
1. Verify tokens with `verifyToken()` or middleware
|
|
921
|
+
2. Use `clerkClient` for user management operations
|
|
922
|
+
3. Never expose secret keys to the client
|
|
923
|
+
|
|
924
|
+
### Authentication Flow
|
|
925
|
+
1. User visits your app
|
|
926
|
+
2. Clerk SDK initializes and checks for existing session
|
|
927
|
+
3. If no session, show sign-in UI
|
|
928
|
+
4. User authenticates (email, OAuth, etc.)
|
|
929
|
+
5. Clerk creates session and returns user object
|
|
930
|
+
6. Your app reacts to auth state and shows user content
|
|
931
|
+
7. Session tokens can be used for API authentication
|
|
932
|
+
|
|
933
|
+
Clerk handles the complexity of authentication, session management, and user profiles, allowing you to focus on building your application.
|
|
934
|
+
|
|
935
|
+
## Common Use Cases & Patterns
|
|
936
|
+
|
|
937
|
+
### Protected Routes (Vanilla JS)
|
|
938
|
+
```javascript
|
|
939
|
+
async function initializeClerk(clerk) {
|
|
940
|
+
await clerk.load();
|
|
941
|
+
|
|
942
|
+
// Protect specific pages
|
|
943
|
+
const protectedPaths = ['/dashboard', '/profile', '/settings'];
|
|
944
|
+
const currentPath = window.location.pathname;
|
|
945
|
+
|
|
946
|
+
if (protectedPaths.includes(currentPath) && !clerk.user) {
|
|
947
|
+
// Redirect to sign-in
|
|
948
|
+
window.location.href = '/sign-in';
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
### Protected Routes (Next.js Middleware)
|
|
954
|
+
```javascript
|
|
955
|
+
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server';
|
|
956
|
+
|
|
957
|
+
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)']);
|
|
958
|
+
|
|
959
|
+
export default clerkMiddleware(async (auth, req) => {
|
|
960
|
+
if (isProtectedRoute(req)) {
|
|
961
|
+
await auth.protect(); // Redirects if not authenticated
|
|
962
|
+
}
|
|
963
|
+
});
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
### Displaying User-Specific Content
|
|
967
|
+
```javascript
|
|
968
|
+
// Vanilla JS
|
|
969
|
+
if (clerk.user) {
|
|
970
|
+
document.getElementById('welcome').textContent =
|
|
971
|
+
`Welcome back, ${clerk.user.firstName}!`;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Next.js/React
|
|
975
|
+
const { user } = useUser();
|
|
976
|
+
return user ? <div>Welcome back, {user.firstName}!</div> : null;
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
### Making Authenticated API Requests
|
|
980
|
+
```javascript
|
|
981
|
+
// Vanilla JS
|
|
982
|
+
const token = await clerk.session.getToken();
|
|
983
|
+
const response = await fetch('/api/data', {
|
|
984
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
// Next.js/React
|
|
988
|
+
const { getToken } = useAuth();
|
|
989
|
+
const token = await getToken();
|
|
990
|
+
const response = await fetch('/api/data', {
|
|
991
|
+
headers: { 'Authorization': `Bearer ${token}` }
|
|
992
|
+
});
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
### Handling OAuth Sign-In
|
|
996
|
+
```javascript
|
|
997
|
+
// Clerk handles OAuth automatically through its UI components
|
|
998
|
+
// Just mount the sign-in component and it includes OAuth buttons
|
|
999
|
+
|
|
1000
|
+
// Vanilla JS
|
|
1001
|
+
clerk.mountSignIn(document.getElementById('sign-in'));
|
|
1002
|
+
|
|
1003
|
+
// Next.js/React
|
|
1004
|
+
<SignIn />
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
### Custom OAuth Redirect URLs
|
|
1008
|
+
```jsx
|
|
1009
|
+
<SignInButton
|
|
1010
|
+
mode='modal'
|
|
1011
|
+
forceRedirectUrl='/dashboard'
|
|
1012
|
+
signUpForceRedirectUrl='/onboarding'
|
|
1013
|
+
>
|
|
1014
|
+
Sign in
|
|
1015
|
+
</SignInButton>
|
|
1016
|
+
```
|
|
1017
|
+
|
|
1018
|
+
## Troubleshooting Common Issues
|
|
1019
|
+
|
|
1020
|
+
### Issue: "Clerk is not defined"
|
|
1021
|
+
**Cause:** Clerk SDK hasn't loaded yet
|
|
1022
|
+
**Solution:** Use the proper loading pattern with both `window.Clerk` check and `clerk:loaded` event
|
|
1023
|
+
|
|
1024
|
+
### Issue: "User is null after sign-in"
|
|
1025
|
+
**Cause:** Forgot to call `clerk.load()` or checking too early
|
|
1026
|
+
**Solution:** Always call `await clerk.load()` and use event listeners for updates
|
|
1027
|
+
|
|
1028
|
+
### Issue: "Cannot read property 'user' of undefined"
|
|
1029
|
+
**Cause:** Accessing Clerk before initialization
|
|
1030
|
+
**Solution:** Wrap all Clerk usage in the initialization function after `load()` completes
|
|
1031
|
+
|
|
1032
|
+
### Issue: "CORS errors when calling Clerk API"
|
|
1033
|
+
**Cause:** Domain not configured in Clerk Dashboard
|
|
1034
|
+
**Solution:** Add your domain to allowed origins in Clerk Dashboard settings
|
|
1035
|
+
|
|
1036
|
+
### Issue: "Session token expired"
|
|
1037
|
+
**Cause:** Sessions expire after period of inactivity
|
|
1038
|
+
**Solution:** Clerk automatically refreshes tokens; ensure you call `getToken()` fresh each time
|
|
1039
|
+
|
|
1040
|
+
### Issue: "Middleware not protecting routes"
|
|
1041
|
+
**Cause:** Incorrect middleware configuration or matcher pattern
|
|
1042
|
+
**Solution:** Verify `clerkMiddleware` is exported as default and matcher patterns are correct
|
|
1043
|
+
|
|
1044
|
+
### Issue: "Can't access user data in Server Component"
|
|
1045
|
+
**Cause:** Using client-side hooks on the server
|
|
1046
|
+
**Solution:** Use `auth()` or `currentUser()` instead of `useUser()` in Server Components
|
|
1047
|
+
|
|
1048
|
+
### Debugging Tips
|
|
1049
|
+
|
|
1050
|
+
1. **Check Clerk Dashboard:** Verify your API keys are correct
|
|
1051
|
+
2. **Console logging:** Add `console.log('Clerk loaded:', clerk)` after `load()`
|
|
1052
|
+
3. **Network tab:** Check for failed requests to Clerk API
|
|
1053
|
+
4. **Browser console:** Look for Clerk SDK errors or warnings
|
|
1054
|
+
5. **Version compatibility:** Ensure your Clerk package versions are up to date
|
|
1055
|
+
|
|
1056
|
+
## Prerequisites
|
|
1057
|
+
|
|
1058
|
+
- Next.js 13.0.4 or later
|
|
1059
|
+
- React 18 or later
|
|
1060
|
+
- Node.js `>=18.17.0` or later
|
|
1061
|
+
- An existing Clerk application. [Create your account for free](https://dashboard.clerk.com/sign-up?utm_source=github&utm_medium=clerk_nextjs).
|
|
1062
|
+
|
|
1063
|
+
## Getting Started
|
|
1064
|
+
|
|
1065
|
+
### Next.js Installation
|
|
1066
|
+
|
|
1067
|
+
The fastest way to get started with Clerk is by following the [Next.js Quickstart](https://clerk.com/docs/quickstarts/nextjs?utm_source=github&utm_medium=clerk_nextjs).
|
|
1068
|
+
|
|
1069
|
+
You'll learn how to install `@clerk/nextjs`, set up your environment keys, add `<ClerkProvider>` to your application, use the Clerk middleware, and use Clerk's prebuilt components.
|
|
1070
|
+
|
|
1071
|
+
### JavaScript Installation
|
|
1072
|
+
|
|
1073
|
+
The fastest way to get started with Clerk is by following the [JavaScript Quickstart](https://clerk.com/docs/quickstarts/javascript?utm_source=github&utm_medium=clerk_js).
|
|
1074
|
+
|
|
1075
|
+
You'll learn how to add the ClerkJS SDK to your application (either through `<script>` tag or NPM module) and use Clerk's prebuilt components.
|
|
1076
|
+
|
|
1077
|
+
## Community
|
|
1078
|
+
|
|
1079
|
+
- Join our official community [Discord server](https://clerk.com/discord)
|
|
1080
|
+
|
|
1081
|
+
## Advanced Middleware Configuration
|
|
1082
|
+
|
|
1083
|
+
```typescript
|
|
1084
|
+
export default clerkMiddleware(async (auth, req) => {
|
|
1085
|
+
if (isProtectedRoute(req)) {
|
|
1086
|
+
await auth.protect();
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
if (isAdminRoute(req)) {
|
|
1090
|
+
await auth.protect({ role: 'org:admin' });
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
if (isCSPRoute(req)) {
|
|
1094
|
+
req.headers.set('Content-Security-Policy', csp.replace(/\n/g, ''));
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
```typescript
|
|
1100
|
+
export const config = {
|
|
1101
|
+
matcher: [
|
|
1102
|
+
'/((?!.*\\..*|_next).*)', // Don't run middleware on static files
|
|
1103
|
+
'/', // Run middleware on index page
|
|
1104
|
+
'/(api|trpc)(.*)',
|
|
1105
|
+
], // Run middleware on API routes
|
|
1106
|
+
};
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
## Example Components
|
|
1110
|
+
|
|
1111
|
+
```typescript
|
|
1112
|
+
function GreetingWithHook() {
|
|
1113
|
+
// Use the useUser hook to get the Clerk.user object
|
|
1114
|
+
// This hook causes a re-render on user changes
|
|
1115
|
+
const { isLoaded, isSignedIn, user } = useUser();
|
|
1116
|
+
|
|
1117
|
+
if (!isLoaded || !isSignedIn) {
|
|
1118
|
+
// You can handle the loading or signed state separately
|
|
1119
|
+
return null;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
return <div>Hello, {user.firstName}!</div>;
|
|
1123
|
+
}
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
## Authentication Protection Details
|
|
1127
|
+
|
|
1128
|
+
`auth` includes a single property, the `protect()` method, which you can use in two ways:
|
|
1129
|
+
- to check if a user is authenticated (signed in)
|
|
1130
|
+
- to check if a user is authorized (has the correct roles or permissions) to access something, such as a component or a route handler
|
|
1131
|
+
|
|
1132
|
+
The following table describes how auth.protect() behaves based on user authentication or authorization status:
|
|
1133
|
+
|
|
1134
|
+
| Authenticated | Authorized | `auth.protect()` will |
|
|
1135
|
+
| - | - | - |
|
|
1136
|
+
| Yes | Yes | Return the [`Auth`](https://clerk.com/docs/references/backend/types/auth-object) object. |
|
|
1137
|
+
| Yes | No | Return a `404` error. |
|
|
1138
|
+
| No | No | Redirect the user to the sign-in page*. |
|
|
1139
|
+
|
|
1140
|
+
> [!IMPORTANT]
|
|
1141
|
+
> *For non-document requests, such as API requests, `auth.protect()` returns a `404` error to users who aren't authenticated.
|
|
1142
|
+
|
|
1143
|
+
`auth.protect()` can be used to check if a user is authenticated or authorized to access certain parts of your application or even entire routes. See detailed examples in the [dedicated guide](https://clerk.com/docs/organizations/verify-user-permissions).
|
|
1144
|
+
|
|
1145
|
+
## auth() Helper Details
|
|
1146
|
+
|
|
1147
|
+
The `auth()` helper returns the [`Auth`](https://clerk.com/docs/references/backend/types/auth-object) object of the currently active user, as well as the [`redirectToSignIn()`](https://clerk.com/docs/references/nextjs/auth#redirect-to-sign-in) method.
|
|
1148
|
+
|
|
1149
|
+
- Only available for App Router.
|
|
1150
|
+
- Only works on the server-side, such as in Server Components, Route Handlers, and Server Actions.
|
|
1151
|
+
- Requires [`clerkMiddleware()`](https://clerk.com/docs/references/nextjs/clerk-middleware) to be configured.
|
|
1152
|
+
|
|
1153
|
+
## currentUser() Details
|
|
1154
|
+
|
|
1155
|
+
- calls `fetch()`, so it is automatically deduped per request.
|
|
1156
|
+
- uses the [`GET /v1/users/{user_id}`](https://clerk.com/docs/reference/backend-api/tag/Users#operation/GetUser) endpoint.
|
|
1157
|
+
- counts towards the [Backend API request rate limit](https://clerk.com/docs/backend-requests/resources/rate-limits).
|
|
1158
|
+
|
|
1159
|
+
**Example:**
|
|
1160
|
+
```tsx
|
|
1161
|
+
// app/page.tsx
|
|
1162
|
+
import { currentUser } from '@clerk/nextjs/server'
|
|
1163
|
+
|
|
1164
|
+
export default async function Page() {
|
|
1165
|
+
const user = await currentUser()
|
|
1166
|
+
|
|
1167
|
+
if (!user) return <div>Not signed in</div>
|
|
1168
|
+
|
|
1169
|
+
return <div>Hello {user?.firstName}</div>
|
|
1170
|
+
}
|
|
1171
|
+
```
|
|
1172
|
+
|
|
1173
|
+
## Advanced SignIn Button Examples
|
|
1174
|
+
|
|
1175
|
+
```tsx
|
|
1176
|
+
<SignInButton
|
|
1177
|
+
mode='modal'
|
|
1178
|
+
forceRedirectUrl='/protected'
|
|
1179
|
+
signUpForceRedirectUrl='/protected'
|
|
1180
|
+
>
|
|
1181
|
+
Sign in button (force)
|
|
1182
|
+
</SignInButton>
|
|
1183
|
+
|
|
1184
|
+
<SignInButton
|
|
1185
|
+
mode='modal'
|
|
1186
|
+
oauthFlow='popup'
|
|
1187
|
+
forceRedirectUrl='/protected'
|
|
1188
|
+
signUpForceRedirectUrl='/protected'
|
|
1189
|
+
>
|
|
1190
|
+
Sign in button (force, popup)
|
|
1191
|
+
</SignInButton>
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
## Type Definitions
|
|
1195
|
+
|
|
1196
|
+
```typescript
|
|
1197
|
+
import type { OrganizationResource } from './organization';
|
|
1198
|
+
import type { ClerkResource } from './resource';
|
|
1199
|
+
import type { PublicUserData } from './session';
|
|
1200
|
+
import type { OrganizationMembershipJSONSnapshot } from './snapshots';
|
|
1201
|
+
import type { Autocomplete } from './utils';
|
|
1202
|
+
|
|
1203
|
+
interface Base {
|
|
1204
|
+
permission: string;
|
|
1205
|
+
role: string;
|
|
1206
|
+
}
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
## About Clerk
|
|
1210
|
+
|
|
1211
|
+
Clerk helps developers build user management. We provide streamlined user experiences for your users to sign up, sign in, and manage their profile.
|
|
1212
|
+
|
|
1213
|
+
This repository contains all the Clerk JavaScript SDKs under the `@clerk` namespace.
|
|
1214
|
+
|
|
1215
|
+
## Documentation
|
|
1216
|
+
|
|
1217
|
+
Clerk's full documentation is available at [clerk.com/docs](https://clerk.com/docs?utm_source=github&utm_medium=clerk_js_repo_readme).
|
|
1218
|
+
|
|
1219
|
+
- **We recommend starting with the [quickstart guides](https://clerk.com/docs/quickstarts/overview?utm_source=github&utm_medium=clerk_js_repo_readme).** They'll help you quickly add Clerk to your application. If you're starting a new project and aren't sure what to pick, check out our most popular quickstart: [Next.js](https://clerk.com/docs/quickstarts/nextjs?utm_source=github&utm_medium=clerk_js_repo_readme).
|
|
1220
|
+
|