workos 0.12.0-beta.1 → 0.12.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 (96) hide show
  1. package/README.md +6 -4
  2. package/dist/bin.js +4 -2
  3. package/dist/bin.js.map +1 -1
  4. package/dist/commands/debug.js +0 -1
  5. package/dist/commands/debug.js.map +1 -1
  6. package/dist/commands/login.js +0 -4
  7. package/dist/commands/login.js.map +1 -1
  8. package/dist/emulate/core/id.d.ts +16 -1
  9. package/dist/emulate/core/id.js +16 -1
  10. package/dist/emulate/core/id.js.map +1 -1
  11. package/dist/emulate/core/index.d.ts +1 -1
  12. package/dist/emulate/core/index.js +1 -1
  13. package/dist/emulate/core/index.js.map +1 -1
  14. package/dist/emulate/core/middleware/auth.d.ts +1 -4
  15. package/dist/emulate/core/middleware/auth.js +7 -18
  16. package/dist/emulate/core/middleware/auth.js.map +1 -1
  17. package/dist/emulate/core/pagination.d.ts +6 -0
  18. package/dist/emulate/core/pagination.js +9 -1
  19. package/dist/emulate/core/pagination.js.map +1 -1
  20. package/dist/emulate/core/server.js +20 -46
  21. package/dist/emulate/core/server.js.map +1 -1
  22. package/dist/emulate/core/store.d.ts +2 -0
  23. package/dist/emulate/core/store.js +22 -1
  24. package/dist/emulate/core/store.js.map +1 -1
  25. package/dist/emulate/workos/constants.d.ts +56 -0
  26. package/dist/emulate/workos/constants.js +56 -0
  27. package/dist/emulate/workos/constants.js.map +1 -0
  28. package/dist/emulate/workos/entities.js.map +1 -1
  29. package/dist/emulate/workos/event-bus.d.ts +6 -1
  30. package/dist/emulate/workos/event-bus.js +33 -8
  31. package/dist/emulate/workos/event-bus.js.map +1 -1
  32. package/dist/emulate/workos/helpers.d.ts +17 -8
  33. package/dist/emulate/workos/helpers.js +61 -368
  34. package/dist/emulate/workos/helpers.js.map +1 -1
  35. package/dist/emulate/workos/index.js +42 -39
  36. package/dist/emulate/workos/index.js.map +1 -1
  37. package/dist/emulate/workos/role-helpers.d.ts +21 -0
  38. package/dist/emulate/workos/role-helpers.js +130 -0
  39. package/dist/emulate/workos/role-helpers.js.map +1 -0
  40. package/dist/emulate/workos/routes/api-keys.js +5 -8
  41. package/dist/emulate/workos/routes/api-keys.js.map +1 -1
  42. package/dist/emulate/workos/routes/audit-logs.js +6 -9
  43. package/dist/emulate/workos/routes/audit-logs.js.map +1 -1
  44. package/dist/emulate/workos/routes/auth.js +7 -6
  45. package/dist/emulate/workos/routes/auth.js.map +1 -1
  46. package/dist/emulate/workos/routes/authorization-checks.js +5 -17
  47. package/dist/emulate/workos/routes/authorization-checks.js.map +1 -1
  48. package/dist/emulate/workos/routes/authorization-org-roles.js +29 -171
  49. package/dist/emulate/workos/routes/authorization-org-roles.js.map +1 -1
  50. package/dist/emulate/workos/routes/authorization-permissions.js +5 -16
  51. package/dist/emulate/workos/routes/authorization-permissions.js.map +1 -1
  52. package/dist/emulate/workos/routes/authorization-resources.js +4 -15
  53. package/dist/emulate/workos/routes/authorization-resources.js.map +1 -1
  54. package/dist/emulate/workos/routes/authorization-roles.d.ts +1 -1
  55. package/dist/emulate/workos/routes/authorization-roles.js +9 -132
  56. package/dist/emulate/workos/routes/authorization-roles.js.map +1 -1
  57. package/dist/emulate/workos/routes/config.js +3 -2
  58. package/dist/emulate/workos/routes/config.js.map +1 -1
  59. package/dist/emulate/workos/routes/connect.js +3 -7
  60. package/dist/emulate/workos/routes/connect.js.map +1 -1
  61. package/dist/emulate/workos/routes/connections.js +3 -7
  62. package/dist/emulate/workos/routes/connections.js.map +1 -1
  63. package/dist/emulate/workos/routes/directories.js +7 -23
  64. package/dist/emulate/workos/routes/directories.js.map +1 -1
  65. package/dist/emulate/workos/routes/events.js +3 -6
  66. package/dist/emulate/workos/routes/events.js.map +1 -1
  67. package/dist/emulate/workos/routes/feature-flags.js +20 -48
  68. package/dist/emulate/workos/routes/feature-flags.js.map +1 -1
  69. package/dist/emulate/workos/routes/invitations.js +10 -13
  70. package/dist/emulate/workos/routes/invitations.js.map +1 -1
  71. package/dist/emulate/workos/routes/memberships.js +3 -7
  72. package/dist/emulate/workos/routes/memberships.js.map +1 -1
  73. package/dist/emulate/workos/routes/organizations.js +13 -15
  74. package/dist/emulate/workos/routes/organizations.js.map +1 -1
  75. package/dist/emulate/workos/routes/pipes.js +3 -7
  76. package/dist/emulate/workos/routes/pipes.js.map +1 -1
  77. package/dist/emulate/workos/routes/radar.js +3 -7
  78. package/dist/emulate/workos/routes/radar.js.map +1 -1
  79. package/dist/emulate/workos/routes/sso.js +7 -6
  80. package/dist/emulate/workos/routes/sso.js.map +1 -1
  81. package/dist/emulate/workos/routes/users.js +3 -7
  82. package/dist/emulate/workos/routes/users.js.map +1 -1
  83. package/dist/emulate/workos/routes/webhook-endpoints.js +3 -7
  84. package/dist/emulate/workos/routes/webhook-endpoints.js.map +1 -1
  85. package/dist/emulate/workos/store.js +68 -59
  86. package/dist/emulate/workos/store.js.map +1 -1
  87. package/dist/lib/run-with-core.js +0 -3
  88. package/dist/lib/run-with-core.js.map +1 -1
  89. package/dist/lib/settings.js +1 -1
  90. package/dist/lib/settings.js.map +1 -1
  91. package/dist/utils/help-json.js +1 -23
  92. package/dist/utils/help-json.js.map +1 -1
  93. package/dist/utils/register-subcommand.d.ts +5 -2
  94. package/dist/utils/register-subcommand.js +16 -19
  95. package/dist/utils/register-subcommand.js.map +1 -1
  96. package/package.json +2 -2
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import { notFound, parseJsonBody, WorkOSApiError, generateId } from '../../core/index.js';
3
3
  import { getWorkOSStore } from '../store.js';
4
4
  import { formatUser, formatDeviceAuthorization, verifyPassword, isExpired, expiresIn, assertLocalRedirectUri, sealSession, } from '../helpers.js';
5
+ import { STORE_KEYS, STORE_KEY_PREFIXES } from '../constants.js';
5
6
  export function authRoutes(ctx) {
6
7
  const { app, store, jwt } = ctx;
7
8
  const ws = getWorkOSStore(store);
@@ -90,7 +91,7 @@ export function authRoutes(ctx) {
90
91
  const code = body.code;
91
92
  if (!code)
92
93
  throw new WorkOSApiError(400, 'code is required', 'invalid_request');
93
- const authCode = ws.authCodes.all().find((ac) => ac.code === code);
94
+ const authCode = ws.authCodes.findOneBy('code', code);
94
95
  if (!authCode)
95
96
  throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');
96
97
  if (isExpired(authCode.expires_at)) {
@@ -201,7 +202,7 @@ export function authRoutes(ctx) {
201
202
  if (!code || !pendingToken || !challengeId) {
202
203
  throw new WorkOSApiError(400, 'code, pending_authentication_token, and authentication_challenge_id are required', 'invalid_request');
203
204
  }
204
- const pending = store.getData(`pending_auth:${pendingToken}`);
205
+ const pending = store.getData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`);
205
206
  if (!pending) {
206
207
  throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');
207
208
  }
@@ -218,7 +219,7 @@ export function authRoutes(ctx) {
218
219
  throw new WorkOSApiError(400, 'Invalid one-time code', 'invalid_one_time_code');
219
220
  }
220
221
  ws.authChallenges.delete(challenge.id);
221
- store.setData(`pending_auth:${pendingToken}`, undefined);
222
+ store.setData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`, undefined);
222
223
  user = ws.users.get(pending.user_id);
223
224
  organizationId = pending.organization_id;
224
225
  authMethod = 'MFA';
@@ -230,14 +231,14 @@ export function authRoutes(ctx) {
230
231
  if (!pendingToken || !orgId) {
231
232
  throw new WorkOSApiError(400, 'pending_authentication_token and organization_id are required', 'invalid_request');
232
233
  }
233
- const pending = store.getData(`pending_auth:${pendingToken}`);
234
+ const pending = store.getData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`);
234
235
  if (!pending) {
235
236
  throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');
236
237
  }
237
238
  const org = ws.organizations.get(orgId);
238
239
  if (!org)
239
240
  throw notFound('Organization');
240
- store.setData(`pending_auth:${pendingToken}`, undefined);
241
+ store.setData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`, undefined);
241
242
  user = ws.users.get(pending.user_id);
242
243
  organizationId = orgId;
243
244
  authMethod = pending.auth_method;
@@ -325,7 +326,7 @@ export function authRoutes(ctx) {
325
326
  ? sealSession({ access_token: accessToken, refresh_token: newRefreshToken.token, session_id: session.id }, sealKey)
326
327
  : null;
327
328
  // Emit authentication event (hybrid Option B for action-specific events)
328
- const eventBus = store.getData('eventBus');
329
+ const eventBus = store.getData(STORE_KEYS.eventBus);
329
330
  if (eventBus) {
330
331
  const authEventType = `authentication.${authMethod.toLowerCase()}_succeeded`;
331
332
  eventBus.emit({
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/auth.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport { type RouteContext, notFound, parseJsonBody, WorkOSApiError, generateId } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport {\n formatUser,\n formatDeviceAuthorization,\n verifyPassword,\n isExpired,\n expiresIn,\n assertLocalRedirectUri,\n sealSession,\n} from '../helpers.js';\nimport type { EventBus } from '../event-bus.js';\n\ninterface PendingAuth {\n user_id: string;\n organization_id: string | null;\n auth_method: string;\n}\n\nexport function authRoutes(ctx: RouteContext): void {\n const { app, store, jwt } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/user_management/authorize', (c) => {\n const url = new URL(c.req.url);\n const redirectUri = url.searchParams.get('redirect_uri');\n const state = url.searchParams.get('state');\n const codeChallenge = url.searchParams.get('code_challenge');\n const codeChallengeMethod = url.searchParams.get('code_challenge_method');\n const loginHint = url.searchParams.get('login_hint');\n\n if (!redirectUri) {\n throw new WorkOSApiError(400, 'redirect_uri is required', 'invalid_request');\n }\n assertLocalRedirectUri(redirectUri);\n\n let user;\n if (loginHint) {\n user = ws.users.findOneBy('email', loginHint);\n if (!user) {\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('error', 'user_not_found');\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n }\n } else {\n const users = ws.users.all();\n user = users[0];\n }\n\n if (!user) {\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('error', 'no_users');\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n }\n\n const authCode = ws.authCodes.insert({\n user_id: user.id,\n organization_id: null,\n code: generateId('auth_code'),\n redirect_uri: redirectUri,\n expires_at: expiresIn(10),\n code_challenge: codeChallenge ?? null,\n code_challenge_method: codeChallengeMethod ?? null,\n });\n\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('code', authCode.code);\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n });\n\n // Device authorization endpoint\n app.post('/user_management/authorize/device', async (c) => {\n const body = await parseJsonBody(c);\n const clientId = body.client_id as string;\n if (!clientId) {\n throw new WorkOSApiError(400, 'client_id is required', 'invalid_request');\n }\n\n // Auto-approve with first user for emulator convenience\n const users = ws.users.all();\n const user = users[0] ?? null;\n\n const deviceAuth = ws.deviceAuthorizations.insert({\n device_code: generateId('dev_code'),\n user_code: Math.random().toString(36).slice(2, 10).toUpperCase(),\n user_id: user?.id ?? null,\n client_id: clientId,\n expires_at: expiresIn(15),\n interval: 5,\n });\n\n return c.json(formatDeviceAuthorization(deviceAuth));\n });\n\n // AuthKit SDK uses /x/authkit/users/authenticate for the same flow\n const authenticateHandler = async (c: any) => {\n const body = await parseJsonBody(c);\n const grantType = body.grant_type as string | undefined;\n const clientId = body.client_id as string | undefined;\n const clientSecret = body.client_secret as string | undefined;\n\n if (!grantType) {\n throw new WorkOSApiError(400, 'grant_type is required', 'invalid_request');\n }\n\n let user;\n let organizationId: string | null = null;\n let authMethod: string;\n\n switch (grantType) {\n case 'authorization_code': {\n const code = body.code as string;\n if (!code) throw new WorkOSApiError(400, 'code is required', 'invalid_request');\n\n const authCode = ws.authCodes.all().find((ac) => ac.code === code);\n if (!authCode) throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n if (isExpired(authCode.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n if (authCode.code_challenge) {\n const codeVerifier = body.code_verifier as string;\n if (!codeVerifier) {\n throw new WorkOSApiError(400, 'code_verifier is required', 'invalid_request');\n }\n const method = authCode.code_challenge_method ?? 'S256';\n let challenge: string;\n if (method === 'S256') {\n challenge = createHash('sha256').update(codeVerifier).digest('base64url');\n } else {\n challenge = codeVerifier;\n }\n if (challenge !== authCode.code_challenge) {\n throw new WorkOSApiError(400, 'Invalid code_verifier', 'invalid_code_verifier');\n }\n }\n\n user = ws.users.get(authCode.user_id);\n organizationId = authCode.organization_id;\n ws.authCodes.delete(authCode.id);\n authMethod = 'OAuth';\n break;\n }\n\n case 'password': {\n const email = body.email as string;\n const password = body.password as string;\n if (!email || !password) {\n throw new WorkOSApiError(400, 'email and password are required', 'invalid_request');\n }\n\n user = ws.users.findOneBy('email', email);\n if (!user || !user.password_hash || !verifyPassword(password, user.password_hash)) {\n throw new WorkOSApiError(401, 'Invalid credentials', 'invalid_credentials');\n }\n authMethod = 'Password';\n break;\n }\n\n // Accept both old and new grant type names for magic-auth\n case 'urn:workos:oauth:grant-type:magic-auth':\n case 'urn:workos:oauth:grant-type:magic-auth:code': {\n const code = body.code as string;\n const email = body.email as string;\n if (!code || !email) {\n throw new WorkOSApiError(400, 'code and email are required', 'invalid_request');\n }\n\n const magicAuth = ws.magicAuths.all().find((ma) => ma.code === code && ma.email === email);\n if (!magicAuth) {\n throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n }\n if (isExpired(magicAuth.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n user = ws.users.get(magicAuth.user_id);\n ws.magicAuths.delete(magicAuth.id);\n authMethod = 'MagicAuth';\n break;\n }\n\n // Accept both old and new grant type names for email-verification\n case 'urn:workos:oauth:grant-type:email-verification':\n case 'urn:workos:oauth:grant-type:email-verification:code': {\n const code = body.code as string;\n const userId = body.user_id as string;\n if (!code || !userId) {\n throw new WorkOSApiError(400, 'code and user_id are required', 'invalid_request');\n }\n\n const ev = ws.emailVerifications.findBy('user_id', userId).find((v) => v.code === code);\n if (!ev) {\n throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n }\n if (isExpired(ev.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n ws.users.update(userId, { email_verified: true });\n ws.emailVerifications.delete(ev.id);\n user = ws.users.get(userId);\n authMethod = 'EmailVerification';\n break;\n }\n\n case 'refresh_token': {\n const token = body.refresh_token as string;\n if (!token) {\n throw new WorkOSApiError(400, 'refresh_token is required', 'invalid_request');\n }\n\n const refreshToken = ws.refreshTokens.findOneBy('token', token);\n if (!refreshToken) {\n throw new WorkOSApiError(400, 'Invalid refresh token', 'invalid_grant');\n }\n if (isExpired(refreshToken.expires_at)) {\n ws.refreshTokens.delete(refreshToken.id);\n throw new WorkOSApiError(400, 'Refresh token has expired', 'invalid_grant');\n }\n\n user = ws.users.get(refreshToken.user_id);\n // Allow body.organization_id to switch org context (switchToOrganization)\n organizationId = (body.organization_id as string) ?? refreshToken.organization_id;\n\n // Rotate: delete old, issue new below\n ws.refreshTokens.delete(refreshToken.id);\n authMethod = 'OAuth';\n break;\n }\n\n case 'urn:workos:oauth:grant-type:mfa-totp': {\n const code = body.code as string;\n const pendingToken = body.pending_authentication_token as string;\n const challengeId = body.authentication_challenge_id as string;\n\n if (!code || !pendingToken || !challengeId) {\n throw new WorkOSApiError(\n 400,\n 'code, pending_authentication_token, and authentication_challenge_id are required',\n 'invalid_request',\n );\n }\n\n const pending = store.getData<PendingAuth>(`pending_auth:${pendingToken}`);\n if (!pending) {\n throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');\n }\n\n const challenge = ws.authChallenges.get(challengeId);\n if (!challenge) {\n throw new WorkOSApiError(400, 'Invalid authentication challenge', 'invalid_request');\n }\n if (isExpired(challenge.expires_at)) {\n ws.authChallenges.delete(challenge.id);\n throw new WorkOSApiError(400, 'Challenge has expired', 'expired_challenge');\n }\n\n // Verify code against the challenge's stored code\n if (challenge.code && code !== challenge.code) {\n throw new WorkOSApiError(400, 'Invalid one-time code', 'invalid_one_time_code');\n }\n\n ws.authChallenges.delete(challenge.id);\n store.setData(`pending_auth:${pendingToken}`, undefined);\n\n user = ws.users.get(pending.user_id);\n organizationId = pending.organization_id;\n authMethod = 'MFA';\n break;\n }\n\n case 'urn:workos:oauth:grant-type:organization-selection': {\n const pendingToken = body.pending_authentication_token as string;\n const orgId = body.organization_id as string;\n\n if (!pendingToken || !orgId) {\n throw new WorkOSApiError(\n 400,\n 'pending_authentication_token and organization_id are required',\n 'invalid_request',\n );\n }\n\n const pending = store.getData<PendingAuth>(`pending_auth:${pendingToken}`);\n if (!pending) {\n throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');\n }\n\n const org = ws.organizations.get(orgId);\n if (!org) throw notFound('Organization');\n\n store.setData(`pending_auth:${pendingToken}`, undefined);\n\n user = ws.users.get(pending.user_id);\n organizationId = orgId;\n authMethod = pending.auth_method;\n break;\n }\n\n case 'urn:ietf:params:oauth:grant-type:device_code': {\n const deviceCode = body.device_code as string;\n if (!deviceCode) {\n throw new WorkOSApiError(400, 'device_code is required', 'invalid_request');\n }\n\n const deviceAuth = ws.deviceAuthorizations.findOneBy('device_code', deviceCode);\n if (!deviceAuth) {\n throw new WorkOSApiError(400, 'Invalid device code', 'invalid_grant');\n }\n if (isExpired(deviceAuth.expires_at)) {\n ws.deviceAuthorizations.delete(deviceAuth.id);\n throw new WorkOSApiError(400, 'Device code has expired', 'expired_token');\n }\n if (!deviceAuth.user_id) {\n throw new WorkOSApiError(400, 'Authorization pending', 'authorization_pending');\n }\n\n user = ws.users.get(deviceAuth.user_id);\n ws.deviceAuthorizations.delete(deviceAuth.id);\n authMethod = 'OAuth';\n break;\n }\n\n default:\n throw new WorkOSApiError(400, `Unsupported grant_type: ${grantType}`, 'invalid_request');\n }\n\n if (!user) throw notFound('User');\n\n ws.users.update(user.id, { last_sign_in_at: new Date().toISOString() });\n const updatedUser = ws.users.get(user.id)!;\n\n const session = ws.sessions.insert({\n object: 'session',\n user_id: user.id,\n organization_id: organizationId,\n ip_address: c.req.header('x-forwarded-for') ?? null,\n user_agent: c.req.header('user-agent') ?? null,\n });\n\n // Resolve role + permissions for org-scoped sessions\n let roleSlug: string | undefined;\n let permissionSlugs: string[] | undefined;\n if (organizationId) {\n const membership = ws.organizationMemberships\n .findBy('organization_id', organizationId)\n .find((m) => m.user_id === user.id);\n if (membership) {\n roleSlug = membership.role.slug;\n const role = ws.roles\n .findBy('slug', membership.role.slug)\n .find((r) => r.organization_id === organizationId || r.type === 'EnvironmentRole');\n if (role) {\n const rps = ws.rolePermissions.findBy('role_id', role.id);\n permissionSlugs = rps\n .map((rp) => ws.permissions.get(rp.permission_id))\n .filter(Boolean)\n .map((p) => p!.slug);\n }\n }\n }\n\n const accessToken = jwt.sign({\n sub: user.id,\n sid: session.id,\n org_id: organizationId ?? undefined,\n role: roleSlug,\n permissions: permissionSlugs,\n aud: clientId ?? 'workos-emulate',\n });\n\n // Store a real refresh token\n const newRefreshToken = ws.refreshTokens.insert({\n token: generateId('ref'),\n user_id: user.id,\n organization_id: organizationId,\n session_id: session.id,\n expires_at: expiresIn(30 * 24 * 60), // 30 days\n });\n\n // Compute sealed session when client_secret is provided\n const apiKey = c.req\n .header('Authorization')\n ?.replace(/^Bearer\\s+/i, '')\n .trim();\n const sealKey = clientSecret ?? apiKey;\n const sealedSession = sealKey\n ? sealSession(\n { access_token: accessToken, refresh_token: newRefreshToken.token, session_id: session.id },\n sealKey,\n )\n : null;\n\n // Emit authentication event (hybrid Option B for action-specific events)\n const eventBus = store.getData<EventBus>('eventBus');\n if (eventBus) {\n const authEventType = `authentication.${authMethod.toLowerCase()}_succeeded`;\n eventBus.emit({\n event: authEventType,\n data: { user_id: user.id, email: updatedUser.email, method: authMethod, ip_address: session.ip_address },\n });\n }\n\n return c.json({\n user: formatUser(updatedUser),\n organization_id: organizationId,\n access_token: accessToken,\n refresh_token: newRefreshToken.token,\n authentication_method: authMethod,\n sealed_session: sealedSession,\n impersonator: updatedUser.impersonator ?? undefined,\n });\n };\n\n app.post('/user_management/authenticate', authenticateHandler);\n app.post('/x/authkit/users/authenticate', authenticateHandler);\n}\n"]}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/auth.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport { type RouteContext, notFound, parseJsonBody, WorkOSApiError, generateId } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport {\n formatUser,\n formatDeviceAuthorization,\n verifyPassword,\n isExpired,\n expiresIn,\n assertLocalRedirectUri,\n sealSession,\n} from '../helpers.js';\nimport type { EventBus } from '../event-bus.js';\nimport { STORE_KEYS, STORE_KEY_PREFIXES } from '../constants.js';\n\ninterface PendingAuth {\n user_id: string;\n organization_id: string | null;\n auth_method: string;\n}\n\nexport function authRoutes(ctx: RouteContext): void {\n const { app, store, jwt } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/user_management/authorize', (c) => {\n const url = new URL(c.req.url);\n const redirectUri = url.searchParams.get('redirect_uri');\n const state = url.searchParams.get('state');\n const codeChallenge = url.searchParams.get('code_challenge');\n const codeChallengeMethod = url.searchParams.get('code_challenge_method');\n const loginHint = url.searchParams.get('login_hint');\n\n if (!redirectUri) {\n throw new WorkOSApiError(400, 'redirect_uri is required', 'invalid_request');\n }\n assertLocalRedirectUri(redirectUri);\n\n let user;\n if (loginHint) {\n user = ws.users.findOneBy('email', loginHint);\n if (!user) {\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('error', 'user_not_found');\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n }\n } else {\n const users = ws.users.all();\n user = users[0];\n }\n\n if (!user) {\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('error', 'no_users');\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n }\n\n const authCode = ws.authCodes.insert({\n user_id: user.id,\n organization_id: null,\n code: generateId('auth_code'),\n redirect_uri: redirectUri,\n expires_at: expiresIn(10),\n code_challenge: codeChallenge ?? null,\n code_challenge_method: codeChallengeMethod ?? null,\n });\n\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('code', authCode.code);\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n });\n\n // Device authorization endpoint\n app.post('/user_management/authorize/device', async (c) => {\n const body = await parseJsonBody(c);\n const clientId = body.client_id as string;\n if (!clientId) {\n throw new WorkOSApiError(400, 'client_id is required', 'invalid_request');\n }\n\n // Auto-approve with first user for emulator convenience\n const users = ws.users.all();\n const user = users[0] ?? null;\n\n const deviceAuth = ws.deviceAuthorizations.insert({\n device_code: generateId('dev_code'),\n user_code: Math.random().toString(36).slice(2, 10).toUpperCase(),\n user_id: user?.id ?? null,\n client_id: clientId,\n expires_at: expiresIn(15),\n interval: 5,\n });\n\n return c.json(formatDeviceAuthorization(deviceAuth));\n });\n\n // AuthKit SDK uses /x/authkit/users/authenticate for the same flow\n const authenticateHandler = async (c: any) => {\n const body = await parseJsonBody(c);\n const grantType = body.grant_type as string | undefined;\n const clientId = body.client_id as string | undefined;\n const clientSecret = body.client_secret as string | undefined;\n\n if (!grantType) {\n throw new WorkOSApiError(400, 'grant_type is required', 'invalid_request');\n }\n\n let user;\n let organizationId: string | null = null;\n let authMethod: string;\n\n switch (grantType) {\n case 'authorization_code': {\n const code = body.code as string;\n if (!code) throw new WorkOSApiError(400, 'code is required', 'invalid_request');\n\n const authCode = ws.authCodes.findOneBy('code', code);\n if (!authCode) throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n if (isExpired(authCode.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n if (authCode.code_challenge) {\n const codeVerifier = body.code_verifier as string;\n if (!codeVerifier) {\n throw new WorkOSApiError(400, 'code_verifier is required', 'invalid_request');\n }\n const method = authCode.code_challenge_method ?? 'S256';\n let challenge: string;\n if (method === 'S256') {\n challenge = createHash('sha256').update(codeVerifier).digest('base64url');\n } else {\n challenge = codeVerifier;\n }\n if (challenge !== authCode.code_challenge) {\n throw new WorkOSApiError(400, 'Invalid code_verifier', 'invalid_code_verifier');\n }\n }\n\n user = ws.users.get(authCode.user_id);\n organizationId = authCode.organization_id;\n ws.authCodes.delete(authCode.id);\n authMethod = 'OAuth';\n break;\n }\n\n case 'password': {\n const email = body.email as string;\n const password = body.password as string;\n if (!email || !password) {\n throw new WorkOSApiError(400, 'email and password are required', 'invalid_request');\n }\n\n user = ws.users.findOneBy('email', email);\n if (!user || !user.password_hash || !verifyPassword(password, user.password_hash)) {\n throw new WorkOSApiError(401, 'Invalid credentials', 'invalid_credentials');\n }\n authMethod = 'Password';\n break;\n }\n\n // Accept both old and new grant type names for magic-auth\n case 'urn:workos:oauth:grant-type:magic-auth':\n case 'urn:workos:oauth:grant-type:magic-auth:code': {\n const code = body.code as string;\n const email = body.email as string;\n if (!code || !email) {\n throw new WorkOSApiError(400, 'code and email are required', 'invalid_request');\n }\n\n const magicAuth = ws.magicAuths.all().find((ma) => ma.code === code && ma.email === email);\n if (!magicAuth) {\n throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n }\n if (isExpired(magicAuth.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n user = ws.users.get(magicAuth.user_id);\n ws.magicAuths.delete(magicAuth.id);\n authMethod = 'MagicAuth';\n break;\n }\n\n // Accept both old and new grant type names for email-verification\n case 'urn:workos:oauth:grant-type:email-verification':\n case 'urn:workos:oauth:grant-type:email-verification:code': {\n const code = body.code as string;\n const userId = body.user_id as string;\n if (!code || !userId) {\n throw new WorkOSApiError(400, 'code and user_id are required', 'invalid_request');\n }\n\n const ev = ws.emailVerifications.findBy('user_id', userId).find((v) => v.code === code);\n if (!ev) {\n throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n }\n if (isExpired(ev.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n ws.users.update(userId, { email_verified: true });\n ws.emailVerifications.delete(ev.id);\n user = ws.users.get(userId);\n authMethod = 'EmailVerification';\n break;\n }\n\n case 'refresh_token': {\n const token = body.refresh_token as string;\n if (!token) {\n throw new WorkOSApiError(400, 'refresh_token is required', 'invalid_request');\n }\n\n const refreshToken = ws.refreshTokens.findOneBy('token', token);\n if (!refreshToken) {\n throw new WorkOSApiError(400, 'Invalid refresh token', 'invalid_grant');\n }\n if (isExpired(refreshToken.expires_at)) {\n ws.refreshTokens.delete(refreshToken.id);\n throw new WorkOSApiError(400, 'Refresh token has expired', 'invalid_grant');\n }\n\n user = ws.users.get(refreshToken.user_id);\n // Allow body.organization_id to switch org context (switchToOrganization)\n organizationId = (body.organization_id as string) ?? refreshToken.organization_id;\n\n // Rotate: delete old, issue new below\n ws.refreshTokens.delete(refreshToken.id);\n authMethod = 'OAuth';\n break;\n }\n\n case 'urn:workos:oauth:grant-type:mfa-totp': {\n const code = body.code as string;\n const pendingToken = body.pending_authentication_token as string;\n const challengeId = body.authentication_challenge_id as string;\n\n if (!code || !pendingToken || !challengeId) {\n throw new WorkOSApiError(\n 400,\n 'code, pending_authentication_token, and authentication_challenge_id are required',\n 'invalid_request',\n );\n }\n\n const pending = store.getData<PendingAuth>(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`);\n if (!pending) {\n throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');\n }\n\n const challenge = ws.authChallenges.get(challengeId);\n if (!challenge) {\n throw new WorkOSApiError(400, 'Invalid authentication challenge', 'invalid_request');\n }\n if (isExpired(challenge.expires_at)) {\n ws.authChallenges.delete(challenge.id);\n throw new WorkOSApiError(400, 'Challenge has expired', 'expired_challenge');\n }\n\n // Verify code against the challenge's stored code\n if (challenge.code && code !== challenge.code) {\n throw new WorkOSApiError(400, 'Invalid one-time code', 'invalid_one_time_code');\n }\n\n ws.authChallenges.delete(challenge.id);\n store.setData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`, undefined);\n\n user = ws.users.get(pending.user_id);\n organizationId = pending.organization_id;\n authMethod = 'MFA';\n break;\n }\n\n case 'urn:workos:oauth:grant-type:organization-selection': {\n const pendingToken = body.pending_authentication_token as string;\n const orgId = body.organization_id as string;\n\n if (!pendingToken || !orgId) {\n throw new WorkOSApiError(\n 400,\n 'pending_authentication_token and organization_id are required',\n 'invalid_request',\n );\n }\n\n const pending = store.getData<PendingAuth>(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`);\n if (!pending) {\n throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');\n }\n\n const org = ws.organizations.get(orgId);\n if (!org) throw notFound('Organization');\n\n store.setData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`, undefined);\n\n user = ws.users.get(pending.user_id);\n organizationId = orgId;\n authMethod = pending.auth_method;\n break;\n }\n\n case 'urn:ietf:params:oauth:grant-type:device_code': {\n const deviceCode = body.device_code as string;\n if (!deviceCode) {\n throw new WorkOSApiError(400, 'device_code is required', 'invalid_request');\n }\n\n const deviceAuth = ws.deviceAuthorizations.findOneBy('device_code', deviceCode);\n if (!deviceAuth) {\n throw new WorkOSApiError(400, 'Invalid device code', 'invalid_grant');\n }\n if (isExpired(deviceAuth.expires_at)) {\n ws.deviceAuthorizations.delete(deviceAuth.id);\n throw new WorkOSApiError(400, 'Device code has expired', 'expired_token');\n }\n if (!deviceAuth.user_id) {\n throw new WorkOSApiError(400, 'Authorization pending', 'authorization_pending');\n }\n\n user = ws.users.get(deviceAuth.user_id);\n ws.deviceAuthorizations.delete(deviceAuth.id);\n authMethod = 'OAuth';\n break;\n }\n\n default:\n throw new WorkOSApiError(400, `Unsupported grant_type: ${grantType}`, 'invalid_request');\n }\n\n if (!user) throw notFound('User');\n\n ws.users.update(user.id, { last_sign_in_at: new Date().toISOString() });\n const updatedUser = ws.users.get(user.id)!;\n\n const session = ws.sessions.insert({\n object: 'session',\n user_id: user.id,\n organization_id: organizationId,\n ip_address: c.req.header('x-forwarded-for') ?? null,\n user_agent: c.req.header('user-agent') ?? null,\n });\n\n // Resolve role + permissions for org-scoped sessions\n let roleSlug: string | undefined;\n let permissionSlugs: string[] | undefined;\n if (organizationId) {\n const membership = ws.organizationMemberships\n .findBy('organization_id', organizationId)\n .find((m) => m.user_id === user.id);\n if (membership) {\n roleSlug = membership.role.slug;\n const role = ws.roles\n .findBy('slug', membership.role.slug)\n .find((r) => r.organization_id === organizationId || r.type === 'EnvironmentRole');\n if (role) {\n const rps = ws.rolePermissions.findBy('role_id', role.id);\n permissionSlugs = rps\n .map((rp) => ws.permissions.get(rp.permission_id))\n .filter(Boolean)\n .map((p) => p!.slug);\n }\n }\n }\n\n const accessToken = jwt.sign({\n sub: user.id,\n sid: session.id,\n org_id: organizationId ?? undefined,\n role: roleSlug,\n permissions: permissionSlugs,\n aud: clientId ?? 'workos-emulate',\n });\n\n // Store a real refresh token\n const newRefreshToken = ws.refreshTokens.insert({\n token: generateId('ref'),\n user_id: user.id,\n organization_id: organizationId,\n session_id: session.id,\n expires_at: expiresIn(30 * 24 * 60), // 30 days\n });\n\n // Compute sealed session when client_secret is provided\n const apiKey = c.req\n .header('Authorization')\n ?.replace(/^Bearer\\s+/i, '')\n .trim();\n const sealKey = clientSecret ?? apiKey;\n const sealedSession = sealKey\n ? sealSession(\n { access_token: accessToken, refresh_token: newRefreshToken.token, session_id: session.id },\n sealKey,\n )\n : null;\n\n // Emit authentication event (hybrid Option B for action-specific events)\n const eventBus = store.getData<EventBus>(STORE_KEYS.eventBus);\n if (eventBus) {\n const authEventType = `authentication.${authMethod.toLowerCase()}_succeeded`;\n eventBus.emit({\n event: authEventType,\n data: { user_id: user.id, email: updatedUser.email, method: authMethod, ip_address: session.ip_address },\n });\n }\n\n return c.json({\n user: formatUser(updatedUser),\n organization_id: organizationId,\n access_token: accessToken,\n refresh_token: newRefreshToken.token,\n authentication_method: authMethod,\n sealed_session: sealedSession,\n impersonator: updatedUser.impersonator ?? undefined,\n });\n };\n\n app.post('/user_management/authenticate', authenticateHandler);\n app.post('/x/authkit/users/authenticate', authenticateHandler);\n}\n"]}
@@ -1,6 +1,6 @@
1
- import { notFound, validationError, parseJsonBody } from '../../core/index.js';
1
+ import { notFound, validationError, parseJsonBody, parseListParams } from '../../core/index.js';
2
2
  import { getWorkOSStore } from '../store.js';
3
- import { formatRoleAssignment, formatAuthorizationResource, parseListParams } from '../helpers.js';
3
+ import { formatRoleAssignment, formatAuthorizationResource, formatListResponse } from '../helpers.js';
4
4
  /**
5
5
  * Gather all permission slugs for a given membership:
6
6
  * 1. From the membership's role (role.slug field)
@@ -40,9 +40,9 @@ function getPermissionsForMembership(ws, membershipId) {
40
40
  }
41
41
  export function authorizationCheckRoutes(ctx) {
42
42
  const { app, store } = ctx;
43
+ const ws = getWorkOSStore(store);
43
44
  // Permission check
44
45
  app.post('/authorization/organization_memberships/:id/check', async (c) => {
45
- const ws = getWorkOSStore(store);
46
46
  const membershipId = c.req.param('id');
47
47
  const membership = ws.organizationMemberships.get(membershipId);
48
48
  if (!membership)
@@ -57,7 +57,6 @@ export function authorizationCheckRoutes(ctx) {
57
57
  });
58
58
  // List resources accessible to a membership (all resources in the membership's org)
59
59
  app.get('/authorization/organization_memberships/:id/resources', (c) => {
60
- const ws = getWorkOSStore(store);
61
60
  const membershipId = c.req.param('id');
62
61
  const membership = ws.organizationMemberships.get(membershipId);
63
62
  if (!membership)
@@ -68,15 +67,10 @@ export function authorizationCheckRoutes(ctx) {
68
67
  ...params,
69
68
  filter: (r) => r.organization_id === membership.organization_id,
70
69
  });
71
- return c.json({
72
- object: 'list',
73
- data: result.data.map(formatAuthorizationResource),
74
- list_metadata: result.list_metadata,
75
- });
70
+ return c.json(formatListResponse(result, formatAuthorizationResource));
76
71
  });
77
72
  // List role assignments for a membership
78
73
  app.get('/authorization/organization_memberships/:id/role_assignments', (c) => {
79
- const ws = getWorkOSStore(store);
80
74
  const membershipId = c.req.param('id');
81
75
  const membership = ws.organizationMemberships.get(membershipId);
82
76
  if (!membership)
@@ -87,15 +81,10 @@ export function authorizationCheckRoutes(ctx) {
87
81
  ...params,
88
82
  filter: (ra) => ra.organization_membership_id === membershipId,
89
83
  });
90
- return c.json({
91
- object: 'list',
92
- data: result.data.map(formatRoleAssignment),
93
- list_metadata: result.list_metadata,
94
- });
84
+ return c.json(formatListResponse(result, formatRoleAssignment));
95
85
  });
96
86
  // Create role assignment
97
87
  app.post('/authorization/organization_memberships/:id/role_assignments', async (c) => {
98
- const ws = getWorkOSStore(store);
99
88
  const membershipId = c.req.param('id');
100
89
  const membership = ws.organizationMemberships.get(membershipId);
101
90
  if (!membership)
@@ -117,7 +106,6 @@ export function authorizationCheckRoutes(ctx) {
117
106
  });
118
107
  // Delete role assignment
119
108
  app.delete('/authorization/organization_memberships/:id/role_assignments/:assignmentId', (c) => {
120
- const ws = getWorkOSStore(store);
121
109
  const membershipId = c.req.param('id');
122
110
  const assignmentId = c.req.param('assignmentId');
123
111
  const membership = ws.organizationMemberships.get(membershipId);
@@ -1 +1 @@
1
- {"version":3,"file":"authorization-checks.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/authorization-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, validationError, parseJsonBody } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatRoleAssignment, formatAuthorizationResource, parseListParams } from '../helpers.js';\n\n/**\n * Gather all permission slugs for a given membership:\n * 1. From the membership's role (role.slug field)\n * 2. From any additional role assignments\n */\nfunction getPermissionsForMembership(ws: ReturnType<typeof getWorkOSStore>, membershipId: string): Set<string> {\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) return new Set();\n\n const permSlugs = new Set<string>();\n\n // Permissions from the membership's primary role\n const primaryRole = ws.roles\n .findBy('slug', membership.role.slug)\n .find((r) => r.organization_id === membership.organization_id || r.type === 'EnvironmentRole');\n if (primaryRole) {\n const rps = ws.rolePermissions.findBy('role_id', primaryRole.id);\n for (const rp of rps) {\n const perm = ws.permissions.get(rp.permission_id);\n if (perm) permSlugs.add(perm.slug);\n }\n }\n\n // Permissions from additional role assignments\n const assignments = ws.roleAssignments.findBy('organization_membership_id', membershipId);\n for (const assignment of assignments) {\n const role = ws.roles.get(assignment.role_id);\n if (!role) continue;\n const rps = ws.rolePermissions.findBy('role_id', role.id);\n for (const rp of rps) {\n const perm = ws.permissions.get(rp.permission_id);\n if (perm) permSlugs.add(perm.slug);\n }\n }\n\n return permSlugs;\n}\n\nexport function authorizationCheckRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n\n // Permission check\n app.post('/authorization/organization_memberships/:id/check', async (c) => {\n const ws = getWorkOSStore(store);\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const body = await parseJsonBody(c);\n const permission = body.permission as string;\n if (!permission) {\n throw validationError('permission is required', [{ field: 'permission', code: 'required' }]);\n }\n\n const permSlugs = getPermissionsForMembership(ws, membershipId);\n return c.json({ authorized: permSlugs.has(permission) });\n });\n\n // List resources accessible to a membership (all resources in the membership's org)\n app.get('/authorization/organization_memberships/:id/resources', (c) => {\n const ws = getWorkOSStore(store);\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n\n const result = ws.authorizationResources.list({\n ...params,\n filter: (r) => r.organization_id === membership.organization_id,\n });\n\n return c.json({\n object: 'list',\n data: result.data.map(formatAuthorizationResource),\n list_metadata: result.list_metadata,\n });\n });\n\n // List role assignments for a membership\n app.get('/authorization/organization_memberships/:id/role_assignments', (c) => {\n const ws = getWorkOSStore(store);\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n\n const result = ws.roleAssignments.list({\n ...params,\n filter: (ra) => ra.organization_membership_id === membershipId,\n });\n\n return c.json({\n object: 'list',\n data: result.data.map(formatRoleAssignment),\n list_metadata: result.list_metadata,\n });\n });\n\n // Create role assignment\n app.post('/authorization/organization_memberships/:id/role_assignments', async (c) => {\n const ws = getWorkOSStore(store);\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const body = await parseJsonBody(c);\n const roleId = body.role_id as string;\n if (!roleId) {\n throw validationError('role_id is required', [{ field: 'role_id', code: 'required' }]);\n }\n\n const role = ws.roles.get(roleId);\n if (!role) throw notFound('Role');\n\n const assignment = ws.roleAssignments.insert({\n object: 'role_assignment',\n organization_membership_id: membershipId,\n role_id: roleId,\n });\n\n return c.json(formatRoleAssignment(assignment), 201);\n });\n\n // Delete role assignment\n app.delete('/authorization/organization_memberships/:id/role_assignments/:assignmentId', (c) => {\n const ws = getWorkOSStore(store);\n const membershipId = c.req.param('id');\n const assignmentId = c.req.param('assignmentId');\n\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const assignment = ws.roleAssignments.get(assignmentId);\n if (!assignment || assignment.organization_membership_id !== membershipId) {\n throw notFound('RoleAssignment');\n }\n\n ws.roleAssignments.delete(assignmentId);\n return c.body(null, 204);\n });\n}\n\nexport { getPermissionsForMembership };\n"]}
1
+ {"version":3,"file":"authorization-checks.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/authorization-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, validationError, parseJsonBody, parseListParams } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatRoleAssignment, formatAuthorizationResource, formatListResponse } from '../helpers.js';\n\n/**\n * Gather all permission slugs for a given membership:\n * 1. From the membership's role (role.slug field)\n * 2. From any additional role assignments\n */\nfunction getPermissionsForMembership(ws: ReturnType<typeof getWorkOSStore>, membershipId: string): Set<string> {\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) return new Set();\n\n const permSlugs = new Set<string>();\n\n // Permissions from the membership's primary role\n const primaryRole = ws.roles\n .findBy('slug', membership.role.slug)\n .find((r) => r.organization_id === membership.organization_id || r.type === 'EnvironmentRole');\n if (primaryRole) {\n const rps = ws.rolePermissions.findBy('role_id', primaryRole.id);\n for (const rp of rps) {\n const perm = ws.permissions.get(rp.permission_id);\n if (perm) permSlugs.add(perm.slug);\n }\n }\n\n // Permissions from additional role assignments\n const assignments = ws.roleAssignments.findBy('organization_membership_id', membershipId);\n for (const assignment of assignments) {\n const role = ws.roles.get(assignment.role_id);\n if (!role) continue;\n const rps = ws.rolePermissions.findBy('role_id', role.id);\n for (const rp of rps) {\n const perm = ws.permissions.get(rp.permission_id);\n if (perm) permSlugs.add(perm.slug);\n }\n }\n\n return permSlugs;\n}\n\nexport function authorizationCheckRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n // Permission check\n app.post('/authorization/organization_memberships/:id/check', async (c) => {\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const body = await parseJsonBody(c);\n const permission = body.permission as string;\n if (!permission) {\n throw validationError('permission is required', [{ field: 'permission', code: 'required' }]);\n }\n\n const permSlugs = getPermissionsForMembership(ws, membershipId);\n return c.json({ authorized: permSlugs.has(permission) });\n });\n\n // List resources accessible to a membership (all resources in the membership's org)\n app.get('/authorization/organization_memberships/:id/resources', (c) => {\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n\n const result = ws.authorizationResources.list({\n ...params,\n filter: (r) => r.organization_id === membership.organization_id,\n });\n\n return c.json(formatListResponse(result, formatAuthorizationResource));\n });\n\n // List role assignments for a membership\n app.get('/authorization/organization_memberships/:id/role_assignments', (c) => {\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n\n const result = ws.roleAssignments.list({\n ...params,\n filter: (ra) => ra.organization_membership_id === membershipId,\n });\n\n return c.json(formatListResponse(result, formatRoleAssignment));\n });\n\n // Create role assignment\n app.post('/authorization/organization_memberships/:id/role_assignments', async (c) => {\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const body = await parseJsonBody(c);\n const roleId = body.role_id as string;\n if (!roleId) {\n throw validationError('role_id is required', [{ field: 'role_id', code: 'required' }]);\n }\n\n const role = ws.roles.get(roleId);\n if (!role) throw notFound('Role');\n\n const assignment = ws.roleAssignments.insert({\n object: 'role_assignment',\n organization_membership_id: membershipId,\n role_id: roleId,\n });\n\n return c.json(formatRoleAssignment(assignment), 201);\n });\n\n // Delete role assignment\n app.delete('/authorization/organization_memberships/:id/role_assignments/:assignmentId', (c) => {\n const membershipId = c.req.param('id');\n const assignmentId = c.req.param('assignmentId');\n\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const assignment = ws.roleAssignments.get(assignmentId);\n if (!assignment || assignment.organization_membership_id !== membershipId) {\n throw notFound('RoleAssignment');\n }\n\n ws.roleAssignments.delete(assignmentId);\n return c.body(null, 204);\n });\n}\n\nexport { getPermissionsForMembership };\n"]}
@@ -1,199 +1,57 @@
1
1
  import { notFound, validationError, parseJsonBody } from '../../core/index.js';
2
2
  import { getWorkOSStore } from '../store.js';
3
- import { formatRole, formatPermission, parseListParams } from '../helpers.js';
3
+ import { formatRole } from '../helpers.js';
4
+ import { findOrgRole, requireOrgRole, registerRoleRoutes } from '../role-helpers.js';
4
5
  export function authorizationOrgRoleRoutes(ctx) {
5
6
  const { app, store } = ctx;
6
- app.post('/authorization/organizations/:orgId/roles', async (c) => {
7
- const ws = getWorkOSStore(store);
8
- const orgId = c.req.param('orgId');
9
- const org = ws.organizations.get(orgId);
10
- if (!org)
11
- throw notFound('Organization');
12
- const body = await parseJsonBody(c);
13
- const slug = body.slug;
14
- const name = body.name;
15
- if (!slug || typeof slug !== 'string') {
16
- throw validationError('slug is required', [{ field: 'slug', code: 'required' }]);
17
- }
18
- if (!name || typeof name !== 'string') {
19
- throw validationError('name is required', [{ field: 'name', code: 'required' }]);
20
- }
21
- // Check uniqueness within this org
22
- const existing = ws.roles
23
- .findBy('organization_id', orgId)
24
- .find((r) => r.slug === slug && r.type === 'OrganizationRole');
25
- if (existing) {
26
- throw validationError('Role with this slug already exists in this organization', [
27
- { field: 'slug', code: 'duplicate' },
28
- ]);
29
- }
30
- const role = ws.roles.insert({
31
- object: 'role',
32
- slug,
33
- name,
34
- description: body.description ?? null,
35
- type: 'OrganizationRole',
36
- organization_id: orgId,
37
- is_default_role: Boolean(body.is_default_role),
38
- priority: typeof body.priority === 'number' ? body.priority : 0,
39
- });
40
- return c.json(formatRole(role), 201);
41
- });
42
- app.get('/authorization/organizations/:orgId/roles', (c) => {
43
- const ws = getWorkOSStore(store);
44
- const orgId = c.req.param('orgId');
45
- const url = new URL(c.req.url);
46
- const params = parseListParams(url);
47
- const result = ws.roles.list({
48
- ...params,
49
- filter: (r) => r.organization_id === orgId && r.type === 'OrganizationRole',
50
- });
51
- return c.json({
52
- object: 'list',
53
- data: result.data.map(formatRole),
54
- list_metadata: result.list_metadata,
55
- });
56
- });
7
+ const ws = getWorkOSStore(store);
8
+ const prefix = '/authorization/organizations/:orgId/roles';
57
9
  // Priority ordering — must be registered before :slug routes
58
- app.put('/authorization/organizations/:orgId/roles/priority', async (c) => {
59
- const ws = getWorkOSStore(store);
10
+ app.put(`${prefix}/priority`, async (c) => {
60
11
  const orgId = c.req.param('orgId');
61
12
  const body = await parseJsonBody(c);
62
13
  const slugs = body.slugs;
63
14
  if (!Array.isArray(slugs)) {
64
15
  throw validationError('slugs must be an array', [{ field: 'slugs', code: 'invalid' }]);
65
16
  }
17
+ // Fetch once, build slug map for O(1) lookups
18
+ const orgRoles = ws.roles.findBy('organization_id', orgId).filter((r) => r.type === 'OrganizationRole');
19
+ const rolesBySlug = new Map(orgRoles.map((r) => [r.slug, r]));
66
20
  for (let i = 0; i < slugs.length; i++) {
67
- const role = ws.roles
68
- .findBy('organization_id', orgId)
69
- .find((r) => r.slug === slugs[i] && r.type === 'OrganizationRole');
21
+ const role = rolesBySlug.get(slugs[i]);
70
22
  if (!role)
71
23
  throw notFound('Role');
72
24
  ws.roles.update(role.id, { priority: i });
73
25
  }
74
- const roles = ws.roles
26
+ // Re-fetch for updated priority values
27
+ const updated = ws.roles
75
28
  .findBy('organization_id', orgId)
76
29
  .filter((r) => r.type === 'OrganizationRole')
77
30
  .sort((a, b) => a.priority - b.priority);
78
31
  return c.json({
79
32
  object: 'list',
80
- data: roles.map(formatRole),
33
+ data: updated.map(formatRole),
81
34
  list_metadata: { before: null, after: null },
82
35
  });
83
36
  });
84
- app.get('/authorization/organizations/:orgId/roles/:slug', (c) => {
85
- const ws = getWorkOSStore(store);
86
- const orgId = c.req.param('orgId');
87
- const slug = c.req.param('slug');
88
- const role = ws.roles
89
- .findBy('organization_id', orgId)
90
- .find((r) => r.slug === slug && r.type === 'OrganizationRole');
91
- if (!role)
92
- throw notFound('Role');
93
- return c.json(formatRole(role));
37
+ registerRoleRoutes(ctx, {
38
+ pathPrefix: prefix,
39
+ roleType: 'OrganizationRole',
40
+ requireRole: (ws, c) => requireOrgRole(ws, c.req.param('orgId'), c.req.param('slug')),
41
+ findRole: (ws, c, slug) => findOrgRole(ws, c.req.param('orgId'), slug),
42
+ listFilter: (c) => (r) => r.organization_id === c.req.param('orgId') && r.type === 'OrganizationRole',
43
+ insertDefaults: (c) => ({ organization_id: c.req.param('orgId') }),
44
+ duplicateMessage: 'Role with this slug already exists in this organization',
45
+ validateBeforeCreate: (ws, c) => {
46
+ const org = ws.organizations.get(c.req.param('orgId'));
47
+ if (!org)
48
+ throw notFound('Organization');
49
+ },
94
50
  });
95
- app.put('/authorization/organizations/:orgId/roles/:slug', async (c) => {
96
- const ws = getWorkOSStore(store);
97
- const orgId = c.req.param('orgId');
98
- const slug = c.req.param('slug');
99
- const role = ws.roles
100
- .findBy('organization_id', orgId)
101
- .find((r) => r.slug === slug && r.type === 'OrganizationRole');
102
- if (!role)
103
- throw notFound('Role');
104
- const body = await parseJsonBody(c);
105
- const updates = {};
106
- if ('name' in body)
107
- updates.name = body.name;
108
- if ('description' in body)
109
- updates.description = body.description ?? null;
110
- if ('is_default_role' in body)
111
- updates.is_default_role = Boolean(body.is_default_role);
112
- if ('priority' in body)
113
- updates.priority = body.priority;
114
- const updated = ws.roles.update(role.id, updates);
115
- return c.json(formatRole(updated));
116
- });
117
- app.delete('/authorization/organizations/:orgId/roles/:slug', (c) => {
118
- const ws = getWorkOSStore(store);
119
- const orgId = c.req.param('orgId');
120
- const slug = c.req.param('slug');
121
- const role = ws.roles
122
- .findBy('organization_id', orgId)
123
- .find((r) => r.slug === slug && r.type === 'OrganizationRole');
124
- if (!role)
125
- throw notFound('Role');
126
- // Cascade: remove role-permission joins and role assignments
127
- const rps = ws.rolePermissions.findBy('role_id', role.id);
128
- for (const rp of rps)
129
- ws.rolePermissions.delete(rp.id);
130
- const ras = ws.roleAssignments.findBy('role_id', role.id);
131
- for (const ra of ras)
132
- ws.roleAssignments.delete(ra.id);
133
- ws.roles.delete(role.id);
134
- return c.body(null, 204);
135
- });
136
- // Org role permissions
137
- app.get('/authorization/organizations/:orgId/roles/:slug/permissions', (c) => {
138
- const ws = getWorkOSStore(store);
139
- const orgId = c.req.param('orgId');
140
- const slug = c.req.param('slug');
141
- const role = ws.roles
142
- .findBy('organization_id', orgId)
143
- .find((r) => r.slug === slug && r.type === 'OrganizationRole');
144
- if (!role)
145
- throw notFound('Role');
146
- const rps = ws.rolePermissions.findBy('role_id', role.id);
147
- const permissions = rps.map((rp) => ws.permissions.get(rp.permission_id)).filter(Boolean);
148
- return c.json({
149
- object: 'list',
150
- data: permissions.map((p) => formatPermission(p)),
151
- list_metadata: { before: null, after: null },
152
- });
153
- });
154
- app.post('/authorization/organizations/:orgId/roles/:slug/permissions', async (c) => {
155
- const ws = getWorkOSStore(store);
156
- const orgId = c.req.param('orgId');
157
- const slug = c.req.param('slug');
158
- const role = ws.roles
159
- .findBy('organization_id', orgId)
160
- .find((r) => r.slug === slug && r.type === 'OrganizationRole');
161
- if (!role)
162
- throw notFound('Role');
163
- const body = await parseJsonBody(c);
164
- const permissionSlugs = body.permissions;
165
- if (!Array.isArray(permissionSlugs)) {
166
- throw validationError('permissions must be an array of slugs', [{ field: 'permissions', code: 'invalid' }]);
167
- }
168
- // Replace all
169
- const existing = ws.rolePermissions.findBy('role_id', role.id);
170
- for (const rp of existing)
171
- ws.rolePermissions.delete(rp.id);
172
- for (const permSlug of permissionSlugs) {
173
- const perm = ws.permissions.findOneBy('slug', permSlug);
174
- if (!perm)
175
- throw notFound('Permission');
176
- ws.rolePermissions.insert({ role_id: role.id, permission_id: perm.id });
177
- }
178
- const rps = ws.rolePermissions.findBy('role_id', role.id);
179
- const permissions = rps.map((rp) => ws.permissions.get(rp.permission_id)).filter(Boolean);
180
- return c.json({
181
- object: 'list',
182
- data: permissions.map((p) => formatPermission(p)),
183
- list_metadata: { before: null, after: null },
184
- });
185
- });
186
- app.delete('/authorization/organizations/:orgId/roles/:slug/permissions/:permissionSlug', (c) => {
187
- const ws = getWorkOSStore(store);
188
- const orgId = c.req.param('orgId');
189
- const slug = c.req.param('slug');
190
- const permissionSlug = c.req.param('permissionSlug');
191
- const role = ws.roles
192
- .findBy('organization_id', orgId)
193
- .find((r) => r.slug === slug && r.type === 'OrganizationRole');
194
- if (!role)
195
- throw notFound('Role');
196
- const perm = ws.permissions.findOneBy('slug', permissionSlug);
51
+ // Org-specific: delete single permission from role
52
+ app.delete(`${prefix}/:slug/permissions/:permissionSlug`, (c) => {
53
+ const role = requireOrgRole(ws, c.req.param('orgId'), c.req.param('slug'));
54
+ const perm = ws.permissions.findOneBy('slug', c.req.param('permissionSlug'));
197
55
  if (!perm)
198
56
  throw notFound('Permission');
199
57
  const rp = ws.rolePermissions.findBy('role_id', role.id).find((rp) => rp.permission_id === perm.id);