diff --git a/CLAUDE.md b/CLAUDE.md index 356cb06..0a6580b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -104,6 +104,7 @@ pnpm eval:calibrate # scorer vs human agreement repor ## Do / Don't **Do:** + - Edit topic files (`plugins/workos/skills/workos/references/*.md`) directly -- they are human-maintained - Start every topic file with doc URLs and "If this file conflicts with fetched docs, follow the docs." - Add gotcha bullets when you discover the LLM produces incorrect output for a topic @@ -111,6 +112,7 @@ pnpm eval:calibrate # scorer vs human agreement repor - Write `.spec.ts` tests in `scripts/tests/` for eval framework changes **Don't:** + - Modify the `workos-widgets` skill when working on reference files - Add frontmatter to topic files -- the router handles discovery - Change the plugin directory structure under `plugins/workos/` without considering caching boundaries diff --git a/README.md b/README.md index a0ceaa0..8e09a1f 100644 --- a/README.md +++ b/README.md @@ -34,27 +34,27 @@ Path helpers are also available for consumers that need file paths (e.g., skill import { getReferencePath, getSkillsDir, getSkillPath } from '@workos/skills'; const refPath = getReferencePath('workos-authkit-nextjs'); // absolute path to .md file -const skillsDir = getSkillsDir(); // directory containing workos/ and workos-widgets/ -const skillPath = getSkillPath('workos'); // absolute path to SKILL.md +const skillsDir = getSkillsDir(); // directory containing workos/ and workos-widgets/ +const skillPath = getSkillPath('workos'); // absolute path to SKILL.md ``` ### Exports -| Function | Returns | -| --- | --- | -| `getReference(name)` | `Promise` — reference file content | -| `getSkill(skillName)` | `Promise` — skill SKILL.md content | -| `getReferencePath(name)` | Absolute path to `references/{name}.md` | -| `getSkillsDir()` | Absolute path to the `skills/` directory | +| Function | Returns | +| ------------------------- | ---------------------------------------------- | +| `getReference(name)` | `Promise` — reference file content | +| `getSkill(skillName)` | `Promise` — skill SKILL.md content | +| `getReferencePath(name)` | Absolute path to `references/{name}.md` | +| `getSkillsDir()` | Absolute path to the `skills/` directory | | `getSkillPath(skillName)` | Absolute path to `skills/{skillName}/SKILL.md` | ## Skills Two registered skills: -| Skill | Description | -| --- | --- | -| `workos` | Router — identifies which reference to load based on the user's task | +| Skill | Description | +| ---------------- | ----------------------------------------------------------------------- | +| `workos` | Router — identifies which reference to load based on the user's task | | `workos-widgets` | Multi-framework widget integration with on-demand OpenAPI spec querying | Everything else is a **reference file** under `references/`. The router dispatches to the right reference via progressive disclosure. @@ -63,72 +63,72 @@ Everything else is a **reference file** under `references/`. The router dispatch #### AuthKit Installation -| Reference | Description | -| --- | --- | -| `workos-authkit-nextjs` | Next.js App Router integration | -| `workos-authkit-react` | React SPA integration | -| `workos-authkit-react-router` | React Router v6/v7 integration | -| `workos-authkit-tanstack-start` | TanStack Start integration | -| `workos-authkit-sveltekit` | SvelteKit integration | -| `workos-authkit-vanilla-js` | Vanilla JS integration | -| `workos-authkit-base` | AuthKit architecture reference | +| Reference | Description | +| ------------------------------- | ------------------------------ | +| `workos-authkit-nextjs` | Next.js App Router integration | +| `workos-authkit-react` | React SPA integration | +| `workos-authkit-react-router` | React Router v6/v7 integration | +| `workos-authkit-tanstack-start` | TanStack Start integration | +| `workos-authkit-sveltekit` | SvelteKit integration | +| `workos-authkit-vanilla-js` | Vanilla JS integration | +| `workos-authkit-base` | AuthKit architecture reference | #### Backend SDK Installation -| Reference | Description | -| --- | --- | -| `workos-node` | Node.js (Express/Fastify/Hono/Koa) | -| `workos-python` | Python (Django/Flask/FastAPI) | -| `workos-dotnet` | .NET (ASP.NET Core) | -| `workos-go` | Go | -| `workos-ruby` | Ruby (Rails) | -| `workos-php` | PHP | -| `workos-php-laravel` | PHP Laravel | -| `workos-kotlin` | Kotlin | -| `workos-elixir` | Elixir | +| Reference | Description | +| -------------------- | ---------------------------------- | +| `workos-node` | Node.js (Express/Fastify/Hono/Koa) | +| `workos-python` | Python (Django/Flask/FastAPI) | +| `workos-dotnet` | .NET (ASP.NET Core) | +| `workos-go` | Go | +| `workos-ruby` | Ruby (Rails) | +| `workos-php` | PHP | +| `workos-php-laravel` | PHP Laravel | +| `workos-kotlin` | Kotlin | +| `workos-elixir` | Elixir | #### Features -| Reference | Description | -| --- | --- | -| `workos-sso` | Single Sign-On with SAML/OIDC | -| `workos-directory-sync` | User directory sync from IdPs | -| `workos-rbac` | Role-based access control | -| `workos-vault` | Encrypted data storage | -| `workos-events` | Webhook event handling | -| `workos-audit-logs` | Compliance audit logging | -| `workos-admin-portal` | Self-service admin portal | -| `workos-mfa` | Multi-factor authentication | -| `workos-custom-domains` | Custom domain configuration | -| `workos-email` | Email delivery configuration | -| `workos-integrations` | Provider lookup table for 60+ IdP integrations | +| Reference | Description | +| ----------------------- | ---------------------------------------------- | +| `workos-sso` | Single Sign-On with SAML/OIDC | +| `workos-directory-sync` | User directory sync from IdPs | +| `workos-rbac` | Role-based access control | +| `workos-vault` | Encrypted data storage | +| `workos-events` | Webhook event handling | +| `workos-audit-logs` | Compliance audit logging | +| `workos-admin-portal` | Self-service admin portal | +| `workos-mfa` | Multi-factor authentication | +| `workos-custom-domains` | Custom domain configuration | +| `workos-email` | Email delivery configuration | +| `workos-integrations` | Provider lookup table for 60+ IdP integrations | #### Migrations -| Reference | Description | -| --- | --- | -| `workos-migrate-auth0` | Migrate from Auth0 | -| `workos-migrate-firebase` | Migrate from Firebase Auth | -| `workos-migrate-clerk` | Migrate from Clerk | -| `workos-migrate-aws-cognito` | Migrate from AWS Cognito | -| `workos-migrate-stytch` | Migrate from Stytch | -| `workos-migrate-supabase-auth` | Migrate from Supabase Auth | -| `workos-migrate-descope` | Migrate from Descope | -| `workos-migrate-better-auth` | Migrate from Better Auth | -| `workos-migrate-other-services` | Migrate from custom auth | +| Reference | Description | +| --------------------------------------- | --------------------------------- | +| `workos-migrate-auth0` | Migrate from Auth0 | +| `workos-migrate-firebase` | Migrate from Firebase Auth | +| `workos-migrate-clerk` | Migrate from Clerk | +| `workos-migrate-aws-cognito` | Migrate from AWS Cognito | +| `workos-migrate-stytch` | Migrate from Stytch | +| `workos-migrate-supabase-auth` | Migrate from Supabase Auth | +| `workos-migrate-descope` | Migrate from Descope | +| `workos-migrate-better-auth` | Migrate from Better Auth | +| `workos-migrate-other-services` | Migrate from custom auth | | `workos-migrate-the-standalone-sso-api` | Upgrade standalone SSO to AuthKit | #### API References -| Reference | Description | -| --- | --- | -| `workos-api-authkit` | AuthKit/User Management API endpoints | -| `workos-api-organization` | Organizations API endpoints | +| Reference | Description | +| ------------------------- | ------------------------------------- | +| `workos-api-authkit` | AuthKit/User Management API endpoints | +| `workos-api-organization` | Organizations API endpoints | #### Management -| Reference | Description | -| --- | --- | +| Reference | Description | +| ------------------- | --------------------------------------------------------------- | | `workos-management` | CLI resource management (orgs, users, roles, webhooks, seeding) | ## Development diff --git a/plugins/workos/skills/workos/SKILL.md b/plugins/workos/skills/workos/SKILL.md index 66138b7..07723b2 100644 --- a/plugins/workos/skills/workos/SKILL.md +++ b/plugins/workos/skills/workos/SKILL.md @@ -19,30 +19,30 @@ When a user needs help with WorkOS, consult the tables below to route to the rig ### AuthKit Installation (Read `references/{name}.md`) -| User wants to... | Read file | -| ----------------------------------- | ------------------------------------------------- | -| Install AuthKit in Next.js | `references/workos-authkit-nextjs.md` | -| Install AuthKit in React SPA | `references/workos-authkit-react.md` | -| Install AuthKit with React Router | `references/workos-authkit-react-router.md` | -| Install AuthKit with TanStack Start | `references/workos-authkit-tanstack-start.md` | -| Install AuthKit with SvelteKit | `references/workos-authkit-sveltekit.md` | -| Install AuthKit in vanilla JS | `references/workos-authkit-vanilla-js.md` | -| AuthKit architecture reference | `references/workos-authkit-base.md` | -| Add WorkOS Widgets | Load `workos-widgets` skill via Skill tool | +| User wants to... | Read file | +| ----------------------------------- | --------------------------------------------- | +| Install AuthKit in Next.js | `references/workos-authkit-nextjs.md` | +| Install AuthKit in React SPA | `references/workos-authkit-react.md` | +| Install AuthKit with React Router | `references/workos-authkit-react-router.md` | +| Install AuthKit with TanStack Start | `references/workos-authkit-tanstack-start.md` | +| Install AuthKit with SvelteKit | `references/workos-authkit-sveltekit.md` | +| Install AuthKit in vanilla JS | `references/workos-authkit-vanilla-js.md` | +| AuthKit architecture reference | `references/workos-authkit-base.md` | +| Add WorkOS Widgets | Load `workos-widgets` skill via Skill tool | ### Backend SDK Installation (Read `references/{name}.md`) -| User wants to... | Read file | -| ------------------------------------ | -------------------------------------- | -| Install AuthKit in Node.js backend | `references/workos-node.md` | -| Install AuthKit in Python | `references/workos-python.md` | -| Install AuthKit in .NET | `references/workos-dotnet.md` | -| Install AuthKit in Go | `references/workos-go.md` | -| Install AuthKit in Ruby | `references/workos-ruby.md` | -| Install AuthKit in PHP | `references/workos-php.md` | -| Install AuthKit in PHP Laravel | `references/workos-php-laravel.md` | -| Install AuthKit in Kotlin | `references/workos-kotlin.md` | -| Install AuthKit in Elixir | `references/workos-elixir.md` | +| User wants to... | Read file | +| ---------------------------------- | ---------------------------------- | +| Install AuthKit in Node.js backend | `references/workos-node.md` | +| Install AuthKit in Python | `references/workos-python.md` | +| Install AuthKit in .NET | `references/workos-dotnet.md` | +| Install AuthKit in Go | `references/workos-go.md` | +| Install AuthKit in Ruby | `references/workos-ruby.md` | +| Install AuthKit in PHP | `references/workos-php.md` | +| Install AuthKit in PHP Laravel | `references/workos-php-laravel.md` | +| Install AuthKit in Kotlin | `references/workos-kotlin.md` | +| Install AuthKit in Elixir | `references/workos-elixir.md` | ### Features (Read `references/{name}.md`) @@ -86,9 +86,9 @@ Feature topic files above include endpoint tables for their respective APIs. Use ### Management (Read `references/{name}.md`) -| User wants to... | Read file | -| ---------------------------------------- | ---------------------------------- | -| Manage WorkOS resources via CLI commands | `references/workos-management.md` | +| User wants to... | Read file | +| ---------------------------------------- | --------------------------------- | +| Manage WorkOS resources via CLI commands | `references/workos-management.md` | ## Routing Decision Tree diff --git a/plugins/workos/skills/workos/references/workos-admin-portal.md b/plugins/workos/skills/workos/references/workos-admin-portal.md index 1e8f81f..6da4def 100644 --- a/plugins/workos/skills/workos/references/workos-admin-portal.md +++ b/plugins/workos/skills/workos/references/workos-admin-portal.md @@ -1,6 +1,7 @@ # WorkOS Admin Portal ## Docs + - https://workos.com/docs/admin-portal/index - https://workos.com/docs/admin-portal/example-apps - https://workos.com/docs/admin-portal/custom-branding @@ -8,9 +9,10 @@ - https://workos.com/docs/reference/admin-portal/portal-link - https://workos.com/docs/reference/admin-portal/portal-link/generate - https://workos.com/docs/reference/admin-portal/provider-icons -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Portal links are single-use and time-limited. Visiting an expired or already-used link returns 404. Must generate a new link each time. - Do NOT email portal links directly from your backend. Links are exposed in email logs. Instead, store the link and have your app's settings page redirect to it. - The `intent` parameter determines which configuration screens appear. It cannot be changed after link generation — must generate a new link for a different intent. @@ -19,6 +21,7 @@ If this file conflicts with fetched docs, follow the docs. - API key must start with `sk_` (secret key). Using `pk_` (publishable key) returns "Unauthorized." ## Endpoints + | Endpoint | Description | | ----------------------- | ------------------------------------- | | `/admin-portal` | admin-portal | diff --git a/plugins/workos/skills/workos/references/workos-api-authkit.md b/plugins/workos/skills/workos/references/workos-api-authkit.md index 688466b..8fabac3 100644 --- a/plugins/workos/skills/workos/references/workos-api-authkit.md +++ b/plugins/workos/skills/workos/references/workos-api-authkit.md @@ -1,101 +1,104 @@ # WorkOS AuthKit API Reference ## Docs + - https://workos.com/docs/reference/authkit - https://workos.com/docs/reference/authkit/api-keys - https://workos.com/docs/reference/authkit/api-keys/create-for-organization - https://workos.com/docs/reference/authkit/api-keys/delete - https://workos.com/docs/reference/authkit/api-keys/list-for-organization -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + (none yet — add as discovered) ## Endpoints -| Endpoint | Description | -|----------|-------------| -| `/authkit` | authkit | -| `/api-keys` | authkit - api-keys | -| `/api-keys/create-for-organization` | authkit - api-keys - create-for-organization | -| `/api-keys/delete` | authkit - api-keys - delete | -| `/api-keys/list-for-organization` | authkit - api-keys - list-for-organization | -| `/api-keys/validate` | Validate an API key and retrieve associated metadata. | -| `/authentication` | authkit - authentication | -| `/authentication-errors` | authkit - authentication-errors | -| `/authentication-errors/email-verification-required-error` | authkit - authentication-errors - email-verification-required-error | -| `/authentication-errors/mfa-challenge-error` | authkit - authentication-errors - mfa-challenge-error | -| `/authentication-errors/mfa-enrollment-error` | authkit - authentication-errors - mfa-enrollment-error | -| `/authentication-errors/organization-authentication-required-error` | authkit - authentication-errors - organization-authentication-required-error | -| `/authentication-errors/organization-selection-error` | authkit - authentication-errors - organization-selection-error | -| `/authentication-errors/sso-required-error` | authkit - authentication-errors - sso-required-error | -| `/authentication/code` | authkit - authentication - code | -| `/authentication/email-verification` | authkit - authentication - email-verification | -| `/authentication/get-authorization-url` | authkit - authentication - get-authorization-url | -| `/authentication/get-authorization-url/error-codes` | authkit - authentication - get-authorization-url - error-codes | -| `/authentication/get-authorization-url/pkce` | authkit - authentication - get-authorization-url - pkce | -| `/authentication/get-authorization-url/redirect-uri` | authkit - authentication - get-authorization-url - redirect-uri | -| `/authentication/magic-auth` | authkit - authentication - magic-auth | -| `/authentication/organization-selection` | authkit - authentication - organization-selection | -| `/authentication/password` | authkit - authentication - password | -| `/authentication/refresh-and-seal-session-data` | authkit - authentication - refresh-and-seal-session-data | -| `/authentication/refresh-token` | authkit - authentication - refresh-token | -| `/authentication/session-cookie` | authkit - authentication - session-cookie | -| `/authentication/totp` | authkit - authentication - totp | -| `/cli-auth` | authkit - cli-auth | -| `/cli-auth/device-authorization` | Initiate the CLI Auth flow by obtaining a device code and verification URLs. | -| `/cli-auth/device-code` | Exchange a device code for access and refresh tokens during the CLI Auth flow. | -| `/cli-auth/error-codes` | authkit - cli-auth - error-codes | -| `/email-verification` | authkit - email-verification | -| `/email-verification/get` | authkit - email-verification - get | -| `/identity` | authkit - identity | -| `/identity/list` | authkit - identity - list | -| `/invitation` | authkit - invitation | -| `/invitation/accept` | authkit - invitation - accept | -| `/invitation/find-by-token` | authkit - invitation - find-by-token | -| `/invitation/get` | authkit - invitation - get | -| `/invitation/list` | authkit - invitation - list | -| `/invitation/resend` | authkit - invitation - resend | -| `/invitation/revoke` | authkit - invitation - revoke | -| `/invitation/send` | authkit - invitation - send | -| `/logout` | authkit - logout | -| `/logout/get-logout-url` | authkit - logout - get-logout-url | -| `/logout/get-logout-url-from-session-cookie` | authkit - logout - get-logout-url-from-session-cookie | -| `/magic-auth` | authkit - magic-auth | -| `/magic-auth/create` | authkit - magic-auth - create | -| `/magic-auth/get` | authkit - magic-auth - get | -| `/mfa` | Enroll users in multi-factor authentication for an additional layer of security. | -| `/mfa/authentication-challenge` | authkit - mfa - authentication-challenge | -| `/mfa/authentication-factor` | authkit - mfa - authentication-factor | -| `/mfa/enroll-auth-factor` | authkit - mfa - enroll-auth-factor | -| `/mfa/list-auth-factors` | authkit - mfa - list-auth-factors | -| `/organization-membership` | authkit - organization-membership | -| `/organization-membership/create` | authkit - organization-membership - create | -| `/organization-membership/deactivate` | authkit - organization-membership - deactivate | -| `/organization-membership/delete` | authkit - organization-membership - delete | -| `/organization-membership/get` | authkit - organization-membership - get | -| `/organization-membership/list` | authkit - organization-membership - list | -| `/organization-membership/reactivate` | authkit - organization-membership - reactivate | -| `/organization-membership/update` | authkit - organization-membership - update | -| `/password-reset` | authkit - password-reset | -| `/password-reset/create` | authkit - password-reset - create | -| `/password-reset/get` | authkit - password-reset - get | -| `/password-reset/reset-password` | authkit - password-reset - reset-password | -| `/session` | authkit - session | -| `/session-helpers` | session-helpers | -| `/session-helpers/authenticate` | authkit - session-helpers - authenticate | -| `/session-helpers/get-logout-url` | authkit - session-helpers - get-logout-url | -| `/session-helpers/load-sealed-session` | authkit - session-helpers - load-sealed-session | -| `/session-helpers/refresh` | authkit - session-helpers - refresh | -| `/session-tokens` | authkit - session-tokens | -| `/session-tokens/access-token` | authkit - session-tokens - access-token | -| `/session-tokens/jwks` | authkit - session-tokens - jwks | -| `/session-tokens/refresh-token` | authkit - session-tokens - refresh-token | -| `/session/list` | authkit - session - list | -| `/session/revoke` | authkit - session - revoke | -| `/user` | authkit - user | -| `/user/create` | authkit - user - create | -| `/user/delete` | authkit - user - delete | -| `/user/get` | authkit - user - get | -| `/user/get-by-external-id` | authkit - user - get-by-external-id | -| `/user/list` | authkit - user - list | -| `/user/update` | authkit - user - update | + +| Endpoint | Description | +| ------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +| `/authkit` | authkit | +| `/api-keys` | authkit - api-keys | +| `/api-keys/create-for-organization` | authkit - api-keys - create-for-organization | +| `/api-keys/delete` | authkit - api-keys - delete | +| `/api-keys/list-for-organization` | authkit - api-keys - list-for-organization | +| `/api-keys/validate` | Validate an API key and retrieve associated metadata. | +| `/authentication` | authkit - authentication | +| `/authentication-errors` | authkit - authentication-errors | +| `/authentication-errors/email-verification-required-error` | authkit - authentication-errors - email-verification-required-error | +| `/authentication-errors/mfa-challenge-error` | authkit - authentication-errors - mfa-challenge-error | +| `/authentication-errors/mfa-enrollment-error` | authkit - authentication-errors - mfa-enrollment-error | +| `/authentication-errors/organization-authentication-required-error` | authkit - authentication-errors - organization-authentication-required-error | +| `/authentication-errors/organization-selection-error` | authkit - authentication-errors - organization-selection-error | +| `/authentication-errors/sso-required-error` | authkit - authentication-errors - sso-required-error | +| `/authentication/code` | authkit - authentication - code | +| `/authentication/email-verification` | authkit - authentication - email-verification | +| `/authentication/get-authorization-url` | authkit - authentication - get-authorization-url | +| `/authentication/get-authorization-url/error-codes` | authkit - authentication - get-authorization-url - error-codes | +| `/authentication/get-authorization-url/pkce` | authkit - authentication - get-authorization-url - pkce | +| `/authentication/get-authorization-url/redirect-uri` | authkit - authentication - get-authorization-url - redirect-uri | +| `/authentication/magic-auth` | authkit - authentication - magic-auth | +| `/authentication/organization-selection` | authkit - authentication - organization-selection | +| `/authentication/password` | authkit - authentication - password | +| `/authentication/refresh-and-seal-session-data` | authkit - authentication - refresh-and-seal-session-data | +| `/authentication/refresh-token` | authkit - authentication - refresh-token | +| `/authentication/session-cookie` | authkit - authentication - session-cookie | +| `/authentication/totp` | authkit - authentication - totp | +| `/cli-auth` | authkit - cli-auth | +| `/cli-auth/device-authorization` | Initiate the CLI Auth flow by obtaining a device code and verification URLs. | +| `/cli-auth/device-code` | Exchange a device code for access and refresh tokens during the CLI Auth flow. | +| `/cli-auth/error-codes` | authkit - cli-auth - error-codes | +| `/email-verification` | authkit - email-verification | +| `/email-verification/get` | authkit - email-verification - get | +| `/identity` | authkit - identity | +| `/identity/list` | authkit - identity - list | +| `/invitation` | authkit - invitation | +| `/invitation/accept` | authkit - invitation - accept | +| `/invitation/find-by-token` | authkit - invitation - find-by-token | +| `/invitation/get` | authkit - invitation - get | +| `/invitation/list` | authkit - invitation - list | +| `/invitation/resend` | authkit - invitation - resend | +| `/invitation/revoke` | authkit - invitation - revoke | +| `/invitation/send` | authkit - invitation - send | +| `/logout` | authkit - logout | +| `/logout/get-logout-url` | authkit - logout - get-logout-url | +| `/logout/get-logout-url-from-session-cookie` | authkit - logout - get-logout-url-from-session-cookie | +| `/magic-auth` | authkit - magic-auth | +| `/magic-auth/create` | authkit - magic-auth - create | +| `/magic-auth/get` | authkit - magic-auth - get | +| `/mfa` | Enroll users in multi-factor authentication for an additional layer of security. | +| `/mfa/authentication-challenge` | authkit - mfa - authentication-challenge | +| `/mfa/authentication-factor` | authkit - mfa - authentication-factor | +| `/mfa/enroll-auth-factor` | authkit - mfa - enroll-auth-factor | +| `/mfa/list-auth-factors` | authkit - mfa - list-auth-factors | +| `/organization-membership` | authkit - organization-membership | +| `/organization-membership/create` | authkit - organization-membership - create | +| `/organization-membership/deactivate` | authkit - organization-membership - deactivate | +| `/organization-membership/delete` | authkit - organization-membership - delete | +| `/organization-membership/get` | authkit - organization-membership - get | +| `/organization-membership/list` | authkit - organization-membership - list | +| `/organization-membership/reactivate` | authkit - organization-membership - reactivate | +| `/organization-membership/update` | authkit - organization-membership - update | +| `/password-reset` | authkit - password-reset | +| `/password-reset/create` | authkit - password-reset - create | +| `/password-reset/get` | authkit - password-reset - get | +| `/password-reset/reset-password` | authkit - password-reset - reset-password | +| `/session` | authkit - session | +| `/session-helpers` | session-helpers | +| `/session-helpers/authenticate` | authkit - session-helpers - authenticate | +| `/session-helpers/get-logout-url` | authkit - session-helpers - get-logout-url | +| `/session-helpers/load-sealed-session` | authkit - session-helpers - load-sealed-session | +| `/session-helpers/refresh` | authkit - session-helpers - refresh | +| `/session-tokens` | authkit - session-tokens | +| `/session-tokens/access-token` | authkit - session-tokens - access-token | +| `/session-tokens/jwks` | authkit - session-tokens - jwks | +| `/session-tokens/refresh-token` | authkit - session-tokens - refresh-token | +| `/session/list` | authkit - session - list | +| `/session/revoke` | authkit - session - revoke | +| `/user` | authkit - user | +| `/user/create` | authkit - user - create | +| `/user/delete` | authkit - user - delete | +| `/user/get` | authkit - user - get | +| `/user/get-by-external-id` | authkit - user - get-by-external-id | +| `/user/list` | authkit - user - list | +| `/user/update` | authkit - user - update | diff --git a/plugins/workos/skills/workos/references/workos-api-organization.md b/plugins/workos/skills/workos/references/workos-api-organization.md index 867d087..a051921 100644 --- a/plugins/workos/skills/workos/references/workos-api-organization.md +++ b/plugins/workos/skills/workos/references/workos-api-organization.md @@ -1,23 +1,26 @@ # WorkOS Organizations API Reference ## Docs + - https://workos.com/docs/reference/organization - https://workos.com/docs/reference/organization/create - https://workos.com/docs/reference/organization/delete - https://workos.com/docs/reference/organization/get - https://workos.com/docs/reference/organization/get-by-external-id -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + (none yet — add as discovered) ## Endpoints -| Endpoint | Description | -|----------|-------------| -| `/organization` | organization | -| `/create` | organization - create | -| `/delete` | organization - delete | -| `/get` | organization - get | + +| Endpoint | Description | +| --------------------- | --------------------------------- | +| `/organization` | organization | +| `/create` | organization - create | +| `/delete` | organization - delete | +| `/get` | organization - get | | `/get-by-external-id` | organization - get-by-external-id | -| `/list` | organization - list | -| `/update` | organization - update | +| `/list` | organization - list | +| `/update` | organization - update | diff --git a/plugins/workos/skills/workos/references/workos-audit-logs.md b/plugins/workos/skills/workos/references/workos-audit-logs.md index b93186c..22141c9 100644 --- a/plugins/workos/skills/workos/references/workos-audit-logs.md +++ b/plugins/workos/skills/workos/references/workos-audit-logs.md @@ -1,6 +1,7 @@ # WorkOS Audit Logs ## Docs + - https://workos.com/docs/audit-logs/metadata-schema - https://workos.com/docs/audit-logs/log-streams - https://workos.com/docs/audit-logs/index @@ -12,9 +13,10 @@ - https://workos.com/docs/reference/audit-logs/event - https://workos.com/docs/reference/audit-logs/event/create - https://workos.com/docs/reference/audit-logs/export -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Event type naming MUST follow `{group}.{object}.{action}` convention (e.g., `user.account.created`). Flat names like `shareCreated` are rejected. - Metadata limits: 50 keys max per metadata object, 40 chars per key name, 500 chars per value. Exceeding silently truncates or fails. - Log Streams to customer SIEMs via HTTP POST require IP allowlisting. WorkOS US egress IPs: `3.217.146.166`, `23.21.184.92`, `34.204.154.149`, `44.213.245.178`, `44.215.236.82`, `50.16.203.9`. EU region uses different IPs — check docs. @@ -22,6 +24,7 @@ If this file conflicts with fetched docs, follow the docs. - Log Stream must show "Active" status in Dashboard to deliver events. Credential or network issues silently stop delivery without failing the event creation call. ## Endpoints + | Endpoint | Description | | ---------------------- | ---------------------------------- | | `/audit-logs` | audit-logs | diff --git a/plugins/workos/skills/workos/references/workos-authkit-nextjs.md b/plugins/workos/skills/workos/references/workos-authkit-nextjs.md index a35a9e9..f13f63c 100644 --- a/plugins/workos/skills/workos/references/workos-authkit-nextjs.md +++ b/plugins/workos/skills/workos/references/workos-authkit-nextjs.md @@ -160,24 +160,53 @@ export default function RootLayout({ children }: { children: React.ReactNode }) ## Step 8: UI Integration -Add auth UI to `app/page.tsx` using SDK functions. See README for auth helper usage (`withAuth`/`getUser`, `getSignInUrl`, `signOut`). +Use **server helpers only for read-only auth checks** in Server Components. Use **client helpers for interactive auth UI** like shared nav/header buttons. -**IMPORTANT: Server Component Cookie Safety (Next.js 15+/16)** +### Server Components (safe) -`getSignInUrl()` internally sets a PKCE cookie via `cookies()`. In Next.js 15+, `cookies()` can only be called in: -- Route Handlers -- Server Actions -- Middleware/Proxy +Use `withAuth()` / `getUser()` in `app/page.tsx`, `app/dashboard/page.tsx`, layouts, and route handlers to read auth state. -It **cannot** be called in a Server Component (like `app/page.tsx`). If you call `getSignInUrl()` directly in a page component, you'll get: -> "Cookies can only be modified in a Server Action or Route Handler" +**Do not** call `getSignInUrl()` / `getSignUpUrl()` during Server Component render. Those helpers set PKCE cookies via `cookies()` and will throw in Next.js 15+/16. -**Correct patterns:** -1. Use `getSignInUrl()` in a Server Action, then pass the URL to the page -2. Use a simple link to `/auth/callback` or the AuthKit-managed sign-in endpoint -3. Use `withAuth()` / `getUser()` for checking auth state (read-only, safe in Server Components) +### Shared nav/header auth UI (preferred) -See README for the recommended approach for your SDK version. +If you need a reusable `nav-auth.tsx` / header auth button, make it a **client component** and use `useAuth()` from the same client entrypoint as `AuthKitProvider` (check README for the exact import path for your SDK version). + +Use `refreshAuth({ ensureSignedIn: true })` from a click handler to start sign-in instead of computing a sign-in URL during render. + +```tsx +'use client'; +// Check README for exact import path for your SDK version +import { useAuth } from '@workos-inc/authkit-nextjs/components'; + +export function NavAuth() { + const { user, isLoading, refreshAuth } = useAuth(); + + if (isLoading) return null; + + if (user) { + return Dashboard; + } + + return ( + + ); +} +``` + +### If you need a server-generated sign-in URL + +Call `getSignInUrl()` **only** inside a Server Action or Route Handler. It is the safe wrapper for AuthKit sign-in/sign-up flows. + +### Critical auth URL gotchas + +- **Never** call `getSignInUrl()` / `getSignUpUrl()` inside a Server Component render (`page.tsx`, `layout.tsx`, async `nav-auth.tsx`, etc.). +- **Never** use raw `getAuthorizationUrl()` for AuthKit UI flows. It returns an object like `{ url, sealedState }`, **not** a URL string. +- If you bypass `getSignInUrl()`, the PKCE cookie is never set (`sealedState` is discarded), which causes `OAuth state mismatch` on the callback. +- If you pass the raw `getAuthorizationUrl()` result to `window.location.href`, the browser will navigate to `/[object Object]`. +- For server-side redirects, use `getSignInUrl()` in a Server Action / Route Handler. For client-side nav buttons, use `refreshAuth({ ensureSignedIn: true })`. **Note:** The SDK renamed `getUser` to `withAuth` in newer versions. Use whichever function the installed SDK version exports — do NOT rename existing working imports. @@ -195,12 +224,25 @@ grep "AuthKitProvider" app/layout.tsx || echo "FAIL: AuthKitProvider missing fro # 3. Check callback route exists find app -name "route.ts" -path "*/callback/*" -# 4. Build succeeds +# 4. Audit for raw getAuthorizationUrl usage — always unsafe in app sign-in/sign-up flows +rg -n "getAuthorizationUrl|window\.location\.href\s*=\s*auth\.signInUrl" app src/app src 2>/dev/null || true + +# 5. Audit getSignInUrl() usage — safe in Server Actions/Route Handlers, unsafe in page/layout/component render +rg -n "getSignInUrl\(" app src/app 2>/dev/null || true + +# 6. Build succeeds npm run build ``` **If check #2 fails:** Go back to Step 6 and add AuthKitProvider. This is not optional. +**Manual verification before marking complete:** + +1. Click **Sign in** and confirm the browser goes to a real WorkOS URL — **not** `/[object Object]` +2. Complete auth and confirm callback succeeds without `OAuth state mismatch` +3. If `rg` finds `getSignInUrl(` in `page.tsx`, `layout.tsx`, or async server-rendered nav components, move that logic to a client component, Server Action, or Route Handler +4. If `rg` finds `getAuthorizationUrl` in sign-in/sign-up code paths, replace it with `getSignInUrl()` / `getSignUpUrl()` + ## Error Recovery ### "cookies was called outside a request scope" (Next.js 15+) @@ -221,9 +263,31 @@ This error causes OAuth codes to expire ("invalid_grant"), so fix the handler fi **Cause:** `getSignInUrl()` or `getSignUpUrl()` called directly in a Server Component. These functions set a PKCE cookie internally and must run in a Server Action or Route Handler. **Fix:** + 1. Move the `getSignInUrl()` call to a Server Action 2. Or create a Route Handler that redirects to the sign-in URL -3. Do NOT call `getSignInUrl()` at the top level of a page component +3. Or convert the shared auth UI to a client component and call `refreshAuth({ ensureSignedIn: true })` +4. Do NOT call `getSignInUrl()` at the top level of a page component + +### `GET /[object%20Object]` or `window.location.href = "[object Object]"` + +**Cause:** Code used raw `getAuthorizationUrl()` output as `signInUrl`. That helper returns `{ url, sealedState }`, not a string. + +**Fix:** + +1. Replace raw `getAuthorizationUrl()` usage with `getSignInUrl()` (or `getSignUpUrl()`) +2. If you must inspect the lower-level helper, extract `.url` and preserve `sealedState` — but prefer the AuthKit wrapper +3. Verify the provider/browser redirect receives a real string URL before assigning `window.location.href` + +### "OAuth state mismatch" + +**Cause:** The code bypassed `getSignInUrl()` and discarded `sealedState`, so the PKCE state cookie was never set. + +**Fix:** + +1. Use `getSignInUrl()` in a Server Action or Route Handler so AuthKit sets the PKCE cookie +2. For client-side sign-in buttons, use `refreshAuth({ ensureSignedIn: true })` +3. Do not hand-roll the sign-in action with raw `getAuthorizationUrl()` unless you also persist `sealedState` exactly as the SDK expects ### "middleware.ts not found" diff --git a/plugins/workos/skills/workos/references/workos-authkit-vanilla-js.md b/plugins/workos/skills/workos/references/workos-authkit-vanilla-js.md index 0bc8369..ff69f02 100644 --- a/plugins/workos/skills/workos/references/workos-authkit-vanilla-js.md +++ b/plugins/workos/skills/workos/references/workos-authkit-vanilla-js.md @@ -69,7 +69,7 @@ pnpm build 2>/dev/null || echo "CDN project — verify manually in browser" ## Error Recovery | Error | Cause | Fix | -| -------------------------------- | -------------------- | ------------------------------------------------------ | +| -------------------------------- | ------------------- | ------------------------------------------------------ | | `WorkOS is not defined` | CDN not loaded | Add script to `` before your code | | `createClient is not a function` | Wrong import | npm: check import path; CDN: use `WorkOS.createClient` | | `clientId is required` | Undefined env var | Check env prefix matches build tool | diff --git a/plugins/workos/skills/workos/references/workos-custom-domains.md b/plugins/workos/skills/workos/references/workos-custom-domains.md index db1ede6..43b66a6 100644 --- a/plugins/workos/skills/workos/references/workos-custom-domains.md +++ b/plugins/workos/skills/workos/references/workos-custom-domains.md @@ -1,14 +1,16 @@ # WorkOS Custom Domains ## Docs + - https://workos.com/docs/custom-domains/index - https://workos.com/docs/custom-domains/email - https://workos.com/docs/custom-domains/authkit - https://workos.com/docs/custom-domains/auth-api - https://workos.com/docs/custom-domains/admin-portal -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Custom domains are production-environment only. Staging environments always use `workos.dev` and cannot be customized. - AuthKit custom domains require updating ALL callback URLs in production code, environment variables (`WORKOS_REDIRECT_URI`), and OAuth app registration in the WorkOS Dashboard. Missing any one causes "Invalid redirect_uri" errors. - DNS verification can take 24-48 hours. Do not proceed with code changes until Dashboard shows "Verified" status. diff --git a/plugins/workos/skills/workos/references/workos-directory-sync.md b/plugins/workos/skills/workos/references/workos-directory-sync.md index 1a8ed32..c384d87 100644 --- a/plugins/workos/skills/workos/references/workos-directory-sync.md +++ b/plugins/workos/skills/workos/references/workos-directory-sync.md @@ -1,19 +1,21 @@ # WorkOS Directory Sync ## Docs + - https://workos.com/docs/directory-sync/quick-start - https://workos.com/docs/directory-sync/understanding-events - https://workos.com/docs/directory-sync/handle-inactive-users - https://workos.com/docs/directory-sync/attributes -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - dsync.deleted sends ONE event for the whole directory — it does NOT send individual dsync.user.deleted or dsync.group.deleted events. You must cascade-delete all users and groups by directory_id yourself. -- Use email as stable user identity, NOT the WorkOS directory_user_* ID — the ID changes if a user is recreated in the IdP. Upsert by email. +- Use email as stable user identity, NOT the WorkOS directory*user*\* ID — the ID changes if a user is recreated in the IdP. Upsert by email. - Return 200 from webhook handler IMMEDIATELY (WorkOS times out at 10s) — process events asynchronously after acknowledging - Webhooks are NOT mandatory — the Events API (workos.events.listEvents) is a fully supported pull-based alternative for batch processing - Webhook signature verification must use the RAW request body, not parsed JSON — parsing first breaks the signature -- Use dsync.* wildcard for Events API filter, not just "dsync" — bare string returns nothing +- Use dsync.\* wildcard for Events API filter, not just "dsync" — bare string returns nothing - Events API after param must be within 30-day retention window - User state "inactive" is far more common than "deleted" — most IdPs deactivate users rather than deleting them. Handle dsync.user.updated with state=inactive as a deprovisioning event. - Webhook handler pattern: call workos.webhooks.verifyEvent() with raw body + workos-signature header + secret, THEN return 200, THEN process event in async handler. Order matters. @@ -22,16 +24,16 @@ If this file conflicts with fetched docs, follow the docs. ## Endpoints -| Endpoint | Description | -|----------|-------------| -| `/directory-sync` | Directory Sync overview | -| `/directory` | Directory management | -| `/directory-group` | Directory group operations | -| `/directory-group/get` | Get a directory group | -| `/directory-group/list` | List directory groups | -| `/directory-user` | Directory user operations | -| `/directory-user/get` | Get a directory user | -| `/directory-user/list` | List directory users | -| `/directory/delete` | Delete a directory | -| `/directory/get` | Get a directory | -| `/directory/list` | List directories | +| Endpoint | Description | +| ----------------------- | -------------------------- | +| `/directory-sync` | Directory Sync overview | +| `/directory` | Directory management | +| `/directory-group` | Directory group operations | +| `/directory-group/get` | Get a directory group | +| `/directory-group/list` | List directory groups | +| `/directory-user` | Directory user operations | +| `/directory-user/get` | Get a directory user | +| `/directory-user/list` | List directory users | +| `/directory/delete` | Delete a directory | +| `/directory/get` | Get a directory | +| `/directory/list` | List directories | diff --git a/plugins/workos/skills/workos/references/workos-email.md b/plugins/workos/skills/workos/references/workos-email.md index 2f34a19..41d2a95 100644 --- a/plugins/workos/skills/workos/references/workos-email.md +++ b/plugins/workos/skills/workos/references/workos-email.md @@ -1,10 +1,12 @@ # WorkOS Email Delivery ## Docs + - https://workos.com/docs/email -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - WorkOS sends auth emails automatically (Magic Auth, invitations, password resets). This feature is about configuring the sender domain, not writing email-sending code. - Do NOT manually configure SPF/DKIM TXT records. WorkOS uses SendGrid's automated security via CNAMEs. Adding custom SPF/DKIM records will break authentication. - You must set up actual inboxes for `welcome@` and `access@`. Email providers check if sender addresses are real — no inbox means higher spam score. diff --git a/plugins/workos/skills/workos/references/workos-events.md b/plugins/workos/skills/workos/references/workos-events.md index 8440c54..74ba619 100644 --- a/plugins/workos/skills/workos/references/workos-events.md +++ b/plugins/workos/skills/workos/references/workos-events.md @@ -1,6 +1,7 @@ # WorkOS Events ## Docs + - https://workos.com/docs/events/index - https://workos.com/docs/events/observability/datadog - https://workos.com/docs/events/data-syncing/webhooks @@ -9,9 +10,10 @@ - https://workos.com/docs/events/data-syncing/data-reconciliation - https://workos.com/docs/reference/events - https://workos.com/docs/reference/events/list -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Do not implement both webhooks and Events API polling simultaneously — this causes duplicate event processing. - Webhook endpoints must return 200 OK within 5 seconds. Acknowledge immediately, process asynchronously. - Verify webhook signature before processing using the raw request body. JSON-parsing the body before verification breaks the signature check. @@ -21,6 +23,7 @@ If this file conflicts with fetched docs, follow the docs. - Do NOT backfill by replaying webhooks. Webhook signatures expire. Always use the Events API for historical data. ## Endpoints + | Endpoint | Description | | --------- | ------------- | | `/events` | events | diff --git a/plugins/workos/skills/workos/references/workos-mfa.md b/plugins/workos/skills/workos/references/workos-mfa.md index 5ab9119..336c4a1 100644 --- a/plugins/workos/skills/workos/references/workos-mfa.md +++ b/plugins/workos/skills/workos/references/workos-mfa.md @@ -1,13 +1,15 @@ # WorkOS Multi-Factor Authentication ## Docs + - https://workos.com/docs/mfa/index - https://workos.com/docs/mfa/example-apps - https://workos.com/docs/mfa/ux/sign-in - https://workos.com/docs/mfa/ux/enrollment -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - MFA API is NOT compatible with WorkOS SSO. For SSO users, use the IdP's built-in MFA features instead. - You must persist `factor.id` in your user database. Without it, the enrolled factor cannot be used for future challenges. - Challenges are single-use. Attempting to verify a challenge twice returns "challenge already verified." Create a new challenge for each sign-in attempt. diff --git a/plugins/workos/skills/workos/references/workos-migrate-auth0.md b/plugins/workos/skills/workos/references/workos-migrate-auth0.md index e5a059d..defc8f9 100644 --- a/plugins/workos/skills/workos/references/workos-migrate-auth0.md +++ b/plugins/workos/skills/workos/references/workos-migrate-auth0.md @@ -1,10 +1,12 @@ # WorkOS Migration: Auth0 ## Docs + - https://workos.com/docs/migrate/auth0 -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Auth0 does NOT export plaintext passwords. Only bcrypt hashes are available, and only via a support ticket (1-2 week turnaround). Decide upfront whether you need them — requesting after starting the export adds weeks to the timeline. - `password_hash_type` MUST be `'bcrypt'` for Auth0 exports. Other algorithm values will silently fail. - Auth0 bcrypt hashes must start with `$2a$`, `$2b$`, or `$2y$`. If the prefix is missing, the export may be incomplete — contact Auth0 support. diff --git a/plugins/workos/skills/workos/references/workos-migrate-aws-cognito.md b/plugins/workos/skills/workos/references/workos-migrate-aws-cognito.md index 6dc10b1..c3d3bf3 100644 --- a/plugins/workos/skills/workos/references/workos-migrate-aws-cognito.md +++ b/plugins/workos/skills/workos/references/workos-migrate-aws-cognito.md @@ -1,10 +1,12 @@ # WorkOS Migration: AWS Cognito ## Docs + - https://workos.com/docs/migrate/aws-cognito -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - AWS Cognito does not export password hashes or MFA keys (Cognito platform limitation). WorkOS supports hash import for other providers (bcrypt, scrypt, argon2, pbkdf2, ssha, firebase-scrypt), but since Cognito won't export them, all migrated users must reset their password. - There is NO JIT (just-in-time) migration path for Cognito. Cognito does not expose a password verification endpoint. The only path is bulk import of user attributes + forced password reset. - OAuth users do NOT need password resets — their provider tokens continue working after migration. diff --git a/plugins/workos/skills/workos/references/workos-migrate-better-auth.md b/plugins/workos/skills/workos/references/workos-migrate-better-auth.md index b781bcf..11dddc5 100644 --- a/plugins/workos/skills/workos/references/workos-migrate-better-auth.md +++ b/plugins/workos/skills/workos/references/workos-migrate-better-auth.md @@ -1,10 +1,12 @@ # WorkOS Migration: Better Auth ## Docs + - https://workos.com/docs/migrate/better-auth -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Password hashes live in the `account` table (not `user`), filtered by `providerId = 'credential'`. Do NOT skip this table. - Better Auth uses scrypt by default. If you customized the hash algorithm, you must know which one it is before importing to WorkOS. - The `password` column contains the full hash string including algorithm parameters. Do NOT strip prefixes or decode it. diff --git a/plugins/workos/skills/workos/references/workos-migrate-clerk.md b/plugins/workos/skills/workos/references/workos-migrate-clerk.md index afb22f1..00492d7 100644 --- a/plugins/workos/skills/workos/references/workos-migrate-clerk.md +++ b/plugins/workos/skills/workos/references/workos-migrate-clerk.md @@ -1,10 +1,12 @@ # WorkOS Migration: Clerk ## Docs + - https://workos.com/docs/migrate/clerk -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Clerk exports multiple emails pipe-separated (e.g., `john@example.com|john.doe@example.com`) and does NOT indicate which is the primary email. If you can't call the Clerk API per user to resolve `primary_email_address_id`, you must pick the first email and document the choice. - Clerk does NOT provide plaintext passwords. Password hashes are only available via the Clerk Backend API export, not the standard dashboard export. - WorkOS users have a single primary email. You must pick ONE from Clerk's pipe-separated list. diff --git a/plugins/workos/skills/workos/references/workos-migrate-descope.md b/plugins/workos/skills/workos/references/workos-migrate-descope.md index 90c8e4c..1ac2df0 100644 --- a/plugins/workos/skills/workos/references/workos-migrate-descope.md +++ b/plugins/workos/skills/workos/references/workos-migrate-descope.md @@ -1,10 +1,12 @@ # WorkOS Migration: Descope ## Docs + - https://workos.com/docs/migrate/descope -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Descope does NOT expose password hashes via API. A support ticket is required to get a CSV export with hashes — this is not self-service. - When requesting the export from Descope support, confirm which hashing algorithm was used (bcrypt, argon2, or pbkdf2). You need this value for the `password_hash_type` parameter during WorkOS import. - The algorithm name must match exactly: `"bcrypt"` not `"bcrypt_sha256"` or `"bcrypt-sha256"`. Typos cause silent rejection. diff --git a/plugins/workos/skills/workos/references/workos-migrate-firebase.md b/plugins/workos/skills/workos/references/workos-migrate-firebase.md index 5b5d525..6a9278e 100644 --- a/plugins/workos/skills/workos/references/workos-migrate-firebase.md +++ b/plugins/workos/skills/workos/references/workos-migrate-firebase.md @@ -1,10 +1,12 @@ # WorkOS Migration: Firebase ## Docs + - https://workos.com/docs/migrate/firebase -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Firebase uses a non-standard scrypt variant. Password hashes must be converted to PHC format with Firebase-specific parameters (`sk=` signer key, `ss=` salt separator). Missing either parameter causes import failure. - Firebase exports use base64 encoding. WorkOS expects base64 in the PHC string — do NOT decode the bytes before importing. - Firebase users can have MULTIPLE auth methods (e.g., password + Google). A user with both needs both migration paths (password import AND OAuth reconnection). diff --git a/plugins/workos/skills/workos/references/workos-migrate-other-services.md b/plugins/workos/skills/workos/references/workos-migrate-other-services.md index a5fb73a..d6f0df6 100644 --- a/plugins/workos/skills/workos/references/workos-migrate-other-services.md +++ b/plugins/workos/skills/workos/references/workos-migrate-other-services.md @@ -1,10 +1,12 @@ # WorkOS Migration: Other Services ## Docs + - https://workos.com/docs/migrate/other-services -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - WorkOS only supports these hash algorithms for password import: bcrypt, scrypt, pbkdf2, argon2, ssha, firebase-scrypt. If your source uses md5, sha1, or a custom algorithm, you cannot import passwords — use the password reset flow instead. - OAuth tokens CANNOT be imported for security reasons. Social auth users must re-authenticate with their provider. WorkOS links accounts automatically by email match — if emails differ between WorkOS and the social profile, the user sees a "create new account" flow instead of linking. - WorkOS user IDs (`user_01...`) are new. You MUST persist the mapping from your old system's IDs. Failing to do so breaks all foreign key references. diff --git a/plugins/workos/skills/workos/references/workos-migrate-stytch.md b/plugins/workos/skills/workos/references/workos-migrate-stytch.md index 9876fac..62bd4ce 100644 --- a/plugins/workos/skills/workos/references/workos-migrate-stytch.md +++ b/plugins/workos/skills/workos/references/workos-migrate-stytch.md @@ -1,10 +1,12 @@ # WorkOS Migration: Stytch ## Docs + - https://workos.com/docs/migrate/stytch -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Stytch password export requires a support ticket (support@stytch.com). Start this FIRST — it's the bottleneck with variable turnaround time. Do not proceed with password import until hashes are received. - Stytch uses scrypt for password hashing. Verify the hash format from the Stytch export matches WorkOS requirements before bulk import — test with ONE user first. - Stytch Search API has a 100 requests/minute rate limit. For large datasets, add delays between batches or you'll get throttled during export. diff --git a/plugins/workos/skills/workos/references/workos-migrate-supabase-auth.md b/plugins/workos/skills/workos/references/workos-migrate-supabase-auth.md index 0a959e2..d6555b6 100644 --- a/plugins/workos/skills/workos/references/workos-migrate-supabase-auth.md +++ b/plugins/workos/skills/workos/references/workos-migrate-supabase-auth.md @@ -1,10 +1,12 @@ # WorkOS Migration: Supabase Auth ## Docs + - https://workos.com/docs/migrate/supabase -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Supabase uses bcrypt. Verify exported hashes start with `$2a$`, `$2b$`, or `$2y$`. If verification fails, re-export — corrupted hashes cannot be recovered. - Do NOT use Supabase UUIDs as WorkOS user IDs. WorkOS generates its own IDs (`user_*`). Store the mapping (`supabase_user_id` -> `workos_user_id`) in your database. - You CANNOT migrate active sessions from Supabase to WorkOS. Sessions are provider-specific. Users with valid Supabase sessions must be forced to re-authenticate with WorkOS. diff --git a/plugins/workos/skills/workos/references/workos-migrate-the-standalone-sso-api.md b/plugins/workos/skills/workos/references/workos-migrate-the-standalone-sso-api.md index 668d8cd..12e584c 100644 --- a/plugins/workos/skills/workos/references/workos-migrate-the-standalone-sso-api.md +++ b/plugins/workos/skills/workos/references/workos-migrate-the-standalone-sso-api.md @@ -1,10 +1,12 @@ # WorkOS Migration: Standalone SSO API ## Docs + - https://workos.com/docs/migrate/standalone-sso -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - User IDs change when migrating from standalone SSO API to AuthKit. Old Profile IDs (`user_xxx` from `sso.getProfileAndToken`) are NOT the same as new AuthKit User IDs. If Profile IDs are foreign keys in your database, you need a migration script before cutover. - `authenticateWithCode` requires a `clientId` parameter (`client_xxx`) that the standalone SSO API's `getProfileAndToken` did not require. Missing or wrong `clientId` causes "Invalid client_id" errors. - AuthKit returns new error types that the standalone SSO API never produced: `email_verification_required`, `mfa_enrollment_required`, and `mfa_challenge_required`. If your callback doesn't handle these, logins will appear to fail silently. diff --git a/plugins/workos/skills/workos/references/workos-rbac.md b/plugins/workos/skills/workos/references/workos-rbac.md index 0cae966..db28487 100644 --- a/plugins/workos/skills/workos/references/workos-rbac.md +++ b/plugins/workos/skills/workos/references/workos-rbac.md @@ -1,13 +1,15 @@ # WorkOS Role-Based Access Control ## Docs + - https://workos.com/docs/rbac/quick-start - https://workos.com/docs/rbac/organization-roles - https://workos.com/docs/rbac/integration - https://workos.com/docs/rbac/idp-role-assignment -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Always check permissions (role.permissions.includes('action')), NOT role slugs (role.slug === 'admin') — slug checks break in multi-org with custom roles. Claude defaults to slug checks. - Role assignment requires the MEMBERSHIP ID, not the user ID — fetch via listOrganizationMemberships() first, then call updateOrganizationMembership(membershipId, { roleSlug }) - IdP group mapping OVERRIDES API/Dashboard role assignments on every auth — updateOrganizationMembership() changes silently revert on next login if IdP mapping exists @@ -19,28 +21,28 @@ If this file conflicts with fetched docs, follow the docs. ## Endpoints -| Endpoint | Description | -|----------|-------------| -| `/roles` | Roles overview | -| `/organization-role` | Organization role management | -| `/organization-role/add-permission` | Add permission to org role | -| `/organization-role/create` | Create org role | -| `/organization-role/delete` | Delete org role | -| `/organization-role/get` | Get org role | -| `/organization-role/list` | List org roles | +| Endpoint | Description | +| -------------------------------------- | ------------------------------- | +| `/roles` | Roles overview | +| `/organization-role` | Organization role management | +| `/organization-role/add-permission` | Add permission to org role | +| `/organization-role/create` | Create org role | +| `/organization-role/delete` | Delete org role | +| `/organization-role/get` | Get org role | +| `/organization-role/list` | List org roles | | `/organization-role/remove-permission` | Remove permission from org role | -| `/organization-role/set-permissions` | Set permissions on org role | -| `/organization-role/update` | Update org role | -| `/permission` | Permission management | -| `/permission/create` | Create permission | -| `/permission/delete` | Delete permission | -| `/permission/get` | Get permission | -| `/permission/list` | List permissions | -| `/permission/update` | Update permission | -| `/role` | Environment role management | -| `/role/add-permission` | Add permission to role | -| `/role/create` | Create role | -| `/role/get` | Get role | -| `/role/list` | List roles | -| `/role/set-permissions` | Set permissions on role | -| `/role/update` | Update role | +| `/organization-role/set-permissions` | Set permissions on org role | +| `/organization-role/update` | Update org role | +| `/permission` | Permission management | +| `/permission/create` | Create permission | +| `/permission/delete` | Delete permission | +| `/permission/get` | Get permission | +| `/permission/list` | List permissions | +| `/permission/update` | Update permission | +| `/role` | Environment role management | +| `/role/add-permission` | Add permission to role | +| `/role/create` | Create role | +| `/role/get` | Get role | +| `/role/list` | List roles | +| `/role/set-permissions` | Set permissions on role | +| `/role/update` | Update role | diff --git a/plugins/workos/skills/workos/references/workos-sso.md b/plugins/workos/skills/workos/references/workos-sso.md index d146680..f028542 100644 --- a/plugins/workos/skills/workos/references/workos-sso.md +++ b/plugins/workos/skills/workos/references/workos-sso.md @@ -1,15 +1,17 @@ # WorkOS Single Sign-On ## Docs + - https://workos.com/docs/sso/guide - https://workos.com/docs/sso/login-flows - https://workos.com/docs/reference/sso/get-authorization-url - https://workos.com/docs/sso/redirect-uris - https://workos.com/docs/sso/test-sso - https://workos.com/docs/sso/launch-checklist -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - Use exactly ONE connection selector (connection, organization, or provider) in getAuthorizationUrl — never combine them, causes error - domain_hint and login_hint are UX params, NOT connection selectors — they pre-fill fields but don't route the request - IdP-initiated flow sends state="" (empty string, not missing) — skip CSRF verification for empty string, reject for null/missing @@ -21,19 +23,19 @@ If this file conflicts with fetched docs, follow the docs. ## Endpoints -| Endpoint | Description | -|----------|-------------| -| `/sso` | SSO overview | -| `/connection` | SSO connection management | -| `/connection/delete` | Delete a connection | -| `/connection/get` | Get a connection | -| `/connection/list` | List connections | -| `/get-authorization-url` | Generate authorization URL | -| `/get-authorization-url/error-codes` | Authorization error codes | -| `/get-authorization-url/redirect-uri` | Redirect URI configuration | -| `/logout` | SSO logout | -| `/logout/authorize` | Authorize logout | -| `/logout/redirect` | Logout redirect | -| `/profile` | User profile | -| `/profile/get-profile-and-token` | Exchange code for profile + token | -| `/profile/get-user-profile` | Get user profile by ID | +| Endpoint | Description | +| ------------------------------------- | --------------------------------- | +| `/sso` | SSO overview | +| `/connection` | SSO connection management | +| `/connection/delete` | Delete a connection | +| `/connection/get` | Get a connection | +| `/connection/list` | List connections | +| `/get-authorization-url` | Generate authorization URL | +| `/get-authorization-url/error-codes` | Authorization error codes | +| `/get-authorization-url/redirect-uri` | Redirect URI configuration | +| `/logout` | SSO logout | +| `/logout/authorize` | Authorize logout | +| `/logout/redirect` | Logout redirect | +| `/profile` | User profile | +| `/profile/get-profile-and-token` | Exchange code for profile + token | +| `/profile/get-user-profile` | Get user profile by ID | diff --git a/plugins/workos/skills/workos/references/workos-vault.md b/plugins/workos/skills/workos/references/workos-vault.md index 1a71b0b..1aaca06 100644 --- a/plugins/workos/skills/workos/references/workos-vault.md +++ b/plugins/workos/skills/workos/references/workos-vault.md @@ -1,6 +1,7 @@ # WorkOS Vault ## Docs + - https://workos.com/docs/vault/quick-start - https://workos.com/docs/vault/key-context - https://workos.com/docs/vault/index @@ -10,9 +11,10 @@ - https://workos.com/docs/reference/vault/key/create-data-key - https://workos.com/docs/reference/vault/key/decrypt-data - https://workos.com/docs/reference/vault/key/decrypt-data-key -If this file conflicts with fetched docs, follow the docs. + If this file conflicts with fetched docs, follow the docs. ## Gotchas + - BYOK requires customer-side IAM permissions granting WorkOS access to their KMS. Your app cannot do this programmatically — provide customers with IAM policy templates from the BYOK docs. - Vault encrypts data per WorkOS organization. Every operation requires an `organization_id` — there is no global/unscoped access. - Do NOT use internal customer IDs as `organization_id`. WorkOS organization IDs have format `org_*`. Always map through WorkOS APIs. @@ -21,6 +23,7 @@ If this file conflicts with fetched docs, follow the docs. - BYOK KMS IAM changes can take 5-10 minutes to propagate. Customer must grant `kms:Decrypt` and `kms:Encrypt` on their key. ## Endpoints + | Endpoint | Description | | ----------------------- | ------------------------------ | | `/vault` | vault | diff --git a/scripts/eval/cases/authkit-nextjs.yaml b/scripts/eval/cases/authkit-nextjs.yaml index cee5609..a1722f1 100644 --- a/scripts/eval/cases/authkit-nextjs.yaml +++ b/scripts/eval/cases/authkit-nextjs.yaml @@ -53,7 +53,6 @@ - getUser - withAuth - signOut - - getSignInUrl envVars: [] imports: - '@workos-inc/authkit-nextjs' @@ -64,6 +63,8 @@ - redirect unauthenticated users - render dashboard for authenticated users antiPatterns: + - getSignInUrl in server component + - form action={await getSignInUrl()} - use client for auth check - localStorage for session - manual JWT parsing @@ -98,3 +99,40 @@ - call cookies at module level - synchronous cookie access hallucinations: [] + +- id: authkit-nextjs-nav-auth-pkce + product: authkit + skill: workos-authkit-nextjs + skillType: hand-crafted + language: node + framework: nextjs + prompt: | + I'm on Next.js 16 with WorkOS AuthKit. I added a shared nav-auth.tsx + component and a provider that redirects with window.location.href = + auth.signInUrl, but I hit three issues: calling getSignInUrl() in the + server component throws "Cookies can only be modified in a Server Action or + Route Handler", sometimes the browser goes to /[object Object], and after + that the callback fails with "OAuth state mismatch". What's the correct + implementation pattern? + expected: + methods: + - AuthKitProvider + - useAuth + - refreshAuth + - getSignInUrl + envVars: [] + imports: [] + params: + - ensureSignedIn + flowSteps: + - wrap app in AuthKitProvider + - make nav auth a client component + - call refreshAuth ensureSignedIn + - use getSignInUrl in a server action + - avoid getAuthorizationUrl + antiPatterns: + - getSignInUrl in server component + - use getAuthorizationUrl directly + - window.location.href = auth.signInUrl + - discard sealedState + hallucinations: [] diff --git a/scripts/tests/eval-scorer.spec.ts b/scripts/tests/eval-scorer.spec.ts index e0d61a7..2b4100a 100644 --- a/scripts/tests/eval-scorer.spec.ts +++ b/scripts/tests/eval-scorer.spec.ts @@ -637,3 +637,85 @@ const url = workos.sso.getAuthorizationUrl({ clientId }); expect(errors).not.toContain('missing_env_var'); }); }); + +describe('authkit nextjs regression coverage', () => { + const expected: ExpectedSignals = { + methods: ['AuthKitProvider', 'useAuth', 'refreshAuth', 'getSignInUrl'], + envVars: [], + imports: [], + params: ['ensureSignedIn'], + flowSteps: [ + 'wrap app in AuthKitProvider', + 'make nav auth a client component', + 'call refreshAuth ensureSignedIn', + 'use getSignInUrl in a server action', + 'avoid getAuthorizationUrl', + ], + antiPatterns: [ + 'getSignInUrl in server component', + 'use getAuthorizationUrl directly', + 'window.location.href = auth.signInUrl', + 'discard sealedState', + ], + hallucinations: [], + }; + + const brokenOutput = ` +// app/components/nav-auth.tsx +import { getSignInUrl } from '@workos-inc/authkit-nextjs'; + +export default async function NavAuth() { + const signInUrl = await getSignInUrl(); // getSignInUrl in server component + return Sign in; +} + +// dist/esm/actions.js +import { getAuthorizationUrl } from './get-authorization-url.js'; + +export async function refreshAuthAction() { + const signInUrl = await getAuthorizationUrl({ screenHint: 'sign-in' }); + return { signInUrl }; +} + +window.location.href = auth.signInUrl; +The implementation discards sealedState after calling getAuthorizationUrl directly. + `; + + const fixedOutput = ` +Wrap the app in AuthKitProvider in app/layout.tsx. + +Use getUser() or withAuth() in Server Components only to read auth state. + +Create a client nav auth component: +'use client' +function NavAuth() { + const { user, isLoading, refreshAuth } = useAuth(); + if (isLoading) return null; + if (user) return Dashboard; + return ( + + ); +} + +If you need a server-generated URL, use getSignInUrl() in a Server Action or Route Handler. +Do not use getAuthorizationUrl directly for AuthKit sign-in, because it returns { url, sealedState }. +Do not discard sealedState, and do not assign window.location.href = auth.signInUrl. + `; + + it('scores the fixed pattern higher than the broken pattern', () => { + const broken = scoreOutput(brokenOutput, expected); + const fixed = scoreOutput(fixedOutput, expected); + + expect(fixed.composite).toBeGreaterThan(broken.composite); + expect(fixed.antiPatternAvoidance).toBeGreaterThan(broken.antiPatternAvoidance); + expect(fixed.composite).toBeGreaterThanOrEqual(80); + expect(broken.composite).toBeLessThanOrEqual(60); + }); + + it('flags the broken pattern as an anti-pattern regression', () => { + const errors = categorizeErrors(brokenOutput, expected); + expect(errors).toContain('security_issue'); + }); +});