workos 0.11.2 → 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 (169) hide show
  1. package/README.md +163 -6
  2. package/dist/bin.js +20 -1
  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/emulate/core/id.d.ts +33 -0
  12. package/dist/emulate/core/id.js +58 -0
  13. package/dist/emulate/core/id.js.map +1 -0
  14. package/dist/emulate/core/index.d.ts +8 -0
  15. package/dist/emulate/core/index.js +8 -0
  16. package/dist/emulate/core/index.js.map +1 -0
  17. package/dist/emulate/core/jwt.d.ts +28 -0
  18. package/dist/emulate/core/jwt.js +78 -0
  19. package/dist/emulate/core/jwt.js.map +1 -0
  20. package/dist/emulate/core/middleware/auth.d.ts +18 -0
  21. package/dist/emulate/core/middleware/auth.js +28 -0
  22. package/dist/emulate/core/middleware/auth.js.map +1 -0
  23. package/dist/emulate/core/middleware/error-handler.d.ts +22 -0
  24. package/dist/emulate/core/middleware/error-handler.js +72 -0
  25. package/dist/emulate/core/middleware/error-handler.js.map +1 -0
  26. package/dist/emulate/core/pagination.d.ts +21 -0
  27. package/dist/emulate/core/pagination.js +35 -0
  28. package/dist/emulate/core/pagination.js.map +1 -0
  29. package/dist/emulate/core/plugin.d.ts +15 -0
  30. package/dist/emulate/core/plugin.js +2 -0
  31. package/dist/emulate/core/plugin.js.map +1 -0
  32. package/dist/emulate/core/server.d.ts +17 -0
  33. package/dist/emulate/core/server.js +116 -0
  34. package/dist/emulate/core/server.js.map +1 -0
  35. package/dist/emulate/core/store.d.ts +42 -0
  36. package/dist/emulate/core/store.js +148 -0
  37. package/dist/emulate/core/store.js.map +1 -0
  38. package/dist/emulate/index.d.ts +25 -0
  39. package/dist/emulate/index.js +47 -0
  40. package/dist/emulate/index.js.map +1 -0
  41. package/dist/emulate/workos/entities.d.ts +360 -0
  42. package/dist/emulate/workos/entities.js +2 -0
  43. package/dist/emulate/workos/entities.js.map +1 -0
  44. package/dist/emulate/workos/event-bus.d.ts +12 -0
  45. package/dist/emulate/workos/event-bus.js +45 -0
  46. package/dist/emulate/workos/event-bus.js.map +1 -0
  47. package/dist/emulate/workos/helpers.d.ts +63 -0
  48. package/dist/emulate/workos/helpers.js +518 -0
  49. package/dist/emulate/workos/helpers.js.map +1 -0
  50. package/dist/emulate/workos/index.d.ts +91 -0
  51. package/dist/emulate/workos/index.js +319 -0
  52. package/dist/emulate/workos/index.js.map +1 -0
  53. package/dist/emulate/workos/routes/api-keys.d.ts +2 -0
  54. package/dist/emulate/workos/routes/api-keys.js +35 -0
  55. package/dist/emulate/workos/routes/api-keys.js.map +1 -0
  56. package/dist/emulate/workos/routes/audit-logs.d.ts +2 -0
  57. package/dist/emulate/workos/routes/audit-logs.js +107 -0
  58. package/dist/emulate/workos/routes/audit-logs.js.map +1 -0
  59. package/dist/emulate/workos/routes/auth-challenges.d.ts +2 -0
  60. package/dist/emulate/workos/routes/auth-challenges.js +51 -0
  61. package/dist/emulate/workos/routes/auth-challenges.js.map +1 -0
  62. package/dist/emulate/workos/routes/auth-factors.d.ts +2 -0
  63. package/dist/emulate/workos/routes/auth-factors.js +51 -0
  64. package/dist/emulate/workos/routes/auth-factors.js.map +1 -0
  65. package/dist/emulate/workos/routes/auth.d.ts +2 -0
  66. package/dist/emulate/workos/routes/auth.js +349 -0
  67. package/dist/emulate/workos/routes/auth.js.map +1 -0
  68. package/dist/emulate/workos/routes/authorization-checks.d.ts +10 -0
  69. package/dist/emulate/workos/routes/authorization-checks.js +135 -0
  70. package/dist/emulate/workos/routes/authorization-checks.js.map +1 -0
  71. package/dist/emulate/workos/routes/authorization-org-roles.d.ts +2 -0
  72. package/dist/emulate/workos/routes/authorization-org-roles.js +206 -0
  73. package/dist/emulate/workos/routes/authorization-org-roles.js.map +1 -0
  74. package/dist/emulate/workos/routes/authorization-permissions.d.ts +2 -0
  75. package/dist/emulate/workos/routes/authorization-permissions.js +78 -0
  76. package/dist/emulate/workos/routes/authorization-permissions.js.map +1 -0
  77. package/dist/emulate/workos/routes/authorization-resources.d.ts +2 -0
  78. package/dist/emulate/workos/routes/authorization-resources.js +128 -0
  79. package/dist/emulate/workos/routes/authorization-resources.js.map +1 -0
  80. package/dist/emulate/workos/routes/authorization-roles.d.ts +2 -0
  81. package/dist/emulate/workos/routes/authorization-roles.js +136 -0
  82. package/dist/emulate/workos/routes/authorization-roles.js.map +1 -0
  83. package/dist/emulate/workos/routes/config.d.ts +2 -0
  84. package/dist/emulate/workos/routes/config.js +56 -0
  85. package/dist/emulate/workos/routes/config.js.map +1 -0
  86. package/dist/emulate/workos/routes/connect.d.ts +2 -0
  87. package/dist/emulate/workos/routes/connect.js +69 -0
  88. package/dist/emulate/workos/routes/connect.js.map +1 -0
  89. package/dist/emulate/workos/routes/connections.d.ts +2 -0
  90. package/dist/emulate/workos/routes/connections.js +77 -0
  91. package/dist/emulate/workos/routes/connections.js.map +1 -0
  92. package/dist/emulate/workos/routes/data-integrations.d.ts +2 -0
  93. package/dist/emulate/workos/routes/data-integrations.js +55 -0
  94. package/dist/emulate/workos/routes/data-integrations.js.map +1 -0
  95. package/dist/emulate/workos/routes/directories.d.ts +2 -0
  96. package/dist/emulate/workos/routes/directories.js +106 -0
  97. package/dist/emulate/workos/routes/directories.js.map +1 -0
  98. package/dist/emulate/workos/routes/email-verification.d.ts +2 -0
  99. package/dist/emulate/workos/routes/email-verification.js +49 -0
  100. package/dist/emulate/workos/routes/email-verification.js.map +1 -0
  101. package/dist/emulate/workos/routes/events.d.ts +2 -0
  102. package/dist/emulate/workos/routes/events.js +21 -0
  103. package/dist/emulate/workos/routes/events.js.map +1 -0
  104. package/dist/emulate/workos/routes/feature-flags.d.ts +2 -0
  105. package/dist/emulate/workos/routes/feature-flags.js +131 -0
  106. package/dist/emulate/workos/routes/feature-flags.js.map +1 -0
  107. package/dist/emulate/workos/routes/invitations.d.ts +2 -0
  108. package/dist/emulate/workos/routes/invitations.js +125 -0
  109. package/dist/emulate/workos/routes/invitations.js.map +1 -0
  110. package/dist/emulate/workos/routes/legacy-mfa.d.ts +2 -0
  111. package/dist/emulate/workos/routes/legacy-mfa.js +75 -0
  112. package/dist/emulate/workos/routes/legacy-mfa.js.map +1 -0
  113. package/dist/emulate/workos/routes/magic-auth.d.ts +2 -0
  114. package/dist/emulate/workos/routes/magic-auth.js +32 -0
  115. package/dist/emulate/workos/routes/magic-auth.js.map +1 -0
  116. package/dist/emulate/workos/routes/memberships.d.ts +2 -0
  117. package/dist/emulate/workos/routes/memberships.js +118 -0
  118. package/dist/emulate/workos/routes/memberships.js.map +1 -0
  119. package/dist/emulate/workos/routes/organization-domains.d.ts +2 -0
  120. package/dist/emulate/workos/routes/organization-domains.js +58 -0
  121. package/dist/emulate/workos/routes/organization-domains.js.map +1 -0
  122. package/dist/emulate/workos/routes/organizations.d.ts +2 -0
  123. package/dist/emulate/workos/routes/organizations.js +133 -0
  124. package/dist/emulate/workos/routes/organizations.js.map +1 -0
  125. package/dist/emulate/workos/routes/password-reset.d.ts +2 -0
  126. package/dist/emulate/workos/routes/password-reset.js +61 -0
  127. package/dist/emulate/workos/routes/password-reset.js.map +1 -0
  128. package/dist/emulate/workos/routes/pipes.d.ts +2 -0
  129. package/dist/emulate/workos/routes/pipes.js +86 -0
  130. package/dist/emulate/workos/routes/pipes.js.map +1 -0
  131. package/dist/emulate/workos/routes/portal.d.ts +2 -0
  132. package/dist/emulate/workos/routes/portal.js +18 -0
  133. package/dist/emulate/workos/routes/portal.js.map +1 -0
  134. package/dist/emulate/workos/routes/radar.d.ts +2 -0
  135. package/dist/emulate/workos/routes/radar.js +45 -0
  136. package/dist/emulate/workos/routes/radar.js.map +1 -0
  137. package/dist/emulate/workos/routes/sessions.d.ts +2 -0
  138. package/dist/emulate/workos/routes/sessions.js +51 -0
  139. package/dist/emulate/workos/routes/sessions.js.map +1 -0
  140. package/dist/emulate/workos/routes/sso.d.ts +2 -0
  141. package/dist/emulate/workos/routes/sso.js +160 -0
  142. package/dist/emulate/workos/routes/sso.js.map +1 -0
  143. package/dist/emulate/workos/routes/user-features.d.ts +2 -0
  144. package/dist/emulate/workos/routes/user-features.js +50 -0
  145. package/dist/emulate/workos/routes/user-features.js.map +1 -0
  146. package/dist/emulate/workos/routes/users.d.ts +2 -0
  147. package/dist/emulate/workos/routes/users.js +133 -0
  148. package/dist/emulate/workos/routes/users.js.map +1 -0
  149. package/dist/emulate/workos/routes/webhook-endpoints.d.ts +2 -0
  150. package/dist/emulate/workos/routes/webhook-endpoints.js +70 -0
  151. package/dist/emulate/workos/routes/webhook-endpoints.js.map +1 -0
  152. package/dist/emulate/workos/routes/widgets.d.ts +2 -0
  153. package/dist/emulate/workos/routes/widgets.js +27 -0
  154. package/dist/emulate/workos/routes/widgets.js.map +1 -0
  155. package/dist/emulate/workos/store.d.ts +48 -0
  156. package/dist/emulate/workos/store.js +93 -0
  157. package/dist/emulate/workos/store.js.map +1 -0
  158. package/dist/emulate/workos/webhook-signer.d.ts +1 -0
  159. package/dist/emulate/workos/webhook-signer.js +8 -0
  160. package/dist/emulate/workos/webhook-signer.js.map +1 -0
  161. package/dist/gen-routes-lib.spec.ts +659 -0
  162. package/dist/gen-routes-lib.ts +647 -0
  163. package/dist/gen-routes.ts +96 -0
  164. package/dist/lib/dev-command.d.ts +26 -0
  165. package/dist/lib/dev-command.js +122 -0
  166. package/dist/lib/dev-command.js.map +1 -0
  167. package/dist/utils/help-json.js +23 -0
  168. package/dist/utils/help-json.js.map +1 -1
  169. package/package.json +20 -7
@@ -0,0 +1,86 @@
1
+ import { notFound, validationError, parseJsonBody } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatPipeConnection, parseListParams } from '../helpers.js';
4
+ const VALID_PROVIDERS = ['github', 'slack', 'google', 'salesforce'];
5
+ export function pipeRoutes(ctx) {
6
+ const { app, store } = ctx;
7
+ const ws = getWorkOSStore(store);
8
+ app.post('/pipes/connections', async (c) => {
9
+ const body = await parseJsonBody(c);
10
+ const userId = body.user_id;
11
+ const provider = body.provider;
12
+ const scopes = body.scopes ?? [];
13
+ if (!userId) {
14
+ throw validationError('user_id is required', [{ field: 'user_id', code: 'required' }]);
15
+ }
16
+ if (!provider) {
17
+ throw validationError('provider is required', [{ field: 'provider', code: 'required' }]);
18
+ }
19
+ if (!VALID_PROVIDERS.includes(provider)) {
20
+ throw validationError(`provider must be one of: ${VALID_PROVIDERS.join(', ')}`, [
21
+ { field: 'provider', code: 'invalid' },
22
+ ]);
23
+ }
24
+ const conn = ws.pipeConnections.insert({
25
+ object: 'pipe_connection',
26
+ user_id: userId,
27
+ provider,
28
+ scopes,
29
+ status: 'connected',
30
+ external_account_id: body.external_account_id ?? null,
31
+ });
32
+ return c.json(formatPipeConnection(conn), 201);
33
+ });
34
+ app.get('/pipes/connections', (c) => {
35
+ const url = new URL(c.req.url);
36
+ const params = parseListParams(url);
37
+ const userIdFilter = url.searchParams.get('user_id') ?? undefined;
38
+ const providerFilter = url.searchParams.get('provider') ?? undefined;
39
+ const result = ws.pipeConnections.list({
40
+ ...params,
41
+ filter: (pc) => {
42
+ if (userIdFilter && pc.user_id !== userIdFilter)
43
+ return false;
44
+ if (providerFilter && pc.provider !== providerFilter)
45
+ return false;
46
+ return true;
47
+ },
48
+ });
49
+ return c.json({
50
+ object: 'list',
51
+ data: result.data.map(formatPipeConnection),
52
+ list_metadata: result.list_metadata,
53
+ });
54
+ });
55
+ app.get('/pipes/connections/:id', (c) => {
56
+ const conn = ws.pipeConnections.get(c.req.param('id'));
57
+ if (!conn)
58
+ throw notFound('Pipe connection');
59
+ return c.json(formatPipeConnection(conn));
60
+ });
61
+ app.delete('/pipes/connections/:id', (c) => {
62
+ const conn = ws.pipeConnections.get(c.req.param('id'));
63
+ if (!conn)
64
+ throw notFound('Pipe connection');
65
+ ws.pipeConnections.delete(conn.id);
66
+ return c.body(null, 204);
67
+ });
68
+ app.post('/pipes/connections/:id/access_token', (c) => {
69
+ const conn = ws.pipeConnections.get(c.req.param('id'));
70
+ if (!conn)
71
+ throw notFound('Pipe connection');
72
+ if (conn.status !== 'connected') {
73
+ return c.json({
74
+ error: 'connection_inactive',
75
+ message: `Connection is ${conn.status}`,
76
+ }, 400);
77
+ }
78
+ return c.json({
79
+ access_token: `pipes_mock_${conn.provider}_${conn.user_id}`,
80
+ token_type: 'bearer',
81
+ scopes: conn.scopes,
82
+ expires_in: 3600,
83
+ });
84
+ });
85
+ }
86
+ //# sourceMappingURL=pipes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipes.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/pipes.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, validationError, parseJsonBody } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatPipeConnection, parseListParams } from '../helpers.js';\nimport type { PipeProvider } from '../entities.js';\n\nconst VALID_PROVIDERS: PipeProvider[] = ['github', 'slack', 'google', 'salesforce'];\n\nexport function pipeRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n app.post('/pipes/connections', async (c) => {\n const body = await parseJsonBody(c);\n const userId = body.user_id as string | undefined;\n const provider = body.provider as PipeProvider | undefined;\n const scopes = (body.scopes as string[]) ?? [];\n\n if (!userId) {\n throw validationError('user_id is required', [{ field: 'user_id', code: 'required' }]);\n }\n if (!provider) {\n throw validationError('provider is required', [{ field: 'provider', code: 'required' }]);\n }\n if (!VALID_PROVIDERS.includes(provider)) {\n throw validationError(`provider must be one of: ${VALID_PROVIDERS.join(', ')}`, [\n { field: 'provider', code: 'invalid' },\n ]);\n }\n\n const conn = ws.pipeConnections.insert({\n object: 'pipe_connection',\n user_id: userId,\n provider,\n scopes,\n status: 'connected',\n external_account_id: (body.external_account_id as string) ?? null,\n });\n\n return c.json(formatPipeConnection(conn), 201);\n });\n\n app.get('/pipes/connections', (c) => {\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n const userIdFilter = url.searchParams.get('user_id') ?? undefined;\n const providerFilter = url.searchParams.get('provider') ?? undefined;\n\n const result = ws.pipeConnections.list({\n ...params,\n filter: (pc) => {\n if (userIdFilter && pc.user_id !== userIdFilter) return false;\n if (providerFilter && pc.provider !== providerFilter) return false;\n return true;\n },\n });\n\n return c.json({\n object: 'list',\n data: result.data.map(formatPipeConnection),\n list_metadata: result.list_metadata,\n });\n });\n\n app.get('/pipes/connections/:id', (c) => {\n const conn = ws.pipeConnections.get(c.req.param('id'));\n if (!conn) throw notFound('Pipe connection');\n return c.json(formatPipeConnection(conn));\n });\n\n app.delete('/pipes/connections/:id', (c) => {\n const conn = ws.pipeConnections.get(c.req.param('id'));\n if (!conn) throw notFound('Pipe connection');\n ws.pipeConnections.delete(conn.id);\n return c.body(null, 204);\n });\n\n app.post('/pipes/connections/:id/access_token', (c) => {\n const conn = ws.pipeConnections.get(c.req.param('id'));\n if (!conn) throw notFound('Pipe connection');\n if (conn.status !== 'connected') {\n return c.json(\n {\n error: 'connection_inactive',\n message: `Connection is ${conn.status}`,\n },\n 400,\n );\n }\n\n return c.json({\n access_token: `pipes_mock_${conn.provider}_${conn.user_id}`,\n token_type: 'bearer',\n scopes: conn.scopes,\n expires_in: 3600,\n });\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function portalRoutes(ctx: RouteContext): void;
@@ -0,0 +1,18 @@
1
+ import { parseJsonBody, validationError } from '../../core/index.js';
2
+ export function portalRoutes(ctx) {
3
+ const { app } = ctx;
4
+ app.post('/portal/generate_link', async (c) => {
5
+ const body = await parseJsonBody(c);
6
+ const intent = body.intent;
7
+ const organization = body.organization;
8
+ if (!intent) {
9
+ throw validationError('intent is required', [{ field: 'intent', code: 'required' }]);
10
+ }
11
+ if (!organization) {
12
+ throw validationError('organization is required', [{ field: 'organization', code: 'required' }]);
13
+ }
14
+ const baseUrl = new URL(c.req.url).origin;
15
+ return c.json({ link: `${baseUrl}/portal/${intent}/${organization}` });
16
+ });
17
+ }
18
+ //# sourceMappingURL=portal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/portal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAExF,MAAM,UAAU,YAAY,CAAC,GAAiB;IAC5C,MAAM,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;IAEpB,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC5C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,MAA4B,CAAC;QACjD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAkC,CAAC;QAE7D,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,eAAe,CAAC,oBAAoB,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QACvF,CAAC;QACD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,eAAe,CAAC,0BAA0B,EAAE,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QACnG,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;QAC1C,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,OAAO,WAAW,MAAM,IAAI,YAAY,EAAE,EAAE,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { type RouteContext, parseJsonBody, validationError } from '../../core/index.js';\n\nexport function portalRoutes(ctx: RouteContext): void {\n const { app } = ctx;\n\n app.post('/portal/generate_link', async (c) => {\n const body = await parseJsonBody(c);\n const intent = body.intent as string | undefined;\n const organization = body.organization as string | undefined;\n\n if (!intent) {\n throw validationError('intent is required', [{ field: 'intent', code: 'required' }]);\n }\n if (!organization) {\n throw validationError('organization is required', [{ field: 'organization', code: 'required' }]);\n }\n\n const baseUrl = new URL(c.req.url).origin;\n return c.json({ link: `${baseUrl}/portal/${intent}/${organization}` });\n });\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import { type RouteContext } from '../../core/index.js';
2
+ export declare function radarRoutes(ctx: RouteContext): void;
@@ -0,0 +1,45 @@
1
+ import { notFound, parseJsonBody } from '../../core/index.js';
2
+ import { getWorkOSStore } from '../store.js';
3
+ import { formatRadarAttempt, parseListParams } 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({
13
+ object: 'list',
14
+ data: result.data.map(formatRadarAttempt),
15
+ list_metadata: result.list_metadata,
16
+ });
17
+ });
18
+ // Get attempt
19
+ app.get('/radar/attempts/:id', (c) => {
20
+ const attempt = ws.radarAttempts.get(c.req.param('id'));
21
+ if (!attempt)
22
+ throw notFound('RadarAttempt');
23
+ return c.json(formatRadarAttempt(attempt));
24
+ });
25
+ // Manage allow/deny lists
26
+ app.post('/radar/lists/:type/:action', async (c) => {
27
+ const listType = c.req.param('type');
28
+ const action = c.req.param('action');
29
+ const body = await parseJsonBody(c);
30
+ const entries = body.entries ?? [];
31
+ const key = `radar_${listType}_list`;
32
+ const existing = store.getData(key) ?? new Set();
33
+ if (action === 'add') {
34
+ for (const entry of entries)
35
+ existing.add(entry);
36
+ }
37
+ else if (action === 'remove') {
38
+ for (const entry of entries)
39
+ existing.delete(entry);
40
+ }
41
+ store.setData(key, existing);
42
+ return c.json({ success: true });
43
+ });
44
+ }
45
+ //# 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,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEpE,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;YACZ,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC;YACzC,aAAa,EAAE,MAAM,CAAC,aAAa;SACpC,CAAC,CAAC;IACL,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 } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatRadarAttempt, parseListParams } 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({\n object: 'list',\n data: result.data.map(formatRadarAttempt),\n list_metadata: result.list_metadata,\n });\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":"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","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,160 @@
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
+ export function ssoRoutes(ctx) {
5
+ const { app, store, jwt } = ctx;
6
+ const ws = getWorkOSStore(store);
7
+ app.get('/sso/authorize', (c) => {
8
+ const url = new URL(c.req.url);
9
+ const redirectUri = url.searchParams.get('redirect_uri');
10
+ const state = url.searchParams.get('state');
11
+ const connectionId = url.searchParams.get('connection');
12
+ const organizationId = url.searchParams.get('organization');
13
+ const domainHint = url.searchParams.get('domain_hint');
14
+ const loginHint = url.searchParams.get('login_hint');
15
+ if (!redirectUri) {
16
+ throw new WorkOSApiError(400, 'Missing required parameter: redirect_uri', 'invalid_request');
17
+ }
18
+ assertLocalRedirectUri(redirectUri);
19
+ let connection;
20
+ if (connectionId) {
21
+ connection = ws.connections.get(connectionId);
22
+ }
23
+ else if (organizationId) {
24
+ connection = ws.connections.findBy('organization_id', organizationId).find((c) => c.state === 'active');
25
+ }
26
+ else if (domainHint) {
27
+ connection = ws.connections
28
+ .all()
29
+ .find((c) => c.state === 'active' && c.domains.some((d) => d.domain === domainHint));
30
+ }
31
+ if (!connection || connection.state !== 'active') {
32
+ throw new WorkOSApiError(404, 'No active connection found', 'connection_not_found');
33
+ }
34
+ const email = loginHint ?? `user@${connection.domains[0]?.domain ?? 'example.com'}`;
35
+ let profile = ws.ssoProfiles.findOneBy('email', email);
36
+ if (!profile || profile.connection_id !== connection.id) {
37
+ profile = ws.ssoProfiles.insert({
38
+ object: 'profile',
39
+ connection_id: connection.id,
40
+ connection_type: connection.connection_type,
41
+ organization_id: connection.organization_id,
42
+ idp_id: `idp_${generateId('usr')}`,
43
+ email,
44
+ first_name: email.split('@')[0],
45
+ last_name: null,
46
+ groups: [],
47
+ raw_attributes: { email },
48
+ });
49
+ }
50
+ const authCode = ws.ssoAuthorizations.insert({
51
+ code: generateId('sso_code'),
52
+ connection_id: connection.id,
53
+ organization_id: connection.organization_id,
54
+ profile_id: profile.id,
55
+ redirect_uri: redirectUri,
56
+ state,
57
+ expires_at: expiresIn(10),
58
+ });
59
+ const redirect = new URL(redirectUri);
60
+ redirect.searchParams.set('code', authCode.code);
61
+ if (state)
62
+ redirect.searchParams.set('state', state);
63
+ return c.redirect(redirect.toString());
64
+ });
65
+ app.post('/sso/token', async (c) => {
66
+ const body = await parseJsonBody(c);
67
+ const grantType = body.grant_type;
68
+ const code = body.code;
69
+ if (grantType !== 'authorization_code') {
70
+ throw new WorkOSApiError(400, 'Unsupported grant_type', 'invalid_request');
71
+ }
72
+ if (!code) {
73
+ throw new WorkOSApiError(400, 'code is required', 'invalid_request');
74
+ }
75
+ const auth = ws.ssoAuthorizations.all().find((a) => a.code === code);
76
+ if (!auth) {
77
+ throw new WorkOSApiError(400, 'Invalid authorization code', 'invalid_code');
78
+ }
79
+ if (isExpired(auth.expires_at)) {
80
+ ws.ssoAuthorizations.delete(auth.id);
81
+ throw new WorkOSApiError(400, 'Authorization code has expired', 'expired_code');
82
+ }
83
+ const profile = ws.ssoProfiles.get(auth.profile_id);
84
+ if (!profile) {
85
+ throw new WorkOSApiError(500, 'Profile not found', 'server_error');
86
+ }
87
+ ws.ssoAuthorizations.delete(auth.id);
88
+ const accessToken = jwt.sign({
89
+ sub: profile.id,
90
+ aud: body.client_id ?? 'workos-emulate',
91
+ org_id: auth.organization_id,
92
+ });
93
+ store.setData(`sso_token:${accessToken}`, profile.id);
94
+ return c.json({
95
+ profile: formatSSOProfile(profile),
96
+ access_token: accessToken,
97
+ });
98
+ });
99
+ app.get('/sso/profile', (c) => {
100
+ const authHeader = c.req.header('Authorization');
101
+ if (!authHeader) {
102
+ throw new WorkOSApiError(401, 'Unauthorized', 'unauthorized');
103
+ }
104
+ const token = authHeader.replace(/^Bearer\s+/i, '').trim();
105
+ const profileId = store.getData(`sso_token:${token}`);
106
+ if (!profileId) {
107
+ try {
108
+ const payload = jwt.verify(token);
109
+ const profile = ws.ssoProfiles.get(payload.sub);
110
+ if (profile)
111
+ return c.json(formatSSOProfile(profile));
112
+ }
113
+ catch {
114
+ // fall through
115
+ }
116
+ throw new WorkOSApiError(401, 'Invalid access token', 'unauthorized');
117
+ }
118
+ const profile = ws.ssoProfiles.get(profileId);
119
+ if (!profile) {
120
+ throw new WorkOSApiError(404, 'Profile not found', 'not_found');
121
+ }
122
+ return c.json(formatSSOProfile(profile));
123
+ });
124
+ app.get('/sso/jwks', (c) => {
125
+ return c.json(jwt.getJWKS());
126
+ });
127
+ // SSO Single Logout — generate logout token
128
+ app.post('/sso/logout/authorize', async (c) => {
129
+ const body = await parseJsonBody(c);
130
+ const profileId = body.profile_id;
131
+ if (!profileId) {
132
+ throw new WorkOSApiError(400, 'profile_id is required', 'invalid_request');
133
+ }
134
+ const profile = ws.ssoProfiles.get(profileId);
135
+ if (!profile) {
136
+ throw new WorkOSApiError(404, 'Profile not found', 'not_found');
137
+ }
138
+ const logoutToken = generateId('sso_logout');
139
+ store.setData(`sso_logout:${logoutToken}`, profile.id);
140
+ return c.json({
141
+ logout_token: logoutToken,
142
+ logout_url: `${ctx.baseUrl}/sso/logout?logout_token=${logoutToken}`,
143
+ });
144
+ });
145
+ // SSO Single Logout — redirect (public, no auth)
146
+ app.get('/sso/logout', (c) => {
147
+ const url = new URL(c.req.url);
148
+ const logoutToken = url.searchParams.get('logout_token');
149
+ if (!logoutToken) {
150
+ throw new WorkOSApiError(400, 'logout_token is required', 'invalid_request');
151
+ }
152
+ const profileId = store.getData(`sso_logout:${logoutToken}`);
153
+ if (!profileId) {
154
+ throw new WorkOSApiError(400, 'Invalid logout token', 'invalid_logout_token');
155
+ }
156
+ store.setData(`sso_logout:${logoutToken}`, undefined);
157
+ return c.json({ success: true });
158
+ });
159
+ }
160
+ //# 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';\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.all().find((a) => a.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(`sso_token:${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>(`sso_token:${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(`sso_logout:${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>(`sso_logout:${logoutToken}`);\n if (!profileId) {\n throw new WorkOSApiError(400, 'Invalid logout token', 'invalid_logout_token');\n }\n\n store.setData(`sso_logout:${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;