Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e0c3c87
fix(install): register redirect URIs for every server-side SDK
jasonbarry Sep 24, 2026
78f1923
fix(install): register redirect URIs and CORS origins for client-only…
jasonbarry Sep 24, 2026
8b78cbd
feat(install): save sign-out and initiate login URIs for every SDK
jasonbarry Sep 24, 2026
3831d08
fix(install): align saved AuthKit URLs with each SDK's docs
jasonbarry Sep 24, 2026
50276b9
refactor(install): simplify AuthKit URL setup across SDKs
jasonbarry Sep 24, 2026
9e4e2da
fix(install): tighten client-only route and redirect URI checks
jasonbarry Sep 24, 2026
42b851b
refactor(install): write client env vars under the bundler prefix
jasonbarry Sep 25, 2026
e5d47f0
fix(install): check only browser code for the client redirect URI var
jasonbarry Sep 25, 2026
f1098b6
refactor(install): key browser env access rules by bundler prefix
jasonbarry Sep 25, 2026
266e624
fix(install): flag process.env redirect URI reads in Vite browser code
jasonbarry Sep 25, 2026
064f86f
refactor(install): define client browser code once for all checks
jasonbarry Sep 25, 2026
480e695
fix(install): scope client browser code by bundler
jasonbarry Sep 25, 2026
bc176f4
refactor(install): read client browser code with one glob
jasonbarry Sep 25, 2026
d5f937e
fix(install): leave nested server code out of the client browser scan
jasonbarry Sep 25, 2026
76fbb18
refactor(install): classify redirect URI reads instead of guessing se…
jasonbarry Sep 25, 2026
00e2860
fix(install): accept a /login pathname check through any variable
jasonbarry Sep 25, 2026
ab52f4a
refactor(install): name the accepted /login route forms in the prompt
jasonbarry Sep 25, 2026
b4ffe86
fix(install): never write localhost URLs with a production API key
nicknisi Sep 25, 2026
56a6f67
fix(install): leave a sandbox's homepage alone unless nothing can be …
nicknisi Sep 25, 2026
40577ab
fix(install): check the client code the browser loads, not every file
nicknisi Sep 25, 2026
9acebb2
revert: restore client validation before browser-source tracing
nicknisi Sep 25, 2026
550ff0e
ci: diagnose smoke authentication with a direct read-only request
nicknisi Sep 25, 2026
08c9985
fix(install): bound client checks and exclude server-only sources
nicknisi Sep 25, 2026
24fb78b
fix(install): report incomplete URL setup without claiming verification
nicknisi Sep 25, 2026
004093c
fix(install): tie sign-in evidence to the matching pathname branch
nicknisi Sep 25, 2026
22a59b3
ci: distinguish unusable smoke credentials from CLI regressions
nicknisi Sep 25, 2026
ea46d36
fix(install): recognize sign-in effects bound to React login routes
nicknisi Sep 25, 2026
4434c07
fix(install): configure all application URLs through one target
nicknisi Sep 25, 2026
6514f04
ci: warn explicitly when smoke credentials are unavailable
nicknisi Sep 25, 2026
4d4cda9
Revert "ci: warn explicitly when smoke credentials are unavailable"
nicknisi Sep 25, 2026
794b1af
Revert "ci: distinguish unusable smoke credentials from CLI regressions"
nicknisi Sep 25, 2026
3ad4831
Revert "ci: diagnose smoke authentication with a direct read-only req…
nicknisi Sep 25, 2026
efe729e
refactor(install): leave client login verification to browser testing
nicknisi Sep 25, 2026
0b21bd6
ci: rerun checks after retargeting installer fixes to main
nicknisi Sep 25, 2026
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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -713,12 +713,12 @@ OAuth credentials are stored in the system keychain (with `~/.workos/credentials

1. **Detects** your framework and project structure
2. **Resolves credentials** — uses existing config, or auto-provisions an unclaimed environment if none found
3. **Auto-configures** WorkOS dashboard (redirect URI, CORS, homepage URL)
3. **Prepares** local environment variables securely
4. **Fetches** latest SDK documentation from workos.com
5. **Uses AI** (Claude) to generate integration code
6. **Installs** SDK with detected package manager
7. **Creates** auth routes, middleware, and UI
8. **Configures** environment variables securely
5. **Uses AI** (Claude) to install the SDK and create auth routes, middleware, and UI
6. **Checks** the generated integration
7. **Configures sandbox URLs** after the agent, using one WorkOS target: the client-ID-matched dashboard application, or the API-key-only fallback. Existing settings are preserved unless an explicit override is allowed; unsupported or unverified settings are reported for manual setup.
8. **Reports** what was configured and what still needs attention. For React and vanilla JS, the agent creates `/login`, but the installer leaves the Initiate login URI unchanged: open `/login` while signed out, confirm it starts sign-in without a click, then set that URL in the WorkOS dashboard. Static source checks cannot verify client routing; unsupported layouts or scan limits do not by themselves fail installation. Test sign-in, sign-out, and invitation/password-reset flows in the browser before treating the integration as complete.

## Telemetry

Expand Down
23 changes: 23 additions & 0 deletions src/cli.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,54 +39,77 @@ export const config = {
nextjs: {
port: 3000,
callbackPath: '/auth/callback',
signInPath: '/sign-in',
},
react: {
port: 5173,
callbackPath: '/callback',
signInPath: '/login',
},
tanstackStart: {
port: 3000,
callbackPath: '/api/auth/callback',
signInPath: '/api/auth/sign-in',
},
reactRouter: {
port: 5173,
callbackPath: '/callback',
signInPath: '/login',
},
vanillaJs: {
port: 5173,
callbackPath: '/callback',
signInPath: '/login',
},
python: {
port: 8000,
callbackPath: '/auth/callback/',
signInPath: '/auth/login/',
},
ruby: {
port: 3000,
callbackPath: '/auth/callback',
signInPath: '/auth/login',
},
php: {
port: 8000,
callbackPath: '/auth/callback',
signInPath: '/login.php',
},
phpLaravel: {
port: 8000,
callbackPath: '/auth/callback',
signInPath: '/login',
},
go: {
port: 8080,
callbackPath: '/auth/callback',
signInPath: '/auth/login',
},
dotnet: {
port: 5000,
callbackPath: '/auth/callback',
signInPath: '/auth/login',
},
elixir: {
port: 4000,
callbackPath: '/auth/callback',
signInPath: '/auth/sign-in',
},
sveltekit: {
port: 5173,
callbackPath: '/callback',
signInPath: '/sign-in',
},
node: {
port: 3000,
callbackPath: '/auth/callback',
signInPath: '/login',
},
kotlin: {
port: 8080,
callbackPath: '/auth/callback',
signInPath: '/auth/login',
},
},

Expand Down
3 changes: 2 additions & 1 deletion src/integrations/dotnet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { initializeAgent, runAgent } from '../../lib/agent-interface.js';
import { autoConfigureWorkOSEnvironment } from '../../lib/workos-management.js';
import { validateInstallation } from '../../lib/validation/index.js';
import { getReference } from '../../lib/skills-assets.js';
import { buildSignInSection } from '../../lib/sign-in-route.js';

export const config: FrameworkConfig = {
metadata: {
Expand Down Expand Up @@ -127,7 +128,7 @@ The following WorkOS credentials should be configured in appsettings.Development

${refContent}

Report your progress using [STATUS] prefixes.
${buildSignInSection(config)}Report your progress using [STATUS] prefixes.

Begin integration now.`;

Expand Down
3 changes: 2 additions & 1 deletion src/integrations/elixir/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getOrAskForWorkOSCredentials } from '../../utils/ui-utils.js';
import { initializeAgent, runAgent } from '../../lib/agent-interface.js';
import { writeEnvLocal } from '../../lib/env-writer.js';
import { getReference } from '../../lib/skills-assets.js';
import { buildSignInSection } from '../../lib/sign-in-route.js';

export const config: FrameworkConfig = {
metadata: {
Expand Down Expand Up @@ -172,7 +173,7 @@ Note: For Elixir/Phoenix, these should be read via System.get_env() in config/ru

${refContent}

Report your progress using [STATUS] prefixes.
${buildSignInSection(config)}Report your progress using [STATUS] prefixes.

Begin integration now.`;
}
3 changes: 2 additions & 1 deletion src/integrations/go/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { autoConfigureWorkOSEnvironment } from '../../lib/workos-management.js';
import { validateInstallation } from '../../lib/validation/index.js';
import { parseEnvFile } from '../../utils/env-parser.js';
import { getReference } from '../../lib/skills-assets.js';
import { buildSignInSection } from '../../lib/sign-in-route.js';

/** Default port for Go HTTP servers */
const GO_DEFAULT_PORT = 8080;
Expand Down Expand Up @@ -184,7 +185,7 @@ The following environment variables have been configured in .env:

${refContent}

Report your progress using [STATUS] prefixes.
${buildSignInSection(config)}Report your progress using [STATUS] prefixes.

Begin integration now.`;

Expand Down
3 changes: 2 additions & 1 deletion src/integrations/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { FrameworkConfig } from '../../lib/framework-config.js';
import type { InstallerOptions } from '../../utils/types.js';
import { enableDebugLogs } from '../../utils/debug.js';
import { getPackageVersion } from '../../utils/package-json.js';
import { getCallbackPath, getSignInPath } from '../../lib/port-detection.js';

export const config: FrameworkConfig = {
metadata: {
Expand Down Expand Up @@ -41,7 +42,7 @@ export const config: FrameworkConfig = {
getOutroChanges: () => [
'Analyzed your Express project structure',
'Installed and configured @workos-inc/node SDK',
'Created authentication routes (/auth/login, /auth/callback, /auth/logout)',
`Created authentication routes (${getSignInPath('node')}, ${getCallbackPath('node')}, /logout)`,
'Configured session management',
],
getOutroNextSteps: () => [
Expand Down
3 changes: 2 additions & 1 deletion src/integrations/python/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { analytics } from '../../utils/analytics.js';
import { INSTALLER_INTERACTION_EVENT_NAME } from '../../lib/constants.js';
import { parseEnvFile } from '../../utils/env-parser.js';
import { getReference } from '../../lib/skills-assets.js';
import { buildSignInSection } from '../../lib/sign-in-route.js';

/**
* Detect which Python package manager the project uses.
Expand Down Expand Up @@ -186,7 +187,7 @@ The following environment variables have been configured in .env:

${refContent}

Report your progress using [STATUS] prefixes.
${buildSignInSection(config)}Report your progress using [STATUS] prefixes.

Begin integration now.`;
}
Expand Down
3 changes: 2 additions & 1 deletion src/integrations/ruby/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { initializeAgent, runAgent } from '../../lib/agent-interface.js';
import { getOrAskForWorkOSCredentials } from '../../utils/ui-utils.js';
import { autoConfigureWorkOSEnvironment } from '../../lib/workos-management.js';
import { getReference } from '../../lib/skills-assets.js';
import { buildSignInSection } from '../../lib/sign-in-route.js';

export const config: FrameworkConfig = {
metadata: {
Expand Down Expand Up @@ -114,7 +115,7 @@ The following environment variables are needed (create a .env file if one does n

${refContent}

Report your progress using [STATUS] prefixes.
${buildSignInSection(config)}Report your progress using [STATUS] prefixes.

Begin integration now.`;

Expand Down
48 changes: 48 additions & 0 deletions src/lib/agent-runner.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,54 @@ describe('installer prompt', () => {
}
});

const promptFor = async (integration: FrameworkConfig['metadata']['integration'], skillName: string) => {
const requiresApiKey = !['react', 'vanilla-js'].includes(integration);
await runAgentInstaller(
{
...config,
metadata: { ...config.metadata, integration, skillName },
environment: { ...config.environment, requiresApiKey },
},
options,
);
return vi.mocked(runAgent).mock.calls[0][1];
};

it('tells a client-only app to add a /login route and pass its redirect URI', async () => {
const prompt = await promptFor('react', 'workos-authkit-react');
expect(prompt).toContain('## Sign-in route (Initiate login URI)');
expect(prompt).toContain('the app origin plus /login');
expect(prompt).toContain('add a /login client route');
expect(prompt).toContain("Use the app's existing router and component conventions");
expect(prompt).toContain('without a click');
expect(prompt).toContain('leaves the Initiate login URI unchanged');
expect(prompt).toContain('confirm automatic sign-in in the browser');
expect(prompt).not.toContain('relative default or named import');
expect(prompt).toContain('VITE_WORKOS_REDIRECT_URI for Vite');
});

it("pins a server SDK to its guide's sign-in route without the client-only steps", async () => {
const prompt = await promptFor('kotlin', 'workos-kotlin');
expect(prompt).toContain('the app origin plus /auth/login');
expect(prompt).not.toContain('client route');
});

it.each([
['react-router', '/login'],
['tanstack-start', '/api/auth/sign-in'],
['sveltekit', '/sign-in'],
['node', '/login'],
['php', '/login.php'],
])('pins %s to its documented sign-in route %s', async (integration, path) => {
const prompt = await promptFor(integration, `workos-${integration}`);
expect(prompt).toContain(`the app origin plus ${path}.`);
});

it('leaves Next.js to its own sign-in instructions', async () => {
const prompt = await promptFor('nextjs', 'workos-authkit-nextjs');
expect(prompt).not.toContain('## Sign-in route (Initiate login URI)');
});

it('declines Pages Router before requesting credentials, writing files, or starting the agent', async () => {
const framework = {
...config,
Expand Down
10 changes: 5 additions & 5 deletions src/lib/agent-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import { initializeAgent, runAgent, type RetryConfig } from './agent-interface.j
import { uploadEnvironmentVariablesStep } from '../steps/index.js';
import { autoConfigureWorkOSEnvironment } from './workos-management.js';
import { assertSupportedNextJsRouter, assertNextjsSignInRouteAvailable } from '../integrations/nextjs/utils.js';
import { detectPort, getCallbackPath } from './port-detection.js';
import { detectPort, resolveRedirectUri } from './port-detection.js';
import { buildSignInSection } from './sign-in-route.js';
import { writeEnvLocal } from './env-writer.js';

/**
Expand Down Expand Up @@ -85,9 +86,7 @@ export async function runAgentInstaller(config: FrameworkConfig, options: Instal
// Write environment variables to .env.local BEFORE agent runs
// Skip if caller already handled this (prevents double-writing)
if (!callerHandledConfig) {
const port = detectPort(config.metadata.integration, options.installDir);
const callbackPath = getCallbackPath(config.metadata.integration);
const redirectUri = options.redirectUri || `http://localhost:${port}${callbackPath}`;
const redirectUri = resolveRedirectUri(config.metadata.integration, options);

// Next.js requires NEXT_PUBLIC_ prefix for client-side env vars
const redirectUriKey =
Expand Down Expand Up @@ -321,6 +320,7 @@ async function buildIntegrationPrompt(
'WORKOS_COOKIE_PASSWORD',
];
const envVarList = envVars.map((v) => `- ${v}`).join('\n');
const signInSection = buildSignInSection(config);

return `You are integrating WorkOS AuthKit into this ${config.metadata.name} application.

Expand Down Expand Up @@ -355,7 +355,7 @@ Do not claim the full integration or browser flows are verified. Report code imp
: ''
}

Report your progress using [STATUS] prefixes.
${signInSection}Report your progress using [STATUS] prefixes.

Begin integration now.`;
}
Expand Down
Loading
Loading