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