Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 24 additions & 14 deletions skills/workos-authkit-base/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ README is the source of truth for: install commands, imports, API usage, code pa

## Task Structure (Required)

| Phase | Task | Blocked By | Purpose |
|-------|------|------------|---------|
| 1 | preflight | - | Verify env vars, detect framework |
| 2 | install | preflight | Install SDK package |
| 3 | callback | install | Create OAuth callback route |
| 4 | provider | install | Setup auth context/middleware |
| 5 | ui | callback, provider | Add sign-in/out UI |
| 6 | verify | ui | Build confirmation |
| Phase | Task | Blocked By | Purpose |
| ----- | --------- | ------------------ | --------------------------------- |
| 1 | preflight | - | Verify env vars, detect framework |
| 2 | install | preflight | Install SDK package |
| 3 | callback | install | Create OAuth callback route |
| 4 | provider | install | Setup auth context/middleware |
| 5 | ui | callback, provider | Add sign-in/out UI |
| 6 | verify | ui | Build confirmation |

## Decision Trees

Expand All @@ -51,57 +51,67 @@ Extract path from `WORKOS_REDIRECT_URI` → create route at that exact path.

## Environment Variables

| Variable | Purpose | When Required |
|----------|---------|---------------|
| `WORKOS_API_KEY` | Server authentication | Server SDKs |
| `WORKOS_CLIENT_ID` | Client identification | All SDKs |
| `WORKOS_REDIRECT_URI` | OAuth callback URL | Server SDKs |
| `WORKOS_COOKIE_PASSWORD` | Session encryption (32+ chars) | Server SDKs |
| Variable | Purpose | When Required |
| ------------------------ | ------------------------------ | ------------- |
| `WORKOS_API_KEY` | Server authentication | Server SDKs |
| `WORKOS_CLIENT_ID` | Client identification | All SDKs |
| `WORKOS_REDIRECT_URI` | OAuth callback URL | Server SDKs |
| `WORKOS_COOKIE_PASSWORD` | Session encryption (32+ chars) | Server SDKs |

Note: Some frameworks use prefixed variants (e.g., `NEXT_PUBLIC_*`). Check README.

## Verification Checklists

### After Install

- [ ] SDK package installed in node_modules
- [ ] No install errors in output

### After Callback Route

- [ ] Route file exists at path matching `WORKOS_REDIRECT_URI`
- [ ] Imports SDK callback handler (not custom OAuth)

### After Provider/Middleware

- [ ] Provider wraps entire app (client-side)
- [ ] Middleware configured in correct location (server-side)

### After UI

- [ ] Home page shows conditional auth state
- [ ] Uses SDK functions for sign-in/out URLs

### Final Verification

- [ ] Build completes with exit code 0
- [ ] No import resolution errors

## Error Recovery

### Module not found

- [ ] Verify install completed successfully
- [ ] Verify SDK exists in node_modules
- [ ] Re-run install if missing

### Build import errors

- [ ] Delete `node_modules`, reinstall
- [ ] Verify package.json has SDK dependency

### Invalid redirect URI

- [ ] Compare route path to `WORKOS_REDIRECT_URI`
- [ ] Paths must match exactly

### Cookie password error

- [ ] Verify `WORKOS_COOKIE_PASSWORD` is 32+ characters
- [ ] Generate new: `openssl rand -base64 32`

### Auth state not persisting

- [ ] Verify provider wraps entire app
- [ ] Check middleware is in correct location

Expand Down
13 changes: 12 additions & 1 deletion skills/workos-authkit-nextjs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ The README is the source of truth. If this skill conflicts with README, follow R
## Step 2: Pre-Flight Validation

### Project Structure

- Confirm `next.config.js` or `next.config.mjs` exists
- Confirm `package.json` contains `"next"` dependency

### Environment Variables

Check `.env.local` for:

- `WORKOS_API_KEY` - starts with `sk_`
- `WORKOS_CLIENT_ID` - starts with `client_`
- `NEXT_PUBLIC_WORKOS_REDIRECT_URI` - valid callback URL
Expand Down Expand Up @@ -106,9 +109,11 @@ All checks must pass before marking complete.
## Error Recovery

### "cookies was called outside a request scope" (Next.js 15+)

**Most common cause:** Route handler not properly async or missing await.

Fix for callback route:

1. Check that `handleAuth()` is exported directly: `export const GET = handleAuth();`
2. If using custom wrapper, ensure it's `async` and awaits any cookie operations
3. Verify authkit-nextjs SDK version supports Next.js 15+ (check README for compatibility)
Expand All @@ -117,25 +122,31 @@ Fix for callback route:
This error causes OAuth codes to expire ("invalid_grant"), so fix the handler first.

### "middleware.ts not found"

- Check: File at project root or `src/`, not inside `app/`
- Check: Filename matches Next.js version (proxy.ts for 16+, middleware.ts for 13-15)

### "Cannot use getUser in client component"

- Check: Component has no `'use client'` directive, or
- Check: Move auth logic to server component/API route

### "Module not found" for SDK import

- Check: SDK installed before writing imports
- Check: SDK package directory exists in node_modules

### "withAuth route not covered by middleware"

- Check: Middleware/proxy file exists at correct location
- Check: Matcher config includes the route path

### Build fails after AuthKitProvider

- Check: README for correct import path (may be subpath export)
- Check: No client/server boundary violations

### NEXT_PUBLIC_ prefix issues
### NEXT*PUBLIC* prefix issues

- Client components need `NEXT_PUBLIC_*` prefix
- Server components use plain env var names
21 changes: 11 additions & 10 deletions skills/workos-authkit-react-router/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ The README is the source of truth. If this skill conflicts with README, **follow

## Phase 2: Detect Router Mode

| Mode | Detection Signal | Key Indicator |
| -------------- | ------------------------------- | --------------------------- |
| v7 Framework | `react-router.config.ts` exists | Routes in `app/routes/` |
| v7 Data | `createBrowserRouter` in source | Loaders in route config |
| v7 Declarative | `<BrowserRouter>` component | Routes as JSX, no loaders |
| v6 | package.json version `"6.x"` | Similar to v7 Declarative |
| Mode | Detection Signal | Key Indicator |
| -------------- | ------------------------------- | ------------------------- |
| v7 Framework | `react-router.config.ts` exists | Routes in `app/routes/` |
| v7 Data | `createBrowserRouter` in source | Loaders in route config |
| v7 Declarative | `<BrowserRouter>` component | Routes as JSX, no loaders |
| v6 | package.json version `"6.x"` | Similar to v7 Declarative |

**Detection order:**

1. Check for `react-router.config.ts` (Framework mode)
2. Grep for `createBrowserRouter` (Data mode)
3. Check package.json version (v6 vs v7)
Expand All @@ -45,10 +46,10 @@ Based on detected mode, apply the corresponding README section. The README conta

### authLoader vs authkitLoader

| Function | Purpose | Where to use |
| -------------- | -------------------------------- | ----------------------- |
| `authLoader` | OAuth callback handler | Callback route ONLY |
| `authkitLoader`| Fetch user data in routes | Any route needing auth |
| Function | Purpose | Where to use |
| --------------- | ------------------------- | ---------------------- |
| `authLoader` | OAuth callback handler | Callback route ONLY |
| `authkitLoader` | Fetch user data in routes | Any route needing auth |

**Common mistake:** Using `authkitLoader` for callback route. Use `authLoader()`.

Expand Down
8 changes: 4 additions & 4 deletions skills/workos-authkit-react/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ START

## Critical: Build Tool Detection

| Marker File | Build Tool | Env Prefix | Access Pattern |
|-------------|------------|------------|----------------|
| `vite.config.ts` | Vite | `VITE_` | `import.meta.env.VITE_*` |
| `craco.config.js` or none | CRA | `REACT_APP_` | `process.env.REACT_APP_*` |
| Marker File | Build Tool | Env Prefix | Access Pattern |
| ------------------------- | ---------- | ------------ | ------------------------- |
| `vite.config.ts` | Vite | `VITE_` | `import.meta.env.VITE_*` |
| `craco.config.js` or none | CRA | `REACT_APP_` | `process.env.REACT_APP_*` |

**Wrong prefix = undefined values at runtime.** This is the #1 integration failure.

Expand Down
14 changes: 8 additions & 6 deletions skills/workos-authkit-tanstack-start/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ description: Integrate WorkOS AuthKit with TanStack Start applications. Full-sta
WebFetch: `https://github.com/workos/authkit-tanstack-start/blob/main/README.md`

From README, extract:

1. Package name from install command (e.g., `pnpm add @workos/...`)
2. Use that exact name for all imports

Expand All @@ -41,12 +42,12 @@ From README, extract:

## Environment Variables

| Variable | Format | Required |
|----------|--------|----------|
| `WORKOS_API_KEY` | `sk_...` | Yes |
| `WORKOS_CLIENT_ID` | `client_...` | Yes |
| `WORKOS_REDIRECT_URI` | Full URL | Yes |
| `WORKOS_COOKIE_PASSWORD` | 32+ chars | Yes |
| Variable | Format | Required |
| ------------------------ | ------------ | -------- |
| `WORKOS_API_KEY` | `sk_...` | Yes |
| `WORKOS_CLIENT_ID` | `client_...` | Yes |
| `WORKOS_REDIRECT_URI` | Full URL | Yes |
| `WORKOS_COOKIE_PASSWORD` | 32+ chars | Yes |

Generate password if missing: `openssl rand -base64 32`

Expand All @@ -71,6 +72,7 @@ Logout requires `signOut()` followed by redirect in a route loader. See README f
## Callback Route

Path must match `WORKOS_REDIRECT_URI`. If URI is `/api/auth/callback`:

- File: `app/routes/api/auth/callback.tsx`
- Use `handleAuth()` from SDK - do not write custom OAuth logic

Expand Down
18 changes: 10 additions & 8 deletions skills/workos-authkit-vanilla-js/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Has package.json with build tool (Vite, webpack, Parcel)?
### Step 4: Implement Per README

Follow README examples for:

- Client initialization
- Sign in/out handlers
- User state management
Expand Down Expand Up @@ -56,20 +57,21 @@ const authkit = await createClient(clientId);
## Environment Variables

**Bundled projects only:**

- Vite: `VITE_WORKOS_CLIENT_ID`
- Webpack: `REACT_APP_WORKOS_CLIENT_ID` or custom
- No `WORKOS_API_KEY` needed (client-side SDK)

## Error Recovery

| Error | Cause | Fix |
|-------|-------|-----|
| `WorkOS is not defined` | CDN not loaded | Add script to `<head>` before your code |
| `createClient is not a function` | Wrong import | npm: check import path; CDN: use `WorkOS.createClient` |
| `clientId is required` | Undefined env var | Check env prefix matches build tool |
| CORS errors | `file://` protocol | Use local dev server (`npx serve`) |
| Popup blocked | Not user gesture | Call `signIn()` only from click handler |
| Auth state lost | Token not persisted | Check localStorage in dev tools |
| Error | Cause | Fix |
| -------------------------------- | ------------------- | ------------------------------------------------------ |
| `WorkOS is not defined` | CDN not loaded | Add script to `<head>` before your code |
| `createClient is not a function` | Wrong import | npm: check import path; CDN: use `WorkOS.createClient` |
| `clientId is required` | Undefined env var | Check env prefix matches build tool |
| CORS errors | `file://` protocol | Use local dev server (`npx serve`) |
| Popup blocked | Not user gesture | Call `signIn()` only from click handler |
| Auth state lost | Token not persisted | Check localStorage in dev tools |

## Task Flow

Expand Down
2 changes: 1 addition & 1 deletion src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export async function runLogin(): Promise<void> {
},
body: new URLSearchParams({
client_id: clientId,
scope: 'openid email',
scope: 'openid email staging-environment:credentials:read',
}),
});

Expand Down
58 changes: 58 additions & 0 deletions src/lib/adapters/cli-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ export class CLIAdapter implements WizardAdapter {
this.subscribe('git:dirty', this.handleGitDirty);
this.subscribe('credentials:found', this.handleCredentialsFound);
this.subscribe('credentials:request', this.handleCredentialsRequest);
this.subscribe('credentials:env:prompt', this.handleEnvScanPrompt);
this.subscribe('device:started', this.handleDeviceStarted);
this.subscribe('device:success', this.handleDeviceSuccess);
this.subscribe('staging:fetching', this.handleStagingFetching);
this.subscribe('staging:success', this.handleStagingSuccess);
this.subscribe('credentials:env:found', this.handleEnvCredentialsFound);
this.subscribe('config:complete', this.handleConfigComplete);
this.subscribe('agent:start', this.handleAgentStart);
this.subscribe('agent:progress', this.handleAgentProgress);
Expand Down Expand Up @@ -189,6 +195,58 @@ export class CLIAdapter implements WizardAdapter {
clack.log.success('Found existing WorkOS credentials in .env.local');
};

private handleEnvScanPrompt = async ({ files }: WizardEvents['credentials:env:prompt']): Promise<void> => {
this.isPromptActive = true;
const fileList = files.length === 1 ? files[0] : files.slice(0, 2).join(', ');
const confirmed = await clack.confirm({
message: `Found ${fileList}. Check for existing WorkOS credentials?`,
initialValue: true,
});
this.isPromptActive = false;
this.flushPendingLogs();

if (clack.isCancel(confirmed)) {
this.sendEvent({ type: 'ENV_SCAN_DECLINED' });
} else if (confirmed) {
this.sendEvent({ type: 'ENV_SCAN_APPROVED' });
} else {
this.sendEvent({ type: 'ENV_SCAN_DECLINED' });
}
};

private handleDeviceStarted = ({ verificationUri, userCode }: WizardEvents['device:started']): void => {
clack.log.info(`\nOpen this URL in your browser:\n`);
console.log(` ${chalk.cyan(verificationUri)}`);
console.log(`\nEnter code: ${chalk.bold(userCode)}\n`);

this.spinner = clack.spinner();
this.spinner.start('Waiting for authentication...');
};

private handleDeviceSuccess = (): void => {
// Spinner will be stopped by handleStagingFetching
};

private handleStagingFetching = (): void => {
if (this.spinner) {
this.spinner.stop('Authenticated');
}
this.spinner = clack.spinner();
this.spinner.start('Fetching your WorkOS credentials...');
};

private handleStagingSuccess = (): void => {
if (this.spinner) {
this.spinner.stop('Credentials fetched');
this.spinner = null;
}
clack.log.success('WorkOS credentials retrieved automatically');
};

private handleEnvCredentialsFound = ({ sourcePath }: WizardEvents['credentials:env:found']): void => {
clack.log.success(`Found existing WorkOS credentials in ${sourcePath}`);
};

private handleGitDirty = async ({ files }: WizardEvents['git:dirty']): Promise<void> => {
clack.log.warn('You have uncommitted or untracked files:');
files.slice(0, 5).forEach((f) => clack.log.info(chalk.dim(` ${f}`)));
Expand Down
2 changes: 2 additions & 0 deletions src/lib/adapters/dashboard-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ export class DashboardAdapter implements WizardAdapter {
private handleConfirmResponse = ({ id, confirmed }: { id: string; confirmed: boolean }): void => {
if (id === 'git-status') {
this.sendEvent({ type: confirmed ? 'GIT_CONFIRMED' : 'GIT_CANCELLED' });
} else if (id === 'env-scan') {
this.sendEvent({ type: confirmed ? 'ENV_SCAN_APPROVED' : 'ENV_SCAN_DECLINED' });
}
};

Expand Down
Loading