workos 0.11.1 → 0.12.0-beta.1

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 (172) hide show
  1. package/README.md +163 -6
  2. package/dist/bin.js +22 -2
  3. package/dist/bin.js.map +1 -1
  4. package/dist/check-coverage.ts +237 -0
  5. package/dist/commands/dev.d.ts +23 -0
  6. package/dist/commands/dev.js +139 -0
  7. package/dist/commands/dev.js.map +1 -0
  8. package/dist/commands/emulate.d.ts +6 -0
  9. package/dist/commands/emulate.js +64 -0
  10. package/dist/commands/emulate.js.map +1 -0
  11. package/dist/commands/seed.d.ts +2 -0
  12. package/dist/commands/seed.js +60 -1
  13. package/dist/commands/seed.js.map +1 -1
  14. package/dist/emulate/core/id.d.ts +33 -0
  15. package/dist/emulate/core/id.js +58 -0
  16. package/dist/emulate/core/id.js.map +1 -0
  17. package/dist/emulate/core/index.d.ts +8 -0
  18. package/dist/emulate/core/index.js +8 -0
  19. package/dist/emulate/core/index.js.map +1 -0
  20. package/dist/emulate/core/jwt.d.ts +28 -0
  21. package/dist/emulate/core/jwt.js +78 -0
  22. package/dist/emulate/core/jwt.js.map +1 -0
  23. package/dist/emulate/core/middleware/auth.d.ts +18 -0
  24. package/dist/emulate/core/middleware/auth.js +28 -0
  25. package/dist/emulate/core/middleware/auth.js.map +1 -0
  26. package/dist/emulate/core/middleware/error-handler.d.ts +22 -0
  27. package/dist/emulate/core/middleware/error-handler.js +72 -0
  28. package/dist/emulate/core/middleware/error-handler.js.map +1 -0
  29. package/dist/emulate/core/pagination.d.ts +21 -0
  30. package/dist/emulate/core/pagination.js +35 -0
  31. package/dist/emulate/core/pagination.js.map +1 -0
  32. package/dist/emulate/core/plugin.d.ts +15 -0
  33. package/dist/emulate/core/plugin.js +2 -0
  34. package/dist/emulate/core/plugin.js.map +1 -0
  35. package/dist/emulate/core/server.d.ts +17 -0
  36. package/dist/emulate/core/server.js +116 -0
  37. package/dist/emulate/core/server.js.map +1 -0
  38. package/dist/emulate/core/store.d.ts +42 -0
  39. package/dist/emulate/core/store.js +148 -0
  40. package/dist/emulate/core/store.js.map +1 -0
  41. package/dist/emulate/index.d.ts +25 -0
  42. package/dist/emulate/index.js +47 -0
  43. package/dist/emulate/index.js.map +1 -0
  44. package/dist/emulate/workos/entities.d.ts +360 -0
  45. package/dist/emulate/workos/entities.js +2 -0
  46. package/dist/emulate/workos/entities.js.map +1 -0
  47. package/dist/emulate/workos/event-bus.d.ts +12 -0
  48. package/dist/emulate/workos/event-bus.js +45 -0
  49. package/dist/emulate/workos/event-bus.js.map +1 -0
  50. package/dist/emulate/workos/helpers.d.ts +63 -0
  51. package/dist/emulate/workos/helpers.js +518 -0
  52. package/dist/emulate/workos/helpers.js.map +1 -0
  53. package/dist/emulate/workos/index.d.ts +91 -0
  54. package/dist/emulate/workos/index.js +319 -0
  55. package/dist/emulate/workos/index.js.map +1 -0
  56. package/dist/emulate/workos/routes/api-keys.d.ts +2 -0
  57. package/dist/emulate/workos/routes/api-keys.js +35 -0
  58. package/dist/emulate/workos/routes/api-keys.js.map +1 -0
  59. package/dist/emulate/workos/routes/audit-logs.d.ts +2 -0
  60. package/dist/emulate/workos/routes/audit-logs.js +107 -0
  61. package/dist/emulate/workos/routes/audit-logs.js.map +1 -0
  62. package/dist/emulate/workos/routes/auth-challenges.d.ts +2 -0
  63. package/dist/emulate/workos/routes/auth-challenges.js +51 -0
  64. package/dist/emulate/workos/routes/auth-challenges.js.map +1 -0
  65. package/dist/emulate/workos/routes/auth-factors.d.ts +2 -0
  66. package/dist/emulate/workos/routes/auth-factors.js +51 -0
  67. package/dist/emulate/workos/routes/auth-factors.js.map +1 -0
  68. package/dist/emulate/workos/routes/auth.d.ts +2 -0
  69. package/dist/emulate/workos/routes/auth.js +349 -0
  70. package/dist/emulate/workos/routes/auth.js.map +1 -0
  71. package/dist/emulate/workos/routes/authorization-checks.d.ts +10 -0
  72. package/dist/emulate/workos/routes/authorization-checks.js +135 -0
  73. package/dist/emulate/workos/routes/authorization-checks.js.map +1 -0
  74. package/dist/emulate/workos/routes/authorization-org-roles.d.ts +2 -0
  75. package/dist/emulate/workos/routes/authorization-org-roles.js +206 -0
  76. package/dist/emulate/workos/routes/authorization-org-roles.js.map +1 -0
  77. package/dist/emulate/workos/routes/authorization-permissions.d.ts +2 -0
  78. package/dist/emulate/workos/routes/authorization-permissions.js +78 -0
  79. package/dist/emulate/workos/routes/authorization-permissions.js.map +1 -0
  80. package/dist/emulate/workos/routes/authorization-resources.d.ts +2 -0
  81. package/dist/emulate/workos/routes/authorization-resources.js +128 -0
  82. package/dist/emulate/workos/routes/authorization-resources.js.map +1 -0
  83. package/dist/emulate/workos/routes/authorization-roles.d.ts +2 -0
  84. package/dist/emulate/workos/routes/authorization-roles.js +136 -0
  85. package/dist/emulate/workos/routes/authorization-roles.js.map +1 -0
  86. package/dist/emulate/workos/routes/config.d.ts +2 -0
  87. package/dist/emulate/workos/routes/config.js +56 -0
  88. package/dist/emulate/workos/routes/config.js.map +1 -0
  89. package/dist/emulate/workos/routes/connect.d.ts +2 -0
  90. package/dist/emulate/workos/routes/connect.js +69 -0
  91. package/dist/emulate/workos/routes/connect.js.map +1 -0
  92. package/dist/emulate/workos/routes/connections.d.ts +2 -0
  93. package/dist/emulate/workos/routes/connections.js +77 -0
  94. package/dist/emulate/workos/routes/connections.js.map +1 -0
  95. package/dist/emulate/workos/routes/data-integrations.d.ts +2 -0
  96. package/dist/emulate/workos/routes/data-integrations.js +55 -0
  97. package/dist/emulate/workos/routes/data-integrations.js.map +1 -0
  98. package/dist/emulate/workos/routes/directories.d.ts +2 -0
  99. package/dist/emulate/workos/routes/directories.js +106 -0
  100. package/dist/emulate/workos/routes/directories.js.map +1 -0
  101. package/dist/emulate/workos/routes/email-verification.d.ts +2 -0
  102. package/dist/emulate/workos/routes/email-verification.js +49 -0
  103. package/dist/emulate/workos/routes/email-verification.js.map +1 -0
  104. package/dist/emulate/workos/routes/events.d.ts +2 -0
  105. package/dist/emulate/workos/routes/events.js +21 -0
  106. package/dist/emulate/workos/routes/events.js.map +1 -0
  107. package/dist/emulate/workos/routes/feature-flags.d.ts +2 -0
  108. package/dist/emulate/workos/routes/feature-flags.js +131 -0
  109. package/dist/emulate/workos/routes/feature-flags.js.map +1 -0
  110. package/dist/emulate/workos/routes/invitations.d.ts +2 -0
  111. package/dist/emulate/workos/routes/invitations.js +125 -0
  112. package/dist/emulate/workos/routes/invitations.js.map +1 -0
  113. package/dist/emulate/workos/routes/legacy-mfa.d.ts +2 -0
  114. package/dist/emulate/workos/routes/legacy-mfa.js +75 -0
  115. package/dist/emulate/workos/routes/legacy-mfa.js.map +1 -0
  116. package/dist/emulate/workos/routes/magic-auth.d.ts +2 -0
  117. package/dist/emulate/workos/routes/magic-auth.js +32 -0
  118. package/dist/emulate/workos/routes/magic-auth.js.map +1 -0
  119. package/dist/emulate/workos/routes/memberships.d.ts +2 -0
  120. package/dist/emulate/workos/routes/memberships.js +118 -0
  121. package/dist/emulate/workos/routes/memberships.js.map +1 -0
  122. package/dist/emulate/workos/routes/organization-domains.d.ts +2 -0
  123. package/dist/emulate/workos/routes/organization-domains.js +58 -0
  124. package/dist/emulate/workos/routes/organization-domains.js.map +1 -0
  125. package/dist/emulate/workos/routes/organizations.d.ts +2 -0
  126. package/dist/emulate/workos/routes/organizations.js +133 -0
  127. package/dist/emulate/workos/routes/organizations.js.map +1 -0
  128. package/dist/emulate/workos/routes/password-reset.d.ts +2 -0
  129. package/dist/emulate/workos/routes/password-reset.js +61 -0
  130. package/dist/emulate/workos/routes/password-reset.js.map +1 -0
  131. package/dist/emulate/workos/routes/pipes.d.ts +2 -0
  132. package/dist/emulate/workos/routes/pipes.js +86 -0
  133. package/dist/emulate/workos/routes/pipes.js.map +1 -0
  134. package/dist/emulate/workos/routes/portal.d.ts +2 -0
  135. package/dist/emulate/workos/routes/portal.js +18 -0
  136. package/dist/emulate/workos/routes/portal.js.map +1 -0
  137. package/dist/emulate/workos/routes/radar.d.ts +2 -0
  138. package/dist/emulate/workos/routes/radar.js +45 -0
  139. package/dist/emulate/workos/routes/radar.js.map +1 -0
  140. package/dist/emulate/workos/routes/sessions.d.ts +2 -0
  141. package/dist/emulate/workos/routes/sessions.js +51 -0
  142. package/dist/emulate/workos/routes/sessions.js.map +1 -0
  143. package/dist/emulate/workos/routes/sso.d.ts +2 -0
  144. package/dist/emulate/workos/routes/sso.js +160 -0
  145. package/dist/emulate/workos/routes/sso.js.map +1 -0
  146. package/dist/emulate/workos/routes/user-features.d.ts +2 -0
  147. package/dist/emulate/workos/routes/user-features.js +50 -0
  148. package/dist/emulate/workos/routes/user-features.js.map +1 -0
  149. package/dist/emulate/workos/routes/users.d.ts +2 -0
  150. package/dist/emulate/workos/routes/users.js +133 -0
  151. package/dist/emulate/workos/routes/users.js.map +1 -0
  152. package/dist/emulate/workos/routes/webhook-endpoints.d.ts +2 -0
  153. package/dist/emulate/workos/routes/webhook-endpoints.js +70 -0
  154. package/dist/emulate/workos/routes/webhook-endpoints.js.map +1 -0
  155. package/dist/emulate/workos/routes/widgets.d.ts +2 -0
  156. package/dist/emulate/workos/routes/widgets.js +27 -0
  157. package/dist/emulate/workos/routes/widgets.js.map +1 -0
  158. package/dist/emulate/workos/store.d.ts +48 -0
  159. package/dist/emulate/workos/store.js +93 -0
  160. package/dist/emulate/workos/store.js.map +1 -0
  161. package/dist/emulate/workos/webhook-signer.d.ts +1 -0
  162. package/dist/emulate/workos/webhook-signer.js +8 -0
  163. package/dist/emulate/workos/webhook-signer.js.map +1 -0
  164. package/dist/gen-routes-lib.spec.ts +659 -0
  165. package/dist/gen-routes-lib.ts +647 -0
  166. package/dist/gen-routes.ts +96 -0
  167. package/dist/lib/dev-command.d.ts +26 -0
  168. package/dist/lib/dev-command.js +122 -0
  169. package/dist/lib/dev-command.js.map +1 -0
  170. package/dist/utils/help-json.js +31 -0
  171. package/dist/utils/help-json.js.map +1 -1
  172. package/package.json +20 -7
@@ -0,0 +1,49 @@
1
+ import { notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatEmailVerification, formatUser, generateCode, expiresIn, isExpired } from '../helpers.js';
4
+ export function emailVerificationRoutes(ctx) {
5
+ const { app, store } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ app.get('/user_management/email_verification/:id', (c) => {
8
+ const ev = ws.emailVerifications.get(c.req.param('id'));
9
+ if (!ev)
10
+ throw notFound('Email Verification');
11
+ return c.json(formatEmailVerification(ev));
12
+ });
13
+ app.post('/user_management/users/:id/email_verification/send', (c) => {
14
+ const user = ws.users.get(c.req.param('id'));
15
+ if (!user)
16
+ throw notFound('User');
17
+ const ev = ws.emailVerifications.insert({
18
+ object: 'email_verification',
19
+ user_id: user.id,
20
+ email: user.email,
21
+ code: generateCode(),
22
+ expires_at: expiresIn(10),
23
+ });
24
+ return c.json(formatEmailVerification(ev), 201);
25
+ });
26
+ app.post('/user_management/users/:id/email_verification/confirm', async (c) => {
27
+ const user = ws.users.get(c.req.param('id'));
28
+ if (!user)
29
+ throw notFound('User');
30
+ const body = await parseJsonBody(c);
31
+ const code = body.code;
32
+ if (!code) {
33
+ throw new WorkOSApiError(400, 'code is required', 'invalid_request');
34
+ }
35
+ const verifications = ws.emailVerifications.findBy('user_id', user.id);
36
+ const ev = verifications.find((v) => v.code === code);
37
+ if (!ev) {
38
+ throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');
39
+ }
40
+ if (isExpired(ev.expires_at)) {
41
+ throw new WorkOSApiError(400, 'Code has expired', 'expired_code');
42
+ }
43
+ ws.users.update(user.id, { email_verified: true });
44
+ ws.emailVerifications.delete(ev.id);
45
+ const updated = ws.users.get(user.id);
46
+ return c.json(formatUser(updated));
47
+ });
48
+ }
49
+ //# sourceMappingURL=email-verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-verification.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/email-verification.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,uBAAuB,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAExG,MAAM,UAAU,uBAAuB,CAAC,GAAiB;IACvD,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IAC3B,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAEjC,GAAG,CAAC,GAAG,CAAC,yCAAyC,EAAE,CAAC,CAAC,EAAE,EAAE;QACvD,MAAM,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACxD,IAAI,CAAC,EAAE;YAAE,MAAM,QAAQ,CAAC,oBAAoB,CAAC,CAAC;QAC9C,OAAO,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,oDAAoD,EAAE,CAAC,CAAC,EAAE,EAAE;QACnE,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,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC;YACtC,MAAM,EAAE,oBAAoB;YAC5B,OAAO,EAAE,IAAI,CAAC,EAAE;YAChB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,YAAY,EAAE;YACpB,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC;SAC1B,CAAC,CAAC;QAEH,OAAO,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,uDAAuD,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC5E,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,IAAI,GAAG,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAA0B,CAAC;QAC7C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,kBAAkB,EAAE,iBAAiB,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,aAAa,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACvE,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAEtD,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,SAAS,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,kBAAkB,EAAE,cAAc,CAAC,CAAC;QACpE,CAAC;QAED,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAEpC,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAE,CAAC;QACvC,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { type RouteContext, notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatEmailVerification, formatUser, generateCode, expiresIn, isExpired } from '../helpers.js';\n\nexport function emailVerificationRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/user_management/email_verification/:id', (c) => {\n const ev = ws.emailVerifications.get(c.req.param('id'));\n if (!ev) throw notFound('Email Verification');\n return c.json(formatEmailVerification(ev));\n });\n\n app.post('/user_management/users/:id/email_verification/send', (c) => {\n const user = ws.users.get(c.req.param('id'));\n if (!user) throw notFound('User');\n\n const ev = ws.emailVerifications.insert({\n object: 'email_verification',\n user_id: user.id,\n email: user.email,\n code: generateCode(),\n expires_at: expiresIn(10),\n });\n\n return c.json(formatEmailVerification(ev), 201);\n });\n\n app.post('/user_management/users/:id/email_verification/confirm', 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 code = body.code as string | undefined;\n if (!code) {\n throw new WorkOSApiError(400, 'code is required', 'invalid_request');\n }\n\n const verifications = ws.emailVerifications.findBy('user_id', user.id);\n const ev = verifications.find((v) => v.code === code);\n\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(user.id, { email_verified: true });\n ws.emailVerifications.delete(ev.id);\n\n const updated = ws.users.get(user.id)!;\n return c.json(formatUser(updated));\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function eventRoutes(ctx: RouteContext): void;
@@ -0,0 +1,21 @@
1
+ import { getWorkOSStore } from '../store.js';
2
+ import { formatEvent, parseListParams } from '../helpers.js';
3
+ export function eventRoutes(ctx) {
4
+ const { app, store } = ctx;
5
+ const ws = getWorkOSStore(store);
6
+ app.get('/events', (c) => {
7
+ const url = new URL(c.req.url);
8
+ const params = parseListParams(url);
9
+ const eventTypes = url.searchParams.getAll('events[]');
10
+ const result = ws.events.list({
11
+ ...params,
12
+ filter: eventTypes.length > 0 ? (e) => eventTypes.includes(e.event) : undefined,
13
+ });
14
+ return c.json({
15
+ object: 'list',
16
+ data: result.data.map(formatEvent),
17
+ list_metadata: result.list_metadata,
18
+ });
19
+ });
20
+ }
21
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/events.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAE7D,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,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;QACvB,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,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAEvD,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;YAC5B,GAAG,MAAM;YACT,MAAM,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;SAChF,CAAC,CAAC;QAEH,OAAO,CAAC,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC;YAClC,aAAa,EAAE,MAAM,CAAC,aAAa;SACpC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { type RouteContext } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatEvent, parseListParams } from '../helpers.js';\n\nexport function eventRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/events', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const eventTypes = url.searchParams.getAll('events[]');\n\n const result = ws.events.list({\n ...params,\n filter: eventTypes.length > 0 ? (e) => eventTypes.includes(e.event) : undefined,\n });\n\n return c.json({\n object: 'list',\n data: result.data.map(formatEvent),\n list_metadata: result.list_metadata,\n });\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function featureFlagRoutes(ctx: RouteContext): void;
@@ -0,0 +1,131 @@
1
+ import { notFound, parseJsonBody } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatFeatureFlag, parseListParams } from '../helpers.js';
4
+ export function featureFlagRoutes(ctx) {
5
+ const { app, store } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ // List all flags
8
+ app.get('/feature-flags', (c) => {
9
+ const url = new URL(c.req.url);
10
+ const params = parseListParams(url);
11
+ const result = ws.featureFlags.list({ ...params });
12
+ return c.json({
13
+ object: 'list',
14
+ data: result.data.map(formatFeatureFlag),
15
+ list_metadata: result.list_metadata,
16
+ });
17
+ });
18
+ // Get flag by slug
19
+ app.get('/feature-flags/:slug', (c) => {
20
+ const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));
21
+ if (!flag)
22
+ throw notFound('FeatureFlag');
23
+ return c.json(formatFeatureFlag(flag));
24
+ });
25
+ // Enable flag
26
+ app.post('/feature-flags/:slug/enable', (c) => {
27
+ const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));
28
+ if (!flag)
29
+ throw notFound('FeatureFlag');
30
+ const updated = ws.featureFlags.update(flag.id, { enabled: true });
31
+ return c.json(formatFeatureFlag(updated));
32
+ });
33
+ // Disable flag
34
+ app.post('/feature-flags/:slug/disable', (c) => {
35
+ const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));
36
+ if (!flag)
37
+ throw notFound('FeatureFlag');
38
+ const updated = ws.featureFlags.update(flag.id, { enabled: false });
39
+ return c.json(formatFeatureFlag(updated));
40
+ });
41
+ // Add/update target
42
+ app.put('/feature-flags/:slug/targets/:resourceId', async (c) => {
43
+ const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));
44
+ if (!flag)
45
+ throw notFound('FeatureFlag');
46
+ const resourceId = c.req.param('resourceId');
47
+ const body = await parseJsonBody(c);
48
+ // Upsert: find existing target or create
49
+ const existing = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === resourceId);
50
+ if (existing) {
51
+ const updated = ws.flagTargets.update(existing.id, {
52
+ value: body.value,
53
+ resource_type: body.resource_type ?? existing.resource_type,
54
+ });
55
+ return c.json({
56
+ object: 'flag_target',
57
+ id: updated.id,
58
+ flag_slug: updated.flag_slug,
59
+ resource_id: updated.resource_id,
60
+ resource_type: updated.resource_type,
61
+ value: updated.value,
62
+ });
63
+ }
64
+ const target = ws.flagTargets.insert({
65
+ object: 'flag_target',
66
+ flag_slug: flag.slug,
67
+ resource_id: resourceId,
68
+ resource_type: body.resource_type ?? 'user',
69
+ value: body.value,
70
+ });
71
+ return c.json({
72
+ object: 'flag_target',
73
+ id: target.id,
74
+ flag_slug: target.flag_slug,
75
+ resource_id: target.resource_id,
76
+ resource_type: target.resource_type,
77
+ value: target.value,
78
+ }, 201);
79
+ });
80
+ // Remove target
81
+ app.delete('/feature-flags/:slug/targets/:resourceId', (c) => {
82
+ const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));
83
+ if (!flag)
84
+ throw notFound('FeatureFlag');
85
+ const resourceId = c.req.param('resourceId');
86
+ const target = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === resourceId);
87
+ if (!target)
88
+ throw notFound('FlagTarget');
89
+ ws.flagTargets.delete(target.id);
90
+ return c.body(null, 204);
91
+ });
92
+ // Evaluate flags for organization
93
+ app.get('/organizations/:orgId/feature-flags', (c) => {
94
+ const orgId = c.req.param('orgId');
95
+ const flags = ws.featureFlags.all();
96
+ const evaluations = flags.map((flag) => {
97
+ const target = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === orgId);
98
+ return {
99
+ slug: flag.slug,
100
+ type: flag.type,
101
+ value: target ? target.value : flag.enabled ? flag.default_value : null,
102
+ enabled: flag.enabled,
103
+ };
104
+ });
105
+ return c.json({
106
+ object: 'list',
107
+ data: evaluations,
108
+ list_metadata: { before: null, after: null },
109
+ });
110
+ });
111
+ // Evaluate flags for user (replaces stub in user-features.ts)
112
+ app.get('/user_management/users/:userId/feature-flags', (c) => {
113
+ const userId = c.req.param('userId');
114
+ const flags = ws.featureFlags.all();
115
+ const evaluations = flags.map((flag) => {
116
+ const target = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === userId);
117
+ return {
118
+ slug: flag.slug,
119
+ type: flag.type,
120
+ value: target ? target.value : flag.enabled ? flag.default_value : null,
121
+ enabled: flag.enabled,
122
+ };
123
+ });
124
+ return c.json({
125
+ object: 'list',
126
+ data: evaluations,
127
+ list_metadata: { before: null, after: null },
128
+ });
129
+ });
130
+ }
131
+ //# sourceMappingURL=feature-flags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-flags.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/feature-flags.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, parseJsonBody } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatFeatureFlag, parseListParams } from '../helpers.js';\n\nexport function featureFlagRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n // List all flags\n app.get('/feature-flags', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const result = ws.featureFlags.list({ ...params });\n return c.json({\n object: 'list',\n data: result.data.map(formatFeatureFlag),\n list_metadata: result.list_metadata,\n });\n });\n\n // Get flag by slug\n app.get('/feature-flags/:slug', (c) => {\n const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));\n if (!flag) throw notFound('FeatureFlag');\n return c.json(formatFeatureFlag(flag));\n });\n\n // Enable flag\n app.post('/feature-flags/:slug/enable', (c) => {\n const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));\n if (!flag) throw notFound('FeatureFlag');\n const updated = ws.featureFlags.update(flag.id, { enabled: true });\n return c.json(formatFeatureFlag(updated!));\n });\n\n // Disable flag\n app.post('/feature-flags/:slug/disable', (c) => {\n const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));\n if (!flag) throw notFound('FeatureFlag');\n const updated = ws.featureFlags.update(flag.id, { enabled: false });\n return c.json(formatFeatureFlag(updated!));\n });\n\n // Add/update target\n app.put('/feature-flags/:slug/targets/:resourceId', async (c) => {\n const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));\n if (!flag) throw notFound('FeatureFlag');\n\n const resourceId = c.req.param('resourceId');\n const body = await parseJsonBody(c);\n\n // Upsert: find existing target or create\n const existing = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === resourceId);\n\n if (existing) {\n const updated = ws.flagTargets.update(existing.id, {\n value: body.value,\n resource_type: (body.resource_type as string) ?? existing.resource_type,\n });\n return c.json({\n object: 'flag_target',\n id: updated!.id,\n flag_slug: updated!.flag_slug,\n resource_id: updated!.resource_id,\n resource_type: updated!.resource_type,\n value: updated!.value,\n });\n }\n\n const target = ws.flagTargets.insert({\n object: 'flag_target',\n flag_slug: flag.slug,\n resource_id: resourceId,\n resource_type: (body.resource_type as string) ?? 'user',\n value: body.value,\n });\n\n return c.json(\n {\n object: 'flag_target',\n id: target.id,\n flag_slug: target.flag_slug,\n resource_id: target.resource_id,\n resource_type: target.resource_type,\n value: target.value,\n },\n 201,\n );\n });\n\n // Remove target\n app.delete('/feature-flags/:slug/targets/:resourceId', (c) => {\n const flag = ws.featureFlags.findOneBy('slug', c.req.param('slug'));\n if (!flag) throw notFound('FeatureFlag');\n\n const resourceId = c.req.param('resourceId');\n const target = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === resourceId);\n if (!target) throw notFound('FlagTarget');\n\n ws.flagTargets.delete(target.id);\n return c.body(null, 204);\n });\n\n // Evaluate flags for organization\n app.get('/organizations/:orgId/feature-flags', (c) => {\n const orgId = c.req.param('orgId');\n const flags = ws.featureFlags.all();\n\n const evaluations = flags.map((flag) => {\n const target = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === orgId);\n\n return {\n slug: flag.slug,\n type: flag.type,\n value: target ? target.value : flag.enabled ? flag.default_value : null,\n enabled: flag.enabled,\n };\n });\n\n return c.json({\n object: 'list',\n data: evaluations,\n list_metadata: { before: null, after: null },\n });\n });\n\n // Evaluate flags for user (replaces stub in user-features.ts)\n app.get('/user_management/users/:userId/feature-flags', (c) => {\n const userId = c.req.param('userId');\n const flags = ws.featureFlags.all();\n\n const evaluations = flags.map((flag) => {\n const target = ws.flagTargets.findBy('flag_slug', flag.slug).find((t) => t.resource_id === userId);\n\n return {\n slug: flag.slug,\n type: flag.type,\n value: target ? target.value : flag.enabled ? flag.default_value : null,\n enabled: flag.enabled,\n };\n });\n\n return c.json({\n object: 'list',\n data: evaluations,\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 invitationRoutes(ctx: RouteContext): void;
@@ -0,0 +1,125 @@
1
+ import { notFound, validationError, parseJsonBody, WorkOSApiError } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatInvitation, generateVerificationToken, expiresIn, parseListParams } from '../helpers.js';
4
+ export function invitationRoutes(ctx) {
5
+ const { app, store, baseUrl } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ app.post('/user_management/invitations', 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 token = generateVerificationToken();
14
+ const inv = ws.invitations.insert({
15
+ object: 'invitation',
16
+ email,
17
+ state: 'pending',
18
+ token,
19
+ accept_invitation_url: `${baseUrl}/user_management/invitations/accept?token=${token}`,
20
+ organization_id: body.organization_id ?? null,
21
+ inviter_user_id: body.inviter_user_id ?? null,
22
+ role_slug: body.role_slug ?? null,
23
+ expires_at: expiresIn(72 * 60), // 72 hours
24
+ });
25
+ return c.json(formatInvitation(inv), 201);
26
+ });
27
+ app.get('/user_management/invitations', (c) => {
28
+ const url = new URL(c.req.url);
29
+ const params = parseListParams(url);
30
+ const emailFilter = url.searchParams.get('email') ?? undefined;
31
+ const orgFilter = url.searchParams.get('organization_id') ?? undefined;
32
+ const result = ws.invitations.list({
33
+ ...params,
34
+ filter: (inv) => {
35
+ if (emailFilter && inv.email !== emailFilter)
36
+ return false;
37
+ if (orgFilter && inv.organization_id !== orgFilter)
38
+ return false;
39
+ return true;
40
+ },
41
+ });
42
+ return c.json({
43
+ object: 'list',
44
+ data: result.data.map(formatInvitation),
45
+ list_metadata: result.list_metadata,
46
+ });
47
+ });
48
+ app.get('/user_management/invitations/by_token/:token', (c) => {
49
+ const inv = ws.invitations.findOneBy('token', c.req.param('token'));
50
+ if (!inv)
51
+ throw notFound('Invitation');
52
+ return c.json(formatInvitation(inv));
53
+ });
54
+ app.get('/user_management/invitations/:id', (c) => {
55
+ const inv = ws.invitations.get(c.req.param('id'));
56
+ if (!inv)
57
+ throw notFound('Invitation');
58
+ return c.json(formatInvitation(inv));
59
+ });
60
+ app.post('/user_management/invitations/:id/accept', (c) => {
61
+ const inv = ws.invitations.get(c.req.param('id'));
62
+ if (!inv)
63
+ throw notFound('Invitation');
64
+ if (inv.state !== 'pending') {
65
+ throw new WorkOSApiError(400, `Invitation is ${inv.state}`, 'invalid_invitation_state');
66
+ }
67
+ ws.invitations.update(inv.id, { state: 'accepted' });
68
+ const eventBus = store.getData('eventBus');
69
+ eventBus?.emit({ event: 'invitation.accepted', data: formatInvitation(ws.invitations.get(inv.id)) });
70
+ // Create org membership if invitation has an organization
71
+ if (inv.organization_id) {
72
+ const user = ws.users.findOneBy('email', inv.email);
73
+ if (user) {
74
+ ws.organizationMemberships.insert({
75
+ object: 'organization_membership',
76
+ organization_id: inv.organization_id,
77
+ user_id: user.id,
78
+ role: { slug: inv.role_slug ?? 'member' },
79
+ status: 'active',
80
+ external_id: null,
81
+ metadata: {},
82
+ });
83
+ }
84
+ }
85
+ const updated = ws.invitations.get(inv.id);
86
+ return c.json(formatInvitation(updated));
87
+ });
88
+ app.post('/user_management/invitations/:id/revoke', (c) => {
89
+ const inv = ws.invitations.get(c.req.param('id'));
90
+ if (!inv)
91
+ throw notFound('Invitation');
92
+ if (inv.state !== 'pending') {
93
+ throw new WorkOSApiError(400, `Invitation is ${inv.state}`, 'invalid_invitation_state');
94
+ }
95
+ ws.invitations.update(inv.id, { state: 'revoked' });
96
+ const eventBus = store.getData('eventBus');
97
+ eventBus?.emit({ event: 'invitation.revoked', data: formatInvitation(ws.invitations.get(inv.id)) });
98
+ const updated = ws.invitations.get(inv.id);
99
+ return c.json(formatInvitation(updated));
100
+ });
101
+ app.post('/user_management/invitations/:id/resend', (c) => {
102
+ const inv = ws.invitations.get(c.req.param('id'));
103
+ if (!inv)
104
+ throw notFound('Invitation');
105
+ const newToken = generateVerificationToken();
106
+ ws.invitations.update(inv.id, {
107
+ token: newToken,
108
+ accept_invitation_url: `${baseUrl}/user_management/invitations/accept?token=${newToken}`,
109
+ expires_at: expiresIn(72 * 60),
110
+ state: 'pending',
111
+ });
112
+ const eventBus = store.getData('eventBus');
113
+ eventBus?.emit({ event: 'invitation.resent', data: formatInvitation(ws.invitations.get(inv.id)) });
114
+ const updated = ws.invitations.get(inv.id);
115
+ return c.json(formatInvitation(updated));
116
+ });
117
+ app.delete('/user_management/invitations/:id', (c) => {
118
+ const inv = ws.invitations.get(c.req.param('id'));
119
+ if (!inv)
120
+ throw notFound('Invitation');
121
+ ws.invitations.delete(inv.id);
122
+ return c.body(null, 204);
123
+ });
124
+ }
125
+ //# sourceMappingURL=invitations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invitations.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/invitations.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, validationError, parseJsonBody, WorkOSApiError } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatInvitation, generateVerificationToken, expiresIn, parseListParams } from '../helpers.js';\nimport type { EventBus } from '../event-bus.js';\n\nexport function invitationRoutes(ctx: RouteContext): void {\n const { app, store, baseUrl } = ctx;\n const ws = getWorkOSStore(store);\n\n app.post('/user_management/invitations', 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 token = generateVerificationToken();\n const inv = ws.invitations.insert({\n object: 'invitation',\n email,\n state: 'pending',\n token,\n accept_invitation_url: `${baseUrl}/user_management/invitations/accept?token=${token}`,\n organization_id: (body.organization_id as string) ?? null,\n inviter_user_id: (body.inviter_user_id as string) ?? null,\n role_slug: (body.role_slug as string) ?? null,\n expires_at: expiresIn(72 * 60), // 72 hours\n });\n\n return c.json(formatInvitation(inv), 201);\n });\n\n app.get('/user_management/invitations', (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 const result = ws.invitations.list({\n ...params,\n filter: (inv) => {\n if (emailFilter && inv.email !== emailFilter) return false;\n if (orgFilter && inv.organization_id !== orgFilter) return false;\n return true;\n },\n });\n\n return c.json({\n object: 'list',\n data: result.data.map(formatInvitation),\n list_metadata: result.list_metadata,\n });\n });\n\n app.get('/user_management/invitations/by_token/:token', (c) => {\n const inv = ws.invitations.findOneBy('token', c.req.param('token'));\n if (!inv) throw notFound('Invitation');\n return c.json(formatInvitation(inv));\n });\n\n app.get('/user_management/invitations/:id', (c) => {\n const inv = ws.invitations.get(c.req.param('id'));\n if (!inv) throw notFound('Invitation');\n return c.json(formatInvitation(inv));\n });\n\n app.post('/user_management/invitations/:id/accept', (c) => {\n const inv = ws.invitations.get(c.req.param('id'));\n if (!inv) throw notFound('Invitation');\n\n if (inv.state !== 'pending') {\n throw new WorkOSApiError(400, `Invitation is ${inv.state}`, 'invalid_invitation_state');\n }\n\n ws.invitations.update(inv.id, { state: 'accepted' });\n const eventBus = store.getData<EventBus>('eventBus');\n eventBus?.emit({ event: 'invitation.accepted', data: formatInvitation(ws.invitations.get(inv.id)!) });\n\n // Create org membership if invitation has an organization\n if (inv.organization_id) {\n const user = ws.users.findOneBy('email', inv.email);\n if (user) {\n ws.organizationMemberships.insert({\n object: 'organization_membership',\n organization_id: inv.organization_id,\n user_id: user.id,\n role: { slug: inv.role_slug ?? 'member' },\n status: 'active',\n external_id: null,\n metadata: {},\n });\n }\n }\n\n const updated = ws.invitations.get(inv.id)!;\n return c.json(formatInvitation(updated));\n });\n\n app.post('/user_management/invitations/:id/revoke', (c) => {\n const inv = ws.invitations.get(c.req.param('id'));\n if (!inv) throw notFound('Invitation');\n\n if (inv.state !== 'pending') {\n throw new WorkOSApiError(400, `Invitation is ${inv.state}`, 'invalid_invitation_state');\n }\n\n ws.invitations.update(inv.id, { state: 'revoked' });\n const eventBus = store.getData<EventBus>('eventBus');\n eventBus?.emit({ event: 'invitation.revoked', data: formatInvitation(ws.invitations.get(inv.id)!) });\n const updated = ws.invitations.get(inv.id)!;\n return c.json(formatInvitation(updated));\n });\n\n app.post('/user_management/invitations/:id/resend', (c) => {\n const inv = ws.invitations.get(c.req.param('id'));\n if (!inv) throw notFound('Invitation');\n\n const newToken = generateVerificationToken();\n ws.invitations.update(inv.id, {\n token: newToken,\n accept_invitation_url: `${baseUrl}/user_management/invitations/accept?token=${newToken}`,\n expires_at: expiresIn(72 * 60),\n state: 'pending',\n });\n\n const eventBus = store.getData<EventBus>('eventBus');\n eventBus?.emit({ event: 'invitation.resent', data: formatInvitation(ws.invitations.get(inv.id)!) });\n const updated = ws.invitations.get(inv.id)!;\n return c.json(formatInvitation(updated));\n });\n\n app.delete('/user_management/invitations/:id', (c) => {\n const inv = ws.invitations.get(c.req.param('id'));\n if (!inv) throw notFound('Invitation');\n ws.invitations.delete(inv.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 legacyMfaRoutes(ctx: RouteContext): void;
@@ -0,0 +1,75 @@
1
+ import { notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatAuthFactor, formatAuthChallenge, expiresIn, isExpired, generateCode } from '../helpers.js';
4
+ import { randomBytes } from 'node:crypto';
5
+ export function legacyMfaRoutes(ctx) {
6
+ const { app, store } = ctx;
7
+ const ws = getWorkOSStore(store);
8
+ // Enroll factor (legacy path — not tied to user management users)
9
+ app.post('/auth/factors/enroll', async (c) => {
10
+ const body = await parseJsonBody(c);
11
+ const type = body.type ?? 'totp';
12
+ const issuer = body.totp_issuer ?? 'WorkOS Emulator';
13
+ const totpUser = body.totp_user ?? 'legacy@emulator';
14
+ const secret = randomBytes(20).toString('hex').slice(0, 32).toUpperCase();
15
+ const uri = `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(totpUser)}?secret=${secret}&issuer=${encodeURIComponent(issuer)}`;
16
+ const factor = ws.authFactors.insert({
17
+ object: 'authentication_factor',
18
+ user_id: 'legacy',
19
+ type: type,
20
+ totp: { issuer, user: totpUser, uri },
21
+ });
22
+ return c.json(formatAuthFactor(factor), 201);
23
+ });
24
+ // Get factor
25
+ app.get('/auth/factors/:id', (c) => {
26
+ const factor = ws.authFactors.get(c.req.param('id'));
27
+ if (!factor)
28
+ throw notFound('AuthenticationFactor');
29
+ return c.json(formatAuthFactor(factor));
30
+ });
31
+ // Delete factor
32
+ app.delete('/auth/factors/:id', (c) => {
33
+ const factor = ws.authFactors.get(c.req.param('id'));
34
+ if (!factor)
35
+ throw notFound('AuthenticationFactor');
36
+ ws.authFactors.delete(factor.id);
37
+ return c.body(null, 204);
38
+ });
39
+ // Create challenge
40
+ app.post('/auth/factors/:id/challenge', async (c) => {
41
+ const factor = ws.authFactors.get(c.req.param('id'));
42
+ if (!factor)
43
+ throw notFound('AuthenticationFactor');
44
+ const code = generateCode();
45
+ const challenge = ws.authChallenges.insert({
46
+ object: 'authentication_challenge',
47
+ user_id: factor.user_id,
48
+ factor_id: factor.id,
49
+ expires_at: expiresIn(10),
50
+ code,
51
+ });
52
+ return c.json(formatAuthChallenge(challenge), 201);
53
+ });
54
+ // Verify challenge
55
+ app.post('/auth/challenges/:id/verify', async (c) => {
56
+ const challenge = ws.authChallenges.get(c.req.param('id'));
57
+ if (!challenge)
58
+ throw notFound('AuthenticationChallenge');
59
+ if (isExpired(challenge.expires_at)) {
60
+ ws.authChallenges.delete(challenge.id);
61
+ throw new WorkOSApiError(400, 'Challenge has expired', 'expired_challenge');
62
+ }
63
+ const body = await parseJsonBody(c);
64
+ const code = body.code;
65
+ if (!code) {
66
+ throw new WorkOSApiError(400, 'code is required', 'invalid_request');
67
+ }
68
+ if (challenge.code && code !== challenge.code) {
69
+ throw new WorkOSApiError(400, 'Invalid one-time code', 'invalid_one_time_code');
70
+ }
71
+ ws.authChallenges.delete(challenge.id);
72
+ return c.json({ challenge: formatAuthChallenge(challenge), valid: true });
73
+ });
74
+ }
75
+ //# sourceMappingURL=legacy-mfa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacy-mfa.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/legacy-mfa.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatAuthFactor, formatAuthChallenge, expiresIn, isExpired, generateCode } from '../helpers.js';\nimport { randomBytes } from 'node:crypto';\n\nexport function legacyMfaRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n // Enroll factor (legacy path — not tied to user management users)\n app.post('/auth/factors/enroll', async (c) => {\n const body = await parseJsonBody(c);\n const type = (body.type as string) ?? 'totp';\n const issuer = (body.totp_issuer as string) ?? 'WorkOS Emulator';\n const totpUser = (body.totp_user as string) ?? 'legacy@emulator';\n const secret = randomBytes(20).toString('hex').slice(0, 32).toUpperCase();\n const uri = `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(totpUser)}?secret=${secret}&issuer=${encodeURIComponent(issuer)}`;\n\n const factor = ws.authFactors.insert({\n object: 'authentication_factor',\n user_id: 'legacy',\n type: type as 'totp',\n totp: { issuer, user: totpUser, uri },\n });\n\n return c.json(formatAuthFactor(factor), 201);\n });\n\n // Get factor\n app.get('/auth/factors/:id', (c) => {\n const factor = ws.authFactors.get(c.req.param('id'));\n if (!factor) throw notFound('AuthenticationFactor');\n return c.json(formatAuthFactor(factor));\n });\n\n // Delete factor\n app.delete('/auth/factors/:id', (c) => {\n const factor = ws.authFactors.get(c.req.param('id'));\n if (!factor) throw notFound('AuthenticationFactor');\n ws.authFactors.delete(factor.id);\n return c.body(null, 204);\n });\n\n // Create challenge\n app.post('/auth/factors/:id/challenge', async (c) => {\n const factor = ws.authFactors.get(c.req.param('id'));\n if (!factor) throw notFound('AuthenticationFactor');\n\n const code = generateCode();\n const challenge = ws.authChallenges.insert({\n object: 'authentication_challenge',\n user_id: factor.user_id,\n factor_id: factor.id,\n expires_at: expiresIn(10),\n code,\n });\n\n return c.json(formatAuthChallenge(challenge), 201);\n });\n\n // Verify challenge\n app.post('/auth/challenges/:id/verify', async (c) => {\n const challenge = ws.authChallenges.get(c.req.param('id'));\n if (!challenge) throw notFound('AuthenticationChallenge');\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 const body = await parseJsonBody(c);\n const code = body.code as string;\n if (!code) {\n throw new WorkOSApiError(400, 'code is required', 'invalid_request');\n }\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 return c.json({ challenge: formatAuthChallenge(challenge), valid: true });\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function magicAuthRoutes(ctx: RouteContext): void;
@@ -0,0 +1,32 @@
1
+ import { notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatMagicAuth, generateCode, expiresIn } from '../helpers.js';
4
+ export function magicAuthRoutes(ctx) {
5
+ const { app, store } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ app.get('/user_management/magic_auth/:id', (c) => {
8
+ const ma = ws.magicAuths.get(c.req.param('id'));
9
+ if (!ma)
10
+ throw notFound('Magic Auth');
11
+ return c.json(formatMagicAuth(ma));
12
+ });
13
+ app.post('/user_management/magic_auth', async (c) => {
14
+ const body = await parseJsonBody(c);
15
+ const email = body.email;
16
+ if (!email) {
17
+ throw new WorkOSApiError(400, 'email is required', 'invalid_request');
18
+ }
19
+ const user = ws.users.findOneBy('email', email);
20
+ if (!user)
21
+ throw notFound('User');
22
+ const ma = ws.magicAuths.insert({
23
+ object: 'magic_auth',
24
+ user_id: user.id,
25
+ email: user.email,
26
+ code: generateCode(),
27
+ expires_at: expiresIn(10),
28
+ });
29
+ return c.json(formatMagicAuth(ma), 201);
30
+ });
31
+ }
32
+ //# sourceMappingURL=magic-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"magic-auth.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/magic-auth.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,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEzE,MAAM,UAAU,eAAe,CAAC,GAAiB;IAC/C,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IAC3B,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAEjC,GAAG,CAAC,GAAG,CAAC,iCAAiC,EAAE,CAAC,CAAC,EAAE,EAAE;QAC/C,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAChD,IAAI,CAAC,EAAE;YAAE,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;QACtC,OAAO,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,6BAA6B,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAClD,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAA2B,CAAC;QAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,mBAAmB,EAAE,iBAAiB,CAAC,CAAC;QACxE,CAAC;QAED,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI;YAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;QAElC,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;YAC9B,MAAM,EAAE,YAAY;YACpB,OAAO,EAAE,IAAI,CAAC,EAAE;YAChB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,YAAY,EAAE;YACpB,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC;SAC1B,CAAC,CAAC;QAEH,OAAO,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { type RouteContext, notFound, parseJsonBody, WorkOSApiError } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatMagicAuth, generateCode, expiresIn } from '../helpers.js';\n\nexport function magicAuthRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/user_management/magic_auth/:id', (c) => {\n const ma = ws.magicAuths.get(c.req.param('id'));\n if (!ma) throw notFound('Magic Auth');\n return c.json(formatMagicAuth(ma));\n });\n\n app.post('/user_management/magic_auth', async (c) => {\n const body = await parseJsonBody(c);\n const email = body.email as string | undefined;\n if (!email) {\n throw new WorkOSApiError(400, 'email is required', 'invalid_request');\n }\n\n const user = ws.users.findOneBy('email', email);\n if (!user) throw notFound('User');\n\n const ma = ws.magicAuths.insert({\n object: 'magic_auth',\n user_id: user.id,\n email: user.email,\n code: generateCode(),\n expires_at: expiresIn(10),\n });\n\n return c.json(formatMagicAuth(ma), 201);\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function membershipRoutes(ctx: RouteContext): void;