workos 0.12.0-beta.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/bin.js +4 -2
- package/dist/bin.js.map +1 -1
- package/dist/commands/debug.js +0 -1
- package/dist/commands/debug.js.map +1 -1
- package/dist/commands/login.js +0 -4
- package/dist/commands/login.js.map +1 -1
- package/dist/emulate/core/id.d.ts +16 -1
- package/dist/emulate/core/id.js +16 -1
- package/dist/emulate/core/id.js.map +1 -1
- package/dist/emulate/core/index.d.ts +1 -1
- package/dist/emulate/core/index.js +1 -1
- package/dist/emulate/core/index.js.map +1 -1
- package/dist/emulate/core/middleware/auth.d.ts +1 -4
- package/dist/emulate/core/middleware/auth.js +7 -18
- package/dist/emulate/core/middleware/auth.js.map +1 -1
- package/dist/emulate/core/pagination.d.ts +6 -0
- package/dist/emulate/core/pagination.js +9 -1
- package/dist/emulate/core/pagination.js.map +1 -1
- package/dist/emulate/core/server.js +20 -46
- package/dist/emulate/core/server.js.map +1 -1
- package/dist/emulate/core/store.d.ts +2 -0
- package/dist/emulate/core/store.js +22 -1
- package/dist/emulate/core/store.js.map +1 -1
- 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.js.map +1 -1
- package/dist/emulate/workos/event-bus.d.ts +6 -1
- package/dist/emulate/workos/event-bus.js +33 -8
- package/dist/emulate/workos/event-bus.js.map +1 -1
- package/dist/emulate/workos/helpers.d.ts +17 -8
- package/dist/emulate/workos/helpers.js +61 -368
- package/dist/emulate/workos/helpers.js.map +1 -1
- package/dist/emulate/workos/index.js +42 -39
- package/dist/emulate/workos/index.js.map +1 -1
- 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.js +5 -8
- package/dist/emulate/workos/routes/api-keys.js.map +1 -1
- package/dist/emulate/workos/routes/audit-logs.js +6 -9
- package/dist/emulate/workos/routes/audit-logs.js.map +1 -1
- package/dist/emulate/workos/routes/auth.js +7 -6
- package/dist/emulate/workos/routes/auth.js.map +1 -1
- package/dist/emulate/workos/routes/authorization-checks.js +5 -17
- package/dist/emulate/workos/routes/authorization-checks.js.map +1 -1
- package/dist/emulate/workos/routes/authorization-org-roles.js +29 -171
- package/dist/emulate/workos/routes/authorization-org-roles.js.map +1 -1
- package/dist/emulate/workos/routes/authorization-permissions.js +5 -16
- package/dist/emulate/workos/routes/authorization-permissions.js.map +1 -1
- package/dist/emulate/workos/routes/authorization-resources.js +4 -15
- package/dist/emulate/workos/routes/authorization-resources.js.map +1 -1
- package/dist/emulate/workos/routes/authorization-roles.d.ts +1 -1
- package/dist/emulate/workos/routes/authorization-roles.js +9 -132
- package/dist/emulate/workos/routes/authorization-roles.js.map +1 -1
- package/dist/emulate/workos/routes/config.js +3 -2
- package/dist/emulate/workos/routes/config.js.map +1 -1
- package/dist/emulate/workos/routes/connect.js +3 -7
- package/dist/emulate/workos/routes/connect.js.map +1 -1
- package/dist/emulate/workos/routes/connections.js +3 -7
- package/dist/emulate/workos/routes/connections.js.map +1 -1
- package/dist/emulate/workos/routes/directories.js +7 -23
- package/dist/emulate/workos/routes/directories.js.map +1 -1
- package/dist/emulate/workos/routes/events.js +3 -6
- package/dist/emulate/workos/routes/events.js.map +1 -1
- package/dist/emulate/workos/routes/feature-flags.js +20 -48
- package/dist/emulate/workos/routes/feature-flags.js.map +1 -1
- package/dist/emulate/workos/routes/invitations.js +10 -13
- package/dist/emulate/workos/routes/invitations.js.map +1 -1
- package/dist/emulate/workos/routes/memberships.js +3 -7
- package/dist/emulate/workos/routes/memberships.js.map +1 -1
- package/dist/emulate/workos/routes/organizations.js +13 -15
- package/dist/emulate/workos/routes/organizations.js.map +1 -1
- package/dist/emulate/workos/routes/pipes.js +3 -7
- package/dist/emulate/workos/routes/pipes.js.map +1 -1
- package/dist/emulate/workos/routes/radar.js +3 -7
- package/dist/emulate/workos/routes/radar.js.map +1 -1
- package/dist/emulate/workos/routes/sso.js +7 -6
- package/dist/emulate/workos/routes/sso.js.map +1 -1
- package/dist/emulate/workos/routes/users.js +3 -7
- package/dist/emulate/workos/routes/users.js.map +1 -1
- package/dist/emulate/workos/routes/webhook-endpoints.js +3 -7
- package/dist/emulate/workos/routes/webhook-endpoints.js.map +1 -1
- package/dist/emulate/workos/store.js +68 -59
- package/dist/emulate/workos/store.js.map +1 -1
- 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 -23
- 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 +2 -2
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { notFound, parseJsonBody, WorkOSApiError, generateId } from '../../core/index.js';
|
|
3
3
|
import { getWorkOSStore } from '../store.js';
|
|
4
4
|
import { formatUser, formatDeviceAuthorization, verifyPassword, isExpired, expiresIn, assertLocalRedirectUri, sealSession, } from '../helpers.js';
|
|
5
|
+
import { STORE_KEYS, STORE_KEY_PREFIXES } from '../constants.js';
|
|
5
6
|
export function authRoutes(ctx) {
|
|
6
7
|
const { app, store, jwt } = ctx;
|
|
7
8
|
const ws = getWorkOSStore(store);
|
|
@@ -90,7 +91,7 @@ export function authRoutes(ctx) {
|
|
|
90
91
|
const code = body.code;
|
|
91
92
|
if (!code)
|
|
92
93
|
throw new WorkOSApiError(400, 'code is required', 'invalid_request');
|
|
93
|
-
const authCode = ws.authCodes.
|
|
94
|
+
const authCode = ws.authCodes.findOneBy('code', code);
|
|
94
95
|
if (!authCode)
|
|
95
96
|
throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');
|
|
96
97
|
if (isExpired(authCode.expires_at)) {
|
|
@@ -201,7 +202,7 @@ export function authRoutes(ctx) {
|
|
|
201
202
|
if (!code || !pendingToken || !challengeId) {
|
|
202
203
|
throw new WorkOSApiError(400, 'code, pending_authentication_token, and authentication_challenge_id are required', 'invalid_request');
|
|
203
204
|
}
|
|
204
|
-
const pending = store.getData(
|
|
205
|
+
const pending = store.getData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`);
|
|
205
206
|
if (!pending) {
|
|
206
207
|
throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');
|
|
207
208
|
}
|
|
@@ -218,7 +219,7 @@ export function authRoutes(ctx) {
|
|
|
218
219
|
throw new WorkOSApiError(400, 'Invalid one-time code', 'invalid_one_time_code');
|
|
219
220
|
}
|
|
220
221
|
ws.authChallenges.delete(challenge.id);
|
|
221
|
-
store.setData(
|
|
222
|
+
store.setData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`, undefined);
|
|
222
223
|
user = ws.users.get(pending.user_id);
|
|
223
224
|
organizationId = pending.organization_id;
|
|
224
225
|
authMethod = 'MFA';
|
|
@@ -230,14 +231,14 @@ export function authRoutes(ctx) {
|
|
|
230
231
|
if (!pendingToken || !orgId) {
|
|
231
232
|
throw new WorkOSApiError(400, 'pending_authentication_token and organization_id are required', 'invalid_request');
|
|
232
233
|
}
|
|
233
|
-
const pending = store.getData(
|
|
234
|
+
const pending = store.getData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`);
|
|
234
235
|
if (!pending) {
|
|
235
236
|
throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');
|
|
236
237
|
}
|
|
237
238
|
const org = ws.organizations.get(orgId);
|
|
238
239
|
if (!org)
|
|
239
240
|
throw notFound('Organization');
|
|
240
|
-
store.setData(
|
|
241
|
+
store.setData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`, undefined);
|
|
241
242
|
user = ws.users.get(pending.user_id);
|
|
242
243
|
organizationId = orgId;
|
|
243
244
|
authMethod = pending.auth_method;
|
|
@@ -325,7 +326,7 @@ export function authRoutes(ctx) {
|
|
|
325
326
|
? sealSession({ access_token: accessToken, refresh_token: newRefreshToken.token, session_id: session.id }, sealKey)
|
|
326
327
|
: null;
|
|
327
328
|
// Emit authentication event (hybrid Option B for action-specific events)
|
|
328
|
-
const eventBus = store.getData(
|
|
329
|
+
const eventBus = store.getData(STORE_KEYS.eventBus);
|
|
329
330
|
if (eventBus) {
|
|
330
331
|
const authEventType = `authentication.${authMethod.toLowerCase()}_succeeded`;
|
|
331
332
|
eventBus.emit({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAqB,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC7G,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,UAAU,EACV,yBAAyB,EACzB,cAAc,EACd,SAAS,EACT,SAAS,EACT,sBAAsB,EACtB,WAAW,GACZ,MAAM,eAAe,CAAC;AASvB,MAAM,UAAU,UAAU,CAAC,GAAiB;IAC1C,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;IAChC,MAAM,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAEjC,GAAG,CAAC,GAAG,CAAC,4BAA4B,EAAE,CAAC,CAAC,EAAE,EAAE;QAC1C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC7D,MAAM,mBAAmB,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QAC1E,MAAM,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAErD,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,0BAA0B,EAAE,iBAAiB,CAAC,CAAC;QAC/E,CAAC;QACD,sBAAsB,CAAC,WAAW,CAAC,CAAC;QAEpC,IAAI,IAAI,CAAC;QACT,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC9C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;gBACtC,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;gBACrD,IAAI,KAAK;oBAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;gBACrD,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YAC7B,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;YACtC,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YAC/C,IAAI,KAAK;gBAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACrD,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,MAAM,QAAQ,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC;YACnC,OAAO,EAAE,IAAI,CAAC,EAAE;YAChB,eAAe,EAAE,IAAI;YACrB,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC;YAC7B,YAAY,EAAE,WAAW;YACzB,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC;YACzB,cAAc,EAAE,aAAa,IAAI,IAAI;YACrC,qBAAqB,EAAE,mBAAmB,IAAI,IAAI;SACnD,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;QACtC,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,KAAK;YAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACrD,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,gCAAgC;IAChC,GAAG,CAAC,IAAI,CAAC,mCAAmC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACxD,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAmB,CAAC;QAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,uBAAuB,EAAE,iBAAiB,CAAC,CAAC;QAC5E,CAAC;QAED,wDAAwD;QACxD,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;QAE9B,MAAM,UAAU,GAAG,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC;YAChD,WAAW,EAAE,UAAU,CAAC,UAAU,CAAC;YACnC,SAAS,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE;YAChE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,IAAI;YACzB,SAAS,EAAE,QAAQ;YACnB,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC;YACzB,QAAQ,EAAE,CAAC;SACZ,CAAC,CAAC;QAEH,OAAO,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,UAAU,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,mEAAmE;IACnE,MAAM,mBAAmB,GAAG,KAAK,EAAE,CAAM,EAAE,EAAE;QAC3C,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,SAAS,GAAG,IAAI,CAAC,UAAgC,CAAC;QACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAA+B,CAAC;QACtD,MAAM,YAAY,GAAG,IAAI,CAAC,aAAmC,CAAC;QAE9D,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,iBAAiB,CAAC,CAAC;QAC7E,CAAC;QAED,IAAI,IAAI,CAAC;QACT,IAAI,cAAc,GAAkB,IAAI,CAAC;QACzC,IAAI,UAAkB,CAAC;QAEvB,QAAQ,SAAS,EAAE,CAAC;YAClB,KAAK,oBAAoB,CAAC,CAAC,CAAC;gBAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAc,CAAC;gBACjC,IAAI,CAAC,IAAI;oBAAE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,kBAAkB,EAAE,iBAAiB,CAAC,CAAC;gBAEhF,MAAM,QAAQ,GAAG,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;gBACnE,IAAI,CAAC,QAAQ;oBAAE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;gBAC7E,IAAI,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;oBACnC,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,kBAAkB,EAAE,cAAc,CAAC,CAAC;gBACpE,CAAC;gBAED,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;oBAC5B,MAAM,YAAY,GAAG,IAAI,CAAC,aAAuB,CAAC;oBAClD,IAAI,CAAC,YAAY,EAAE,CAAC;wBAClB,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,2BAA2B,EAAE,iBAAiB,CAAC,CAAC;oBAChF,CAAC;oBACD,MAAM,MAAM,GAAG,QAAQ,CAAC,qBAAqB,IAAI,MAAM,CAAC;oBACxD,IAAI,SAAiB,CAAC;oBACtB,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;wBACtB,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;oBAC5E,CAAC;yBAAM,CAAC;wBACN,SAAS,GAAG,YAAY,CAAC;oBAC3B,CAAC;oBACD,IAAI,SAAS,KAAK,QAAQ,CAAC,cAAc,EAAE,CAAC;wBAC1C,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,uBAAuB,EAAE,uBAAuB,CAAC,CAAC;oBAClF,CAAC;gBACH,CAAC;gBAED,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;gBACtC,cAAc,GAAG,QAAQ,CAAC,eAAe,CAAC;gBAC1C,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;gBACjC,UAAU,GAAG,OAAO,CAAC;gBACrB,MAAM;YACR,CAAC;YAED,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAe,CAAC;gBACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAkB,CAAC;gBACzC,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACxB,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,iCAAiC,EAAE,iBAAiB,CAAC,CAAC;gBACtF,CAAC;gBAED,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;gBAC1C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;oBAClF,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,qBAAqB,EAAE,qBAAqB,CAAC,CAAC;gBAC9E,CAAC;gBACD,UAAU,GAAG,UAAU,CAAC;gBACxB,MAAM;YACR,CAAC;YAED,0DAA0D;YAC1D,KAAK,wCAAwC,CAAC;YAC9C,KAAK,6CAA6C,CAAC,CAAC,CAAC;gBACnD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAc,CAAC;gBACjC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAe,CAAC;gBACnC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;oBACpB,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,6BAA6B,EAAE,iBAAiB,CAAC,CAAC;gBAClF,CAAC;gBAED,MAAM,SAAS,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;gBAC3F,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;gBAChE,CAAC;gBACD,IAAI,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;oBACpC,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,kBAAkB,EAAE,cAAc,CAAC,CAAC;gBACpE,CAAC;gBAED,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;gBACvC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBACnC,UAAU,GAAG,WAAW,CAAC;gBACzB,MAAM;YACR,CAAC;YAED,kEAAkE;YAClE,KAAK,gDAAgD,CAAC;YACtD,KAAK,qDAAqD,CAAC,CAAC,CAAC;gBAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAc,CAAC;gBACjC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAiB,CAAC;gBACtC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;oBACrB,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,+BAA+B,EAAE,iBAAiB,CAAC,CAAC;gBACpF,CAAC;gBAED,MAAM,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;gBACxF,IAAI,CAAC,EAAE,EAAE,CAAC;oBACR,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;gBAChE,CAAC;gBACD,IAAI,SAAS,CAAC,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC7B,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,kBAAkB,EAAE,cAAc,CAAC,CAAC;gBACpE,CAAC;gBAED,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClD,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;gBACpC,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC5B,UAAU,GAAG,mBAAmB,CAAC;gBACjC,MAAM;YACR,CAAC;YAED,KAAK,eAAe,CAAC,CAAC,CAAC;gBACrB,MAAM,KAAK,GAAG,IAAI,CAAC,aAAuB,CAAC;gBAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,2BAA2B,EAAE,iBAAiB,CAAC,CAAC;gBAChF,CAAC;gBAED,MAAM,YAAY,GAAG,EAAE,CAAC,aAAa,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;gBAChE,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,uBAAuB,EAAE,eAAe,CAAC,CAAC;gBAC1E,CAAC;gBACD,IAAI,SAAS,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;oBACvC,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;oBACzC,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,2BAA2B,EAAE,eAAe,CAAC,CAAC;gBAC9E,CAAC;gBAED,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;gBAC1C,0EAA0E;gBAC1E,cAAc,GAAI,IAAI,CAAC,eAA0B,IAAI,YAAY,CAAC,eAAe,CAAC;gBAElF,sCAAsC;gBACtC,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBACzC,UAAU,GAAG,OAAO,CAAC;gBACrB,MAAM;YACR,CAAC;YAED,KAAK,sCAAsC,CAAC,CAAC,CAAC;gBAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAc,CAAC;gBACjC,MAAM,YAAY,GAAG,IAAI,CAAC,4BAAsC,CAAC;gBACjE,MAAM,WAAW,GAAG,IAAI,CAAC,2BAAqC,CAAC;gBAE/D,IAAI,CAAC,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,WAAW,EAAE,CAAC;oBAC3C,MAAM,IAAI,cAAc,CACtB,GAAG,EACH,kFAAkF,EAClF,iBAAiB,CAClB,CAAC;gBACJ,CAAC;gBAED,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAc,gBAAgB,YAAY,EAAE,CAAC,CAAC;gBAC3E,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,sCAAsC,EAAE,sCAAsC,CAAC,CAAC;gBAChH,CAAC;gBAED,MAAM,SAAS,GAAG,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;gBACrD,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,kCAAkC,EAAE,iBAAiB,CAAC,CAAC;gBACvF,CAAC;gBACD,IAAI,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,CAAC;oBACpC,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;oBACvC,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,uBAAuB,EAAE,mBAAmB,CAAC,CAAC;gBAC9E,CAAC;gBAED,kDAAkD;gBAClD,IAAI,SAAS,CAAC,IAAI,IAAI,IAAI,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;oBAC9C,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,uBAAuB,EAAE,uBAAuB,CAAC,CAAC;gBAClF,CAAC;gBAED,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;gBACvC,KAAK,CAAC,OAAO,CAAC,gBAAgB,YAAY,EAAE,EAAE,SAAS,CAAC,CAAC;gBAEzD,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBACrC,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;gBACzC,UAAU,GAAG,KAAK,CAAC;gBACnB,MAAM;YACR,CAAC;YAED,KAAK,oDAAoD,CAAC,CAAC,CAAC;gBAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,4BAAsC,CAAC;gBACjE,MAAM,KAAK,GAAG,IAAI,CAAC,eAAyB,CAAC;gBAE7C,IAAI,CAAC,YAAY,IAAI,CAAC,KAAK,EAAE,CAAC;oBAC5B,MAAM,IAAI,cAAc,CACtB,GAAG,EACH,+DAA+D,EAC/D,iBAAiB,CAClB,CAAC;gBACJ,CAAC;gBAED,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAc,gBAAgB,YAAY,EAAE,CAAC,CAAC;gBAC3E,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,sCAAsC,EAAE,sCAAsC,CAAC,CAAC;gBAChH,CAAC;gBAED,MAAM,GAAG,GAAG,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACxC,IAAI,CAAC,GAAG;oBAAE,MAAM,QAAQ,CAAC,cAAc,CAAC,CAAC;gBAEzC,KAAK,CAAC,OAAO,CAAC,gBAAgB,YAAY,EAAE,EAAE,SAAS,CAAC,CAAC;gBAEzD,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBACrC,cAAc,GAAG,KAAK,CAAC;gBACvB,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC;gBACjC,MAAM;YACR,CAAC;YAED,KAAK,8CAA8C,CAAC,CAAC,CAAC;gBACpD,MAAM,UAAU,GAAG,IAAI,CAAC,WAAqB,CAAC;gBAC9C,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,yBAAyB,EAAE,iBAAiB,CAAC,CAAC;gBAC9E,CAAC;gBAED,MAAM,UAAU,GAAG,EAAE,CAAC,oBAAoB,CAAC,SAAS,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC;gBAChF,IAAI,CAAC,UAAU,EAAE,CAAC;oBAChB,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,qBAAqB,EAAE,eAAe,CAAC,CAAC;gBACxE,CAAC;gBACD,IAAI,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;oBACrC,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;oBAC9C,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,yBAAyB,EAAE,eAAe,CAAC,CAAC;gBAC5E,CAAC;gBACD,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;oBACxB,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,uBAAuB,EAAE,uBAAuB,CAAC,CAAC;gBAClF,CAAC;gBAED,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;gBACxC,EAAE,CAAC,oBAAoB,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBAC9C,UAAU,GAAG,OAAO,CAAC;gBACrB,MAAM;YACR,CAAC;YAED;gBACE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,2BAA2B,SAAS,EAAE,EAAE,iBAAiB,CAAC,CAAC;QAC7F,CAAC;QAED,IAAI,CAAC,IAAI;YAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;QAElC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,eAAe,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QACxE,MAAM,WAAW,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAE,CAAC;QAE3C,MAAM,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;YACjC,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,IAAI,CAAC,EAAE;YAChB,eAAe,EAAE,cAAc;YAC/B,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,IAAI,IAAI;YACnD,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,IAAI;SAC/C,CAAC,CAAC;QAEH,qDAAqD;QACrD,IAAI,QAA4B,CAAC;QACjC,IAAI,eAAqC,CAAC;QAC1C,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,UAAU,GAAG,EAAE,CAAC,uBAAuB;iBAC1C,MAAM,CAAC,iBAAiB,EAAE,cAAc,CAAC;iBACzC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;YACtC,IAAI,UAAU,EAAE,CAAC;gBACf,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;gBAChC,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK;qBAClB,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;qBACpC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,KAAK,cAAc,IAAI,CAAC,CAAC,IAAI,KAAK,iBAAiB,CAAC,CAAC;gBACrF,IAAI,IAAI,EAAE,CAAC;oBACT,MAAM,GAAG,GAAG,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;oBAC1D,eAAe,GAAG,GAAG;yBAClB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC;yBACjD,MAAM,CAAC,OAAO,CAAC;yBACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC;gBACzB,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,WAAW,GAAG,GAAG,CAAC,IAAI,CAAC;YAC3B,GAAG,EAAE,IAAI,CAAC,EAAE;YACZ,GAAG,EAAE,OAAO,CAAC,EAAE;YACf,MAAM,EAAE,cAAc,IAAI,SAAS;YACnC,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,eAAe;YAC5B,GAAG,EAAE,QAAQ,IAAI,gBAAgB;SAClC,CAAC,CAAC;QAEH,6BAA6B;QAC7B,MAAM,eAAe,GAAG,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC;YAC9C,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC;YACxB,OAAO,EAAE,IAAI,CAAC,EAAE;YAChB,eAAe,EAAE,cAAc;YAC/B,UAAU,EAAE,OAAO,CAAC,EAAE;YACtB,UAAU,EAAE,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,UAAU;SAChD,CAAC,CAAC;QAEH,wDAAwD;QACxD,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG;aACjB,MAAM,CAAC,eAAe,CAAC;YACxB,EAAE,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;aAC3B,IAAI,EAAE,CAAC;QACV,MAAM,OAAO,GAAG,YAAY,IAAI,MAAM,CAAC;QACvC,MAAM,aAAa,GAAG,OAAO;YAC3B,CAAC,CAAC,WAAW,CACT,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,EAAE,EAC3F,OAAO,CACR;YACH,CAAC,CAAC,IAAI,CAAC;QAET,yEAAyE;QACzE,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAW,UAAU,CAAC,CAAC;QACrD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,aAAa,GAAG,kBAAkB,UAAU,CAAC,WAAW,EAAE,YAAY,CAAC;YAC7E,QAAQ,CAAC,IAAI,CAAC;gBACZ,KAAK,EAAE,aAAa;gBACpB,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE;aACzG,CAAC,CAAC;QACL,CAAC;QAED,OAAO,CAAC,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC;YAC7B,eAAe,EAAE,cAAc;YAC/B,YAAY,EAAE,WAAW;YACzB,aAAa,EAAE,eAAe,CAAC,KAAK;YACpC,qBAAqB,EAAE,UAAU;YACjC,cAAc,EAAE,aAAa;YAC7B,YAAY,EAAE,WAAW,CAAC,YAAY,IAAI,SAAS;SACpD,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,GAAG,CAAC,IAAI,CAAC,+BAA+B,EAAE,mBAAmB,CAAC,CAAC;IAC/D,GAAG,CAAC,IAAI,CAAC,+BAA+B,EAAE,mBAAmB,CAAC,CAAC;AACjE,CAAC","sourcesContent":["import { createHash } from 'node:crypto';\nimport { type RouteContext, notFound, parseJsonBody, WorkOSApiError, generateId } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport {\n formatUser,\n formatDeviceAuthorization,\n verifyPassword,\n isExpired,\n expiresIn,\n assertLocalRedirectUri,\n sealSession,\n} from '../helpers.js';\nimport type { EventBus } from '../event-bus.js';\n\ninterface PendingAuth {\n user_id: string;\n organization_id: string | null;\n auth_method: string;\n}\n\nexport function authRoutes(ctx: RouteContext): void {\n const { app, store, jwt } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/user_management/authorize', (c) => {\n const url = new URL(c.req.url);\n const redirectUri = url.searchParams.get('redirect_uri');\n const state = url.searchParams.get('state');\n const codeChallenge = url.searchParams.get('code_challenge');\n const codeChallengeMethod = url.searchParams.get('code_challenge_method');\n const loginHint = url.searchParams.get('login_hint');\n\n if (!redirectUri) {\n throw new WorkOSApiError(400, 'redirect_uri is required', 'invalid_request');\n }\n assertLocalRedirectUri(redirectUri);\n\n let user;\n if (loginHint) {\n user = ws.users.findOneBy('email', loginHint);\n if (!user) {\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('error', 'user_not_found');\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n }\n } else {\n const users = ws.users.all();\n user = users[0];\n }\n\n if (!user) {\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('error', 'no_users');\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n }\n\n const authCode = ws.authCodes.insert({\n user_id: user.id,\n organization_id: null,\n code: generateId('auth_code'),\n redirect_uri: redirectUri,\n expires_at: expiresIn(10),\n code_challenge: codeChallenge ?? null,\n code_challenge_method: codeChallengeMethod ?? null,\n });\n\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('code', authCode.code);\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n });\n\n // Device authorization endpoint\n app.post('/user_management/authorize/device', async (c) => {\n const body = await parseJsonBody(c);\n const clientId = body.client_id as string;\n if (!clientId) {\n throw new WorkOSApiError(400, 'client_id is required', 'invalid_request');\n }\n\n // Auto-approve with first user for emulator convenience\n const users = ws.users.all();\n const user = users[0] ?? null;\n\n const deviceAuth = ws.deviceAuthorizations.insert({\n device_code: generateId('dev_code'),\n user_code: Math.random().toString(36).slice(2, 10).toUpperCase(),\n user_id: user?.id ?? null,\n client_id: clientId,\n expires_at: expiresIn(15),\n interval: 5,\n });\n\n return c.json(formatDeviceAuthorization(deviceAuth));\n });\n\n // AuthKit SDK uses /x/authkit/users/authenticate for the same flow\n const authenticateHandler = async (c: any) => {\n const body = await parseJsonBody(c);\n const grantType = body.grant_type as string | undefined;\n const clientId = body.client_id as string | undefined;\n const clientSecret = body.client_secret as string | undefined;\n\n if (!grantType) {\n throw new WorkOSApiError(400, 'grant_type is required', 'invalid_request');\n }\n\n let user;\n let organizationId: string | null = null;\n let authMethod: string;\n\n switch (grantType) {\n case 'authorization_code': {\n const code = body.code as string;\n if (!code) throw new WorkOSApiError(400, 'code is required', 'invalid_request');\n\n const authCode = ws.authCodes.all().find((ac) => ac.code === code);\n if (!authCode) throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n if (isExpired(authCode.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n if (authCode.code_challenge) {\n const codeVerifier = body.code_verifier as string;\n if (!codeVerifier) {\n throw new WorkOSApiError(400, 'code_verifier is required', 'invalid_request');\n }\n const method = authCode.code_challenge_method ?? 'S256';\n let challenge: string;\n if (method === 'S256') {\n challenge = createHash('sha256').update(codeVerifier).digest('base64url');\n } else {\n challenge = codeVerifier;\n }\n if (challenge !== authCode.code_challenge) {\n throw new WorkOSApiError(400, 'Invalid code_verifier', 'invalid_code_verifier');\n }\n }\n\n user = ws.users.get(authCode.user_id);\n organizationId = authCode.organization_id;\n ws.authCodes.delete(authCode.id);\n authMethod = 'OAuth';\n break;\n }\n\n case 'password': {\n const email = body.email as string;\n const password = body.password as string;\n if (!email || !password) {\n throw new WorkOSApiError(400, 'email and password are required', 'invalid_request');\n }\n\n user = ws.users.findOneBy('email', email);\n if (!user || !user.password_hash || !verifyPassword(password, user.password_hash)) {\n throw new WorkOSApiError(401, 'Invalid credentials', 'invalid_credentials');\n }\n authMethod = 'Password';\n break;\n }\n\n // Accept both old and new grant type names for magic-auth\n case 'urn:workos:oauth:grant-type:magic-auth':\n case 'urn:workos:oauth:grant-type:magic-auth:code': {\n const code = body.code as string;\n const email = body.email as string;\n if (!code || !email) {\n throw new WorkOSApiError(400, 'code and email are required', 'invalid_request');\n }\n\n const magicAuth = ws.magicAuths.all().find((ma) => ma.code === code && ma.email === email);\n if (!magicAuth) {\n throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n }\n if (isExpired(magicAuth.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n user = ws.users.get(magicAuth.user_id);\n ws.magicAuths.delete(magicAuth.id);\n authMethod = 'MagicAuth';\n break;\n }\n\n // Accept both old and new grant type names for email-verification\n case 'urn:workos:oauth:grant-type:email-verification':\n case 'urn:workos:oauth:grant-type:email-verification:code': {\n const code = body.code as string;\n const userId = body.user_id as string;\n if (!code || !userId) {\n throw new WorkOSApiError(400, 'code and user_id are required', 'invalid_request');\n }\n\n const ev = ws.emailVerifications.findBy('user_id', userId).find((v) => v.code === code);\n if (!ev) {\n throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n }\n if (isExpired(ev.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n ws.users.update(userId, { email_verified: true });\n ws.emailVerifications.delete(ev.id);\n user = ws.users.get(userId);\n authMethod = 'EmailVerification';\n break;\n }\n\n case 'refresh_token': {\n const token = body.refresh_token as string;\n if (!token) {\n throw new WorkOSApiError(400, 'refresh_token is required', 'invalid_request');\n }\n\n const refreshToken = ws.refreshTokens.findOneBy('token', token);\n if (!refreshToken) {\n throw new WorkOSApiError(400, 'Invalid refresh token', 'invalid_grant');\n }\n if (isExpired(refreshToken.expires_at)) {\n ws.refreshTokens.delete(refreshToken.id);\n throw new WorkOSApiError(400, 'Refresh token has expired', 'invalid_grant');\n }\n\n user = ws.users.get(refreshToken.user_id);\n // Allow body.organization_id to switch org context (switchToOrganization)\n organizationId = (body.organization_id as string) ?? refreshToken.organization_id;\n\n // Rotate: delete old, issue new below\n ws.refreshTokens.delete(refreshToken.id);\n authMethod = 'OAuth';\n break;\n }\n\n case 'urn:workos:oauth:grant-type:mfa-totp': {\n const code = body.code as string;\n const pendingToken = body.pending_authentication_token as string;\n const challengeId = body.authentication_challenge_id as string;\n\n if (!code || !pendingToken || !challengeId) {\n throw new WorkOSApiError(\n 400,\n 'code, pending_authentication_token, and authentication_challenge_id are required',\n 'invalid_request',\n );\n }\n\n const pending = store.getData<PendingAuth>(`pending_auth:${pendingToken}`);\n if (!pending) {\n throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');\n }\n\n const challenge = ws.authChallenges.get(challengeId);\n if (!challenge) {\n throw new WorkOSApiError(400, 'Invalid authentication challenge', 'invalid_request');\n }\n if (isExpired(challenge.expires_at)) {\n ws.authChallenges.delete(challenge.id);\n throw new WorkOSApiError(400, 'Challenge has expired', 'expired_challenge');\n }\n\n // Verify code against the challenge's stored code\n if (challenge.code && code !== challenge.code) {\n throw new WorkOSApiError(400, 'Invalid one-time code', 'invalid_one_time_code');\n }\n\n ws.authChallenges.delete(challenge.id);\n store.setData(`pending_auth:${pendingToken}`, undefined);\n\n user = ws.users.get(pending.user_id);\n organizationId = pending.organization_id;\n authMethod = 'MFA';\n break;\n }\n\n case 'urn:workos:oauth:grant-type:organization-selection': {\n const pendingToken = body.pending_authentication_token as string;\n const orgId = body.organization_id as string;\n\n if (!pendingToken || !orgId) {\n throw new WorkOSApiError(\n 400,\n 'pending_authentication_token and organization_id are required',\n 'invalid_request',\n );\n }\n\n const pending = store.getData<PendingAuth>(`pending_auth:${pendingToken}`);\n if (!pending) {\n throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');\n }\n\n const org = ws.organizations.get(orgId);\n if (!org) throw notFound('Organization');\n\n store.setData(`pending_auth:${pendingToken}`, undefined);\n\n user = ws.users.get(pending.user_id);\n organizationId = orgId;\n authMethod = pending.auth_method;\n break;\n }\n\n case 'urn:ietf:params:oauth:grant-type:device_code': {\n const deviceCode = body.device_code as string;\n if (!deviceCode) {\n throw new WorkOSApiError(400, 'device_code is required', 'invalid_request');\n }\n\n const deviceAuth = ws.deviceAuthorizations.findOneBy('device_code', deviceCode);\n if (!deviceAuth) {\n throw new WorkOSApiError(400, 'Invalid device code', 'invalid_grant');\n }\n if (isExpired(deviceAuth.expires_at)) {\n ws.deviceAuthorizations.delete(deviceAuth.id);\n throw new WorkOSApiError(400, 'Device code has expired', 'expired_token');\n }\n if (!deviceAuth.user_id) {\n throw new WorkOSApiError(400, 'Authorization pending', 'authorization_pending');\n }\n\n user = ws.users.get(deviceAuth.user_id);\n ws.deviceAuthorizations.delete(deviceAuth.id);\n authMethod = 'OAuth';\n break;\n }\n\n default:\n throw new WorkOSApiError(400, `Unsupported grant_type: ${grantType}`, 'invalid_request');\n }\n\n if (!user) throw notFound('User');\n\n ws.users.update(user.id, { last_sign_in_at: new Date().toISOString() });\n const updatedUser = ws.users.get(user.id)!;\n\n const session = ws.sessions.insert({\n object: 'session',\n user_id: user.id,\n organization_id: organizationId,\n ip_address: c.req.header('x-forwarded-for') ?? null,\n user_agent: c.req.header('user-agent') ?? null,\n });\n\n // Resolve role + permissions for org-scoped sessions\n let roleSlug: string | undefined;\n let permissionSlugs: string[] | undefined;\n if (organizationId) {\n const membership = ws.organizationMemberships\n .findBy('organization_id', organizationId)\n .find((m) => m.user_id === user.id);\n if (membership) {\n roleSlug = membership.role.slug;\n const role = ws.roles\n .findBy('slug', membership.role.slug)\n .find((r) => r.organization_id === organizationId || r.type === 'EnvironmentRole');\n if (role) {\n const rps = ws.rolePermissions.findBy('role_id', role.id);\n permissionSlugs = rps\n .map((rp) => ws.permissions.get(rp.permission_id))\n .filter(Boolean)\n .map((p) => p!.slug);\n }\n }\n }\n\n const accessToken = jwt.sign({\n sub: user.id,\n sid: session.id,\n org_id: organizationId ?? undefined,\n role: roleSlug,\n permissions: permissionSlugs,\n aud: clientId ?? 'workos-emulate',\n });\n\n // Store a real refresh token\n const newRefreshToken = ws.refreshTokens.insert({\n token: generateId('ref'),\n user_id: user.id,\n organization_id: organizationId,\n session_id: session.id,\n expires_at: expiresIn(30 * 24 * 60), // 30 days\n });\n\n // Compute sealed session when client_secret is provided\n const apiKey = c.req\n .header('Authorization')\n ?.replace(/^Bearer\\s+/i, '')\n .trim();\n const sealKey = clientSecret ?? apiKey;\n const sealedSession = sealKey\n ? sealSession(\n { access_token: accessToken, refresh_token: newRefreshToken.token, session_id: session.id },\n sealKey,\n )\n : null;\n\n // Emit authentication event (hybrid Option B for action-specific events)\n const eventBus = store.getData<EventBus>('eventBus');\n if (eventBus) {\n const authEventType = `authentication.${authMethod.toLowerCase()}_succeeded`;\n eventBus.emit({\n event: authEventType,\n data: { user_id: user.id, email: updatedUser.email, method: authMethod, ip_address: session.ip_address },\n });\n }\n\n return c.json({\n user: formatUser(updatedUser),\n organization_id: organizationId,\n access_token: accessToken,\n refresh_token: newRefreshToken.token,\n authentication_method: authMethod,\n sealed_session: sealedSession,\n impersonator: updatedUser.impersonator ?? undefined,\n });\n };\n\n app.post('/user_management/authenticate', authenticateHandler);\n app.post('/x/authkit/users/authenticate', authenticateHandler);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/auth.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport { type RouteContext, notFound, parseJsonBody, WorkOSApiError, generateId } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport {\n formatUser,\n formatDeviceAuthorization,\n verifyPassword,\n isExpired,\n expiresIn,\n assertLocalRedirectUri,\n sealSession,\n} from '../helpers.js';\nimport type { EventBus } from '../event-bus.js';\nimport { STORE_KEYS, STORE_KEY_PREFIXES } from '../constants.js';\n\ninterface PendingAuth {\n user_id: string;\n organization_id: string | null;\n auth_method: string;\n}\n\nexport function authRoutes(ctx: RouteContext): void {\n const { app, store, jwt } = ctx;\n const ws = getWorkOSStore(store);\n\n app.get('/user_management/authorize', (c) => {\n const url = new URL(c.req.url);\n const redirectUri = url.searchParams.get('redirect_uri');\n const state = url.searchParams.get('state');\n const codeChallenge = url.searchParams.get('code_challenge');\n const codeChallengeMethod = url.searchParams.get('code_challenge_method');\n const loginHint = url.searchParams.get('login_hint');\n\n if (!redirectUri) {\n throw new WorkOSApiError(400, 'redirect_uri is required', 'invalid_request');\n }\n assertLocalRedirectUri(redirectUri);\n\n let user;\n if (loginHint) {\n user = ws.users.findOneBy('email', loginHint);\n if (!user) {\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('error', 'user_not_found');\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n }\n } else {\n const users = ws.users.all();\n user = users[0];\n }\n\n if (!user) {\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('error', 'no_users');\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n }\n\n const authCode = ws.authCodes.insert({\n user_id: user.id,\n organization_id: null,\n code: generateId('auth_code'),\n redirect_uri: redirectUri,\n expires_at: expiresIn(10),\n code_challenge: codeChallenge ?? null,\n code_challenge_method: codeChallengeMethod ?? null,\n });\n\n const redirect = new URL(redirectUri);\n redirect.searchParams.set('code', authCode.code);\n if (state) redirect.searchParams.set('state', state);\n return c.redirect(redirect.toString());\n });\n\n // Device authorization endpoint\n app.post('/user_management/authorize/device', async (c) => {\n const body = await parseJsonBody(c);\n const clientId = body.client_id as string;\n if (!clientId) {\n throw new WorkOSApiError(400, 'client_id is required', 'invalid_request');\n }\n\n // Auto-approve with first user for emulator convenience\n const users = ws.users.all();\n const user = users[0] ?? null;\n\n const deviceAuth = ws.deviceAuthorizations.insert({\n device_code: generateId('dev_code'),\n user_code: Math.random().toString(36).slice(2, 10).toUpperCase(),\n user_id: user?.id ?? null,\n client_id: clientId,\n expires_at: expiresIn(15),\n interval: 5,\n });\n\n return c.json(formatDeviceAuthorization(deviceAuth));\n });\n\n // AuthKit SDK uses /x/authkit/users/authenticate for the same flow\n const authenticateHandler = async (c: any) => {\n const body = await parseJsonBody(c);\n const grantType = body.grant_type as string | undefined;\n const clientId = body.client_id as string | undefined;\n const clientSecret = body.client_secret as string | undefined;\n\n if (!grantType) {\n throw new WorkOSApiError(400, 'grant_type is required', 'invalid_request');\n }\n\n let user;\n let organizationId: string | null = null;\n let authMethod: string;\n\n switch (grantType) {\n case 'authorization_code': {\n const code = body.code as string;\n if (!code) throw new WorkOSApiError(400, 'code is required', 'invalid_request');\n\n const authCode = ws.authCodes.findOneBy('code', code);\n if (!authCode) throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n if (isExpired(authCode.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n if (authCode.code_challenge) {\n const codeVerifier = body.code_verifier as string;\n if (!codeVerifier) {\n throw new WorkOSApiError(400, 'code_verifier is required', 'invalid_request');\n }\n const method = authCode.code_challenge_method ?? 'S256';\n let challenge: string;\n if (method === 'S256') {\n challenge = createHash('sha256').update(codeVerifier).digest('base64url');\n } else {\n challenge = codeVerifier;\n }\n if (challenge !== authCode.code_challenge) {\n throw new WorkOSApiError(400, 'Invalid code_verifier', 'invalid_code_verifier');\n }\n }\n\n user = ws.users.get(authCode.user_id);\n organizationId = authCode.organization_id;\n ws.authCodes.delete(authCode.id);\n authMethod = 'OAuth';\n break;\n }\n\n case 'password': {\n const email = body.email as string;\n const password = body.password as string;\n if (!email || !password) {\n throw new WorkOSApiError(400, 'email and password are required', 'invalid_request');\n }\n\n user = ws.users.findOneBy('email', email);\n if (!user || !user.password_hash || !verifyPassword(password, user.password_hash)) {\n throw new WorkOSApiError(401, 'Invalid credentials', 'invalid_credentials');\n }\n authMethod = 'Password';\n break;\n }\n\n // Accept both old and new grant type names for magic-auth\n case 'urn:workos:oauth:grant-type:magic-auth':\n case 'urn:workos:oauth:grant-type:magic-auth:code': {\n const code = body.code as string;\n const email = body.email as string;\n if (!code || !email) {\n throw new WorkOSApiError(400, 'code and email are required', 'invalid_request');\n }\n\n const magicAuth = ws.magicAuths.all().find((ma) => ma.code === code && ma.email === email);\n if (!magicAuth) {\n throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n }\n if (isExpired(magicAuth.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n user = ws.users.get(magicAuth.user_id);\n ws.magicAuths.delete(magicAuth.id);\n authMethod = 'MagicAuth';\n break;\n }\n\n // Accept both old and new grant type names for email-verification\n case 'urn:workos:oauth:grant-type:email-verification':\n case 'urn:workos:oauth:grant-type:email-verification:code': {\n const code = body.code as string;\n const userId = body.user_id as string;\n if (!code || !userId) {\n throw new WorkOSApiError(400, 'code and user_id are required', 'invalid_request');\n }\n\n const ev = ws.emailVerifications.findBy('user_id', userId).find((v) => v.code === code);\n if (!ev) {\n throw new WorkOSApiError(400, 'Invalid code', 'invalid_code');\n }\n if (isExpired(ev.expires_at)) {\n throw new WorkOSApiError(400, 'Code has expired', 'expired_code');\n }\n\n ws.users.update(userId, { email_verified: true });\n ws.emailVerifications.delete(ev.id);\n user = ws.users.get(userId);\n authMethod = 'EmailVerification';\n break;\n }\n\n case 'refresh_token': {\n const token = body.refresh_token as string;\n if (!token) {\n throw new WorkOSApiError(400, 'refresh_token is required', 'invalid_request');\n }\n\n const refreshToken = ws.refreshTokens.findOneBy('token', token);\n if (!refreshToken) {\n throw new WorkOSApiError(400, 'Invalid refresh token', 'invalid_grant');\n }\n if (isExpired(refreshToken.expires_at)) {\n ws.refreshTokens.delete(refreshToken.id);\n throw new WorkOSApiError(400, 'Refresh token has expired', 'invalid_grant');\n }\n\n user = ws.users.get(refreshToken.user_id);\n // Allow body.organization_id to switch org context (switchToOrganization)\n organizationId = (body.organization_id as string) ?? refreshToken.organization_id;\n\n // Rotate: delete old, issue new below\n ws.refreshTokens.delete(refreshToken.id);\n authMethod = 'OAuth';\n break;\n }\n\n case 'urn:workos:oauth:grant-type:mfa-totp': {\n const code = body.code as string;\n const pendingToken = body.pending_authentication_token as string;\n const challengeId = body.authentication_challenge_id as string;\n\n if (!code || !pendingToken || !challengeId) {\n throw new WorkOSApiError(\n 400,\n 'code, pending_authentication_token, and authentication_challenge_id are required',\n 'invalid_request',\n );\n }\n\n const pending = store.getData<PendingAuth>(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`);\n if (!pending) {\n throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');\n }\n\n const challenge = ws.authChallenges.get(challengeId);\n if (!challenge) {\n throw new WorkOSApiError(400, 'Invalid authentication challenge', 'invalid_request');\n }\n if (isExpired(challenge.expires_at)) {\n ws.authChallenges.delete(challenge.id);\n throw new WorkOSApiError(400, 'Challenge has expired', 'expired_challenge');\n }\n\n // Verify code against the challenge's stored code\n if (challenge.code && code !== challenge.code) {\n throw new WorkOSApiError(400, 'Invalid one-time code', 'invalid_one_time_code');\n }\n\n ws.authChallenges.delete(challenge.id);\n store.setData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`, undefined);\n\n user = ws.users.get(pending.user_id);\n organizationId = pending.organization_id;\n authMethod = 'MFA';\n break;\n }\n\n case 'urn:workos:oauth:grant-type:organization-selection': {\n const pendingToken = body.pending_authentication_token as string;\n const orgId = body.organization_id as string;\n\n if (!pendingToken || !orgId) {\n throw new WorkOSApiError(\n 400,\n 'pending_authentication_token and organization_id are required',\n 'invalid_request',\n );\n }\n\n const pending = store.getData<PendingAuth>(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`);\n if (!pending) {\n throw new WorkOSApiError(400, 'Invalid pending authentication token', 'invalid_pending_authentication_token');\n }\n\n const org = ws.organizations.get(orgId);\n if (!org) throw notFound('Organization');\n\n store.setData(`${STORE_KEY_PREFIXES.pendingAuth}${pendingToken}`, undefined);\n\n user = ws.users.get(pending.user_id);\n organizationId = orgId;\n authMethod = pending.auth_method;\n break;\n }\n\n case 'urn:ietf:params:oauth:grant-type:device_code': {\n const deviceCode = body.device_code as string;\n if (!deviceCode) {\n throw new WorkOSApiError(400, 'device_code is required', 'invalid_request');\n }\n\n const deviceAuth = ws.deviceAuthorizations.findOneBy('device_code', deviceCode);\n if (!deviceAuth) {\n throw new WorkOSApiError(400, 'Invalid device code', 'invalid_grant');\n }\n if (isExpired(deviceAuth.expires_at)) {\n ws.deviceAuthorizations.delete(deviceAuth.id);\n throw new WorkOSApiError(400, 'Device code has expired', 'expired_token');\n }\n if (!deviceAuth.user_id) {\n throw new WorkOSApiError(400, 'Authorization pending', 'authorization_pending');\n }\n\n user = ws.users.get(deviceAuth.user_id);\n ws.deviceAuthorizations.delete(deviceAuth.id);\n authMethod = 'OAuth';\n break;\n }\n\n default:\n throw new WorkOSApiError(400, `Unsupported grant_type: ${grantType}`, 'invalid_request');\n }\n\n if (!user) throw notFound('User');\n\n ws.users.update(user.id, { last_sign_in_at: new Date().toISOString() });\n const updatedUser = ws.users.get(user.id)!;\n\n const session = ws.sessions.insert({\n object: 'session',\n user_id: user.id,\n organization_id: organizationId,\n ip_address: c.req.header('x-forwarded-for') ?? null,\n user_agent: c.req.header('user-agent') ?? null,\n });\n\n // Resolve role + permissions for org-scoped sessions\n let roleSlug: string | undefined;\n let permissionSlugs: string[] | undefined;\n if (organizationId) {\n const membership = ws.organizationMemberships\n .findBy('organization_id', organizationId)\n .find((m) => m.user_id === user.id);\n if (membership) {\n roleSlug = membership.role.slug;\n const role = ws.roles\n .findBy('slug', membership.role.slug)\n .find((r) => r.organization_id === organizationId || r.type === 'EnvironmentRole');\n if (role) {\n const rps = ws.rolePermissions.findBy('role_id', role.id);\n permissionSlugs = rps\n .map((rp) => ws.permissions.get(rp.permission_id))\n .filter(Boolean)\n .map((p) => p!.slug);\n }\n }\n }\n\n const accessToken = jwt.sign({\n sub: user.id,\n sid: session.id,\n org_id: organizationId ?? undefined,\n role: roleSlug,\n permissions: permissionSlugs,\n aud: clientId ?? 'workos-emulate',\n });\n\n // Store a real refresh token\n const newRefreshToken = ws.refreshTokens.insert({\n token: generateId('ref'),\n user_id: user.id,\n organization_id: organizationId,\n session_id: session.id,\n expires_at: expiresIn(30 * 24 * 60), // 30 days\n });\n\n // Compute sealed session when client_secret is provided\n const apiKey = c.req\n .header('Authorization')\n ?.replace(/^Bearer\\s+/i, '')\n .trim();\n const sealKey = clientSecret ?? apiKey;\n const sealedSession = sealKey\n ? sealSession(\n { access_token: accessToken, refresh_token: newRefreshToken.token, session_id: session.id },\n sealKey,\n )\n : null;\n\n // Emit authentication event (hybrid Option B for action-specific events)\n const eventBus = store.getData<EventBus>(STORE_KEYS.eventBus);\n if (eventBus) {\n const authEventType = `authentication.${authMethod.toLowerCase()}_succeeded`;\n eventBus.emit({\n event: authEventType,\n data: { user_id: user.id, email: updatedUser.email, method: authMethod, ip_address: session.ip_address },\n });\n }\n\n return c.json({\n user: formatUser(updatedUser),\n organization_id: organizationId,\n access_token: accessToken,\n refresh_token: newRefreshToken.token,\n authentication_method: authMethod,\n sealed_session: sealedSession,\n impersonator: updatedUser.impersonator ?? undefined,\n });\n };\n\n app.post('/user_management/authenticate', authenticateHandler);\n app.post('/x/authkit/users/authenticate', authenticateHandler);\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { notFound, validationError, parseJsonBody } from '../../core/index.js';
|
|
1
|
+
import { notFound, validationError, parseJsonBody, parseListParams } from '../../core/index.js';
|
|
2
2
|
import { getWorkOSStore } from '../store.js';
|
|
3
|
-
import { formatRoleAssignment, formatAuthorizationResource,
|
|
3
|
+
import { formatRoleAssignment, formatAuthorizationResource, formatListResponse } from '../helpers.js';
|
|
4
4
|
/**
|
|
5
5
|
* Gather all permission slugs for a given membership:
|
|
6
6
|
* 1. From the membership's role (role.slug field)
|
|
@@ -40,9 +40,9 @@ function getPermissionsForMembership(ws, membershipId) {
|
|
|
40
40
|
}
|
|
41
41
|
export function authorizationCheckRoutes(ctx) {
|
|
42
42
|
const { app, store } = ctx;
|
|
43
|
+
const ws = getWorkOSStore(store);
|
|
43
44
|
// Permission check
|
|
44
45
|
app.post('/authorization/organization_memberships/:id/check', async (c) => {
|
|
45
|
-
const ws = getWorkOSStore(store);
|
|
46
46
|
const membershipId = c.req.param('id');
|
|
47
47
|
const membership = ws.organizationMemberships.get(membershipId);
|
|
48
48
|
if (!membership)
|
|
@@ -57,7 +57,6 @@ export function authorizationCheckRoutes(ctx) {
|
|
|
57
57
|
});
|
|
58
58
|
// List resources accessible to a membership (all resources in the membership's org)
|
|
59
59
|
app.get('/authorization/organization_memberships/:id/resources', (c) => {
|
|
60
|
-
const ws = getWorkOSStore(store);
|
|
61
60
|
const membershipId = c.req.param('id');
|
|
62
61
|
const membership = ws.organizationMemberships.get(membershipId);
|
|
63
62
|
if (!membership)
|
|
@@ -68,15 +67,10 @@ export function authorizationCheckRoutes(ctx) {
|
|
|
68
67
|
...params,
|
|
69
68
|
filter: (r) => r.organization_id === membership.organization_id,
|
|
70
69
|
});
|
|
71
|
-
return c.json(
|
|
72
|
-
object: 'list',
|
|
73
|
-
data: result.data.map(formatAuthorizationResource),
|
|
74
|
-
list_metadata: result.list_metadata,
|
|
75
|
-
});
|
|
70
|
+
return c.json(formatListResponse(result, formatAuthorizationResource));
|
|
76
71
|
});
|
|
77
72
|
// List role assignments for a membership
|
|
78
73
|
app.get('/authorization/organization_memberships/:id/role_assignments', (c) => {
|
|
79
|
-
const ws = getWorkOSStore(store);
|
|
80
74
|
const membershipId = c.req.param('id');
|
|
81
75
|
const membership = ws.organizationMemberships.get(membershipId);
|
|
82
76
|
if (!membership)
|
|
@@ -87,15 +81,10 @@ export function authorizationCheckRoutes(ctx) {
|
|
|
87
81
|
...params,
|
|
88
82
|
filter: (ra) => ra.organization_membership_id === membershipId,
|
|
89
83
|
});
|
|
90
|
-
return c.json(
|
|
91
|
-
object: 'list',
|
|
92
|
-
data: result.data.map(formatRoleAssignment),
|
|
93
|
-
list_metadata: result.list_metadata,
|
|
94
|
-
});
|
|
84
|
+
return c.json(formatListResponse(result, formatRoleAssignment));
|
|
95
85
|
});
|
|
96
86
|
// Create role assignment
|
|
97
87
|
app.post('/authorization/organization_memberships/:id/role_assignments', async (c) => {
|
|
98
|
-
const ws = getWorkOSStore(store);
|
|
99
88
|
const membershipId = c.req.param('id');
|
|
100
89
|
const membership = ws.organizationMemberships.get(membershipId);
|
|
101
90
|
if (!membership)
|
|
@@ -117,7 +106,6 @@ export function authorizationCheckRoutes(ctx) {
|
|
|
117
106
|
});
|
|
118
107
|
// Delete role assignment
|
|
119
108
|
app.delete('/authorization/organization_memberships/:id/role_assignments/:assignmentId', (c) => {
|
|
120
|
-
const ws = getWorkOSStore(store);
|
|
121
109
|
const membershipId = c.req.param('id');
|
|
122
110
|
const assignmentId = c.req.param('assignmentId');
|
|
123
111
|
const membership = ws.organizationMemberships.get(membershipId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorization-checks.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/authorization-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, validationError, parseJsonBody } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatRoleAssignment, formatAuthorizationResource, parseListParams } from '../helpers.js';\n\n/**\n * Gather all permission slugs for a given membership:\n * 1. From the membership's role (role.slug field)\n * 2. From any additional role assignments\n */\nfunction getPermissionsForMembership(ws: ReturnType<typeof getWorkOSStore>, membershipId: string): Set<string> {\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) return new Set();\n\n const permSlugs = new Set<string>();\n\n // Permissions from the membership's primary role\n const primaryRole = ws.roles\n .findBy('slug', membership.role.slug)\n .find((r) => r.organization_id === membership.organization_id || r.type === 'EnvironmentRole');\n if (primaryRole) {\n const rps = ws.rolePermissions.findBy('role_id', primaryRole.id);\n for (const rp of rps) {\n const perm = ws.permissions.get(rp.permission_id);\n if (perm) permSlugs.add(perm.slug);\n }\n }\n\n // Permissions from additional role assignments\n const assignments = ws.roleAssignments.findBy('organization_membership_id', membershipId);\n for (const assignment of assignments) {\n const role = ws.roles.get(assignment.role_id);\n if (!role) continue;\n const rps = ws.rolePermissions.findBy('role_id', role.id);\n for (const rp of rps) {\n const perm = ws.permissions.get(rp.permission_id);\n if (perm) permSlugs.add(perm.slug);\n }\n }\n\n return permSlugs;\n}\n\nexport function authorizationCheckRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n\n // Permission check\n app.post('/authorization/organization_memberships/:id/check', async (c) => {\n const ws = getWorkOSStore(store);\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const body = await parseJsonBody(c);\n const permission = body.permission as string;\n if (!permission) {\n throw validationError('permission is required', [{ field: 'permission', code: 'required' }]);\n }\n\n const permSlugs = getPermissionsForMembership(ws, membershipId);\n return c.json({ authorized: permSlugs.has(permission) });\n });\n\n // List resources accessible to a membership (all resources in the membership's org)\n app.get('/authorization/organization_memberships/:id/resources', (c) => {\n const ws = getWorkOSStore(store);\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n\n const result = ws.authorizationResources.list({\n ...params,\n filter: (r) => r.organization_id === membership.organization_id,\n });\n\n return c.json({\n object: 'list',\n data: result.data.map(formatAuthorizationResource),\n list_metadata: result.list_metadata,\n });\n });\n\n // List role assignments for a membership\n app.get('/authorization/organization_memberships/:id/role_assignments', (c) => {\n const ws = getWorkOSStore(store);\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n\n const result = ws.roleAssignments.list({\n ...params,\n filter: (ra) => ra.organization_membership_id === membershipId,\n });\n\n return c.json({\n object: 'list',\n data: result.data.map(formatRoleAssignment),\n list_metadata: result.list_metadata,\n });\n });\n\n // Create role assignment\n app.post('/authorization/organization_memberships/:id/role_assignments', async (c) => {\n const ws = getWorkOSStore(store);\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const body = await parseJsonBody(c);\n const roleId = body.role_id as string;\n if (!roleId) {\n throw validationError('role_id is required', [{ field: 'role_id', code: 'required' }]);\n }\n\n const role = ws.roles.get(roleId);\n if (!role) throw notFound('Role');\n\n const assignment = ws.roleAssignments.insert({\n object: 'role_assignment',\n organization_membership_id: membershipId,\n role_id: roleId,\n });\n\n return c.json(formatRoleAssignment(assignment), 201);\n });\n\n // Delete role assignment\n app.delete('/authorization/organization_memberships/:id/role_assignments/:assignmentId', (c) => {\n const ws = getWorkOSStore(store);\n const membershipId = c.req.param('id');\n const assignmentId = c.req.param('assignmentId');\n\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const assignment = ws.roleAssignments.get(assignmentId);\n if (!assignment || assignment.organization_membership_id !== membershipId) {\n throw notFound('RoleAssignment');\n }\n\n ws.roleAssignments.delete(assignmentId);\n return c.body(null, 204);\n });\n}\n\nexport { getPermissionsForMembership };\n"]}
|
|
1
|
+
{"version":3,"file":"authorization-checks.js","sourceRoot":"","sources":["../../../../src/emulate/workos/routes/authorization-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { type RouteContext, notFound, validationError, parseJsonBody, parseListParams } from '../../core/index.js';\nimport { getWorkOSStore } from '../store.js';\nimport { formatRoleAssignment, formatAuthorizationResource, formatListResponse } from '../helpers.js';\n\n/**\n * Gather all permission slugs for a given membership:\n * 1. From the membership's role (role.slug field)\n * 2. From any additional role assignments\n */\nfunction getPermissionsForMembership(ws: ReturnType<typeof getWorkOSStore>, membershipId: string): Set<string> {\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) return new Set();\n\n const permSlugs = new Set<string>();\n\n // Permissions from the membership's primary role\n const primaryRole = ws.roles\n .findBy('slug', membership.role.slug)\n .find((r) => r.organization_id === membership.organization_id || r.type === 'EnvironmentRole');\n if (primaryRole) {\n const rps = ws.rolePermissions.findBy('role_id', primaryRole.id);\n for (const rp of rps) {\n const perm = ws.permissions.get(rp.permission_id);\n if (perm) permSlugs.add(perm.slug);\n }\n }\n\n // Permissions from additional role assignments\n const assignments = ws.roleAssignments.findBy('organization_membership_id', membershipId);\n for (const assignment of assignments) {\n const role = ws.roles.get(assignment.role_id);\n if (!role) continue;\n const rps = ws.rolePermissions.findBy('role_id', role.id);\n for (const rp of rps) {\n const perm = ws.permissions.get(rp.permission_id);\n if (perm) permSlugs.add(perm.slug);\n }\n }\n\n return permSlugs;\n}\n\nexport function authorizationCheckRoutes(ctx: RouteContext): void {\n const { app, store } = ctx;\n const ws = getWorkOSStore(store);\n\n // Permission check\n app.post('/authorization/organization_memberships/:id/check', async (c) => {\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const body = await parseJsonBody(c);\n const permission = body.permission as string;\n if (!permission) {\n throw validationError('permission is required', [{ field: 'permission', code: 'required' }]);\n }\n\n const permSlugs = getPermissionsForMembership(ws, membershipId);\n return c.json({ authorized: permSlugs.has(permission) });\n });\n\n // List resources accessible to a membership (all resources in the membership's org)\n app.get('/authorization/organization_memberships/:id/resources', (c) => {\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n\n const result = ws.authorizationResources.list({\n ...params,\n filter: (r) => r.organization_id === membership.organization_id,\n });\n\n return c.json(formatListResponse(result, formatAuthorizationResource));\n });\n\n // List role assignments for a membership\n app.get('/authorization/organization_memberships/:id/role_assignments', (c) => {\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const url = new URL(c.req.url);\n const params = parseListParams(url);\n\n const result = ws.roleAssignments.list({\n ...params,\n filter: (ra) => ra.organization_membership_id === membershipId,\n });\n\n return c.json(formatListResponse(result, formatRoleAssignment));\n });\n\n // Create role assignment\n app.post('/authorization/organization_memberships/:id/role_assignments', async (c) => {\n const membershipId = c.req.param('id');\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const body = await parseJsonBody(c);\n const roleId = body.role_id as string;\n if (!roleId) {\n throw validationError('role_id is required', [{ field: 'role_id', code: 'required' }]);\n }\n\n const role = ws.roles.get(roleId);\n if (!role) throw notFound('Role');\n\n const assignment = ws.roleAssignments.insert({\n object: 'role_assignment',\n organization_membership_id: membershipId,\n role_id: roleId,\n });\n\n return c.json(formatRoleAssignment(assignment), 201);\n });\n\n // Delete role assignment\n app.delete('/authorization/organization_memberships/:id/role_assignments/:assignmentId', (c) => {\n const membershipId = c.req.param('id');\n const assignmentId = c.req.param('assignmentId');\n\n const membership = ws.organizationMemberships.get(membershipId);\n if (!membership) throw notFound('OrganizationMembership');\n\n const assignment = ws.roleAssignments.get(assignmentId);\n if (!assignment || assignment.organization_membership_id !== membershipId) {\n throw notFound('RoleAssignment');\n }\n\n ws.roleAssignments.delete(assignmentId);\n return c.body(null, 204);\n });\n}\n\nexport { getPermissionsForMembership };\n"]}
|
|
@@ -1,199 +1,57 @@
|
|
|
1
1
|
import { notFound, validationError, parseJsonBody } from '../../core/index.js';
|
|
2
2
|
import { getWorkOSStore } from '../store.js';
|
|
3
|
-
import { formatRole
|
|
3
|
+
import { formatRole } from '../helpers.js';
|
|
4
|
+
import { findOrgRole, requireOrgRole, registerRoleRoutes } from '../role-helpers.js';
|
|
4
5
|
export function authorizationOrgRoleRoutes(ctx) {
|
|
5
6
|
const { app, store } = ctx;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const orgId = c.req.param('orgId');
|
|
9
|
-
const org = ws.organizations.get(orgId);
|
|
10
|
-
if (!org)
|
|
11
|
-
throw notFound('Organization');
|
|
12
|
-
const body = await parseJsonBody(c);
|
|
13
|
-
const slug = body.slug;
|
|
14
|
-
const name = body.name;
|
|
15
|
-
if (!slug || typeof slug !== 'string') {
|
|
16
|
-
throw validationError('slug is required', [{ field: 'slug', code: 'required' }]);
|
|
17
|
-
}
|
|
18
|
-
if (!name || typeof name !== 'string') {
|
|
19
|
-
throw validationError('name is required', [{ field: 'name', code: 'required' }]);
|
|
20
|
-
}
|
|
21
|
-
// Check uniqueness within this org
|
|
22
|
-
const existing = ws.roles
|
|
23
|
-
.findBy('organization_id', orgId)
|
|
24
|
-
.find((r) => r.slug === slug && r.type === 'OrganizationRole');
|
|
25
|
-
if (existing) {
|
|
26
|
-
throw validationError('Role with this slug already exists in this organization', [
|
|
27
|
-
{ field: 'slug', code: 'duplicate' },
|
|
28
|
-
]);
|
|
29
|
-
}
|
|
30
|
-
const role = ws.roles.insert({
|
|
31
|
-
object: 'role',
|
|
32
|
-
slug,
|
|
33
|
-
name,
|
|
34
|
-
description: body.description ?? null,
|
|
35
|
-
type: 'OrganizationRole',
|
|
36
|
-
organization_id: orgId,
|
|
37
|
-
is_default_role: Boolean(body.is_default_role),
|
|
38
|
-
priority: typeof body.priority === 'number' ? body.priority : 0,
|
|
39
|
-
});
|
|
40
|
-
return c.json(formatRole(role), 201);
|
|
41
|
-
});
|
|
42
|
-
app.get('/authorization/organizations/:orgId/roles', (c) => {
|
|
43
|
-
const ws = getWorkOSStore(store);
|
|
44
|
-
const orgId = c.req.param('orgId');
|
|
45
|
-
const url = new URL(c.req.url);
|
|
46
|
-
const params = parseListParams(url);
|
|
47
|
-
const result = ws.roles.list({
|
|
48
|
-
...params,
|
|
49
|
-
filter: (r) => r.organization_id === orgId && r.type === 'OrganizationRole',
|
|
50
|
-
});
|
|
51
|
-
return c.json({
|
|
52
|
-
object: 'list',
|
|
53
|
-
data: result.data.map(formatRole),
|
|
54
|
-
list_metadata: result.list_metadata,
|
|
55
|
-
});
|
|
56
|
-
});
|
|
7
|
+
const ws = getWorkOSStore(store);
|
|
8
|
+
const prefix = '/authorization/organizations/:orgId/roles';
|
|
57
9
|
// Priority ordering — must be registered before :slug routes
|
|
58
|
-
app.put(
|
|
59
|
-
const ws = getWorkOSStore(store);
|
|
10
|
+
app.put(`${prefix}/priority`, async (c) => {
|
|
60
11
|
const orgId = c.req.param('orgId');
|
|
61
12
|
const body = await parseJsonBody(c);
|
|
62
13
|
const slugs = body.slugs;
|
|
63
14
|
if (!Array.isArray(slugs)) {
|
|
64
15
|
throw validationError('slugs must be an array', [{ field: 'slugs', code: 'invalid' }]);
|
|
65
16
|
}
|
|
17
|
+
// Fetch once, build slug map for O(1) lookups
|
|
18
|
+
const orgRoles = ws.roles.findBy('organization_id', orgId).filter((r) => r.type === 'OrganizationRole');
|
|
19
|
+
const rolesBySlug = new Map(orgRoles.map((r) => [r.slug, r]));
|
|
66
20
|
for (let i = 0; i < slugs.length; i++) {
|
|
67
|
-
const role =
|
|
68
|
-
.findBy('organization_id', orgId)
|
|
69
|
-
.find((r) => r.slug === slugs[i] && r.type === 'OrganizationRole');
|
|
21
|
+
const role = rolesBySlug.get(slugs[i]);
|
|
70
22
|
if (!role)
|
|
71
23
|
throw notFound('Role');
|
|
72
24
|
ws.roles.update(role.id, { priority: i });
|
|
73
25
|
}
|
|
74
|
-
|
|
26
|
+
// Re-fetch for updated priority values
|
|
27
|
+
const updated = ws.roles
|
|
75
28
|
.findBy('organization_id', orgId)
|
|
76
29
|
.filter((r) => r.type === 'OrganizationRole')
|
|
77
30
|
.sort((a, b) => a.priority - b.priority);
|
|
78
31
|
return c.json({
|
|
79
32
|
object: 'list',
|
|
80
|
-
data:
|
|
33
|
+
data: updated.map(formatRole),
|
|
81
34
|
list_metadata: { before: null, after: null },
|
|
82
35
|
});
|
|
83
36
|
});
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
37
|
+
registerRoleRoutes(ctx, {
|
|
38
|
+
pathPrefix: prefix,
|
|
39
|
+
roleType: 'OrganizationRole',
|
|
40
|
+
requireRole: (ws, c) => requireOrgRole(ws, c.req.param('orgId'), c.req.param('slug')),
|
|
41
|
+
findRole: (ws, c, slug) => findOrgRole(ws, c.req.param('orgId'), slug),
|
|
42
|
+
listFilter: (c) => (r) => r.organization_id === c.req.param('orgId') && r.type === 'OrganizationRole',
|
|
43
|
+
insertDefaults: (c) => ({ organization_id: c.req.param('orgId') }),
|
|
44
|
+
duplicateMessage: 'Role with this slug already exists in this organization',
|
|
45
|
+
validateBeforeCreate: (ws, c) => {
|
|
46
|
+
const org = ws.organizations.get(c.req.param('orgId'));
|
|
47
|
+
if (!org)
|
|
48
|
+
throw notFound('Organization');
|
|
49
|
+
},
|
|
94
50
|
});
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
const role = ws.roles
|
|
100
|
-
.findBy('organization_id', orgId)
|
|
101
|
-
.find((r) => r.slug === slug && r.type === 'OrganizationRole');
|
|
102
|
-
if (!role)
|
|
103
|
-
throw notFound('Role');
|
|
104
|
-
const body = await parseJsonBody(c);
|
|
105
|
-
const updates = {};
|
|
106
|
-
if ('name' in body)
|
|
107
|
-
updates.name = body.name;
|
|
108
|
-
if ('description' in body)
|
|
109
|
-
updates.description = body.description ?? null;
|
|
110
|
-
if ('is_default_role' in body)
|
|
111
|
-
updates.is_default_role = Boolean(body.is_default_role);
|
|
112
|
-
if ('priority' in body)
|
|
113
|
-
updates.priority = body.priority;
|
|
114
|
-
const updated = ws.roles.update(role.id, updates);
|
|
115
|
-
return c.json(formatRole(updated));
|
|
116
|
-
});
|
|
117
|
-
app.delete('/authorization/organizations/:orgId/roles/:slug', (c) => {
|
|
118
|
-
const ws = getWorkOSStore(store);
|
|
119
|
-
const orgId = c.req.param('orgId');
|
|
120
|
-
const slug = c.req.param('slug');
|
|
121
|
-
const role = ws.roles
|
|
122
|
-
.findBy('organization_id', orgId)
|
|
123
|
-
.find((r) => r.slug === slug && r.type === 'OrganizationRole');
|
|
124
|
-
if (!role)
|
|
125
|
-
throw notFound('Role');
|
|
126
|
-
// Cascade: remove role-permission joins and role assignments
|
|
127
|
-
const rps = ws.rolePermissions.findBy('role_id', role.id);
|
|
128
|
-
for (const rp of rps)
|
|
129
|
-
ws.rolePermissions.delete(rp.id);
|
|
130
|
-
const ras = ws.roleAssignments.findBy('role_id', role.id);
|
|
131
|
-
for (const ra of ras)
|
|
132
|
-
ws.roleAssignments.delete(ra.id);
|
|
133
|
-
ws.roles.delete(role.id);
|
|
134
|
-
return c.body(null, 204);
|
|
135
|
-
});
|
|
136
|
-
// Org role permissions
|
|
137
|
-
app.get('/authorization/organizations/:orgId/roles/:slug/permissions', (c) => {
|
|
138
|
-
const ws = getWorkOSStore(store);
|
|
139
|
-
const orgId = c.req.param('orgId');
|
|
140
|
-
const slug = c.req.param('slug');
|
|
141
|
-
const role = ws.roles
|
|
142
|
-
.findBy('organization_id', orgId)
|
|
143
|
-
.find((r) => r.slug === slug && r.type === 'OrganizationRole');
|
|
144
|
-
if (!role)
|
|
145
|
-
throw notFound('Role');
|
|
146
|
-
const rps = ws.rolePermissions.findBy('role_id', role.id);
|
|
147
|
-
const permissions = rps.map((rp) => ws.permissions.get(rp.permission_id)).filter(Boolean);
|
|
148
|
-
return c.json({
|
|
149
|
-
object: 'list',
|
|
150
|
-
data: permissions.map((p) => formatPermission(p)),
|
|
151
|
-
list_metadata: { before: null, after: null },
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
app.post('/authorization/organizations/:orgId/roles/:slug/permissions', async (c) => {
|
|
155
|
-
const ws = getWorkOSStore(store);
|
|
156
|
-
const orgId = c.req.param('orgId');
|
|
157
|
-
const slug = c.req.param('slug');
|
|
158
|
-
const role = ws.roles
|
|
159
|
-
.findBy('organization_id', orgId)
|
|
160
|
-
.find((r) => r.slug === slug && r.type === 'OrganizationRole');
|
|
161
|
-
if (!role)
|
|
162
|
-
throw notFound('Role');
|
|
163
|
-
const body = await parseJsonBody(c);
|
|
164
|
-
const permissionSlugs = body.permissions;
|
|
165
|
-
if (!Array.isArray(permissionSlugs)) {
|
|
166
|
-
throw validationError('permissions must be an array of slugs', [{ field: 'permissions', code: 'invalid' }]);
|
|
167
|
-
}
|
|
168
|
-
// Replace all
|
|
169
|
-
const existing = ws.rolePermissions.findBy('role_id', role.id);
|
|
170
|
-
for (const rp of existing)
|
|
171
|
-
ws.rolePermissions.delete(rp.id);
|
|
172
|
-
for (const permSlug of permissionSlugs) {
|
|
173
|
-
const perm = ws.permissions.findOneBy('slug', permSlug);
|
|
174
|
-
if (!perm)
|
|
175
|
-
throw notFound('Permission');
|
|
176
|
-
ws.rolePermissions.insert({ role_id: role.id, permission_id: perm.id });
|
|
177
|
-
}
|
|
178
|
-
const rps = ws.rolePermissions.findBy('role_id', role.id);
|
|
179
|
-
const permissions = rps.map((rp) => ws.permissions.get(rp.permission_id)).filter(Boolean);
|
|
180
|
-
return c.json({
|
|
181
|
-
object: 'list',
|
|
182
|
-
data: permissions.map((p) => formatPermission(p)),
|
|
183
|
-
list_metadata: { before: null, after: null },
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
app.delete('/authorization/organizations/:orgId/roles/:slug/permissions/:permissionSlug', (c) => {
|
|
187
|
-
const ws = getWorkOSStore(store);
|
|
188
|
-
const orgId = c.req.param('orgId');
|
|
189
|
-
const slug = c.req.param('slug');
|
|
190
|
-
const permissionSlug = c.req.param('permissionSlug');
|
|
191
|
-
const role = ws.roles
|
|
192
|
-
.findBy('organization_id', orgId)
|
|
193
|
-
.find((r) => r.slug === slug && r.type === 'OrganizationRole');
|
|
194
|
-
if (!role)
|
|
195
|
-
throw notFound('Role');
|
|
196
|
-
const perm = ws.permissions.findOneBy('slug', permissionSlug);
|
|
51
|
+
// Org-specific: delete single permission from role
|
|
52
|
+
app.delete(`${prefix}/:slug/permissions/:permissionSlug`, (c) => {
|
|
53
|
+
const role = requireOrgRole(ws, c.req.param('orgId'), c.req.param('slug'));
|
|
54
|
+
const perm = ws.permissions.findOneBy('slug', c.req.param('permissionSlug'));
|
|
197
55
|
if (!perm)
|
|
198
56
|
throw notFound('Permission');
|
|
199
57
|
const rp = ws.rolePermissions.findBy('role_id', role.id).find((rp) => rp.permission_id === perm.id);
|