workos 0.8.1 → 0.9.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.
Files changed (52) hide show
  1. package/README.md +5 -2
  2. package/dist/bin.js +23 -13
  3. package/dist/bin.js.map +1 -1
  4. package/dist/commands/auth-status.d.ts +1 -0
  5. package/dist/commands/auth-status.js +56 -0
  6. package/dist/commands/auth-status.js.map +1 -0
  7. package/dist/commands/login.js +7 -5
  8. package/dist/commands/login.js.map +1 -1
  9. package/dist/doctor/checks/ai-analysis.js +3 -3
  10. package/dist/doctor/checks/ai-analysis.js.map +1 -1
  11. package/dist/doctor/checks/auth-patterns.js +10 -5
  12. package/dist/doctor/checks/auth-patterns.js.map +1 -1
  13. package/dist/lib/adapters/cli-adapter.js +1 -1
  14. package/dist/lib/adapters/cli-adapter.js.map +1 -1
  15. package/dist/lib/agent-interface.js +5 -5
  16. package/dist/lib/agent-interface.js.map +1 -1
  17. package/dist/lib/config-store.js +8 -7
  18. package/dist/lib/config-store.js.map +1 -1
  19. package/dist/lib/credential-proxy.js +1 -1
  20. package/dist/lib/credential-proxy.js.map +1 -1
  21. package/dist/lib/credential-store.js +8 -2
  22. package/dist/lib/credential-store.js.map +1 -1
  23. package/dist/lib/ensure-auth.js +12 -24
  24. package/dist/lib/ensure-auth.js.map +1 -1
  25. package/dist/lib/run-with-core.js +1 -1
  26. package/dist/lib/run-with-core.js.map +1 -1
  27. package/dist/lib/token-refresh-client.js +1 -1
  28. package/dist/lib/token-refresh-client.js.map +1 -1
  29. package/dist/lib/token-refresh.js +1 -1
  30. package/dist/lib/token-refresh.js.map +1 -1
  31. package/dist/lib/version-check.js +2 -1
  32. package/dist/lib/version-check.js.map +1 -1
  33. package/dist/utils/exit-codes.js +1 -1
  34. package/dist/utils/exit-codes.js.map +1 -1
  35. package/dist/utils/help-json.js +7 -2
  36. package/dist/utils/help-json.js.map +1 -1
  37. package/package.json +1 -1
  38. package/skills/workos-authkit-nextjs/SKILL.md +1 -1
  39. package/skills/workos-authkit-react/SKILL.md +19 -10
  40. package/skills/workos-authkit-react-router/SKILL.md +18 -8
  41. package/skills/workos-authkit-sveltekit/SKILL.md +55 -7
  42. package/skills/workos-authkit-tanstack-start/SKILL.md +67 -16
  43. package/skills/workos-authkit-vanilla-js/SKILL.md +19 -10
  44. package/skills/workos-elixir/SKILL.md +37 -0
  45. package/skills/workos-go/SKILL.md +36 -1
  46. package/skills/workos-kotlin/SKILL.md +34 -0
  47. package/skills/workos-management/SKILL.md +1 -1
  48. package/skills/workos-node/SKILL.md +33 -0
  49. package/skills/workos-php/SKILL.md +34 -1
  50. package/skills/workos-php-laravel/SKILL.md +36 -1
  51. package/skills/workos-python/SKILL.md +34 -0
  52. package/skills/workos-ruby/SKILL.md +35 -0
@@ -0,0 +1 @@
1
+ export declare function runAuthStatus(): Promise<void>;
@@ -0,0 +1,56 @@
1
+ import chalk from 'chalk';
2
+ import { getCredentials, isTokenExpired } from '../lib/credentials.js';
3
+ import { getActiveEnvironment } from '../lib/config-store.js';
4
+ import { isJsonMode, outputJson } from '../utils/output.js';
5
+ function formatTimeRemaining(ms) {
6
+ if (ms <= 0)
7
+ return 'expired';
8
+ const seconds = Math.floor(ms / 1000);
9
+ const minutes = Math.floor(seconds / 60);
10
+ const hours = Math.floor(minutes / 60);
11
+ if (hours > 0)
12
+ return `${hours}h ${minutes % 60}m`;
13
+ if (minutes > 0)
14
+ return `${minutes}m ${seconds % 60}s`;
15
+ return `${seconds}s`;
16
+ }
17
+ export async function runAuthStatus() {
18
+ const creds = getCredentials();
19
+ const activeEnv = getActiveEnvironment();
20
+ if (!creds) {
21
+ if (isJsonMode()) {
22
+ outputJson({ authenticated: false });
23
+ return;
24
+ }
25
+ console.log(chalk.yellow('Not logged in'));
26
+ console.log(chalk.dim('Run `workos auth login` to authenticate'));
27
+ return;
28
+ }
29
+ const expired = isTokenExpired(creds);
30
+ const timeRemaining = creds.expiresAt - Date.now();
31
+ if (isJsonMode()) {
32
+ outputJson({
33
+ authenticated: true,
34
+ email: creds.email ?? null,
35
+ userId: creds.userId,
36
+ tokenExpired: expired,
37
+ tokenExpiresAt: new Date(creds.expiresAt).toISOString(),
38
+ tokenExpiresIn: expired ? null : formatTimeRemaining(timeRemaining),
39
+ hasRefreshToken: !!creds.refreshToken,
40
+ activeEnvironment: activeEnv ? { name: activeEnv.name, type: activeEnv.type } : null,
41
+ });
42
+ return;
43
+ }
44
+ console.log(chalk.green(`Logged in as ${creds.email ?? creds.userId}`));
45
+ if (expired) {
46
+ console.log(chalk.yellow(`Token expired ${formatTimeRemaining(-timeRemaining)} ago`));
47
+ }
48
+ else {
49
+ console.log(chalk.dim(`Token expires in ${formatTimeRemaining(timeRemaining)}`));
50
+ }
51
+ console.log(chalk.dim(`Refresh token: ${creds.refreshToken ? 'present' : 'absent'}`));
52
+ if (activeEnv) {
53
+ console.log(chalk.dim(`Environment: ${activeEnv.name} (${activeEnv.type})`));
54
+ }
55
+ }
56
+ //# sourceMappingURL=auth-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-status.js","sourceRoot":"","sources":["../../src/commands/auth-status.ts"],"names":[],"mappings":"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","sourcesContent":["import chalk from 'chalk';\nimport { getCredentials, isTokenExpired } from '../lib/credentials.js';\nimport { getActiveEnvironment } from '../lib/config-store.js';\nimport { isJsonMode, outputJson } from '../utils/output.js';\n\nfunction formatTimeRemaining(ms: number): string {\n if (ms <= 0) return 'expired';\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n if (hours > 0) return `${hours}h ${minutes % 60}m`;\n if (minutes > 0) return `${minutes}m ${seconds % 60}s`;\n return `${seconds}s`;\n}\n\nexport async function runAuthStatus(): Promise<void> {\n const creds = getCredentials();\n const activeEnv = getActiveEnvironment();\n\n if (!creds) {\n if (isJsonMode()) {\n outputJson({ authenticated: false });\n return;\n }\n console.log(chalk.yellow('Not logged in'));\n console.log(chalk.dim('Run `workos auth login` to authenticate'));\n return;\n }\n\n const expired = isTokenExpired(creds);\n const timeRemaining = creds.expiresAt - Date.now();\n\n if (isJsonMode()) {\n outputJson({\n authenticated: true,\n email: creds.email ?? null,\n userId: creds.userId,\n tokenExpired: expired,\n tokenExpiresAt: new Date(creds.expiresAt).toISOString(),\n tokenExpiresIn: expired ? null : formatTimeRemaining(timeRemaining),\n hasRefreshToken: !!creds.refreshToken,\n activeEnvironment: activeEnv ? { name: activeEnv.name, type: activeEnv.type } : null,\n });\n return;\n }\n\n console.log(chalk.green(`Logged in as ${creds.email ?? creds.userId}`));\n\n if (expired) {\n console.log(chalk.yellow(`Token expired ${formatTimeRemaining(-timeRemaining)} ago`));\n } else {\n console.log(chalk.dim(`Token expires in ${formatTimeRemaining(timeRemaining)}`));\n }\n\n console.log(chalk.dim(`Refresh token: ${creds.refreshToken ? 'present' : 'absent'}`));\n\n if (activeEnv) {\n console.log(chalk.dim(`Environment: ${activeEnv.name} (${activeEnv.type})`));\n }\n}\n"]}
@@ -1,8 +1,10 @@
1
1
  import open from 'opn';
2
+ import chalk from 'chalk';
2
3
  import clack from '../utils/clack.js';
3
4
  import { saveCredentials, getCredentials, getAccessToken, isTokenExpired, updateTokens } from '../lib/credentials.js';
4
5
  import { getCliAuthClientId, getAuthkitDomain } from '../lib/settings.js';
5
6
  import { refreshAccessToken } from '../lib/token-refresh-client.js';
7
+ import { logInfo } from '../utils/debug.js';
6
8
  /**
7
9
  * Parse JWT payload
8
10
  */
@@ -49,8 +51,8 @@ export async function runLogin() {
49
51
  // Check if already logged in with valid token
50
52
  if (getAccessToken()) {
51
53
  const creds = getCredentials();
52
- clack.log.info(`Already logged in as ${creds?.email ?? 'unknown'}`);
53
- clack.log.info('Run `workos logout` to log out');
54
+ console.log(chalk.green(`Already logged in as ${creds?.email ?? 'unknown'}`));
55
+ console.log(chalk.dim('Run `workos auth logout` to log out'));
54
56
  return;
55
57
  }
56
58
  // Try to refresh if we have expired credentials with a refresh token
@@ -61,9 +63,9 @@ export async function runLogin() {
61
63
  const result = await refreshAccessToken(authkitDomain, clientId);
62
64
  if (result.accessToken && result.expiresAt) {
63
65
  updateTokens(result.accessToken, result.expiresAt, result.refreshToken);
64
- clack.log.info(`Already logged in as ${existingCreds.email ?? 'unknown'}`);
65
- clack.log.info('(Session refreshed)');
66
- clack.log.info('Run `workos logout` to log out');
66
+ logInfo('[login] Session refreshed via refresh token');
67
+ console.log(chalk.green(`Already logged in as ${existingCreds.email ?? 'unknown'}`));
68
+ console.log(chalk.dim('Run `workos auth logout` to log out'));
67
69
  return;
68
70
  }
69
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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","sourcesContent":["import open from 'opn';\nimport clack from '../utils/clack.js';\nimport { saveCredentials, getCredentials, getAccessToken, isTokenExpired, updateTokens } from '../lib/credentials.js';\nimport { getCliAuthClientId, getAuthkitDomain } from '../lib/settings.js';\nimport { refreshAccessToken } from '../lib/token-refresh-client.js';\n\n/**\n * Parse JWT payload\n */\nfunction parseJwt(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Extract expiry time from JWT token\n */\nfunction getJwtExpiry(token: string): number | null {\n const payload = parseJwt(token);\n if (!payload || typeof payload.exp !== 'number') return null;\n return payload.exp * 1000;\n}\n\nconst POLL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Get Connect OAuth endpoints from AuthKit domain\n */\nfunction getConnectEndpoints() {\n const domain = getAuthkitDomain();\n return {\n deviceAuthorization: `${domain}/oauth2/device_authorization`,\n token: `${domain}/oauth2/token`,\n };\n}\n\ninterface DeviceAuthResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n expires_in: number;\n interval: number;\n}\n\ninterface ConnectTokenResponse {\n access_token: string;\n id_token: string;\n token_type: string;\n expires_in: number;\n refresh_token?: string;\n}\n\ninterface AuthErrorResponse {\n error: string;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function runLogin(): Promise<void> {\n const clientId = getCliAuthClientId();\n\n if (!clientId) {\n clack.log.error('CLI auth not configured. Set WORKOS_CLI_CLIENT_ID environment variable.');\n process.exit(1);\n }\n\n // Check if already logged in with valid token\n if (getAccessToken()) {\n const creds = getCredentials();\n clack.log.info(`Already logged in as ${creds?.email ?? 'unknown'}`);\n clack.log.info('Run `workos logout` to log out');\n return;\n }\n\n // Try to refresh if we have expired credentials with a refresh token\n const existingCreds = getCredentials();\n if (existingCreds?.refreshToken && isTokenExpired(existingCreds)) {\n try {\n const authkitDomain = getAuthkitDomain();\n const result = await refreshAccessToken(authkitDomain, clientId);\n if (result.accessToken && result.expiresAt) {\n updateTokens(result.accessToken, result.expiresAt, result.refreshToken);\n clack.log.info(`Already logged in as ${existingCreds.email ?? 'unknown'}`);\n clack.log.info('(Session refreshed)');\n clack.log.info('Run `workos logout` to log out');\n return;\n }\n } catch {\n // Refresh failed, proceed with fresh login\n }\n }\n\n clack.log.step('Starting authentication...');\n\n const endpoints = getConnectEndpoints();\n\n const authResponse = await fetch(endpoints.deviceAuthorization, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n client_id: clientId,\n scope: 'openid email staging-environment:credentials:read offline_access',\n }),\n });\n\n if (!authResponse.ok) {\n clack.log.error(`Failed to start authentication: ${authResponse.status}`);\n process.exit(1);\n }\n\n const deviceAuth = (await authResponse.json()) as DeviceAuthResponse;\n const pollIntervalMs = (deviceAuth.interval || 5) * 1000;\n\n clack.log.info(`\\nOpen this URL in your browser:\\n`);\n console.log(` ${deviceAuth.verification_uri}`);\n console.log(`\\nEnter code: ${deviceAuth.user_code}\\n`);\n\n try {\n open(deviceAuth.verification_uri_complete);\n clack.log.info('Browser opened automatically');\n } catch {\n // User can open manually\n }\n\n const spinner = clack.spinner();\n spinner.start('Waiting for authentication...');\n\n const startTime = Date.now();\n let currentInterval = pollIntervalMs;\n\n while (Date.now() - startTime < POLL_TIMEOUT_MS) {\n await sleep(currentInterval);\n\n try {\n const tokenResponse = await fetch(endpoints.token, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceAuth.device_code,\n client_id: clientId,\n }),\n });\n\n const data = await tokenResponse.json();\n\n if (tokenResponse.ok) {\n const result = data as ConnectTokenResponse;\n\n // Parse user info from id_token JWT\n const idTokenPayload = parseJwt(result.id_token);\n const userId = (idTokenPayload?.sub as string) || 'unknown';\n const email = (idTokenPayload?.email as string) || undefined;\n\n // Extract actual expiry from access token JWT, fallback to response or 15 min\n const jwtExpiry = getJwtExpiry(result.access_token);\n const expiresAt =\n jwtExpiry ?? (result.expires_in ? Date.now() + result.expires_in * 1000 : Date.now() + 15 * 60 * 1000);\n\n const expiresInSec = Math.round((expiresAt - Date.now()) / 1000);\n\n saveCredentials({\n accessToken: result.access_token,\n expiresAt,\n userId,\n email,\n refreshToken: result.refresh_token,\n });\n\n spinner.stop('Authentication successful!');\n clack.log.success(`Logged in as ${email || userId}`);\n clack.log.info(`Token expires in ${expiresInSec} seconds`);\n return;\n }\n\n const errorData = data as AuthErrorResponse;\n if (errorData.error === 'authorization_pending') continue;\n if (errorData.error === 'slow_down') {\n currentInterval += 5000;\n continue;\n }\n\n spinner.stop('Authentication failed');\n clack.log.error(`Authentication error: ${errorData.error}`);\n process.exit(1);\n } catch {\n continue;\n }\n }\n\n spinner.stop('Authentication timed out');\n clack.log.error('Authentication timed out. Please try again.');\n process.exit(1);\n}\n"]}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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","sourcesContent":["import open from 'opn';\nimport chalk from 'chalk';\nimport clack from '../utils/clack.js';\nimport { saveCredentials, getCredentials, getAccessToken, isTokenExpired, updateTokens } from '../lib/credentials.js';\nimport { getCliAuthClientId, getAuthkitDomain } from '../lib/settings.js';\nimport { refreshAccessToken } from '../lib/token-refresh-client.js';\nimport { logInfo } from '../utils/debug.js';\n\n/**\n * Parse JWT payload\n */\nfunction parseJwt(token: string): Record<string, unknown> | null {\n try {\n const parts = token.split('.');\n if (parts.length !== 3) return null;\n return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Extract expiry time from JWT token\n */\nfunction getJwtExpiry(token: string): number | null {\n const payload = parseJwt(token);\n if (!payload || typeof payload.exp !== 'number') return null;\n return payload.exp * 1000;\n}\n\nconst POLL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Get Connect OAuth endpoints from AuthKit domain\n */\nfunction getConnectEndpoints() {\n const domain = getAuthkitDomain();\n return {\n deviceAuthorization: `${domain}/oauth2/device_authorization`,\n token: `${domain}/oauth2/token`,\n };\n}\n\ninterface DeviceAuthResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n expires_in: number;\n interval: number;\n}\n\ninterface ConnectTokenResponse {\n access_token: string;\n id_token: string;\n token_type: string;\n expires_in: number;\n refresh_token?: string;\n}\n\ninterface AuthErrorResponse {\n error: string;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport async function runLogin(): Promise<void> {\n const clientId = getCliAuthClientId();\n\n if (!clientId) {\n clack.log.error('CLI auth not configured. Set WORKOS_CLI_CLIENT_ID environment variable.');\n process.exit(1);\n }\n\n // Check if already logged in with valid token\n if (getAccessToken()) {\n const creds = getCredentials();\n console.log(chalk.green(`Already logged in as ${creds?.email ?? 'unknown'}`));\n console.log(chalk.dim('Run `workos auth logout` to log out'));\n return;\n }\n\n // Try to refresh if we have expired credentials with a refresh token\n const existingCreds = getCredentials();\n if (existingCreds?.refreshToken && isTokenExpired(existingCreds)) {\n try {\n const authkitDomain = getAuthkitDomain();\n const result = await refreshAccessToken(authkitDomain, clientId);\n if (result.accessToken && result.expiresAt) {\n updateTokens(result.accessToken, result.expiresAt, result.refreshToken);\n logInfo('[login] Session refreshed via refresh token');\n console.log(chalk.green(`Already logged in as ${existingCreds.email ?? 'unknown'}`));\n console.log(chalk.dim('Run `workos auth logout` to log out'));\n return;\n }\n } catch {\n // Refresh failed, proceed with fresh login\n }\n }\n\n clack.log.step('Starting authentication...');\n\n const endpoints = getConnectEndpoints();\n\n const authResponse = await fetch(endpoints.deviceAuthorization, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n client_id: clientId,\n scope: 'openid email staging-environment:credentials:read offline_access',\n }),\n });\n\n if (!authResponse.ok) {\n clack.log.error(`Failed to start authentication: ${authResponse.status}`);\n process.exit(1);\n }\n\n const deviceAuth = (await authResponse.json()) as DeviceAuthResponse;\n const pollIntervalMs = (deviceAuth.interval || 5) * 1000;\n\n clack.log.info(`\\nOpen this URL in your browser:\\n`);\n console.log(` ${deviceAuth.verification_uri}`);\n console.log(`\\nEnter code: ${deviceAuth.user_code}\\n`);\n\n try {\n open(deviceAuth.verification_uri_complete);\n clack.log.info('Browser opened automatically');\n } catch {\n // User can open manually\n }\n\n const spinner = clack.spinner();\n spinner.start('Waiting for authentication...');\n\n const startTime = Date.now();\n let currentInterval = pollIntervalMs;\n\n while (Date.now() - startTime < POLL_TIMEOUT_MS) {\n await sleep(currentInterval);\n\n try {\n const tokenResponse = await fetch(endpoints.token, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceAuth.device_code,\n client_id: clientId,\n }),\n });\n\n const data = await tokenResponse.json();\n\n if (tokenResponse.ok) {\n const result = data as ConnectTokenResponse;\n\n // Parse user info from id_token JWT\n const idTokenPayload = parseJwt(result.id_token);\n const userId = (idTokenPayload?.sub as string) || 'unknown';\n const email = (idTokenPayload?.email as string) || undefined;\n\n // Extract actual expiry from access token JWT, fallback to response or 15 min\n const jwtExpiry = getJwtExpiry(result.access_token);\n const expiresAt =\n jwtExpiry ?? (result.expires_in ? Date.now() + result.expires_in * 1000 : Date.now() + 15 * 60 * 1000);\n\n const expiresInSec = Math.round((expiresAt - Date.now()) / 1000);\n\n saveCredentials({\n accessToken: result.access_token,\n expiresAt,\n userId,\n email,\n refreshToken: result.refresh_token,\n });\n\n spinner.stop('Authentication successful!');\n clack.log.success(`Logged in as ${email || userId}`);\n clack.log.info(`Token expires in ${expiresInSec} seconds`);\n return;\n }\n\n const errorData = data as AuthErrorResponse;\n if (errorData.error === 'authorization_pending') continue;\n if (errorData.error === 'slow_down') {\n currentInterval += 5000;\n continue;\n }\n\n spinner.stop('Authentication failed');\n clack.log.error(`Authentication error: ${errorData.error}`);\n process.exit(1);\n } catch {\n continue;\n }\n }\n\n spinner.stop('Authentication timed out');\n clack.log.error('Authentication timed out. Please try again.');\n process.exit(1);\n}\n"]}
@@ -55,10 +55,10 @@ async function callModel(prompt, model) {
55
55
  throw new Error('Not authenticated');
56
56
  if (isTokenExpired(creds)) {
57
57
  if (!creds.refreshToken)
58
- throw new Error('Session expired — run `workos login` to re-authenticate');
58
+ throw new Error('Session expired — run `workos auth login` to re-authenticate');
59
59
  const result = await refreshAccessToken(getAuthkitDomain(), getCliAuthClientId());
60
60
  if (!result.success || !result.accessToken || !result.expiresAt) {
61
- throw new Error('Session expired — run `workos login` to re-authenticate');
61
+ throw new Error('Session expired — run `workos auth login` to re-authenticate');
62
62
  }
63
63
  updateTokens(result.accessToken, result.expiresAt, result.refreshToken);
64
64
  creds = getCredentials();
@@ -99,7 +99,7 @@ export async function checkAiAnalysis(context, options) {
99
99
  process.stderr.write(` ${line}\n`);
100
100
  }
101
101
  process.stderr.write('\n');
102
- return skippedResult('Not authenticated — run `workos login` for AI-powered analysis');
102
+ return skippedResult('Not authenticated — run `workos auth login` for AI-powered analysis');
103
103
  }
104
104
  const startTime = Date.now();
105
105
  const spinner = startSpinner('Analyzing project with AI...');
@@ -1 +1 @@
1
- {"version":3,"file":"ai-analysis.js","sourceRoot":"","sources":["../../../src/doctor/checks/ai-analysis.ts"],"names":[],"mappings":"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","sourcesContent":["import Anthropic from '@anthropic-ai/sdk';\nimport { getLlmGatewayUrl, getAuthkitDomain, getCliAuthClientId, getConfig } from '../../lib/settings.js';\nimport { getCredentials, isTokenExpired, updateTokens, diagnoseCredentials } from '../../lib/credentials.js';\nimport { refreshAccessToken } from '../../lib/token-refresh-client.js';\nimport { buildDoctorPrompt, type AnalysisContext } from '../agent-prompt.js';\nimport type { AiAnalysis, AiFinding } from '../types.js';\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nfunction startSpinner(message: string): { stop: () => void } {\n let i = 0;\n const interval = setInterval(() => {\n process.stderr.write(`\\r ${SPINNER_FRAMES[i++ % SPINNER_FRAMES.length]} ${message}`);\n }, 80);\n return {\n stop: () => {\n clearInterval(interval);\n process.stderr.write(`\\r${' '.repeat(message.length + 6)}\\r`);\n },\n };\n}\n\nexport function parseAiResponse(text: string): { findings: AiFinding[]; summary: string } {\n const codeBlockMatch = text.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/);\n const jsonStr = codeBlockMatch ? codeBlockMatch[1] : text;\n\n try {\n const parsed = JSON.parse(jsonStr.trim());\n const findings = Array.isArray(parsed.findings)\n ? parsed.findings.map((f: Record<string, unknown>) => ({\n severity: (['error', 'warning', 'info'].includes(f.severity as string) ? f.severity : 'info') as\n | 'error'\n | 'warning'\n | 'info',\n title: String(f.title ?? ''),\n detail: String(f.detail ?? ''),\n remediation: String(f.remediation ?? ''),\n filePath: f.filePath ? String(f.filePath) : undefined,\n }))\n : [];\n return { findings, summary: String(parsed.summary ?? '') };\n } catch {\n const jsonMatch = text.match(/\\{[\\s\\S]*\"findings\"[\\s\\S]*\\}/);\n if (jsonMatch) {\n try {\n const parsed = JSON.parse(jsonMatch[0]);\n return {\n findings: Array.isArray(parsed.findings) ? parsed.findings : [],\n summary: String(parsed.summary ?? ''),\n };\n } catch {\n // give up\n }\n }\n return { findings: [], summary: text.slice(0, 500) };\n }\n}\n\nasync function callModel(prompt: string, model: string): Promise<string> {\n let creds = getCredentials();\n if (!creds) throw new Error('Not authenticated');\n\n if (isTokenExpired(creds)) {\n if (!creds.refreshToken) throw new Error('Session expired — run `workos login` to re-authenticate');\n const result = await refreshAccessToken(getAuthkitDomain(), getCliAuthClientId());\n if (!result.success || !result.accessToken || !result.expiresAt) {\n throw new Error('Session expired — run `workos login` to re-authenticate');\n }\n updateTokens(result.accessToken, result.expiresAt, result.refreshToken);\n creds = getCredentials()!;\n }\n\n const client = new Anthropic({\n baseURL: getLlmGatewayUrl(),\n apiKey: 'gateway',\n defaultHeaders: { Authorization: `Bearer ${creds.accessToken}` },\n });\n\n const response = await client.messages.create({\n model,\n max_tokens: 2048,\n messages: [{ role: 'user', content: prompt }],\n });\n\n const text = response.content[0];\n if (text.type === 'text') return text.text;\n throw new Error('Unexpected response format');\n}\n\nexport async function checkAiAnalysis(context: AnalysisContext, options: { skipAi?: boolean }): Promise<AiAnalysis> {\n const model = getConfig().doctorModel;\n\n const skippedResult = (reason: string): AiAnalysis => ({\n findings: [],\n summary: '',\n model,\n durationMs: 0,\n skipped: true,\n skipReason: reason,\n });\n\n if (options.skipAi) {\n return skippedResult('Skipped (--skip-ai flag)');\n }\n\n const creds = getCredentials();\n if (!creds) {\n const diag = diagnoseCredentials();\n process.stderr.write('\\n [credential-debug]\\n');\n for (const line of diag) {\n process.stderr.write(` ${line}\\n`);\n }\n process.stderr.write('\\n');\n return skippedResult('Not authenticated — run `workos login` for AI-powered analysis');\n }\n\n const startTime = Date.now();\n const spinner = startSpinner('Analyzing project with AI...');\n\n try {\n const prompt = await buildDoctorPrompt(context);\n const responseText = await callModel(prompt, model);\n const durationMs = Date.now() - startTime;\n const { findings, summary } = parseAiResponse(responseText);\n return { findings, summary, model, durationMs };\n } catch (error) {\n const durationMs = Date.now() - startTime;\n const errMsg = error instanceof Error ? error.message : 'Unknown error';\n return { ...skippedResult(`Analysis failed: ${errMsg}`), durationMs };\n } finally {\n spinner.stop();\n }\n}\n"]}
1
+ {"version":3,"file":"ai-analysis.js","sourceRoot":"","sources":["../../../src/doctor/checks/ai-analysis.ts"],"names":[],"mappings":"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","sourcesContent":["import Anthropic from '@anthropic-ai/sdk';\nimport { getLlmGatewayUrl, getAuthkitDomain, getCliAuthClientId, getConfig } from '../../lib/settings.js';\nimport { getCredentials, isTokenExpired, updateTokens, diagnoseCredentials } from '../../lib/credentials.js';\nimport { refreshAccessToken } from '../../lib/token-refresh-client.js';\nimport { buildDoctorPrompt, type AnalysisContext } from '../agent-prompt.js';\nimport type { AiAnalysis, AiFinding } from '../types.js';\n\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nfunction startSpinner(message: string): { stop: () => void } {\n let i = 0;\n const interval = setInterval(() => {\n process.stderr.write(`\\r ${SPINNER_FRAMES[i++ % SPINNER_FRAMES.length]} ${message}`);\n }, 80);\n return {\n stop: () => {\n clearInterval(interval);\n process.stderr.write(`\\r${' '.repeat(message.length + 6)}\\r`);\n },\n };\n}\n\nexport function parseAiResponse(text: string): { findings: AiFinding[]; summary: string } {\n const codeBlockMatch = text.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/);\n const jsonStr = codeBlockMatch ? codeBlockMatch[1] : text;\n\n try {\n const parsed = JSON.parse(jsonStr.trim());\n const findings = Array.isArray(parsed.findings)\n ? parsed.findings.map((f: Record<string, unknown>) => ({\n severity: (['error', 'warning', 'info'].includes(f.severity as string) ? f.severity : 'info') as\n | 'error'\n | 'warning'\n | 'info',\n title: String(f.title ?? ''),\n detail: String(f.detail ?? ''),\n remediation: String(f.remediation ?? ''),\n filePath: f.filePath ? String(f.filePath) : undefined,\n }))\n : [];\n return { findings, summary: String(parsed.summary ?? '') };\n } catch {\n const jsonMatch = text.match(/\\{[\\s\\S]*\"findings\"[\\s\\S]*\\}/);\n if (jsonMatch) {\n try {\n const parsed = JSON.parse(jsonMatch[0]);\n return {\n findings: Array.isArray(parsed.findings) ? parsed.findings : [],\n summary: String(parsed.summary ?? ''),\n };\n } catch {\n // give up\n }\n }\n return { findings: [], summary: text.slice(0, 500) };\n }\n}\n\nasync function callModel(prompt: string, model: string): Promise<string> {\n let creds = getCredentials();\n if (!creds) throw new Error('Not authenticated');\n\n if (isTokenExpired(creds)) {\n if (!creds.refreshToken) throw new Error('Session expired — run `workos auth login` to re-authenticate');\n const result = await refreshAccessToken(getAuthkitDomain(), getCliAuthClientId());\n if (!result.success || !result.accessToken || !result.expiresAt) {\n throw new Error('Session expired — run `workos auth login` to re-authenticate');\n }\n updateTokens(result.accessToken, result.expiresAt, result.refreshToken);\n creds = getCredentials()!;\n }\n\n const client = new Anthropic({\n baseURL: getLlmGatewayUrl(),\n apiKey: 'gateway',\n defaultHeaders: { Authorization: `Bearer ${creds.accessToken}` },\n });\n\n const response = await client.messages.create({\n model,\n max_tokens: 2048,\n messages: [{ role: 'user', content: prompt }],\n });\n\n const text = response.content[0];\n if (text.type === 'text') return text.text;\n throw new Error('Unexpected response format');\n}\n\nexport async function checkAiAnalysis(context: AnalysisContext, options: { skipAi?: boolean }): Promise<AiAnalysis> {\n const model = getConfig().doctorModel;\n\n const skippedResult = (reason: string): AiAnalysis => ({\n findings: [],\n summary: '',\n model,\n durationMs: 0,\n skipped: true,\n skipReason: reason,\n });\n\n if (options.skipAi) {\n return skippedResult('Skipped (--skip-ai flag)');\n }\n\n const creds = getCredentials();\n if (!creds) {\n const diag = diagnoseCredentials();\n process.stderr.write('\\n [credential-debug]\\n');\n for (const line of diag) {\n process.stderr.write(` ${line}\\n`);\n }\n process.stderr.write('\\n');\n return skippedResult('Not authenticated — run `workos auth login` for AI-powered analysis');\n }\n\n const startTime = Date.now();\n const spinner = startSpinner('Analyzing project with AI...');\n\n try {\n const prompt = await buildDoctorPrompt(context);\n const responseText = await callModel(prompt, model);\n const durationMs = Date.now() - startTime;\n const { findings, summary } = parseAiResponse(responseText);\n return { findings, summary, model, durationMs };\n } catch (error) {\n const durationMs = Date.now() - startTime;\n const errMsg = error instanceof Error ? error.message : 'Unknown error';\n return { ...skippedResult(`Analysis failed: ${errMsg}`), durationMs };\n } finally {\n spinner.stop();\n }\n}\n"]}
@@ -256,13 +256,16 @@ function checkCallbackRouteMissing(ctx) {
256
256
  }
257
257
  }
258
258
  else if (ctx.framework.name === 'TanStack Start') {
259
- // Modern flat: src/routes/api.auth.callback.tsx Legacy nested: app/routes/api/auth/callback.tsx
259
+ // Flat: routes/api.auth.callback.tsx Nested: routes/api/auth/callback.tsx
260
+ // Both conventions work in both src/ and app/ directories
260
261
  const segments = callbackPath.replace(/^\//, '').split('/');
261
262
  const flat = segments.join('.');
262
263
  const nested = segments.join('/');
263
- for (const ext of ['tsx', 'jsx', 'ts', 'js']) {
264
- possiblePaths.push(join(ctx.installDir, 'src', 'routes', `${flat}.${ext}`));
265
- possiblePaths.push(join(ctx.installDir, 'app', 'routes', nested + `.${ext}`));
264
+ for (const prefix of ['src', 'app']) {
265
+ for (const ext of ['tsx', 'jsx', 'ts', 'js']) {
266
+ possiblePaths.push(join(ctx.installDir, prefix, 'routes', `${flat}.${ext}`));
267
+ possiblePaths.push(join(ctx.installDir, prefix, 'routes', nested + `.${ext}`));
268
+ }
266
269
  }
267
270
  }
268
271
  if (possiblePaths.length === 0)
@@ -365,7 +368,9 @@ function checkMissingAuthkitMiddleware(ctx) {
365
368
  severity: 'warning',
366
369
  message: 'start.ts does not reference authkitMiddleware — AuthKit session handling requires it',
367
370
  filePath: relative(ctx.installDir, startFile),
368
- remediation: 'Add authkitMiddleware to your start.ts server middleware configuration.',
371
+ remediation: 'Add authkitMiddleware to requestMiddleware in src/start.ts:\n' +
372
+ ' import { authkitMiddleware } from "@workos/authkit-tanstack-react-start";\n' +
373
+ ' export default createStart({ requestMiddleware: [authkitMiddleware()] });',
369
374
  },
370
375
  ];
371
376
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auth-patterns.js","sourceRoot":"","sources":["../../../src/doctor/checks/auth-patterns.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type {\n AuthPatternFinding,\n AuthPatternInfo,\n FrameworkInfo,\n EnvironmentInfo,\n SdkInfo,\n DoctorOptions,\n} from '../types.js';\n\n// --- Helpers ---\n\n/** Return the first path that exists, or null */\nfunction findFile(paths: string[]): string | null {\n for (const p of paths) {\n if (existsSync(p)) return p;\n }\n return null;\n}\n\n/** Read file content safely, return null on any error */\nfunction readFileSafe(filePath: string): string | null {\n try {\n return readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n}\n\n/** Test if a file exists and its content matches a regex */\nfunction fileContains(filePath: string, pattern: RegExp): boolean {\n const content = readFileSafe(filePath);\n if (!content) return false;\n return pattern.test(content);\n}\n\nconst SKIP_DIRS = new Set(['node_modules', '.next', '.turbo', 'dist', '.git', 'coverage']);\n\n/**\n * Find files matching a name pattern in a directory tree, limited to maxDepth levels.\n * Skips node_modules, .next, dist, etc.\n */\nfunction findFilesShallow(dir: string, namePattern: RegExp, maxDepth = 3): string[] {\n const results: string[] = [];\n if (!existsSync(dir)) return results;\n\n function walk(currentDir: string, depth: number) {\n if (depth > maxDepth) return;\n try {\n const dirents = readdirSync(currentDir, { withFileTypes: true });\n for (const dirent of dirents) {\n const fullPath = join(currentDir, dirent.name);\n if (dirent.isDirectory()) {\n if (!SKIP_DIRS.has(dirent.name)) {\n walk(fullPath, depth + 1);\n }\n } else if (dirent.isFile() && namePattern.test(dirent.name)) {\n results.push(fullPath);\n }\n }\n } catch {\n // Directory unreadable\n }\n }\n\n walk(dir, 0);\n return results;\n}\n\nfunction parseEnvFile(content: string): Record<string, string> {\n const result: Record<string, string> = {};\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const entry = trimmed.startsWith('export ') ? trimmed.slice(7) : trimmed;\n const eqIndex = entry.indexOf('=');\n if (eqIndex === -1) continue;\n const key = entry.slice(0, eqIndex).trim();\n let value = entry.slice(eqIndex + 1).trim();\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1);\n }\n result[key] = value;\n }\n return result;\n}\n\n/** Load all env vars from .env and .env.local files */\nfunction loadProjectEnvRaw(installDir: string): Record<string, string> {\n const env: Record<string, string> = {};\n for (const file of ['.env', '.env.local']) {\n const content = readFileSafe(join(installDir, file));\n if (content) Object.assign(env, parseEnvFile(content));\n }\n return env;\n}\n\n// --- Check Context ---\n\ninterface CheckContext {\n framework: FrameworkInfo;\n environment: EnvironmentInfo;\n sdk: SdkInfo;\n installDir: string;\n}\n\n// --- Individual Checks ---\n\n/** Resolve the app directory root (app/ or src/app/) for Next.js */\nfunction resolveAppDir(installDir: string): string | null {\n const srcApp = join(installDir, 'src', 'app');\n if (existsSync(srcApp)) return srcApp;\n const app = join(installDir, 'app');\n if (existsSync(app)) return app;\n return null;\n}\n\nfunction checkSignoutGetHandler(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'Next.js') return [];\n const appDir = resolveAppDir(ctx.installDir);\n if (!appDir) return [];\n\n const routeFiles = findFilesShallow(appDir, /^route\\.(ts|tsx|js|jsx)$/);\n const signoutRoutes = routeFiles.filter((f) => /[/\\\\](sign-?out|logout)[/\\\\]/.test(f));\n\n const findings: AuthPatternFinding[] = [];\n const GET_EXPORT = /export\\s+(async\\s+)?function\\s+GET|export\\s+const\\s+GET/;\n\n for (const route of signoutRoutes) {\n if (fileContains(route, GET_EXPORT)) {\n findings.push({\n code: 'SIGNOUT_GET_HANDLER',\n severity: 'error',\n message: 'Signout/logout route uses GET handler — vulnerable to CSRF and prefetch-triggered logouts',\n filePath: relative(ctx.installDir, route),\n remediation:\n 'Convert to a POST server action. GET routes with side effects are vulnerable to CSRF and will be triggered by Next.js link prefetching.',\n docsUrl: 'https://workos.com/docs/authkit/sign-out',\n });\n }\n }\n return findings;\n}\n\nfunction checkSignoutLinkPrefetch(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'Next.js') return [];\n const appDir = resolveAppDir(ctx.installDir);\n if (!appDir) return [];\n\n const tsxFiles = findFilesShallow(appDir, /\\.(tsx|jsx)$/);\n // Match <Link>, <NextLink>, or other common aliases\n const LINK_PATTERN = /<(?:Next)?Link\\s[^>]*href\\s*=\\s*[\"'{`/]*(\\/sign-?out|\\/logout)/;\n\n const findings: AuthPatternFinding[] = [];\n for (const file of tsxFiles) {\n if (fileContains(file, LINK_PATTERN)) {\n findings.push({\n code: 'SIGNOUT_LINK_PREFETCH',\n severity: 'warning',\n message:\n 'Link component points to signout/logout — Next.js will prefetch this in production, potentially triggering logouts',\n filePath: relative(ctx.installDir, file),\n remediation:\n 'Use a <form> with a server action or <button> with onClick handler instead of <Link> for signout.',\n });\n }\n }\n return findings;\n}\n\nfunction checkMissingMiddleware(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'Next.js') return [];\n\n const middlewarePaths = [\n 'middleware.ts',\n 'middleware.js',\n 'proxy.ts',\n 'proxy.js',\n 'src/middleware.ts',\n 'src/middleware.js',\n 'src/proxy.ts',\n 'src/proxy.js',\n ].map((p) => join(ctx.installDir, p));\n\n if (findFile(middlewarePaths)) return [];\n\n return [\n {\n code: 'MISSING_MIDDLEWARE',\n severity: 'error',\n message: 'No middleware.ts or proxy.ts found — AuthKit session handling requires middleware',\n remediation: 'Create middleware.ts at the project root with authkitMiddleware() from @workos-inc/authkit-nextjs.',\n docsUrl: 'https://workos.com/docs/authkit/nextjs/middleware',\n },\n ];\n}\n\nfunction checkMiddlewareWrongLocation(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'Next.js') return [];\n\n const wrongPaths = ['app/middleware.ts', 'app/middleware.js', 'src/app/middleware.ts', 'src/app/middleware.js'].map(\n (p) => join(ctx.installDir, p),\n );\n\n const found = findFile(wrongPaths);\n if (!found) return [];\n\n return [\n {\n code: 'MIDDLEWARE_WRONG_LOCATION',\n severity: 'warning',\n message: 'middleware.ts found inside app/ directory — must be at project root or src/',\n filePath: relative(ctx.installDir, found),\n remediation: 'Move middleware.ts to the project root (or src/ if using src/ directory).',\n docsUrl: 'https://workos.com/docs/authkit/nextjs/middleware',\n },\n ];\n}\n\nfunction checkMissingAuthKitProvider(ctx: CheckContext): AuthPatternFinding[] {\n const layoutPaths: string[] = [];\n\n if (ctx.framework.name === 'Next.js') {\n layoutPaths.push(\n join(ctx.installDir, 'app', 'layout.tsx'),\n join(ctx.installDir, 'app', 'layout.jsx'),\n join(ctx.installDir, 'src', 'app', 'layout.tsx'),\n join(ctx.installDir, 'src', 'app', 'layout.jsx'),\n );\n } else if (ctx.framework.name === 'React Router' && ctx.framework.variant === 'declarative') {\n layoutPaths.push(\n join(ctx.installDir, 'src', 'App.tsx'),\n join(ctx.installDir, 'src', 'App.jsx'),\n join(ctx.installDir, 'app', 'root.tsx'),\n join(ctx.installDir, 'app', 'root.jsx'),\n );\n } else {\n return [];\n }\n\n const layoutFile = findFile(layoutPaths);\n if (!layoutFile) return []; // Can't check if layout doesn't exist\n\n if (fileContains(layoutFile, /AuthKitProvider/)) return [];\n\n return [\n {\n code: 'MISSING_AUTHKIT_PROVIDER',\n severity: 'warning',\n message: 'AuthKitProvider not found in root layout — required for AuthKit session management',\n filePath: relative(ctx.installDir, layoutFile),\n remediation: 'Wrap your app with <AuthKitProvider> in the root layout.',\n docsUrl: 'https://workos.com/docs/authkit/nextjs/setup',\n },\n ];\n}\n\n/** Extract callback path from redirect URI env vars or framework default */\nfunction resolveCallbackPath(ctx: CheckContext): string | null {\n // Check env vars for actual redirect URI (including NEXT_PUBLIC_ variant)\n const projectEnv = loadProjectEnvRaw(ctx.installDir);\n const redirectUri =\n projectEnv.WORKOS_REDIRECT_URI ?? projectEnv.NEXT_PUBLIC_WORKOS_REDIRECT_URI ?? ctx.environment.redirectUri;\n\n if (redirectUri) {\n try {\n return new URL(redirectUri).pathname;\n } catch {\n // Invalid URL, fall through to framework default\n }\n }\n\n return ctx.framework.expectedCallbackPath ?? null;\n}\n\nfunction checkCallbackRouteMissing(ctx: CheckContext): AuthPatternFinding[] {\n const callbackPath = resolveCallbackPath(ctx);\n if (!callbackPath) return [];\n\n // Build expected route file paths based on framework\n const possiblePaths: string[] = [];\n\n if (ctx.framework.name === 'Next.js') {\n // app/auth/callback/route.ts (or src/app/)\n const routeDir = callbackPath.replace(/^\\//, ''); // 'auth/callback'\n for (const prefix of ['app', 'src/app']) {\n for (const ext of ['ts', 'tsx', 'js', 'jsx']) {\n possiblePaths.push(join(ctx.installDir, prefix, routeDir, `route.${ext}`));\n }\n }\n } else if (ctx.framework.name === 'React Router') {\n // Flat: app/routes/auth.callback.tsx Nested: app/routes/auth/callback.tsx\n const segments = callbackPath.replace(/^\\//, '').split('/');\n const flat = segments.join('.');\n const nested = segments.join('/');\n for (const ext of ['tsx', 'jsx', 'ts', 'js']) {\n possiblePaths.push(join(ctx.installDir, 'app', 'routes', `${flat}.${ext}`));\n possiblePaths.push(join(ctx.installDir, 'app', 'routes', nested + `.${ext}`));\n }\n } else if (ctx.framework.name === 'TanStack Start') {\n // Modern flat: src/routes/api.auth.callback.tsx Legacy nested: app/routes/api/auth/callback.tsx\n const segments = callbackPath.replace(/^\\//, '').split('/');\n const flat = segments.join('.');\n const nested = segments.join('/');\n for (const ext of ['tsx', 'jsx', 'ts', 'js']) {\n possiblePaths.push(join(ctx.installDir, 'src', 'routes', `${flat}.${ext}`));\n possiblePaths.push(join(ctx.installDir, 'app', 'routes', nested + `.${ext}`));\n }\n }\n\n if (possiblePaths.length === 0) return [];\n if (findFile(possiblePaths)) return [];\n\n return [\n {\n code: 'CALLBACK_ROUTE_MISSING',\n severity: 'error',\n message: `No callback route found at expected path ${callbackPath}`,\n remediation: `Create the callback route handler at the path matching your WORKOS_REDIRECT_URI.`,\n docsUrl: 'https://workos.com/docs/authkit/redirect-uri',\n },\n ];\n}\n\nconst CLIENT_ENV_PREFIXES = ['NEXT_PUBLIC_', 'VITE_', 'REACT_APP_', 'EXPO_PUBLIC_'];\n\nfunction checkApiKeyLeakedToClient(ctx: CheckContext): AuthPatternFinding[] {\n const projectEnv = loadProjectEnvRaw(ctx.installDir);\n const findings: AuthPatternFinding[] = [];\n\n for (const [key, value] of Object.entries(projectEnv)) {\n const hasClientPrefix = CLIENT_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));\n if (!hasClientPrefix) continue;\n\n const isApiKey = key.includes('WORKOS_API_KEY');\n const isSecretValue = value?.startsWith('sk_test_') || value?.startsWith('sk_live_');\n\n if (isApiKey || isSecretValue) {\n findings.push({\n code: 'API_KEY_LEAKED_TO_CLIENT',\n severity: 'error',\n message: `Secret API key exposed via client-accessible env var ${key}`,\n remediation: `Remove the client prefix. WORKOS_API_KEY must be server-only (no NEXT_PUBLIC_, VITE_, REACT_APP_, or EXPO_PUBLIC_ prefix).`,\n });\n }\n }\n return findings;\n}\n\nfunction checkWrongCallbackLoader(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'React Router') return [];\n const callbackPath = ctx.framework.expectedCallbackPath;\n if (!callbackPath) return [];\n\n const segments = callbackPath.replace(/^\\//, '').split('/');\n const flat = segments.join('.');\n const nested = segments.join('/');\n\n const possiblePaths: string[] = [];\n for (const ext of ['tsx', 'jsx', 'ts', 'js']) {\n possiblePaths.push(join(ctx.installDir, 'app', 'routes', `${flat}.${ext}`));\n possiblePaths.push(join(ctx.installDir, 'app', 'routes', nested + `.${ext}`));\n }\n\n const callbackFile = findFile(possiblePaths);\n if (!callbackFile) return []; // No callback route to check\n\n // authkitLoader is for regular routes; authLoader is for the callback\n if (fileContains(callbackFile, /authkitLoader/) && !fileContains(callbackFile, /authLoader/)) {\n return [\n {\n code: 'WRONG_CALLBACK_LOADER',\n severity: 'warning',\n message: 'Callback route uses authkitLoader instead of authLoader',\n filePath: relative(ctx.installDir, callbackFile),\n remediation:\n 'Use authLoader (not authkitLoader) for the callback route. authkitLoader is for regular routes that need auth context.',\n },\n ];\n }\n return [];\n}\n\nfunction checkMissingRootAuthLoader(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'React Router') return [];\n\n const rootPaths = ['app/root.tsx', 'app/root.jsx', 'app/routes/_index.tsx', 'app/routes/_index.jsx'].map((p) =>\n join(ctx.installDir, p),\n );\n\n const rootFile = findFile(rootPaths);\n if (!rootFile) return [];\n\n if (fileContains(rootFile, /authkitLoader/)) return [];\n\n return [\n {\n code: 'MISSING_ROOT_AUTH_LOADER',\n severity: 'warning',\n message: 'Root route does not use authkitLoader — child routes will not have auth context',\n filePath: relative(ctx.installDir, rootFile),\n remediation: 'Add authkitLoader to your root route so child routes can access auth state.',\n },\n ];\n}\n\nfunction checkMissingAuthkitMiddleware(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'TanStack Start') return [];\n\n const startPaths = ['src/start.ts', 'src/start.tsx', 'app/start.ts', 'app/start.tsx'].map((p) =>\n join(ctx.installDir, p),\n );\n\n const startFile = findFile(startPaths);\n if (!startFile) return []; // Can't check if start file doesn't exist\n\n if (fileContains(startFile, /authkitMiddleware/)) return [];\n\n return [\n {\n code: 'MISSING_AUTHKIT_MIDDLEWARE',\n severity: 'warning',\n message: 'start.ts does not reference authkitMiddleware — AuthKit session handling requires it',\n filePath: relative(ctx.installDir, startFile),\n remediation: 'Add authkitMiddleware to your start.ts server middleware configuration.',\n },\n ];\n}\n\nfunction checkCookiePasswordTooShort(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'React Router' && ctx.framework.name !== 'TanStack Start') return [];\n\n const projectEnv = loadProjectEnvRaw(ctx.installDir);\n const password = projectEnv.WORKOS_COOKIE_PASSWORD;\n\n // Only warn if password is set but too short; missing password is a separate concern\n if (!password || password.length >= 32) return [];\n\n return [\n {\n code: 'COOKIE_PASSWORD_TOO_SHORT',\n severity: 'warning',\n message: `WORKOS_COOKIE_PASSWORD is ${password.length} characters — minimum 32 required for secure encryption`,\n remediation: 'Set WORKOS_COOKIE_PASSWORD to a random string of at least 32 characters.',\n },\n ];\n}\n\nconst REACT_SPA_SDKS = new Set(['@workos-inc/authkit-react']);\n\nfunction checkMissingApiHostname(ctx: CheckContext): AuthPatternFinding[] {\n if (!ctx.sdk.name || !REACT_SPA_SDKS.has(ctx.sdk.name)) return [];\n\n const sourceFiles = findFilesShallow(ctx.installDir, /\\.(tsx|jsx|ts|js)$/, 4);\n const providerFiles = sourceFiles.filter((f) => fileContains(f, /AuthKitProvider/));\n if (providerFiles.length === 0) return [];\n\n const hasApiHostname = providerFiles.some((f) => fileContains(f, /apiHostname/));\n if (hasApiHostname) return [];\n\n return [\n {\n code: 'MISSING_API_HOSTNAME',\n severity: 'warning',\n message: 'AuthKitProvider does not specify apiHostname — authorize requests will route through api.workos.com',\n filePath: relative(ctx.installDir, providerFiles[0]),\n remediation:\n 'Set the apiHostname prop on AuthKitProvider to your custom Authentication API domain (e.g. auth.example.com) to avoid routing through api.workos.com.',\n docsUrl: 'https://workos.com/docs/custom-domains',\n },\n ];\n}\n\n// --- Cross-language checks (run for ALL projects, not just JS/AuthKit) ---\n\nconst SOURCE_EXTENSIONS = /\\.(ts|tsx|js|jsx|py|rb|go|java|kt|php|cs|swift|dart)$/;\n\nfunction checkApiKeyInSource(ctx: CheckContext): AuthPatternFinding[] {\n const API_KEY_PATTERN = /sk_(test|live)_[A-Za-z0-9]{10,}/;\n const sourceFiles = findFilesShallow(ctx.installDir, SOURCE_EXTENSIONS, 4);\n const findings: AuthPatternFinding[] = [];\n\n for (const file of sourceFiles) {\n const content = readFileSafe(file);\n if (!content) continue;\n if (API_KEY_PATTERN.test(content)) {\n findings.push({\n code: 'API_KEY_IN_SOURCE',\n severity: 'error',\n message: `WorkOS API key hardcoded in source file`,\n filePath: relative(ctx.installDir, file),\n remediation:\n 'Move the API key to an environment variable (WORKOS_API_KEY) and load it from .env or your secret manager.',\n });\n }\n }\n return findings;\n}\n\nfunction checkEnvFileNotGitignored(ctx: CheckContext): AuthPatternFinding[] {\n const envFiles = ['.env', '.env.local'].filter((f) => existsSync(join(ctx.installDir, f)));\n if (envFiles.length === 0) return [];\n\n const gitignorePath = join(ctx.installDir, '.gitignore');\n const gitignore = readFileSafe(gitignorePath);\n\n const findings: AuthPatternFinding[] = [];\n for (const envFile of envFiles) {\n const isIgnored =\n gitignore !== null &&\n gitignore.split('\\n').some((line) => {\n const trimmed = line.trim();\n if (trimmed.startsWith('#') || trimmed === '') return false;\n return trimmed === envFile || trimmed === '.env*' || trimmed === '.env.*' || trimmed === '.env';\n });\n\n if (!isIgnored) {\n findings.push({\n code: 'ENV_FILE_NOT_GITIGNORED',\n severity: 'warning',\n message: `${envFile} is not in .gitignore — secrets may be committed to version control`,\n filePath: envFile,\n remediation: `Add ${envFile} to your .gitignore file.`,\n });\n }\n }\n return findings;\n}\n\nfunction checkMixedEnvironmentCredentials(ctx: CheckContext): AuthPatternFinding[] {\n const projectEnv = loadProjectEnvRaw(ctx.installDir);\n const apiKey = projectEnv.WORKOS_API_KEY;\n const redirectUri = projectEnv.WORKOS_REDIRECT_URI ?? projectEnv.NEXT_PUBLIC_WORKOS_REDIRECT_URI;\n\n if (!apiKey || !redirectUri) return [];\n\n const isTestKey = apiKey.startsWith('sk_test_');\n const isLiveKey = apiKey.startsWith('sk_live_');\n\n let isProductionUri = false;\n try {\n const url = new URL(redirectUri);\n isProductionUri = url.hostname !== 'localhost' && !url.hostname.startsWith('127.0.0.');\n } catch {\n return [];\n }\n\n if (isTestKey && isProductionUri) {\n return [\n {\n code: 'MIXED_ENVIRONMENT',\n severity: 'warning',\n message: 'Staging API key (sk_test_) used with a production redirect URI',\n remediation:\n 'Use sk_live_ API key for production redirect URIs, or change the redirect URI to localhost for staging.',\n },\n ];\n }\n\n if (isLiveKey && !isProductionUri) {\n return [\n {\n code: 'MIXED_ENVIRONMENT',\n severity: 'warning',\n message: 'Production API key (sk_live_) used with a localhost redirect URI',\n remediation:\n 'Use sk_test_ API key for localhost development, or update the redirect URI to your production domain.',\n },\n ];\n }\n\n return [];\n}\n\n// --- Main Entry Point ---\n\ntype CheckFn = (ctx: CheckContext) => AuthPatternFinding[];\n\nconst CROSS_FRAMEWORK_CHECKS: CheckFn[] = [\n checkApiKeyLeakedToClient,\n checkApiKeyInSource,\n checkEnvFileNotGitignored,\n checkMixedEnvironmentCredentials,\n checkMissingApiHostname,\n];\n\nconst NEXTJS_CHECKS: CheckFn[] = [\n checkSignoutGetHandler,\n checkSignoutLinkPrefetch,\n checkMissingMiddleware,\n checkMiddlewareWrongLocation,\n checkMissingAuthKitProvider,\n checkCallbackRouteMissing,\n];\n\nconst REACT_ROUTER_CHECKS: CheckFn[] = [\n checkWrongCallbackLoader,\n checkMissingRootAuthLoader,\n checkMissingAuthKitProvider,\n checkCallbackRouteMissing,\n checkCookiePasswordTooShort,\n];\n\nconst TANSTACK_CHECKS: CheckFn[] = [\n checkMissingAuthkitMiddleware,\n checkCallbackRouteMissing,\n checkCookiePasswordTooShort,\n];\n\nexport async function checkAuthPatterns(\n options: DoctorOptions,\n framework: FrameworkInfo,\n environment: EnvironmentInfo,\n sdk: SdkInfo,\n): Promise<AuthPatternInfo> {\n const ctx: CheckContext = {\n framework,\n environment,\n sdk,\n installDir: options.installDir,\n };\n\n const checks: CheckFn[] = [...CROSS_FRAMEWORK_CHECKS];\n\n switch (framework.name) {\n case 'Next.js':\n checks.push(...NEXTJS_CHECKS);\n break;\n case 'React Router':\n checks.push(...REACT_ROUTER_CHECKS);\n break;\n case 'TanStack Start':\n checks.push(...TANSTACK_CHECKS);\n break;\n }\n\n const findings: AuthPatternFinding[] = [];\n for (const check of checks) {\n findings.push(...check(ctx));\n }\n\n return {\n checksRun: checks.length,\n findings,\n };\n}\n"]}
1
+ {"version":3,"file":"auth-patterns.js","sourceRoot":"","sources":["../../../src/doctor/checks/auth-patterns.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport type {\n AuthPatternFinding,\n AuthPatternInfo,\n FrameworkInfo,\n EnvironmentInfo,\n SdkInfo,\n DoctorOptions,\n} from '../types.js';\n\n// --- Helpers ---\n\n/** Return the first path that exists, or null */\nfunction findFile(paths: string[]): string | null {\n for (const p of paths) {\n if (existsSync(p)) return p;\n }\n return null;\n}\n\n/** Read file content safely, return null on any error */\nfunction readFileSafe(filePath: string): string | null {\n try {\n return readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n}\n\n/** Test if a file exists and its content matches a regex */\nfunction fileContains(filePath: string, pattern: RegExp): boolean {\n const content = readFileSafe(filePath);\n if (!content) return false;\n return pattern.test(content);\n}\n\nconst SKIP_DIRS = new Set(['node_modules', '.next', '.turbo', 'dist', '.git', 'coverage']);\n\n/**\n * Find files matching a name pattern in a directory tree, limited to maxDepth levels.\n * Skips node_modules, .next, dist, etc.\n */\nfunction findFilesShallow(dir: string, namePattern: RegExp, maxDepth = 3): string[] {\n const results: string[] = [];\n if (!existsSync(dir)) return results;\n\n function walk(currentDir: string, depth: number) {\n if (depth > maxDepth) return;\n try {\n const dirents = readdirSync(currentDir, { withFileTypes: true });\n for (const dirent of dirents) {\n const fullPath = join(currentDir, dirent.name);\n if (dirent.isDirectory()) {\n if (!SKIP_DIRS.has(dirent.name)) {\n walk(fullPath, depth + 1);\n }\n } else if (dirent.isFile() && namePattern.test(dirent.name)) {\n results.push(fullPath);\n }\n }\n } catch {\n // Directory unreadable\n }\n }\n\n walk(dir, 0);\n return results;\n}\n\nfunction parseEnvFile(content: string): Record<string, string> {\n const result: Record<string, string> = {};\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const entry = trimmed.startsWith('export ') ? trimmed.slice(7) : trimmed;\n const eqIndex = entry.indexOf('=');\n if (eqIndex === -1) continue;\n const key = entry.slice(0, eqIndex).trim();\n let value = entry.slice(eqIndex + 1).trim();\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1);\n }\n result[key] = value;\n }\n return result;\n}\n\n/** Load all env vars from .env and .env.local files */\nfunction loadProjectEnvRaw(installDir: string): Record<string, string> {\n const env: Record<string, string> = {};\n for (const file of ['.env', '.env.local']) {\n const content = readFileSafe(join(installDir, file));\n if (content) Object.assign(env, parseEnvFile(content));\n }\n return env;\n}\n\n// --- Check Context ---\n\ninterface CheckContext {\n framework: FrameworkInfo;\n environment: EnvironmentInfo;\n sdk: SdkInfo;\n installDir: string;\n}\n\n// --- Individual Checks ---\n\n/** Resolve the app directory root (app/ or src/app/) for Next.js */\nfunction resolveAppDir(installDir: string): string | null {\n const srcApp = join(installDir, 'src', 'app');\n if (existsSync(srcApp)) return srcApp;\n const app = join(installDir, 'app');\n if (existsSync(app)) return app;\n return null;\n}\n\nfunction checkSignoutGetHandler(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'Next.js') return [];\n const appDir = resolveAppDir(ctx.installDir);\n if (!appDir) return [];\n\n const routeFiles = findFilesShallow(appDir, /^route\\.(ts|tsx|js|jsx)$/);\n const signoutRoutes = routeFiles.filter((f) => /[/\\\\](sign-?out|logout)[/\\\\]/.test(f));\n\n const findings: AuthPatternFinding[] = [];\n const GET_EXPORT = /export\\s+(async\\s+)?function\\s+GET|export\\s+const\\s+GET/;\n\n for (const route of signoutRoutes) {\n if (fileContains(route, GET_EXPORT)) {\n findings.push({\n code: 'SIGNOUT_GET_HANDLER',\n severity: 'error',\n message: 'Signout/logout route uses GET handler — vulnerable to CSRF and prefetch-triggered logouts',\n filePath: relative(ctx.installDir, route),\n remediation:\n 'Convert to a POST server action. GET routes with side effects are vulnerable to CSRF and will be triggered by Next.js link prefetching.',\n docsUrl: 'https://workos.com/docs/authkit/sign-out',\n });\n }\n }\n return findings;\n}\n\nfunction checkSignoutLinkPrefetch(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'Next.js') return [];\n const appDir = resolveAppDir(ctx.installDir);\n if (!appDir) return [];\n\n const tsxFiles = findFilesShallow(appDir, /\\.(tsx|jsx)$/);\n // Match <Link>, <NextLink>, or other common aliases\n const LINK_PATTERN = /<(?:Next)?Link\\s[^>]*href\\s*=\\s*[\"'{`/]*(\\/sign-?out|\\/logout)/;\n\n const findings: AuthPatternFinding[] = [];\n for (const file of tsxFiles) {\n if (fileContains(file, LINK_PATTERN)) {\n findings.push({\n code: 'SIGNOUT_LINK_PREFETCH',\n severity: 'warning',\n message:\n 'Link component points to signout/logout — Next.js will prefetch this in production, potentially triggering logouts',\n filePath: relative(ctx.installDir, file),\n remediation:\n 'Use a <form> with a server action or <button> with onClick handler instead of <Link> for signout.',\n });\n }\n }\n return findings;\n}\n\nfunction checkMissingMiddleware(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'Next.js') return [];\n\n const middlewarePaths = [\n 'middleware.ts',\n 'middleware.js',\n 'proxy.ts',\n 'proxy.js',\n 'src/middleware.ts',\n 'src/middleware.js',\n 'src/proxy.ts',\n 'src/proxy.js',\n ].map((p) => join(ctx.installDir, p));\n\n if (findFile(middlewarePaths)) return [];\n\n return [\n {\n code: 'MISSING_MIDDLEWARE',\n severity: 'error',\n message: 'No middleware.ts or proxy.ts found — AuthKit session handling requires middleware',\n remediation: 'Create middleware.ts at the project root with authkitMiddleware() from @workos-inc/authkit-nextjs.',\n docsUrl: 'https://workos.com/docs/authkit/nextjs/middleware',\n },\n ];\n}\n\nfunction checkMiddlewareWrongLocation(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'Next.js') return [];\n\n const wrongPaths = ['app/middleware.ts', 'app/middleware.js', 'src/app/middleware.ts', 'src/app/middleware.js'].map(\n (p) => join(ctx.installDir, p),\n );\n\n const found = findFile(wrongPaths);\n if (!found) return [];\n\n return [\n {\n code: 'MIDDLEWARE_WRONG_LOCATION',\n severity: 'warning',\n message: 'middleware.ts found inside app/ directory — must be at project root or src/',\n filePath: relative(ctx.installDir, found),\n remediation: 'Move middleware.ts to the project root (or src/ if using src/ directory).',\n docsUrl: 'https://workos.com/docs/authkit/nextjs/middleware',\n },\n ];\n}\n\nfunction checkMissingAuthKitProvider(ctx: CheckContext): AuthPatternFinding[] {\n const layoutPaths: string[] = [];\n\n if (ctx.framework.name === 'Next.js') {\n layoutPaths.push(\n join(ctx.installDir, 'app', 'layout.tsx'),\n join(ctx.installDir, 'app', 'layout.jsx'),\n join(ctx.installDir, 'src', 'app', 'layout.tsx'),\n join(ctx.installDir, 'src', 'app', 'layout.jsx'),\n );\n } else if (ctx.framework.name === 'React Router' && ctx.framework.variant === 'declarative') {\n layoutPaths.push(\n join(ctx.installDir, 'src', 'App.tsx'),\n join(ctx.installDir, 'src', 'App.jsx'),\n join(ctx.installDir, 'app', 'root.tsx'),\n join(ctx.installDir, 'app', 'root.jsx'),\n );\n } else {\n return [];\n }\n\n const layoutFile = findFile(layoutPaths);\n if (!layoutFile) return []; // Can't check if layout doesn't exist\n\n if (fileContains(layoutFile, /AuthKitProvider/)) return [];\n\n return [\n {\n code: 'MISSING_AUTHKIT_PROVIDER',\n severity: 'warning',\n message: 'AuthKitProvider not found in root layout — required for AuthKit session management',\n filePath: relative(ctx.installDir, layoutFile),\n remediation: 'Wrap your app with <AuthKitProvider> in the root layout.',\n docsUrl: 'https://workos.com/docs/authkit/nextjs/setup',\n },\n ];\n}\n\n/** Extract callback path from redirect URI env vars or framework default */\nfunction resolveCallbackPath(ctx: CheckContext): string | null {\n // Check env vars for actual redirect URI (including NEXT_PUBLIC_ variant)\n const projectEnv = loadProjectEnvRaw(ctx.installDir);\n const redirectUri =\n projectEnv.WORKOS_REDIRECT_URI ?? projectEnv.NEXT_PUBLIC_WORKOS_REDIRECT_URI ?? ctx.environment.redirectUri;\n\n if (redirectUri) {\n try {\n return new URL(redirectUri).pathname;\n } catch {\n // Invalid URL, fall through to framework default\n }\n }\n\n return ctx.framework.expectedCallbackPath ?? null;\n}\n\nfunction checkCallbackRouteMissing(ctx: CheckContext): AuthPatternFinding[] {\n const callbackPath = resolveCallbackPath(ctx);\n if (!callbackPath) return [];\n\n // Build expected route file paths based on framework\n const possiblePaths: string[] = [];\n\n if (ctx.framework.name === 'Next.js') {\n // app/auth/callback/route.ts (or src/app/)\n const routeDir = callbackPath.replace(/^\\//, ''); // 'auth/callback'\n for (const prefix of ['app', 'src/app']) {\n for (const ext of ['ts', 'tsx', 'js', 'jsx']) {\n possiblePaths.push(join(ctx.installDir, prefix, routeDir, `route.${ext}`));\n }\n }\n } else if (ctx.framework.name === 'React Router') {\n // Flat: app/routes/auth.callback.tsx Nested: app/routes/auth/callback.tsx\n const segments = callbackPath.replace(/^\\//, '').split('/');\n const flat = segments.join('.');\n const nested = segments.join('/');\n for (const ext of ['tsx', 'jsx', 'ts', 'js']) {\n possiblePaths.push(join(ctx.installDir, 'app', 'routes', `${flat}.${ext}`));\n possiblePaths.push(join(ctx.installDir, 'app', 'routes', nested + `.${ext}`));\n }\n } else if (ctx.framework.name === 'TanStack Start') {\n // Flat: routes/api.auth.callback.tsx Nested: routes/api/auth/callback.tsx\n // Both conventions work in both src/ and app/ directories\n const segments = callbackPath.replace(/^\\//, '').split('/');\n const flat = segments.join('.');\n const nested = segments.join('/');\n for (const prefix of ['src', 'app']) {\n for (const ext of ['tsx', 'jsx', 'ts', 'js']) {\n possiblePaths.push(join(ctx.installDir, prefix, 'routes', `${flat}.${ext}`));\n possiblePaths.push(join(ctx.installDir, prefix, 'routes', nested + `.${ext}`));\n }\n }\n }\n\n if (possiblePaths.length === 0) return [];\n if (findFile(possiblePaths)) return [];\n\n return [\n {\n code: 'CALLBACK_ROUTE_MISSING',\n severity: 'error',\n message: `No callback route found at expected path ${callbackPath}`,\n remediation: `Create the callback route handler at the path matching your WORKOS_REDIRECT_URI.`,\n docsUrl: 'https://workos.com/docs/authkit/redirect-uri',\n },\n ];\n}\n\nconst CLIENT_ENV_PREFIXES = ['NEXT_PUBLIC_', 'VITE_', 'REACT_APP_', 'EXPO_PUBLIC_'];\n\nfunction checkApiKeyLeakedToClient(ctx: CheckContext): AuthPatternFinding[] {\n const projectEnv = loadProjectEnvRaw(ctx.installDir);\n const findings: AuthPatternFinding[] = [];\n\n for (const [key, value] of Object.entries(projectEnv)) {\n const hasClientPrefix = CLIENT_ENV_PREFIXES.some((prefix) => key.startsWith(prefix));\n if (!hasClientPrefix) continue;\n\n const isApiKey = key.includes('WORKOS_API_KEY');\n const isSecretValue = value?.startsWith('sk_test_') || value?.startsWith('sk_live_');\n\n if (isApiKey || isSecretValue) {\n findings.push({\n code: 'API_KEY_LEAKED_TO_CLIENT',\n severity: 'error',\n message: `Secret API key exposed via client-accessible env var ${key}`,\n remediation: `Remove the client prefix. WORKOS_API_KEY must be server-only (no NEXT_PUBLIC_, VITE_, REACT_APP_, or EXPO_PUBLIC_ prefix).`,\n });\n }\n }\n return findings;\n}\n\nfunction checkWrongCallbackLoader(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'React Router') return [];\n const callbackPath = ctx.framework.expectedCallbackPath;\n if (!callbackPath) return [];\n\n const segments = callbackPath.replace(/^\\//, '').split('/');\n const flat = segments.join('.');\n const nested = segments.join('/');\n\n const possiblePaths: string[] = [];\n for (const ext of ['tsx', 'jsx', 'ts', 'js']) {\n possiblePaths.push(join(ctx.installDir, 'app', 'routes', `${flat}.${ext}`));\n possiblePaths.push(join(ctx.installDir, 'app', 'routes', nested + `.${ext}`));\n }\n\n const callbackFile = findFile(possiblePaths);\n if (!callbackFile) return []; // No callback route to check\n\n // authkitLoader is for regular routes; authLoader is for the callback\n if (fileContains(callbackFile, /authkitLoader/) && !fileContains(callbackFile, /authLoader/)) {\n return [\n {\n code: 'WRONG_CALLBACK_LOADER',\n severity: 'warning',\n message: 'Callback route uses authkitLoader instead of authLoader',\n filePath: relative(ctx.installDir, callbackFile),\n remediation:\n 'Use authLoader (not authkitLoader) for the callback route. authkitLoader is for regular routes that need auth context.',\n },\n ];\n }\n return [];\n}\n\nfunction checkMissingRootAuthLoader(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'React Router') return [];\n\n const rootPaths = ['app/root.tsx', 'app/root.jsx', 'app/routes/_index.tsx', 'app/routes/_index.jsx'].map((p) =>\n join(ctx.installDir, p),\n );\n\n const rootFile = findFile(rootPaths);\n if (!rootFile) return [];\n\n if (fileContains(rootFile, /authkitLoader/)) return [];\n\n return [\n {\n code: 'MISSING_ROOT_AUTH_LOADER',\n severity: 'warning',\n message: 'Root route does not use authkitLoader — child routes will not have auth context',\n filePath: relative(ctx.installDir, rootFile),\n remediation: 'Add authkitLoader to your root route so child routes can access auth state.',\n },\n ];\n}\n\nfunction checkMissingAuthkitMiddleware(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'TanStack Start') return [];\n\n const startPaths = ['src/start.ts', 'src/start.tsx', 'app/start.ts', 'app/start.tsx'].map((p) =>\n join(ctx.installDir, p),\n );\n\n const startFile = findFile(startPaths);\n if (!startFile) return []; // Can't check if start file doesn't exist\n\n if (fileContains(startFile, /authkitMiddleware/)) return [];\n\n return [\n {\n code: 'MISSING_AUTHKIT_MIDDLEWARE',\n severity: 'warning',\n message: 'start.ts does not reference authkitMiddleware — AuthKit session handling requires it',\n filePath: relative(ctx.installDir, startFile),\n remediation:\n 'Add authkitMiddleware to requestMiddleware in src/start.ts:\\n' +\n ' import { authkitMiddleware } from \"@workos/authkit-tanstack-react-start\";\\n' +\n ' export default createStart({ requestMiddleware: [authkitMiddleware()] });',\n },\n ];\n}\n\nfunction checkCookiePasswordTooShort(ctx: CheckContext): AuthPatternFinding[] {\n if (ctx.framework.name !== 'React Router' && ctx.framework.name !== 'TanStack Start') return [];\n\n const projectEnv = loadProjectEnvRaw(ctx.installDir);\n const password = projectEnv.WORKOS_COOKIE_PASSWORD;\n\n // Only warn if password is set but too short; missing password is a separate concern\n if (!password || password.length >= 32) return [];\n\n return [\n {\n code: 'COOKIE_PASSWORD_TOO_SHORT',\n severity: 'warning',\n message: `WORKOS_COOKIE_PASSWORD is ${password.length} characters — minimum 32 required for secure encryption`,\n remediation: 'Set WORKOS_COOKIE_PASSWORD to a random string of at least 32 characters.',\n },\n ];\n}\n\nconst REACT_SPA_SDKS = new Set(['@workos-inc/authkit-react']);\n\nfunction checkMissingApiHostname(ctx: CheckContext): AuthPatternFinding[] {\n if (!ctx.sdk.name || !REACT_SPA_SDKS.has(ctx.sdk.name)) return [];\n\n const sourceFiles = findFilesShallow(ctx.installDir, /\\.(tsx|jsx|ts|js)$/, 4);\n const providerFiles = sourceFiles.filter((f) => fileContains(f, /AuthKitProvider/));\n if (providerFiles.length === 0) return [];\n\n const hasApiHostname = providerFiles.some((f) => fileContains(f, /apiHostname/));\n if (hasApiHostname) return [];\n\n return [\n {\n code: 'MISSING_API_HOSTNAME',\n severity: 'warning',\n message: 'AuthKitProvider does not specify apiHostname — authorize requests will route through api.workos.com',\n filePath: relative(ctx.installDir, providerFiles[0]),\n remediation:\n 'Set the apiHostname prop on AuthKitProvider to your custom Authentication API domain (e.g. auth.example.com) to avoid routing through api.workos.com.',\n docsUrl: 'https://workos.com/docs/custom-domains',\n },\n ];\n}\n\n// --- Cross-language checks (run for ALL projects, not just JS/AuthKit) ---\n\nconst SOURCE_EXTENSIONS = /\\.(ts|tsx|js|jsx|py|rb|go|java|kt|php|cs|swift|dart)$/;\n\nfunction checkApiKeyInSource(ctx: CheckContext): AuthPatternFinding[] {\n const API_KEY_PATTERN = /sk_(test|live)_[A-Za-z0-9]{10,}/;\n const sourceFiles = findFilesShallow(ctx.installDir, SOURCE_EXTENSIONS, 4);\n const findings: AuthPatternFinding[] = [];\n\n for (const file of sourceFiles) {\n const content = readFileSafe(file);\n if (!content) continue;\n if (API_KEY_PATTERN.test(content)) {\n findings.push({\n code: 'API_KEY_IN_SOURCE',\n severity: 'error',\n message: `WorkOS API key hardcoded in source file`,\n filePath: relative(ctx.installDir, file),\n remediation:\n 'Move the API key to an environment variable (WORKOS_API_KEY) and load it from .env or your secret manager.',\n });\n }\n }\n return findings;\n}\n\nfunction checkEnvFileNotGitignored(ctx: CheckContext): AuthPatternFinding[] {\n const envFiles = ['.env', '.env.local'].filter((f) => existsSync(join(ctx.installDir, f)));\n if (envFiles.length === 0) return [];\n\n const gitignorePath = join(ctx.installDir, '.gitignore');\n const gitignore = readFileSafe(gitignorePath);\n\n const findings: AuthPatternFinding[] = [];\n for (const envFile of envFiles) {\n const isIgnored =\n gitignore !== null &&\n gitignore.split('\\n').some((line) => {\n const trimmed = line.trim();\n if (trimmed.startsWith('#') || trimmed === '') return false;\n return trimmed === envFile || trimmed === '.env*' || trimmed === '.env.*' || trimmed === '.env';\n });\n\n if (!isIgnored) {\n findings.push({\n code: 'ENV_FILE_NOT_GITIGNORED',\n severity: 'warning',\n message: `${envFile} is not in .gitignore — secrets may be committed to version control`,\n filePath: envFile,\n remediation: `Add ${envFile} to your .gitignore file.`,\n });\n }\n }\n return findings;\n}\n\nfunction checkMixedEnvironmentCredentials(ctx: CheckContext): AuthPatternFinding[] {\n const projectEnv = loadProjectEnvRaw(ctx.installDir);\n const apiKey = projectEnv.WORKOS_API_KEY;\n const redirectUri = projectEnv.WORKOS_REDIRECT_URI ?? projectEnv.NEXT_PUBLIC_WORKOS_REDIRECT_URI;\n\n if (!apiKey || !redirectUri) return [];\n\n const isTestKey = apiKey.startsWith('sk_test_');\n const isLiveKey = apiKey.startsWith('sk_live_');\n\n let isProductionUri = false;\n try {\n const url = new URL(redirectUri);\n isProductionUri = url.hostname !== 'localhost' && !url.hostname.startsWith('127.0.0.');\n } catch {\n return [];\n }\n\n if (isTestKey && isProductionUri) {\n return [\n {\n code: 'MIXED_ENVIRONMENT',\n severity: 'warning',\n message: 'Staging API key (sk_test_) used with a production redirect URI',\n remediation:\n 'Use sk_live_ API key for production redirect URIs, or change the redirect URI to localhost for staging.',\n },\n ];\n }\n\n if (isLiveKey && !isProductionUri) {\n return [\n {\n code: 'MIXED_ENVIRONMENT',\n severity: 'warning',\n message: 'Production API key (sk_live_) used with a localhost redirect URI',\n remediation:\n 'Use sk_test_ API key for localhost development, or update the redirect URI to your production domain.',\n },\n ];\n }\n\n return [];\n}\n\n// --- Main Entry Point ---\n\ntype CheckFn = (ctx: CheckContext) => AuthPatternFinding[];\n\nconst CROSS_FRAMEWORK_CHECKS: CheckFn[] = [\n checkApiKeyLeakedToClient,\n checkApiKeyInSource,\n checkEnvFileNotGitignored,\n checkMixedEnvironmentCredentials,\n checkMissingApiHostname,\n];\n\nconst NEXTJS_CHECKS: CheckFn[] = [\n checkSignoutGetHandler,\n checkSignoutLinkPrefetch,\n checkMissingMiddleware,\n checkMiddlewareWrongLocation,\n checkMissingAuthKitProvider,\n checkCallbackRouteMissing,\n];\n\nconst REACT_ROUTER_CHECKS: CheckFn[] = [\n checkWrongCallbackLoader,\n checkMissingRootAuthLoader,\n checkMissingAuthKitProvider,\n checkCallbackRouteMissing,\n checkCookiePasswordTooShort,\n];\n\nconst TANSTACK_CHECKS: CheckFn[] = [\n checkMissingAuthkitMiddleware,\n checkCallbackRouteMissing,\n checkCookiePasswordTooShort,\n];\n\nexport async function checkAuthPatterns(\n options: DoctorOptions,\n framework: FrameworkInfo,\n environment: EnvironmentInfo,\n sdk: SdkInfo,\n): Promise<AuthPatternInfo> {\n const ctx: CheckContext = {\n framework,\n environment,\n sdk,\n installDir: options.installDir,\n };\n\n const checks: CheckFn[] = [...CROSS_FRAMEWORK_CHECKS];\n\n switch (framework.name) {\n case 'Next.js':\n checks.push(...NEXTJS_CHECKS);\n break;\n case 'React Router':\n checks.push(...REACT_ROUTER_CHECKS);\n break;\n case 'TanStack Start':\n checks.push(...TANSTACK_CHECKS);\n break;\n }\n\n const findings: AuthPatternFinding[] = [];\n for (const check of checks) {\n findings.push(...check(ctx));\n }\n\n return {\n checksRun: checks.length,\n findings,\n };\n}\n"]}
@@ -345,7 +345,7 @@ export class CLIAdapter {
345
345
  clack.log.error(message);
346
346
  // Add actionable hints for common errors
347
347
  if (message.includes('authentication') || message.includes('auth')) {
348
- clack.log.info('Try running: workos logout && workos install');
348
+ clack.log.info('Try running: workos auth logout && workos install');
349
349
  }
350
350
  if (message.includes('ENOENT') || message.includes('not found')) {
351
351
  clack.log.info('Ensure you are in a project directory');