workos 0.11.2 → 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 (186) hide show
  1. package/README.md +165 -6
  2. package/dist/bin.js +22 -1
  3. package/dist/bin.js.map +1 -1
  4. package/dist/check-coverage.ts +237 -0
  5. package/dist/commands/debug.js +0 -1
  6. package/dist/commands/debug.js.map +1 -1
  7. package/dist/commands/dev.d.ts +23 -0
  8. package/dist/commands/dev.js +139 -0
  9. package/dist/commands/dev.js.map +1 -0
  10. package/dist/commands/emulate.d.ts +6 -0
  11. package/dist/commands/emulate.js +64 -0
  12. package/dist/commands/emulate.js.map +1 -0
  13. package/dist/commands/login.js +0 -4
  14. package/dist/commands/login.js.map +1 -1
  15. package/dist/emulate/core/id.d.ts +48 -0
  16. package/dist/emulate/core/id.js +73 -0
  17. package/dist/emulate/core/id.js.map +1 -0
  18. package/dist/emulate/core/index.d.ts +8 -0
  19. package/dist/emulate/core/index.js +8 -0
  20. package/dist/emulate/core/index.js.map +1 -0
  21. package/dist/emulate/core/jwt.d.ts +28 -0
  22. package/dist/emulate/core/jwt.js +78 -0
  23. package/dist/emulate/core/jwt.js.map +1 -0
  24. package/dist/emulate/core/middleware/auth.d.ts +15 -0
  25. package/dist/emulate/core/middleware/auth.js +17 -0
  26. package/dist/emulate/core/middleware/auth.js.map +1 -0
  27. package/dist/emulate/core/middleware/error-handler.d.ts +22 -0
  28. package/dist/emulate/core/middleware/error-handler.js +72 -0
  29. package/dist/emulate/core/middleware/error-handler.js.map +1 -0
  30. package/dist/emulate/core/pagination.d.ts +27 -0
  31. package/dist/emulate/core/pagination.js +43 -0
  32. package/dist/emulate/core/pagination.js.map +1 -0
  33. package/dist/emulate/core/plugin.d.ts +15 -0
  34. package/dist/emulate/core/plugin.js +2 -0
  35. package/dist/emulate/core/plugin.js.map +1 -0
  36. package/dist/emulate/core/server.d.ts +17 -0
  37. package/dist/emulate/core/server.js +90 -0
  38. package/dist/emulate/core/server.js.map +1 -0
  39. package/dist/emulate/core/store.d.ts +44 -0
  40. package/dist/emulate/core/store.js +169 -0
  41. package/dist/emulate/core/store.js.map +1 -0
  42. package/dist/emulate/index.d.ts +25 -0
  43. package/dist/emulate/index.js +47 -0
  44. package/dist/emulate/index.js.map +1 -0
  45. package/dist/emulate/workos/constants.d.ts +56 -0
  46. package/dist/emulate/workos/constants.js +56 -0
  47. package/dist/emulate/workos/constants.js.map +1 -0
  48. package/dist/emulate/workos/entities.d.ts +360 -0
  49. package/dist/emulate/workos/entities.js +2 -0
  50. package/dist/emulate/workos/entities.js.map +1 -0
  51. package/dist/emulate/workos/event-bus.d.ts +17 -0
  52. package/dist/emulate/workos/event-bus.js +70 -0
  53. package/dist/emulate/workos/event-bus.js.map +1 -0
  54. package/dist/emulate/workos/helpers.d.ts +72 -0
  55. package/dist/emulate/workos/helpers.js +211 -0
  56. package/dist/emulate/workos/helpers.js.map +1 -0
  57. package/dist/emulate/workos/index.d.ts +91 -0
  58. package/dist/emulate/workos/index.js +322 -0
  59. package/dist/emulate/workos/index.js.map +1 -0
  60. package/dist/emulate/workos/role-helpers.d.ts +21 -0
  61. package/dist/emulate/workos/role-helpers.js +130 -0
  62. package/dist/emulate/workos/role-helpers.js.map +1 -0
  63. package/dist/emulate/workos/routes/api-keys.d.ts +2 -0
  64. package/dist/emulate/workos/routes/api-keys.js +32 -0
  65. package/dist/emulate/workos/routes/api-keys.js.map +1 -0
  66. package/dist/emulate/workos/routes/audit-logs.d.ts +2 -0
  67. package/dist/emulate/workos/routes/audit-logs.js +104 -0
  68. package/dist/emulate/workos/routes/audit-logs.js.map +1 -0
  69. package/dist/emulate/workos/routes/auth-challenges.d.ts +2 -0
  70. package/dist/emulate/workos/routes/auth-challenges.js +51 -0
  71. package/dist/emulate/workos/routes/auth-challenges.js.map +1 -0
  72. package/dist/emulate/workos/routes/auth-factors.d.ts +2 -0
  73. package/dist/emulate/workos/routes/auth-factors.js +51 -0
  74. package/dist/emulate/workos/routes/auth-factors.js.map +1 -0
  75. package/dist/emulate/workos/routes/auth.d.ts +2 -0
  76. package/dist/emulate/workos/routes/auth.js +350 -0
  77. package/dist/emulate/workos/routes/auth.js.map +1 -0
  78. package/dist/emulate/workos/routes/authorization-checks.d.ts +10 -0
  79. package/dist/emulate/workos/routes/authorization-checks.js +123 -0
  80. package/dist/emulate/workos/routes/authorization-checks.js.map +1 -0
  81. package/dist/emulate/workos/routes/authorization-org-roles.d.ts +2 -0
  82. package/dist/emulate/workos/routes/authorization-org-roles.js +64 -0
  83. package/dist/emulate/workos/routes/authorization-org-roles.js.map +1 -0
  84. package/dist/emulate/workos/routes/authorization-permissions.d.ts +2 -0
  85. package/dist/emulate/workos/routes/authorization-permissions.js +67 -0
  86. package/dist/emulate/workos/routes/authorization-permissions.js.map +1 -0
  87. package/dist/emulate/workos/routes/authorization-resources.d.ts +2 -0
  88. package/dist/emulate/workos/routes/authorization-resources.js +117 -0
  89. package/dist/emulate/workos/routes/authorization-resources.js.map +1 -0
  90. package/dist/emulate/workos/routes/authorization-roles.d.ts +2 -0
  91. package/dist/emulate/workos/routes/authorization-roles.js +13 -0
  92. package/dist/emulate/workos/routes/authorization-roles.js.map +1 -0
  93. package/dist/emulate/workos/routes/config.d.ts +2 -0
  94. package/dist/emulate/workos/routes/config.js +57 -0
  95. package/dist/emulate/workos/routes/config.js.map +1 -0
  96. package/dist/emulate/workos/routes/connect.d.ts +2 -0
  97. package/dist/emulate/workos/routes/connect.js +65 -0
  98. package/dist/emulate/workos/routes/connect.js.map +1 -0
  99. package/dist/emulate/workos/routes/connections.d.ts +2 -0
  100. package/dist/emulate/workos/routes/connections.js +73 -0
  101. package/dist/emulate/workos/routes/connections.js.map +1 -0
  102. package/dist/emulate/workos/routes/data-integrations.d.ts +2 -0
  103. package/dist/emulate/workos/routes/data-integrations.js +55 -0
  104. package/dist/emulate/workos/routes/data-integrations.js.map +1 -0
  105. package/dist/emulate/workos/routes/directories.d.ts +2 -0
  106. package/dist/emulate/workos/routes/directories.js +90 -0
  107. package/dist/emulate/workos/routes/directories.js.map +1 -0
  108. package/dist/emulate/workos/routes/email-verification.d.ts +2 -0
  109. package/dist/emulate/workos/routes/email-verification.js +49 -0
  110. package/dist/emulate/workos/routes/email-verification.js.map +1 -0
  111. package/dist/emulate/workos/routes/events.d.ts +2 -0
  112. package/dist/emulate/workos/routes/events.js +18 -0
  113. package/dist/emulate/workos/routes/events.js.map +1 -0
  114. package/dist/emulate/workos/routes/feature-flags.d.ts +2 -0
  115. package/dist/emulate/workos/routes/feature-flags.js +103 -0
  116. package/dist/emulate/workos/routes/feature-flags.js.map +1 -0
  117. package/dist/emulate/workos/routes/invitations.d.ts +2 -0
  118. package/dist/emulate/workos/routes/invitations.js +122 -0
  119. package/dist/emulate/workos/routes/invitations.js.map +1 -0
  120. package/dist/emulate/workos/routes/legacy-mfa.d.ts +2 -0
  121. package/dist/emulate/workos/routes/legacy-mfa.js +75 -0
  122. package/dist/emulate/workos/routes/legacy-mfa.js.map +1 -0
  123. package/dist/emulate/workos/routes/magic-auth.d.ts +2 -0
  124. package/dist/emulate/workos/routes/magic-auth.js +32 -0
  125. package/dist/emulate/workos/routes/magic-auth.js.map +1 -0
  126. package/dist/emulate/workos/routes/memberships.d.ts +2 -0
  127. package/dist/emulate/workos/routes/memberships.js +114 -0
  128. package/dist/emulate/workos/routes/memberships.js.map +1 -0
  129. package/dist/emulate/workos/routes/organization-domains.d.ts +2 -0
  130. package/dist/emulate/workos/routes/organization-domains.js +58 -0
  131. package/dist/emulate/workos/routes/organization-domains.js.map +1 -0
  132. package/dist/emulate/workos/routes/organizations.d.ts +2 -0
  133. package/dist/emulate/workos/routes/organizations.js +131 -0
  134. package/dist/emulate/workos/routes/organizations.js.map +1 -0
  135. package/dist/emulate/workos/routes/password-reset.d.ts +2 -0
  136. package/dist/emulate/workos/routes/password-reset.js +61 -0
  137. package/dist/emulate/workos/routes/password-reset.js.map +1 -0
  138. package/dist/emulate/workos/routes/pipes.d.ts +2 -0
  139. package/dist/emulate/workos/routes/pipes.js +82 -0
  140. package/dist/emulate/workos/routes/pipes.js.map +1 -0
  141. package/dist/emulate/workos/routes/portal.d.ts +2 -0
  142. package/dist/emulate/workos/routes/portal.js +18 -0
  143. package/dist/emulate/workos/routes/portal.js.map +1 -0
  144. package/dist/emulate/workos/routes/radar.d.ts +2 -0
  145. package/dist/emulate/workos/routes/radar.js +41 -0
  146. package/dist/emulate/workos/routes/radar.js.map +1 -0
  147. package/dist/emulate/workos/routes/sessions.d.ts +2 -0
  148. package/dist/emulate/workos/routes/sessions.js +51 -0
  149. package/dist/emulate/workos/routes/sessions.js.map +1 -0
  150. package/dist/emulate/workos/routes/sso.d.ts +2 -0
  151. package/dist/emulate/workos/routes/sso.js +161 -0
  152. package/dist/emulate/workos/routes/sso.js.map +1 -0
  153. package/dist/emulate/workos/routes/user-features.d.ts +2 -0
  154. package/dist/emulate/workos/routes/user-features.js +50 -0
  155. package/dist/emulate/workos/routes/user-features.js.map +1 -0
  156. package/dist/emulate/workos/routes/users.d.ts +2 -0
  157. package/dist/emulate/workos/routes/users.js +129 -0
  158. package/dist/emulate/workos/routes/users.js.map +1 -0
  159. package/dist/emulate/workos/routes/webhook-endpoints.d.ts +2 -0
  160. package/dist/emulate/workos/routes/webhook-endpoints.js +66 -0
  161. package/dist/emulate/workos/routes/webhook-endpoints.js.map +1 -0
  162. package/dist/emulate/workos/routes/widgets.d.ts +2 -0
  163. package/dist/emulate/workos/routes/widgets.js +27 -0
  164. package/dist/emulate/workos/routes/widgets.js.map +1 -0
  165. package/dist/emulate/workos/store.d.ts +48 -0
  166. package/dist/emulate/workos/store.js +102 -0
  167. package/dist/emulate/workos/store.js.map +1 -0
  168. package/dist/emulate/workos/webhook-signer.d.ts +1 -0
  169. package/dist/emulate/workos/webhook-signer.js +8 -0
  170. package/dist/emulate/workos/webhook-signer.js.map +1 -0
  171. package/dist/gen-routes-lib.spec.ts +659 -0
  172. package/dist/gen-routes-lib.ts +647 -0
  173. package/dist/gen-routes.ts +96 -0
  174. package/dist/lib/dev-command.d.ts +26 -0
  175. package/dist/lib/dev-command.js +122 -0
  176. package/dist/lib/dev-command.js.map +1 -0
  177. package/dist/lib/run-with-core.js +0 -3
  178. package/dist/lib/run-with-core.js.map +1 -1
  179. package/dist/lib/settings.js +1 -1
  180. package/dist/lib/settings.js.map +1 -1
  181. package/dist/utils/help-json.js +1 -0
  182. package/dist/utils/help-json.js.map +1 -1
  183. package/dist/utils/register-subcommand.d.ts +5 -2
  184. package/dist/utils/register-subcommand.js +16 -19
  185. package/dist/utils/register-subcommand.js.map +1 -1
  186. package/package.json +21 -8
@@ -0,0 +1,41 @@
1
+ import { notFound, parseJsonBody, parseListParams } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatRadarAttempt, formatListResponse } from '../helpers.js';
4
+ export function radarRoutes(ctx) {
5
+ const { app, store } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ // List attempts
8
+ app.get('/radar/attempts', (c) => {
9
+ const url = new URL(c.req.url);
10
+ const params = parseListParams(url);
11
+ const result = ws.radarAttempts.list({ ...params });
12
+ return c.json(formatListResponse(result, formatRadarAttempt));
13
+ });
14
+ // Get attempt
15
+ app.get('/radar/attempts/:id', (c) => {
16
+ const attempt = ws.radarAttempts.get(c.req.param('id'));
17
+ if (!attempt)
18
+ throw notFound('RadarAttempt');
19
+ return c.json(formatRadarAttempt(attempt));
20
+ });
21
+ // Manage allow/deny lists
22
+ app.post('/radar/lists/:type/:action', async (c) => {
23
+ const listType = c.req.param('type');
24
+ const action = c.req.param('action');
25
+ const body = await parseJsonBody(c);
26
+ const entries = body.entries ?? [];
27
+ const key = `radar_${listType}_list`;
28
+ const existing = store.getData(key) ?? new Set();
29
+ if (action === 'add') {
30
+ for (const entry of entries)
31
+ existing.add(entry);
32
+ }
33
+ else if (action === 'remove') {
34
+ for (const entry of entries)
35
+ existing.delete(entry);
36
+ }
37
+ store.setData(key, existing);
38
+ return c.json({ success: true });
39
+ });
40
+ }
41
+ //# sourceMappingURL=radar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radar.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/radar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAClG,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEvE,MAAM,UAAU,WAAW,CAAC,GAAiB;IAC3C,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IAC3B,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAEjC,gBAAgB;IAChB,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC,EAAE,EAAE;QAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC;QACpD,OAAO,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,cAAc;IACd,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC,CAAC,EAAE,EAAE;QACnC,MAAM,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,OAAO;YAAE,MAAM,QAAQ,CAAC,cAAc,CAAC,CAAC;QAC7C,OAAO,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,GAAG,CAAC,IAAI,CAAC,4BAA4B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACjD,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,OAAO,GAAI,IAAI,CAAC,OAAoB,IAAI,EAAE,CAAC;QAEjD,MAAM,GAAG,GAAG,SAAS,QAAQ,OAAO,CAAC;QACrC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAc,GAAG,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QAEtE,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACrB,KAAK,MAAM,KAAK,IAAI,OAAO;gBAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnD,CAAC;aAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,KAAK,MAAM,KAAK,IAAI,OAAO;gBAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtD,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAC7B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { type RouteContext, notFound, parseJsonBody, parseListParams } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatRadarAttempt, formatListResponse } from '../helpers.js';\n\nexport function radarRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n // List attempts\n app.get('/radar/attempts', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const result = ws.radarAttempts.list({ ...params });\n return c.json(formatListResponse(result, formatRadarAttempt));\n });\n\n // Get attempt\n app.get('/radar/attempts/:id', (c) => {\n const attempt = ws.radarAttempts.get(c.req.param('id'));\n if (!attempt) throw notFound('RadarAttempt');\n return c.json(formatRadarAttempt(attempt));\n });\n\n // Manage allow/deny lists\n app.post('/radar/lists/:type/:action', async (c) => {\n const listType = c.req.param('type');\n const action = c.req.param('action');\n const body = await parseJsonBody(c);\n const entries = (body.entries as string[]) ?? [];\n\n const key = `radar_${listType}_list`;\n const existing = store.getData<Set<string>>(key) ?? new Set<string>();\n\n if (action === 'add') {\n for (const entry of entries) existing.add(entry);\n } else if (action === 'remove') {\n for (const entry of entries) existing.delete(entry);\n }\n\n store.setData(key, existing);\n return c.json({ success: true });\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function sessionRoutes(ctx: RouteContext): void;
@@ -0,0 +1,51 @@
1
+ import { notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatSession, assertLocalRedirectUri } from '../helpers.js';
4
+ export function sessionRoutes(ctx) {
5
+ const { app, store, jwt } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ app.get('/user_management/users/:id/sessions', (c) => {
8
+ const user = ws.users.get(c.req.param('id'));
9
+ if (!user)
10
+ throw notFound('User');
11
+ const sessions = ws.sessions.findBy('user_id', user.id);
12
+ return c.json({
13
+ object: 'list',
14
+ data: sessions.map(formatSession),
15
+ list_metadata: { before: null, after: null },
16
+ });
17
+ });
18
+ app.post('/user_management/sessions/revoke', async (c) => {
19
+ const body = await parseJsonBody(c);
20
+ const sessionId = body.session_id;
21
+ if (!sessionId) {
22
+ throw new WorkOSApiError(400, 'session_id is required', 'invalid_request');
23
+ }
24
+ const session = ws.sessions.get(sessionId);
25
+ if (!session)
26
+ throw notFound('Session');
27
+ ws.sessions.delete(session.id);
28
+ return c.json({ success: true });
29
+ });
30
+ // Public endpoint — no auth required (security: [])
31
+ app.get('/user_management/sessions/logout', (c) => {
32
+ const url = new URL(c.req.url);
33
+ const sessionId = url.searchParams.get('session_id');
34
+ const returnTo = url.searchParams.get('return_to');
35
+ if (!sessionId) {
36
+ throw new WorkOSApiError(422, 'session_id is required', 'invalid_request');
37
+ }
38
+ const session = ws.sessions.get(sessionId);
39
+ if (session)
40
+ ws.sessions.delete(session.id);
41
+ if (returnTo) {
42
+ assertLocalRedirectUri(returnTo);
43
+ return c.redirect(returnTo);
44
+ }
45
+ return c.json({ success: true });
46
+ });
47
+ app.get('/user_management/sessions/jwks/:clientId', (c) => {
48
+ return c.json(jwt.getJWKS());
49
+ });
50
+ }
51
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAEtE,MAAM,UAAU,aAAa,CAAC,GAAiB;IAC7C,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;IAChC,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAEjC,GAAG,CAAC,GAAG,CAAC,qCAAqC,EAAE,CAAC,CAAC,EAAE,EAAE;QACnD,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI;YAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;QAElC,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACxD,OAAO,CAAC,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC;YACjC,aAAa,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;SAC7C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,kCAAkC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACvD,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAgC,CAAC;QACxD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,iBAAiB,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,OAAO;YAAE,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;QAExC,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,oDAAoD;IACpD,GAAG,CAAC,GAAG,CAAC,kCAAkC,EAAE,CAAC,CAAC,EAAE,EAAE;QAChD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACrD,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAEnD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,iBAAiB,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,OAAO;YAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAE5C,IAAI,QAAQ,EAAE,CAAC;YACb,sBAAsB,CAAC,QAAQ,CAAC,CAAC;YACjC,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,GAAG,CAAC,0CAA0C,EAAE,CAAC,CAAC,EAAE,EAAE;QACxD,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { type RouteContext, notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatSession, assertLocalRedirectUri } from '../helpers.js';\n\nexport function sessionRoutes(ctx: RouteContext): void {\n const { app, store, jwt } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/user_management/users/:id/sessions', (c) => {\n const user = ws.users.get(c.req.param('id'));\n if (!user) throw notFound('User');\n\n const sessions = ws.sessions.findBy('user_id', user.id);\n return c.json({\n object: 'list',\n data: sessions.map(formatSession),\n list_metadata: { before: null, after: null },\n });\n });\n\n app.post('/user_management/sessions/revoke', async (c) => {\n const body = await parseJsonBody(c);\n const sessionId = body.session_id as string | undefined;\n if (!sessionId) {\n throw new WorkOSApiError(400, 'session_id is required', 'invalid_request');\n }\n\n const session = ws.sessions.get(sessionId);\n if (!session) throw notFound('Session');\n\n ws.sessions.delete(session.id);\n return c.json({ success: true });\n });\n\n // Public endpoint — no auth required (security: [])\n app.get('/user_management/sessions/logout', (c) => {\n const url = new URL(c.req.url);\n const sessionId = url.searchParams.get('session_id');\n const returnTo = url.searchParams.get('return_to');\n\n if (!sessionId) {\n throw new WorkOSApiError(422, 'session_id is required', 'invalid_request');\n }\n\n const session = ws.sessions.get(sessionId);\n if (session) ws.sessions.delete(session.id);\n\n if (returnTo) {\n assertLocalRedirectUri(returnTo);\n return c.redirect(returnTo);\n }\n return c.json({ success: true });\n });\n\n app.get('/user_management/sessions/jwks/:clientId', (c) => {\n return c.json(jwt.getJWKS());\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function ssoRoutes(ctx: RouteContext): void;
@@ -0,0 +1,161 @@
1
+ import { parseJsonBody, WorkOSApiError, generateId } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatSSOProfile, expiresIn, isExpired, assertLocalRedirectUri } from '../helpers.js';
4
+ import { STORE_KEY_PREFIXES } from '../constants.js';
5
+ export function ssoRoutes(ctx) {
6
+ const { app, store, jwt } = ctx;
7
+ const ws = getWorkOSStore(store);
8
+ app.get('/sso/authorize', (c) => {
9
+ const url = new URL(c.req.url);
10
+ const redirectUri = url.searchParams.get('redirect_uri');
11
+ const state = url.searchParams.get('state');
12
+ const connectionId = url.searchParams.get('connection');
13
+ const organizationId = url.searchParams.get('organization');
14
+ const domainHint = url.searchParams.get('domain_hint');
15
+ const loginHint = url.searchParams.get('login_hint');
16
+ if (!redirectUri) {
17
+ throw new WorkOSApiError(400, 'Missing required parameter: redirect_uri', 'invalid_request');
18
+ }
19
+ assertLocalRedirectUri(redirectUri);
20
+ let connection;
21
+ if (connectionId) {
22
+ connection = ws.connections.get(connectionId);
23
+ }
24
+ else if (organizationId) {
25
+ connection = ws.connections.findBy('organization_id', organizationId).find((c) => c.state === 'active');
26
+ }
27
+ else if (domainHint) {
28
+ connection = ws.connections
29
+ .all()
30
+ .find((c) => c.state === 'active' && c.domains.some((d) => d.domain === domainHint));
31
+ }
32
+ if (!connection || connection.state !== 'active') {
33
+ throw new WorkOSApiError(404, 'No active connection found', 'connection_not_found');
34
+ }
35
+ const email = loginHint ?? `user@${connection.domains[0]?.domain ?? 'example.com'}`;
36
+ let profile = ws.ssoProfiles.findOneBy('email', email);
37
+ if (!profile || profile.connection_id !== connection.id) {
38
+ profile = ws.ssoProfiles.insert({
39
+ object: 'profile',
40
+ connection_id: connection.id,
41
+ connection_type: connection.connection_type,
42
+ organization_id: connection.organization_id,
43
+ idp_id: `idp_${generateId('usr')}`,
44
+ email,
45
+ first_name: email.split('@')[0],
46
+ last_name: null,
47
+ groups: [],
48
+ raw_attributes: { email },
49
+ });
50
+ }
51
+ const authCode = ws.ssoAuthorizations.insert({
52
+ code: generateId('sso_code'),
53
+ connection_id: connection.id,
54
+ organization_id: connection.organization_id,
55
+ profile_id: profile.id,
56
+ redirect_uri: redirectUri,
57
+ state,
58
+ expires_at: expiresIn(10),
59
+ });
60
+ const redirect = new URL(redirectUri);
61
+ redirect.searchParams.set('code', authCode.code);
62
+ if (state)
63
+ redirect.searchParams.set('state', state);
64
+ return c.redirect(redirect.toString());
65
+ });
66
+ app.post('/sso/token', async (c) => {
67
+ const body = await parseJsonBody(c);
68
+ const grantType = body.grant_type;
69
+ const code = body.code;
70
+ if (grantType !== 'authorization_code') {
71
+ throw new WorkOSApiError(400, 'Unsupported grant_type', 'invalid_request');
72
+ }
73
+ if (!code) {
74
+ throw new WorkOSApiError(400, 'code is required', 'invalid_request');
75
+ }
76
+ const auth = ws.ssoAuthorizations.findOneBy('code', code);
77
+ if (!auth) {
78
+ throw new WorkOSApiError(400, 'Invalid authorization code', 'invalid_code');
79
+ }
80
+ if (isExpired(auth.expires_at)) {
81
+ ws.ssoAuthorizations.delete(auth.id);
82
+ throw new WorkOSApiError(400, 'Authorization code has expired', 'expired_code');
83
+ }
84
+ const profile = ws.ssoProfiles.get(auth.profile_id);
85
+ if (!profile) {
86
+ throw new WorkOSApiError(500, 'Profile not found', 'server_error');
87
+ }
88
+ ws.ssoAuthorizations.delete(auth.id);
89
+ const accessToken = jwt.sign({
90
+ sub: profile.id,
91
+ aud: body.client_id ?? 'workos-emulate',
92
+ org_id: auth.organization_id,
93
+ });
94
+ store.setData(`${STORE_KEY_PREFIXES.ssoToken}${accessToken}`, profile.id);
95
+ return c.json({
96
+ profile: formatSSOProfile(profile),
97
+ access_token: accessToken,
98
+ });
99
+ });
100
+ app.get('/sso/profile', (c) => {
101
+ const authHeader = c.req.header('Authorization');
102
+ if (!authHeader) {
103
+ throw new WorkOSApiError(401, 'Unauthorized', 'unauthorized');
104
+ }
105
+ const token = authHeader.replace(/^Bearer\s+/i, '').trim();
106
+ const profileId = store.getData(`${STORE_KEY_PREFIXES.ssoToken}${token}`);
107
+ if (!profileId) {
108
+ try {
109
+ const payload = jwt.verify(token);
110
+ const profile = ws.ssoProfiles.get(payload.sub);
111
+ if (profile)
112
+ return c.json(formatSSOProfile(profile));
113
+ }
114
+ catch {
115
+ // fall through
116
+ }
117
+ throw new WorkOSApiError(401, 'Invalid access token', 'unauthorized');
118
+ }
119
+ const profile = ws.ssoProfiles.get(profileId);
120
+ if (!profile) {
121
+ throw new WorkOSApiError(404, 'Profile not found', 'not_found');
122
+ }
123
+ return c.json(formatSSOProfile(profile));
124
+ });
125
+ app.get('/sso/jwks', (c) => {
126
+ return c.json(jwt.getJWKS());
127
+ });
128
+ // SSO Single Logout — generate logout token
129
+ app.post('/sso/logout/authorize', async (c) => {
130
+ const body = await parseJsonBody(c);
131
+ const profileId = body.profile_id;
132
+ if (!profileId) {
133
+ throw new WorkOSApiError(400, 'profile_id is required', 'invalid_request');
134
+ }
135
+ const profile = ws.ssoProfiles.get(profileId);
136
+ if (!profile) {
137
+ throw new WorkOSApiError(404, 'Profile not found', 'not_found');
138
+ }
139
+ const logoutToken = generateId('sso_logout');
140
+ store.setData(`${STORE_KEY_PREFIXES.ssoLogout}${logoutToken}`, profile.id);
141
+ return c.json({
142
+ logout_token: logoutToken,
143
+ logout_url: `${ctx.baseUrl}/sso/logout?logout_token=${logoutToken}`,
144
+ });
145
+ });
146
+ // SSO Single Logout — redirect (public, no auth)
147
+ app.get('/sso/logout', (c) => {
148
+ const url = new URL(c.req.url);
149
+ const logoutToken = url.searchParams.get('logout_token');
150
+ if (!logoutToken) {
151
+ throw new WorkOSApiError(400, 'logout_token is required', 'invalid_request');
152
+ }
153
+ const profileId = store.getData(`${STORE_KEY_PREFIXES.ssoLogout}${logoutToken}`);
154
+ if (!profileId) {
155
+ throw new WorkOSApiError(400, 'Invalid logout token', 'invalid_logout_token');
156
+ }
157
+ store.setData(`${STORE_KEY_PREFIXES.ssoLogout}${logoutToken}`, undefined);
158
+ return c.json({ success: true });
159
+ });
160
+ }
161
+ //# sourceMappingURL=sso.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/sso.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, parseJsonBody, WorkOSApiError, generateId } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatSSOProfile, expiresIn, isExpired, assertLocalRedirectUri } from '../helpers.js';\nimport type { WorkOSConnection } from '../entities.js';\nimport { STORE_KEY_PREFIXES } from '../constants.js';\n\nexport function ssoRoutes(ctx: RouteContext): void {\n const { app, store, jwt } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/sso/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 connectionId = url.searchParams.get('connection');\n const organizationId = url.searchParams.get('organization');\n const domainHint = url.searchParams.get('domain_hint');\n const loginHint = url.searchParams.get('login_hint');\n\n if (!redirectUri) {\n throw new WorkOSApiError(400, 'Missing required parameter: redirect_uri', 'invalid_request');\n }\n assertLocalRedirectUri(redirectUri);\n\n let connection: WorkOSConnection | undefined;\n\n if (connectionId) {\n connection = ws.connections.get(connectionId);\n } else if (organizationId) {\n connection = ws.connections.findBy('organization_id', organizationId).find((c) => c.state === 'active');\n } else if (domainHint) {\n connection = ws.connections\n .all()\n .find((c) => c.state === 'active' && c.domains.some((d) => d.domain === domainHint));\n }\n\n if (!connection || connection.state !== 'active') {\n throw new WorkOSApiError(404, 'No active connection found', 'connection_not_found');\n }\n\n const email = loginHint ?? `user@${connection.domains[0]?.domain ?? 'example.com'}`;\n let profile = ws.ssoProfiles.findOneBy('email', email);\n if (!profile || profile.connection_id !== connection.id) {\n profile = ws.ssoProfiles.insert({\n object: 'profile',\n connection_id: connection.id,\n connection_type: connection.connection_type,\n organization_id: connection.organization_id,\n idp_id: `idp_${generateId('usr')}`,\n email,\n first_name: email.split('@')[0],\n last_name: null,\n groups: [],\n raw_attributes: { email },\n });\n }\n\n const authCode = ws.ssoAuthorizations.insert({\n code: generateId('sso_code'),\n connection_id: connection.id,\n organization_id: connection.organization_id,\n profile_id: profile.id,\n redirect_uri: redirectUri,\n state,\n expires_at: expiresIn(10),\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 app.post('/sso/token', async (c) => {\n const body = await parseJsonBody(c);\n const grantType = body.grant_type as string;\n const code = body.code as string;\n\n if (grantType !== 'authorization_code') {\n throw new WorkOSApiError(400, 'Unsupported grant_type', 'invalid_request');\n }\n if (!code) {\n throw new WorkOSApiError(400, 'code is required', 'invalid_request');\n }\n\n const auth = ws.ssoAuthorizations.findOneBy('code', code);\n if (!auth) {\n throw new WorkOSApiError(400, 'Invalid authorization code', 'invalid_code');\n }\n if (isExpired(auth.expires_at)) {\n ws.ssoAuthorizations.delete(auth.id);\n throw new WorkOSApiError(400, 'Authorization code has expired', 'expired_code');\n }\n\n const profile = ws.ssoProfiles.get(auth.profile_id);\n if (!profile) {\n throw new WorkOSApiError(500, 'Profile not found', 'server_error');\n }\n\n ws.ssoAuthorizations.delete(auth.id);\n\n const accessToken = jwt.sign({\n sub: profile.id,\n aud: (body.client_id as string) ?? 'workos-emulate',\n org_id: auth.organization_id,\n });\n\n store.setData(`${STORE_KEY_PREFIXES.ssoToken}${accessToken}`, profile.id);\n\n return c.json({\n profile: formatSSOProfile(profile),\n access_token: accessToken,\n });\n });\n\n app.get('/sso/profile', (c) => {\n const authHeader = c.req.header('Authorization');\n if (!authHeader) {\n throw new WorkOSApiError(401, 'Unauthorized', 'unauthorized');\n }\n const token = authHeader.replace(/^Bearer\\s+/i, '').trim();\n\n const profileId = store.getData<string>(`${STORE_KEY_PREFIXES.ssoToken}${token}`);\n if (!profileId) {\n try {\n const payload = jwt.verify(token);\n const profile = ws.ssoProfiles.get(payload.sub);\n if (profile) return c.json(formatSSOProfile(profile));\n } catch {\n // fall through\n }\n throw new WorkOSApiError(401, 'Invalid access token', 'unauthorized');\n }\n\n const profile = ws.ssoProfiles.get(profileId);\n if (!profile) {\n throw new WorkOSApiError(404, 'Profile not found', 'not_found');\n }\n\n return c.json(formatSSOProfile(profile));\n });\n\n app.get('/sso/jwks', (c) => {\n return c.json(jwt.getJWKS());\n });\n\n // SSO Single Logout — generate logout token\n app.post('/sso/logout/authorize', async (c) => {\n const body = await parseJsonBody(c);\n const profileId = body.profile_id as string;\n if (!profileId) {\n throw new WorkOSApiError(400, 'profile_id is required', 'invalid_request');\n }\n\n const profile = ws.ssoProfiles.get(profileId);\n if (!profile) {\n throw new WorkOSApiError(404, 'Profile not found', 'not_found');\n }\n\n const logoutToken = generateId('sso_logout');\n store.setData(`${STORE_KEY_PREFIXES.ssoLogout}${logoutToken}`, profile.id);\n\n return c.json({\n logout_token: logoutToken,\n logout_url: `${ctx.baseUrl}/sso/logout?logout_token=${logoutToken}`,\n });\n });\n\n // SSO Single Logout — redirect (public, no auth)\n app.get('/sso/logout', (c) => {\n const url = new URL(c.req.url);\n const logoutToken = url.searchParams.get('logout_token');\n\n if (!logoutToken) {\n throw new WorkOSApiError(400, 'logout_token is required', 'invalid_request');\n }\n\n const profileId = store.getData<string>(`${STORE_KEY_PREFIXES.ssoLogout}${logoutToken}`);\n if (!profileId) {\n throw new WorkOSApiError(400, 'Invalid logout token', 'invalid_logout_token');\n }\n\n store.setData(`${STORE_KEY_PREFIXES.ssoLogout}${logoutToken}`, undefined);\n return c.json({ success: true });\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function userFeatureRoutes(ctx: RouteContext): void;
@@ -0,0 +1,50 @@
1
+ import { notFound } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatAuthorizedApplication, formatConnectedAccount, formatPipeConnection } from '../helpers.js';
4
+ export function userFeatureRoutes(ctx) {
5
+ const { app, store } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ app.get('/user_management/users/:user_id/authorized_applications', (c) => {
8
+ const user = ws.users.get(c.req.param('user_id'));
9
+ if (!user)
10
+ throw notFound('User');
11
+ const apps = ws.authorizedApplications.findBy('user_id', user.id);
12
+ return c.json({
13
+ object: 'list',
14
+ data: apps.map(formatAuthorizedApplication),
15
+ list_metadata: { before: null, after: null },
16
+ });
17
+ });
18
+ app.delete('/user_management/users/:user_id/authorized_applications/:application_id', (c) => {
19
+ const user = ws.users.get(c.req.param('user_id'));
20
+ if (!user)
21
+ throw notFound('User');
22
+ const appItem = ws.authorizedApplications.get(c.req.param('application_id'));
23
+ if (!appItem || appItem.user_id !== user.id)
24
+ throw notFound('Authorized Application');
25
+ ws.authorizedApplications.delete(appItem.id);
26
+ return c.body(null, 204);
27
+ });
28
+ app.get('/user_management/users/:user_id/connected_accounts/:slug', (c) => {
29
+ const user = ws.users.get(c.req.param('user_id'));
30
+ if (!user)
31
+ throw notFound('User');
32
+ const slug = c.req.param('slug');
33
+ const account = ws.connectedAccounts.findBy('user_id', user.id).find((a) => a.provider === slug);
34
+ if (!account)
35
+ throw notFound('Connected Account');
36
+ return c.json(formatConnectedAccount(account));
37
+ });
38
+ app.get('/user_management/users/:user_id/data_providers', (c) => {
39
+ const user = ws.users.get(c.req.param('user_id'));
40
+ if (!user)
41
+ throw notFound('User');
42
+ const pipes = ws.pipeConnections.findBy('user_id', user.id);
43
+ return c.json({
44
+ object: 'list',
45
+ data: pipes.map(formatPipeConnection),
46
+ list_metadata: { before: null, after: null },
47
+ });
48
+ });
49
+ }
50
+ //# sourceMappingURL=user-features.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-features.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/user-features.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatAuthorizedApplication, formatConnectedAccount, formatPipeConnection } from '../helpers.js';\n\nexport function userFeatureRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/user_management/users/:user_id/authorized_applications', (c) => {\n const user = ws.users.get(c.req.param('user_id'));\n if (!user) throw notFound('User');\n\n const apps = ws.authorizedApplications.findBy('user_id', user.id);\n return c.json({\n object: 'list',\n data: apps.map(formatAuthorizedApplication),\n list_metadata: { before: null, after: null },\n });\n });\n\n app.delete('/user_management/users/:user_id/authorized_applications/:application_id', (c) => {\n const user = ws.users.get(c.req.param('user_id'));\n if (!user) throw notFound('User');\n\n const appItem = ws.authorizedApplications.get(c.req.param('application_id'));\n if (!appItem || appItem.user_id !== user.id) throw notFound('Authorized Application');\n\n ws.authorizedApplications.delete(appItem.id);\n return c.body(null, 204);\n });\n\n app.get('/user_management/users/:user_id/connected_accounts/:slug', (c) => {\n const user = ws.users.get(c.req.param('user_id'));\n if (!user) throw notFound('User');\n\n const slug = c.req.param('slug');\n const account = ws.connectedAccounts.findBy('user_id', user.id).find((a) => a.provider === slug);\n\n if (!account) throw notFound('Connected Account');\n return c.json(formatConnectedAccount(account));\n });\n\n app.get('/user_management/users/:user_id/data_providers', (c) => {\n const user = ws.users.get(c.req.param('user_id'));\n if (!user) throw notFound('User');\n\n const pipes = ws.pipeConnections.findBy('user_id', user.id);\n return c.json({\n object: 'list',\n data: pipes.map(formatPipeConnection),\n list_metadata: { before: null, after: null },\n });\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function userRoutes(ctx: RouteContext): void;
@@ -0,0 +1,129 @@
1
+ import { notFound, validationError, parseJsonBody, WorkOSApiError, parseListParams, } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatUser, formatIdentity, hashPassword, formatListResponse } from '../helpers.js';
4
+ export function userRoutes(ctx) {
5
+ const { app, store } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ app.post('/user_management/users', async (c) => {
8
+ const body = await parseJsonBody(c);
9
+ const email = body.email;
10
+ if (!email) {
11
+ throw validationError('email is required', [{ field: 'email', code: 'required' }]);
12
+ }
13
+ const existing = ws.users.findOneBy('email', email);
14
+ if (existing) {
15
+ throw new WorkOSApiError(409, 'A user with this email already exists', 'user_already_exists');
16
+ }
17
+ const password = body.password;
18
+ const user = ws.users.insert({
19
+ object: 'user',
20
+ email,
21
+ first_name: body.first_name ?? null,
22
+ last_name: body.last_name ?? null,
23
+ email_verified: body.email_verified ?? false,
24
+ profile_picture_url: null,
25
+ last_sign_in_at: null,
26
+ external_id: body.external_id ?? null,
27
+ metadata: body.metadata ?? {},
28
+ locale: null,
29
+ password_hash: password ? hashPassword(password) : null,
30
+ impersonator: null,
31
+ });
32
+ return c.json(formatUser(user), 201);
33
+ });
34
+ app.get('/user_management/users', (c) => {
35
+ const url = new URL(c.req.url);
36
+ const params = parseListParams(url);
37
+ const emailFilter = url.searchParams.get('email') ?? undefined;
38
+ const orgFilter = url.searchParams.get('organization_id') ?? undefined;
39
+ let orgUserIds;
40
+ if (orgFilter) {
41
+ orgUserIds = new Set(ws.organizationMemberships.findBy('organization_id', orgFilter).map((m) => m.user_id));
42
+ }
43
+ const result = ws.users.list({
44
+ ...params,
45
+ filter: (user) => {
46
+ if (emailFilter && user.email !== emailFilter)
47
+ return false;
48
+ if (orgUserIds && !orgUserIds.has(user.id))
49
+ return false;
50
+ return true;
51
+ },
52
+ });
53
+ return c.json(formatListResponse(result, formatUser));
54
+ });
55
+ app.get('/user_management/users/:id', (c) => {
56
+ const user = ws.users.get(c.req.param('id'));
57
+ if (!user)
58
+ throw notFound('User');
59
+ return c.json(formatUser(user));
60
+ });
61
+ app.get('/user_management/users/external_id/:external_id', (c) => {
62
+ const user = ws.users.findOneBy('external_id', c.req.param('external_id'));
63
+ if (!user)
64
+ throw notFound('User');
65
+ return c.json(formatUser(user));
66
+ });
67
+ app.put('/user_management/users/:id', async (c) => {
68
+ const user = ws.users.get(c.req.param('id'));
69
+ if (!user)
70
+ throw notFound('User');
71
+ const body = await parseJsonBody(c);
72
+ const updates = {};
73
+ if ('first_name' in body)
74
+ updates.first_name = body.first_name ?? null;
75
+ if ('last_name' in body)
76
+ updates.last_name = body.last_name ?? null;
77
+ if ('email_verified' in body)
78
+ updates.email_verified = body.email_verified;
79
+ if ('external_id' in body)
80
+ updates.external_id = body.external_id ?? null;
81
+ if ('metadata' in body)
82
+ updates.metadata = body.metadata ?? {};
83
+ if ('password' in body && body.password) {
84
+ updates.password_hash = hashPassword(body.password);
85
+ }
86
+ const updated = ws.users.update(user.id, updates);
87
+ return c.json(formatUser(updated));
88
+ });
89
+ app.delete('/user_management/users/:id', (c) => {
90
+ const user = ws.users.get(c.req.param('id'));
91
+ if (!user)
92
+ throw notFound('User');
93
+ for (const s of ws.sessions.findBy('user_id', user.id)) {
94
+ ws.sessions.delete(s.id);
95
+ }
96
+ for (const m of ws.organizationMemberships.findBy('user_id', user.id)) {
97
+ ws.organizationMemberships.delete(m.id);
98
+ }
99
+ for (const f of ws.authFactors.findBy('user_id', user.id)) {
100
+ ws.authFactors.delete(f.id);
101
+ }
102
+ for (const i of ws.identities.findBy('user_id', user.id)) {
103
+ ws.identities.delete(i.id);
104
+ }
105
+ for (const pr of ws.passwordResets.findBy('user_id', user.id)) {
106
+ ws.passwordResets.delete(pr.id);
107
+ }
108
+ for (const ev of ws.emailVerifications.findBy('user_id', user.id)) {
109
+ ws.emailVerifications.delete(ev.id);
110
+ }
111
+ for (const ma of ws.magicAuths.findBy('user_id', user.id)) {
112
+ ws.magicAuths.delete(ma.id);
113
+ }
114
+ ws.users.delete(user.id);
115
+ return c.body(null, 204);
116
+ });
117
+ app.get('/user_management/users/:id/identities', (c) => {
118
+ const user = ws.users.get(c.req.param('id'));
119
+ if (!user)
120
+ throw notFound('User');
121
+ const identities = ws.identities.findBy('user_id', user.id);
122
+ return c.json({
123
+ object: 'list',
124
+ data: identities.map(formatIdentity),
125
+ list_metadata: { before: null, after: null },
126
+ });
127
+ });
128
+ }
129
+ //# sourceMappingURL=users.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"users.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/users.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type RouteContext,\n notFound,\n validationError,\n parseJsonBody,\n WorkOSApiError,\n parseListParams,\n} from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatUser, formatIdentity, hashPassword, formatListResponse } from '../helpers.js';\n\nexport function userRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n app.post('/user_management/users', async (c) => {\n const body = await parseJsonBody(c);\n const email = body.email as string | undefined;\n if (!email) {\n throw validationError('email is required', [{ field: 'email', code: 'required' }]);\n }\n\n const existing = ws.users.findOneBy('email', email);\n if (existing) {\n throw new WorkOSApiError(409, 'A user with this email already exists', 'user_already_exists');\n }\n\n const password = body.password as string | undefined;\n const user = ws.users.insert({\n object: 'user',\n email,\n first_name: (body.first_name as string) ?? null,\n last_name: (body.last_name as string) ?? null,\n email_verified: (body.email_verified as boolean) ?? false,\n profile_picture_url: null,\n last_sign_in_at: null,\n external_id: (body.external_id as string) ?? null,\n metadata: (body.metadata as Record<string, string>) ?? {},\n locale: null,\n password_hash: password ? hashPassword(password) : null,\n impersonator: null,\n });\n\n return c.json(formatUser(user), 201);\n });\n\n app.get('/user_management/users', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const emailFilter = url.searchParams.get('email') ?? undefined;\n const orgFilter = url.searchParams.get('organization_id') ?? undefined;\n\n let orgUserIds: Set<string> | undefined;\n if (orgFilter) {\n orgUserIds = new Set(ws.organizationMemberships.findBy('organization_id', orgFilter).map((m) => m.user_id));\n }\n\n const result = ws.users.list({\n ...params,\n filter: (user) => {\n if (emailFilter && user.email !== emailFilter) return false;\n if (orgUserIds && !orgUserIds.has(user.id)) return false;\n return true;\n },\n });\n\n return c.json(formatListResponse(result, formatUser));\n });\n\n app.get('/user_management/users/:id', (c) => {\n const user = ws.users.get(c.req.param('id'));\n if (!user) throw notFound('User');\n return c.json(formatUser(user));\n });\n\n app.get('/user_management/users/external_id/:external_id', (c) => {\n const user = ws.users.findOneBy('external_id', c.req.param('external_id'));\n if (!user) throw notFound('User');\n return c.json(formatUser(user));\n });\n\n app.put('/user_management/users/:id', async (c) => {\n const user = ws.users.get(c.req.param('id'));\n if (!user) throw notFound('User');\n\n const body = await parseJsonBody(c);\n const updates: Record<string, unknown> = {};\n\n if ('first_name' in body) updates.first_name = body.first_name ?? null;\n if ('last_name' in body) updates.last_name = body.last_name ?? null;\n if ('email_verified' in body) updates.email_verified = body.email_verified;\n if ('external_id' in body) updates.external_id = body.external_id ?? null;\n if ('metadata' in body) updates.metadata = body.metadata ?? {};\n if ('password' in body && body.password) {\n updates.password_hash = hashPassword(body.password as string);\n }\n\n const updated = ws.users.update(user.id, updates);\n return c.json(formatUser(updated!));\n });\n\n app.delete('/user_management/users/:id', (c) => {\n const user = ws.users.get(c.req.param('id'));\n if (!user) throw notFound('User');\n\n for (const s of ws.sessions.findBy('user_id', user.id)) {\n ws.sessions.delete(s.id);\n }\n for (const m of ws.organizationMemberships.findBy('user_id', user.id)) {\n ws.organizationMemberships.delete(m.id);\n }\n for (const f of ws.authFactors.findBy('user_id', user.id)) {\n ws.authFactors.delete(f.id);\n }\n for (const i of ws.identities.findBy('user_id', user.id)) {\n ws.identities.delete(i.id);\n }\n for (const pr of ws.passwordResets.findBy('user_id', user.id)) {\n ws.passwordResets.delete(pr.id);\n }\n for (const ev of ws.emailVerifications.findBy('user_id', user.id)) {\n ws.emailVerifications.delete(ev.id);\n }\n for (const ma of ws.magicAuths.findBy('user_id', user.id)) {\n ws.magicAuths.delete(ma.id);\n }\n\n ws.users.delete(user.id);\n return c.body(null, 204);\n });\n\n app.get('/user_management/users/:id/identities', (c) => {\n const user = ws.users.get(c.req.param('id'));\n if (!user) throw notFound('User');\n\n const identities = ws.identities.findBy('user_id', user.id);\n return c.json({\n object: 'list',\n data: identities.map(formatIdentity),\n list_metadata: { before: null, after: null },\n });\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function webhookEndpointRoutes(ctx: RouteContext): void;
@@ -0,0 +1,66 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { notFound, validationError, parseJsonBody, parseListParams } from '../../core/index.js';
3
+ import { getWorkOSStore } from '../store.js';
4
+ import { formatWebhookEndpoint, formatListResponse } from '../helpers.js';
5
+ export function webhookEndpointRoutes(ctx) {
6
+ const { app, store } = ctx;
7
+ const ws = getWorkOSStore(store);
8
+ app.post('/webhook_endpoints', async (c) => {
9
+ const body = await parseJsonBody(c);
10
+ const url = body.url;
11
+ if (!url || typeof url !== 'string') {
12
+ throw validationError('URL is required', [{ field: 'url', code: 'required' }]);
13
+ }
14
+ const secret = body.secret ?? randomBytes(32).toString('hex');
15
+ const endpoint = ws.webhookEndpoints.insert({
16
+ object: 'webhook_endpoint',
17
+ url,
18
+ secret,
19
+ enabled: body.enabled !== false,
20
+ events: Array.isArray(body.events) ? body.events : [],
21
+ description: body.description ?? null,
22
+ });
23
+ return c.json(formatWebhookEndpoint(endpoint, { includeSecret: true }), 201);
24
+ });
25
+ app.get('/webhook_endpoints', (c) => {
26
+ const url = new URL(c.req.url);
27
+ const params = parseListParams(url);
28
+ const result = ws.webhookEndpoints.list(params);
29
+ return c.json(formatListResponse(result, (ep) => formatWebhookEndpoint(ep)));
30
+ });
31
+ app.get('/webhook_endpoints/:id', (c) => {
32
+ const ep = ws.webhookEndpoints.get(c.req.param('id'));
33
+ if (!ep)
34
+ throw notFound('WebhookEndpoint');
35
+ return c.json(formatWebhookEndpoint(ep));
36
+ });
37
+ app.put('/webhook_endpoints/:id', async (c) => {
38
+ const ep = ws.webhookEndpoints.get(c.req.param('id'));
39
+ if (!ep)
40
+ throw notFound('WebhookEndpoint');
41
+ const body = await parseJsonBody(c);
42
+ const updates = {};
43
+ if ('url' in body) {
44
+ if (!body.url || typeof body.url !== 'string') {
45
+ throw validationError('URL is required', [{ field: 'url', code: 'required' }]);
46
+ }
47
+ updates.url = body.url;
48
+ }
49
+ if ('enabled' in body)
50
+ updates.enabled = !!body.enabled;
51
+ if ('events' in body)
52
+ updates.events = Array.isArray(body.events) ? body.events : [];
53
+ if ('description' in body)
54
+ updates.description = body.description ?? null;
55
+ const updated = ws.webhookEndpoints.update(ep.id, updates);
56
+ return c.json(formatWebhookEndpoint(updated));
57
+ });
58
+ app.delete('/webhook_endpoints/:id', (c) => {
59
+ const ep = ws.webhookEndpoints.get(c.req.param('id'));
60
+ if (!ep)
61
+ throw notFound('WebhookEndpoint');
62
+ ws.webhookEndpoints.delete(ep.id);
63
+ return c.body(null, 204);
64
+ });
65
+ }
66
+ //# sourceMappingURL=webhook-endpoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-endpoints.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/webhook-endpoints.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes } from 'node:crypto';\nimport { type RouteContext, notFound, validationError, parseJsonBody, parseListParams } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatWebhookEndpoint, formatListResponse } from '../helpers.js';\n\nexport function webhookEndpointRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n app.post('/webhook_endpoints', async (c) => {\n const body = await parseJsonBody(c);\n const url = body.url as string | undefined;\n if (!url || typeof url !== 'string') {\n throw validationError('URL is required', [{ field: 'url', code: 'required' }]);\n }\n\n const secret = (body.secret as string) ?? randomBytes(32).toString('hex');\n\n const endpoint = ws.webhookEndpoints.insert({\n object: 'webhook_endpoint',\n url,\n secret,\n enabled: body.enabled !== false,\n events: Array.isArray(body.events) ? (body.events as string[]) : [],\n description: (body.description as string) ?? null,\n });\n\n return c.json(formatWebhookEndpoint(endpoint, { includeSecret: true }), 201);\n });\n\n app.get('/webhook_endpoints', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n\n const result = ws.webhookEndpoints.list(params);\n return c.json(formatListResponse(result, (ep) => formatWebhookEndpoint(ep)));\n });\n\n app.get('/webhook_endpoints/:id', (c) => {\n const ep = ws.webhookEndpoints.get(c.req.param('id'));\n if (!ep) throw notFound('WebhookEndpoint');\n return c.json(formatWebhookEndpoint(ep));\n });\n\n app.put('/webhook_endpoints/:id', async (c) => {\n const ep = ws.webhookEndpoints.get(c.req.param('id'));\n if (!ep) throw notFound('WebhookEndpoint');\n\n const body = await parseJsonBody(c);\n const updates: Record<string, unknown> = {};\n\n if ('url' in body) {\n if (!body.url || typeof body.url !== 'string') {\n throw validationError('URL is required', [{ field: 'url', code: 'required' }]);\n }\n updates.url = body.url;\n }\n if ('enabled' in body) updates.enabled = !!body.enabled;\n if ('events' in body) updates.events = Array.isArray(body.events) ? body.events : [];\n if ('description' in body) updates.description = body.description ?? null;\n\n const updated = ws.webhookEndpoints.update(ep.id, updates);\n return c.json(formatWebhookEndpoint(updated!));\n });\n\n app.delete('/webhook_endpoints/:id', (c) => {\n const ep = ws.webhookEndpoints.get(c.req.param('id'));\n if (!ep) throw notFound('WebhookEndpoint');\n ws.webhookEndpoints.delete(ep.id);\n return c.body(null, 204);\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function widgetRoutes(ctx: RouteContext): void;