forgeos 0.1.0-alpha.58 → 0.1.0-alpha.60
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/AGENTS.md +1 -1
- package/CHANGELOG.md +18 -0
- package/docs/changelog.md +14 -0
- package/package.json +1 -1
- package/src/forge/_generated/releaseManifest.json +1 -1
- package/src/forge/_generated/releaseManifest.ts +3 -3
- package/src/forge/cli/workos.ts +12 -2
- package/src/forge/compiler/integration/add.ts +2 -0
- package/src/forge/compiler/integration/templates/workos.ts +81 -3
- package/src/forge/dev/server.ts +117 -21
- package/src/forge/version.ts +1 -1
package/AGENTS.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @forge-generated generator=0.1.0-alpha.
|
|
1
|
+
// @forge-generated generator=0.1.0-alpha.60 input=dd73ff0861775ded0c4b42b55e3261a44043d4c09aa3c9f5641d3c23b17e2e6c content=721818a6f9a664aa898092d4aa5cd68cbbd8ad6e150c7d3ad3ef785a2e3fcf53
|
|
2
2
|
# AGENTS.md
|
|
3
3
|
|
|
4
4
|
<!-- forge-generated:start -->
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# forgeos
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.60
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fix the real WorkOS/AuthKit session bridge for Forge runtime calls.
|
|
8
|
+
|
|
9
|
+
- Generated WorkOS React bridges now pass normalized `/session` claims into
|
|
10
|
+
`ForgeProvider` auth so commands, queries, and liveQueries receive user,
|
|
11
|
+
role, permissions, membership, and tenant headers.
|
|
12
|
+
- WorkOS browser env sync now includes optional tenant mapping variables for
|
|
13
|
+
apps that use internal UUID tenants while WorkOS emits hosted `org_...`
|
|
14
|
+
organization ids.
|
|
15
|
+
- WorkOS dev sessions now derive permissions from active Forge policies when
|
|
16
|
+
AuthKit returns a role without permission claims, and normalize WorkOS
|
|
17
|
+
organization ids before cookie-backed runtime calls touch tenant-scoped data.
|
|
18
|
+
- `forge workos doctor` now validates that generated browser bridges pass
|
|
19
|
+
WorkOS claims into Forge auth instead of only exposing a UI session.
|
|
20
|
+
|
|
3
21
|
## 0.1.0-alpha.58
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/docs/changelog.md
CHANGED
|
@@ -6,6 +6,20 @@ The canonical source file in the repository is `CHANGELOG.md`.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 0.1.0-alpha.60
|
|
10
|
+
|
|
11
|
+
- Fixed the real WorkOS/AuthKit session bridge so generated React apps pass
|
|
12
|
+
normalized `/session` claims into `ForgeProvider` auth for commands, queries,
|
|
13
|
+
and liveQueries.
|
|
14
|
+
- Added optional WorkOS organization-to-Forge tenant mapping env support for
|
|
15
|
+
apps that use internal UUID tenant ids while WorkOS emits hosted `org_...`
|
|
16
|
+
organization ids.
|
|
17
|
+
- WorkOS dev sessions now derive permissions from active Forge policies when
|
|
18
|
+
AuthKit returns a role without permission claims, then normalize WorkOS
|
|
19
|
+
organization ids before cookie-backed runtime calls touch tenant-scoped data.
|
|
20
|
+
- `forge workos doctor` now validates that generated browser bridges pass
|
|
21
|
+
WorkOS claims into Forge auth instead of only exposing a UI session.
|
|
22
|
+
|
|
9
23
|
## 0.1.0-alpha.58
|
|
10
24
|
|
|
11
25
|
- Fixed generated WorkOS AuthKit route helper TypeScript output by reading
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.
|
|
1
|
+
{"defaultProvider":"local","diagnostics":[],"env":{"deployEnv":"FORGE_DEPLOY_ENV","deployId":"FORGE_DEPLOY_ID","publicReleaseId":"NEXT_PUBLIC_FORGE_RELEASE_ID","releaseId":"FORGE_RELEASE_ID"},"gitSha":"unknown","optionalProviders":["local","sentry-compatible","sentry","glitchtip","bugsink","otel","custom"],"packageName":"forgeos","packageVersion":"0.1.0-alpha.60","releaseId":"forgeos@0.1.0-alpha.60+unknown","schemaVersion":"0.1.0"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @forge-generated generator=0.1.0-alpha.
|
|
1
|
+
// @forge-generated generator=0.1.0-alpha.60 input=dd73ff0861775ded0c4b42b55e3261a44043d4c09aa3c9f5641d3c23b17e2e6c content=185c42e22dbf3b1cc6e329af86d71f513f19cde067ec0e4ea0a228df020c30f9
|
|
2
2
|
export const releaseManifest = {
|
|
3
3
|
"defaultProvider": "local",
|
|
4
4
|
"diagnostics": [],
|
|
@@ -19,7 +19,7 @@ export const releaseManifest = {
|
|
|
19
19
|
"custom"
|
|
20
20
|
],
|
|
21
21
|
"packageName": "forgeos",
|
|
22
|
-
"packageVersion": "0.1.0-alpha.
|
|
23
|
-
"releaseId": "forgeos@0.1.0-alpha.
|
|
22
|
+
"packageVersion": "0.1.0-alpha.60",
|
|
23
|
+
"releaseId": "forgeos@0.1.0-alpha.60+unknown",
|
|
24
24
|
"schemaVersion": "0.1.0"
|
|
25
25
|
} as const;
|
package/src/forge/cli/workos.ts
CHANGED
|
@@ -1819,6 +1819,15 @@ function collectWorkOSChecks(workspaceRoot: string, preferredSeedPath = DEFAULT_
|
|
|
1819
1819
|
["/login", "/logout", "/session"].every((route) => generatedFrontendAuthBridge.includes(route));
|
|
1820
1820
|
const authBridgeProvidesSessionClaims =
|
|
1821
1821
|
includesAll(generatedFrontendAuthBridge, ["useForgeWorkOSSession", "/session", "claims"]);
|
|
1822
|
+
const authBridgePassesClaimsToForge =
|
|
1823
|
+
includesAll(generatedFrontendAuthBridge, [
|
|
1824
|
+
"workOSSessionToForgeAuth",
|
|
1825
|
+
"forgeTenantIdForWorkOSOrganization",
|
|
1826
|
+
"workOSAuthProvider",
|
|
1827
|
+
"auth={workOSAuthProvider}",
|
|
1828
|
+
"permissions: claims.permissions",
|
|
1829
|
+
"organizationId: forgeTenantId",
|
|
1830
|
+
]);
|
|
1822
1831
|
const authSessionProxyConfigured = webAuthSessionProxyConfigured(workspaceRoot);
|
|
1823
1832
|
|
|
1824
1833
|
return [
|
|
@@ -1877,9 +1886,10 @@ function collectWorkOSChecks(workspaceRoot: string, preferredSeedPath = DEFAULT_
|
|
|
1877
1886
|
name: "browser-authkit-bridge",
|
|
1878
1887
|
ok: !hasWeb || generatedFrontendAuthBridge.includes("ForgeProvider") &&
|
|
1879
1888
|
authBridgeUsesBackendRoutes &&
|
|
1880
|
-
authBridgeProvidesSessionClaims
|
|
1889
|
+
authBridgeProvidesSessionClaims &&
|
|
1890
|
+
authBridgePassesClaimsToForge,
|
|
1881
1891
|
detail: hasWeb
|
|
1882
|
-
? "generated web/src/lib/workos-auth.tsx bridge uses backend-owned AuthKit routes
|
|
1892
|
+
? "generated web/src/lib/workos-auth.tsx bridge uses backend-owned AuthKit routes and passes normalized WorkOS claims into ForgeProvider auth"
|
|
1883
1893
|
: "no web workspace detected",
|
|
1884
1894
|
},
|
|
1885
1895
|
{
|
|
@@ -552,6 +552,8 @@ function syncWorkOSPublicWebEnv(workspaceRoot: string, frontendWorkspace: string
|
|
|
552
552
|
VITE_FORGE_URL: source.VITE_FORGE_URL || "http://localhost:3765",
|
|
553
553
|
VITE_WORKOS_CLIENT_ID: clientId,
|
|
554
554
|
VITE_WORKOS_REDIRECT_URI: redirectUri,
|
|
555
|
+
VITE_FORGE_WORKOS_TENANT_MAP: source.VITE_FORGE_WORKOS_TENANT_MAP || "",
|
|
556
|
+
VITE_FORGE_WORKOS_TENANT_ID_FALLBACK: source.VITE_FORGE_WORKOS_TENANT_ID_FALLBACK || "",
|
|
555
557
|
};
|
|
556
558
|
const envRel = `${frontendWorkspace}/.env.local`;
|
|
557
559
|
const envPath = join(workspaceRoot, envRel);
|
|
@@ -288,6 +288,9 @@ export function renderWorkosEnvExample(_input: IntegrationTemplateInput): string
|
|
|
288
288
|
"VITE_FORGE_URL=http://localhost:3765",
|
|
289
289
|
"VITE_WORKOS_CLIENT_ID=",
|
|
290
290
|
"VITE_WORKOS_REDIRECT_URI=http://localhost:5173/callback",
|
|
291
|
+
"# Optional: map hosted WorkOS organization IDs to Forge tenant IDs when your app uses internal UUID tenants.",
|
|
292
|
+
"# VITE_FORGE_WORKOS_TENANT_MAP={\"org_...\":\"11111111-1111-4111-8111-111111111111\"}",
|
|
293
|
+
"# VITE_FORGE_WORKOS_TENANT_ID_FALLBACK=",
|
|
291
294
|
"",
|
|
292
295
|
"# WorkOS token claims expected by ForgeOS after forge add auth workos:",
|
|
293
296
|
"# sub, email, organization_id, role, roles, permissions, organization_membership_id",
|
|
@@ -306,6 +309,8 @@ export function renderWorkosReactBridge(_input: IntegrationTemplateInput): strin
|
|
|
306
309
|
"const clientId = import.meta.env.VITE_WORKOS_CLIENT_ID as string | undefined;",
|
|
307
310
|
"const redirectUri = import.meta.env.VITE_WORKOS_REDIRECT_URI as string | undefined;",
|
|
308
311
|
"const forgeApiUrl = import.meta.env.VITE_FORGE_URL as string | undefined;",
|
|
312
|
+
"const workosTenantMap = import.meta.env.VITE_FORGE_WORKOS_TENANT_MAP as string | undefined;",
|
|
313
|
+
"const workosTenantFallback = import.meta.env.VITE_FORGE_WORKOS_TENANT_ID_FALLBACK as string | undefined;",
|
|
309
314
|
"",
|
|
310
315
|
"function workOSApiUrl(path: string): URL {",
|
|
311
316
|
" if (typeof window !== 'undefined' && forgeApiUrl) {",
|
|
@@ -321,12 +326,70 @@ export function renderWorkosReactBridge(_input: IntegrationTemplateInput): strin
|
|
|
321
326
|
" return Boolean(clientId && redirectUri);",
|
|
322
327
|
"}",
|
|
323
328
|
"",
|
|
329
|
+
"function forgeTenantIdForWorkOSOrganization(claims: ForgeWorkOSBrowserSession['claims']) {",
|
|
330
|
+
" if (!claims) {",
|
|
331
|
+
" return undefined;",
|
|
332
|
+
" }",
|
|
333
|
+
" const explicitTenant = claims.tenant_id ?? claims.forge_tenant_id;",
|
|
334
|
+
" if (explicitTenant) {",
|
|
335
|
+
" return explicitTenant;",
|
|
336
|
+
" }",
|
|
337
|
+
" const organizationId = claims.organization_id;",
|
|
338
|
+
" if (!organizationId) {",
|
|
339
|
+
" return undefined;",
|
|
340
|
+
" }",
|
|
341
|
+
" if (workosTenantMap) {",
|
|
342
|
+
" try {",
|
|
343
|
+
" const parsed = JSON.parse(workosTenantMap) as Record<string, string>;",
|
|
344
|
+
" if (parsed[organizationId]) {",
|
|
345
|
+
" return parsed[organizationId];",
|
|
346
|
+
" }",
|
|
347
|
+
" } catch {",
|
|
348
|
+
" // Keep authentication usable; forge workos doctor validates mapping syntax separately.",
|
|
349
|
+
" }",
|
|
350
|
+
" }",
|
|
351
|
+
" if (organizationId.startsWith('org_') && workosTenantFallback) {",
|
|
352
|
+
" return workosTenantFallback;",
|
|
353
|
+
" }",
|
|
354
|
+
" return organizationId;",
|
|
355
|
+
"}",
|
|
356
|
+
"",
|
|
357
|
+
"function workOSSessionToForgeAuth(session: ForgeWorkOSBrowserSession | null | undefined) {",
|
|
358
|
+
" const claims = session?.claims;",
|
|
359
|
+
" if (!claims?.sub) {",
|
|
360
|
+
" return {};",
|
|
361
|
+
" }",
|
|
362
|
+
" const forgeTenantId = forgeTenantIdForWorkOSOrganization(claims);",
|
|
363
|
+
" return {",
|
|
364
|
+
" userId: claims.sub,",
|
|
365
|
+
" tenantId: forgeTenantId,",
|
|
366
|
+
" organizationId: forgeTenantId,",
|
|
367
|
+
" organizationMembershipId: claims.organization_membership_id,",
|
|
368
|
+
" role: claims.role ?? claims.roles?.[0],",
|
|
369
|
+
" roles: claims.roles,",
|
|
370
|
+
" permissions: claims.permissions,",
|
|
371
|
+
" claims,",
|
|
372
|
+
" };",
|
|
373
|
+
"}",
|
|
374
|
+
"",
|
|
375
|
+
"const workOSAuthProvider = async () => {",
|
|
376
|
+
" const response = await fetch(workOSApiUrl('/session'), { credentials: 'include' });",
|
|
377
|
+
" if (response.status === 401) {",
|
|
378
|
+
" return {};",
|
|
379
|
+
" }",
|
|
380
|
+
" if (!response.ok) {",
|
|
381
|
+
" throw new Error(`WorkOS session request failed with HTTP ${response.status}`);",
|
|
382
|
+
" }",
|
|
383
|
+
" const payload = await response.json() as { ok?: boolean; session?: ForgeWorkOSBrowserSession | null };",
|
|
384
|
+
" return workOSSessionToForgeAuth(payload.ok ? payload.session ?? null : null);",
|
|
385
|
+
"};",
|
|
386
|
+
"",
|
|
324
387
|
"export function ForgeWorkOSAuthProvider({ children }: { children: ReactNode }) {",
|
|
325
388
|
" if (!clientId || !redirectUri) {",
|
|
326
389
|
" throw new Error(\"WorkOS AuthKit requires VITE_WORKOS_CLIENT_ID and VITE_WORKOS_REDIRECT_URI\");",
|
|
327
390
|
" }",
|
|
328
391
|
" return (",
|
|
329
|
-
" <ForgeProvider url={forgeUrl}>",
|
|
392
|
+
" <ForgeProvider url={forgeUrl} auth={workOSAuthProvider}>",
|
|
330
393
|
" {children}",
|
|
331
394
|
" </ForgeProvider>",
|
|
332
395
|
" );",
|
|
@@ -338,6 +401,8 @@ export function renderWorkosReactBridge(_input: IntegrationTemplateInput): strin
|
|
|
338
401
|
" sub?: string;",
|
|
339
402
|
" email?: string;",
|
|
340
403
|
" organization_id?: string;",
|
|
404
|
+
" tenant_id?: string;",
|
|
405
|
+
" forge_tenant_id?: string;",
|
|
341
406
|
" organization_membership_id?: string;",
|
|
342
407
|
" role?: string;",
|
|
343
408
|
" roles?: string[];",
|
|
@@ -836,7 +901,8 @@ export function renderWorkosSession(_input: IntegrationTemplateInput): string {
|
|
|
836
901
|
].join("\n");
|
|
837
902
|
}
|
|
838
903
|
|
|
839
|
-
export function renderWorkosAuthRoutes(
|
|
904
|
+
export function renderWorkosAuthRoutes(input: IntegrationTemplateInput): string {
|
|
905
|
+
const fallbackRolePermissions = rolePermissions(permissionsFromApp(input));
|
|
840
906
|
return [
|
|
841
907
|
"/** Forge generated framework-agnostic WorkOS AuthKit routes. */",
|
|
842
908
|
'import type { WorkOS } from "@workos-inc/node";',
|
|
@@ -878,6 +944,18 @@ export function renderWorkosAuthRoutes(_input: IntegrationTemplateInput): string
|
|
|
878
944
|
" };",
|
|
879
945
|
"}",
|
|
880
946
|
"",
|
|
947
|
+
"const forgeWorkOSFallbackRolePermissions = {",
|
|
948
|
+
...Object.entries(fallbackRolePermissions).map(([role, permissions]) =>
|
|
949
|
+
` ${JSON.stringify(role)}: [${permissions.map((permission) => JSON.stringify(permission)).join(", ")}],`
|
|
950
|
+
),
|
|
951
|
+
"} as const;",
|
|
952
|
+
"",
|
|
953
|
+
"function fallbackPermissionsForRole(role: string | undefined): string[] {",
|
|
954
|
+
" if (!role) return [];",
|
|
955
|
+
" const permissions = forgeWorkOSFallbackRolePermissions[role.toLowerCase() as keyof typeof forgeWorkOSFallbackRolePermissions];",
|
|
956
|
+
" return permissions ? [...permissions] : [];",
|
|
957
|
+
"}",
|
|
958
|
+
"",
|
|
881
959
|
"export function getWorkOSAuthorizationUrl(input: { workos: WorkOS; config: ForgeWorkOSAuthKitConfig; state?: string }): string {",
|
|
882
960
|
" if (!input.config.redirectUri) throw new Error(\"WORKOS_REDIRECT_URI is required\");",
|
|
883
961
|
" const url = new URL(input.workos.userManagement.getAuthorizationUrl({",
|
|
@@ -915,7 +993,7 @@ export function renderWorkosAuthRoutes(_input: IntegrationTemplateInput): string
|
|
|
915
993
|
" const organizationMembershipId = resolved?.organizationMembershipId ?? resultOrganizationMembershipId;",
|
|
916
994
|
" const role = resolved?.role ?? (typeof rawResult.role === \"string\" ? rawResult.role : resultRoles[0]);",
|
|
917
995
|
" const roles = resolved?.roles ?? resultRoles;",
|
|
918
|
-
" const permissions = resolved?.permissions ?? resultPermissions;",
|
|
996
|
+
" const permissions = resolved?.permissions ?? (resultPermissions.length > 0 ? resultPermissions : fallbackPermissionsForRole(role));",
|
|
919
997
|
" return {",
|
|
920
998
|
" user: workOSSessionUser(user),",
|
|
921
999
|
" accessToken: result.accessToken,",
|
package/src/forge/dev/server.ts
CHANGED
|
@@ -508,8 +508,85 @@ function workOSDevSessionClaims(session: WorkOSDevSession): Record<string, unkno
|
|
|
508
508
|
};
|
|
509
509
|
}
|
|
510
510
|
|
|
511
|
+
function parseWorkOSTenantMap(value: string | undefined): Record<string, string> {
|
|
512
|
+
if (!value) {
|
|
513
|
+
return {};
|
|
514
|
+
}
|
|
515
|
+
try {
|
|
516
|
+
const parsed = JSON.parse(value) as unknown;
|
|
517
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
518
|
+
return {};
|
|
519
|
+
}
|
|
520
|
+
return Object.fromEntries(
|
|
521
|
+
Object.entries(parsed as Record<string, unknown>)
|
|
522
|
+
.filter((entry): entry is [string, string] =>
|
|
523
|
+
typeof entry[0] === "string" &&
|
|
524
|
+
typeof entry[1] === "string" &&
|
|
525
|
+
entry[0].length > 0 &&
|
|
526
|
+
entry[1].length > 0
|
|
527
|
+
),
|
|
528
|
+
);
|
|
529
|
+
} catch {
|
|
530
|
+
return {};
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function mapWorkOSTenantId(
|
|
535
|
+
envStore: ReturnType<typeof getRuntimeEnvStore>,
|
|
536
|
+
organizationId: unknown,
|
|
537
|
+
): string | undefined {
|
|
538
|
+
if (typeof organizationId !== "string" || organizationId.length === 0) {
|
|
539
|
+
return undefined;
|
|
540
|
+
}
|
|
541
|
+
const tenantMap = {
|
|
542
|
+
...parseWorkOSTenantMap(envStore.resolve("FORGE_WORKOS_TENANT_MAP")),
|
|
543
|
+
...parseWorkOSTenantMap(envStore.resolve("VITE_FORGE_WORKOS_TENANT_MAP")),
|
|
544
|
+
};
|
|
545
|
+
if (tenantMap[organizationId]) {
|
|
546
|
+
return tenantMap[organizationId];
|
|
547
|
+
}
|
|
548
|
+
const fallback =
|
|
549
|
+
normalizeOptionalId(envStore.resolve("FORGE_WORKOS_TENANT_ID_FALLBACK")) ??
|
|
550
|
+
normalizeOptionalId(envStore.resolve("VITE_FORGE_WORKOS_TENANT_ID_FALLBACK"));
|
|
551
|
+
if (organizationId.startsWith("org_") && fallback) {
|
|
552
|
+
return fallback;
|
|
553
|
+
}
|
|
554
|
+
return organizationId;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function mapWorkOSClaimsTenant(
|
|
558
|
+
envStore: ReturnType<typeof getRuntimeEnvStore>,
|
|
559
|
+
claims: Record<string, unknown>,
|
|
560
|
+
): Record<string, unknown> {
|
|
561
|
+
const rawOrganizationId = claims.organization_id;
|
|
562
|
+
const mappedTenantId = mapWorkOSTenantId(envStore, rawOrganizationId);
|
|
563
|
+
if (!mappedTenantId || mappedTenantId === rawOrganizationId) {
|
|
564
|
+
return claims;
|
|
565
|
+
}
|
|
566
|
+
return {
|
|
567
|
+
...claims,
|
|
568
|
+
workos_organization_id: rawOrganizationId,
|
|
569
|
+
organization_id: mappedTenantId,
|
|
570
|
+
tenant_id: mappedTenantId,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function enrichWorkOSDevSession(workspaceRoot: string, session: WorkOSDevSession): WorkOSDevSession {
|
|
575
|
+
const role = session.role ?? session.roles?.[0];
|
|
576
|
+
const permissions = session.permissions && session.permissions.length > 0
|
|
577
|
+
? session.permissions
|
|
578
|
+
: deriveDevPermissionsForRole(workspaceRoot, role);
|
|
579
|
+
return {
|
|
580
|
+
...session,
|
|
581
|
+
...(role && !session.role ? { role } : {}),
|
|
582
|
+
...(role && (!session.roles || session.roles.length === 0) ? { roles: [role] } : {}),
|
|
583
|
+
...(permissions.length > 0 ? { permissions } : {}),
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
511
587
|
function authFromWorkOSDevSessionCookie(
|
|
512
588
|
request: Request,
|
|
589
|
+
workspaceRoot: string,
|
|
513
590
|
envStore: ReturnType<typeof getRuntimeEnvStore>,
|
|
514
591
|
): AuthContext | null {
|
|
515
592
|
const cookiePassword = envStore.resolve("WORKOS_COOKIE_PASSWORD");
|
|
@@ -523,7 +600,10 @@ function authFromWorkOSDevSessionCookie(
|
|
|
523
600
|
if (!session) {
|
|
524
601
|
return null;
|
|
525
602
|
}
|
|
526
|
-
const claims =
|
|
603
|
+
const claims = mapWorkOSClaimsTenant(
|
|
604
|
+
envStore,
|
|
605
|
+
workOSDevSessionClaims(enrichWorkOSDevSession(workspaceRoot, session)),
|
|
606
|
+
);
|
|
527
607
|
return resolveAuthFromHeaders({
|
|
528
608
|
userId: typeof claims.sub === "string" ? claims.sub : undefined,
|
|
529
609
|
tenantId: typeof claims.organization_id === "string" ? claims.organization_id : undefined,
|
|
@@ -541,6 +621,7 @@ function authFromWorkOSDevSessionCookie(
|
|
|
541
621
|
|
|
542
622
|
async function authenticateDevRequest(
|
|
543
623
|
request: Request,
|
|
624
|
+
workspaceRoot: string,
|
|
544
625
|
authConfig: ReturnType<typeof loadAuthConfigFromEnv>,
|
|
545
626
|
envStore: ReturnType<typeof getRuntimeEnvStore>,
|
|
546
627
|
): Promise<AuthContext> {
|
|
@@ -549,13 +630,13 @@ async function authenticateDevRequest(
|
|
|
549
630
|
if (headerAuth.kind !== "anonymous") {
|
|
550
631
|
return headerAuth;
|
|
551
632
|
}
|
|
552
|
-
return authFromWorkOSDevSessionCookie(request, envStore) ?? headerAuth;
|
|
633
|
+
return authFromWorkOSDevSessionCookie(request, workspaceRoot, envStore) ?? headerAuth;
|
|
553
634
|
}
|
|
554
635
|
|
|
555
636
|
try {
|
|
556
637
|
return await authenticateHeaders(request.headers, authConfig);
|
|
557
638
|
} catch (error) {
|
|
558
|
-
const cookieAuth = authFromWorkOSDevSessionCookie(request, envStore);
|
|
639
|
+
const cookieAuth = authFromWorkOSDevSessionCookie(request, workspaceRoot, envStore);
|
|
559
640
|
if (cookieAuth) {
|
|
560
641
|
return cookieAuth;
|
|
561
642
|
}
|
|
@@ -1585,6 +1666,20 @@ export async function startDevServer(
|
|
|
1585
1666
|
const membership = (result.organizationMembership ?? {}) as Record<string, unknown>;
|
|
1586
1667
|
const accessToken = typeof result.accessToken === "string" ? result.accessToken : "";
|
|
1587
1668
|
const refreshToken = typeof result.refreshToken === "string" ? result.refreshToken : undefined;
|
|
1669
|
+
const roles = Array.isArray(result.roles)
|
|
1670
|
+
? result.roles.filter((role): role is string => typeof role === "string")
|
|
1671
|
+
: [];
|
|
1672
|
+
const role =
|
|
1673
|
+
typeof result.role === "string"
|
|
1674
|
+
? result.role
|
|
1675
|
+
: typeof (membership.role as Record<string, unknown> | undefined)?.slug === "string"
|
|
1676
|
+
? String((membership.role as Record<string, unknown>).slug)
|
|
1677
|
+
: roles[0];
|
|
1678
|
+
const permissions = Array.isArray(result.permissions)
|
|
1679
|
+
? result.permissions.filter((permission): permission is string => typeof permission === "string")
|
|
1680
|
+
: [];
|
|
1681
|
+
const effectivePermissions =
|
|
1682
|
+
permissions.length > 0 ? permissions : deriveDevPermissionsForRole(workspaceRoot, role);
|
|
1588
1683
|
const session: WorkOSDevSession = {
|
|
1589
1684
|
user,
|
|
1590
1685
|
accessToken,
|
|
@@ -1599,17 +1694,9 @@ export async function startDevServer(
|
|
|
1599
1694
|
: typeof membership.id === "string"
|
|
1600
1695
|
? { organizationMembershipId: membership.id }
|
|
1601
1696
|
: {}),
|
|
1602
|
-
...(
|
|
1603
|
-
...(
|
|
1604
|
-
|
|
1605
|
-
: {}),
|
|
1606
|
-
...(Array.isArray(result.permissions)
|
|
1607
|
-
? {
|
|
1608
|
-
permissions: result.permissions.filter(
|
|
1609
|
-
(permission): permission is string => typeof permission === "string",
|
|
1610
|
-
),
|
|
1611
|
-
}
|
|
1612
|
-
: {}),
|
|
1697
|
+
...(role ? { role } : {}),
|
|
1698
|
+
...(roles.length > 0 ? { roles } : role ? { roles: [role] } : {}),
|
|
1699
|
+
...(effectivePermissions.length > 0 ? { permissions: effectivePermissions } : {}),
|
|
1613
1700
|
issuedAt: Math.floor(Date.now() / 1000),
|
|
1614
1701
|
};
|
|
1615
1702
|
return redirectResponse(
|
|
@@ -1660,7 +1747,16 @@ export async function startDevServer(
|
|
|
1660
1747
|
cookiePassword,
|
|
1661
1748
|
);
|
|
1662
1749
|
return session
|
|
1663
|
-
? jsonResponse({
|
|
1750
|
+
? jsonResponse({
|
|
1751
|
+
ok: true,
|
|
1752
|
+
session: {
|
|
1753
|
+
user: session.user,
|
|
1754
|
+
claims: mapWorkOSClaimsTenant(
|
|
1755
|
+
envStore,
|
|
1756
|
+
workOSDevSessionClaims(enrichWorkOSDevSession(workspaceRoot, session)),
|
|
1757
|
+
),
|
|
1758
|
+
},
|
|
1759
|
+
})
|
|
1664
1760
|
: jsonResponse({ ok: false, session: null }, 401);
|
|
1665
1761
|
}
|
|
1666
1762
|
}
|
|
@@ -1885,7 +1981,7 @@ export async function startDevServer(
|
|
|
1885
1981
|
}
|
|
1886
1982
|
}
|
|
1887
1983
|
|
|
1888
|
-
const auth = await authenticateDevRequest(request, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
1984
|
+
const auth = await authenticateDevRequest(request, workspaceRoot, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
1889
1985
|
const lastRevision = Number(
|
|
1890
1986
|
request.headers.get("last-event-id") ??
|
|
1891
1987
|
url.searchParams.get("lastRevision") ??
|
|
@@ -2009,7 +2105,7 @@ export async function startDevServer(
|
|
|
2009
2105
|
400,
|
|
2010
2106
|
);
|
|
2011
2107
|
}
|
|
2012
|
-
const auth = await authenticateDevRequest(request, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
2108
|
+
const auth = await authenticateDevRequest(request, workspaceRoot, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
2013
2109
|
const envStore = getRuntimeEnvStore(workspaceRoot);
|
|
2014
2110
|
const secretRegistry = loadSecretRegistry(workspaceRoot);
|
|
2015
2111
|
const bundle = createRuntimeSecretsBundle({
|
|
@@ -2129,7 +2225,7 @@ export async function startDevServer(
|
|
|
2129
2225
|
}
|
|
2130
2226
|
|
|
2131
2227
|
const purpose = body.purpose ?? "dev_agent_chat";
|
|
2132
|
-
const auth = await authenticateDevRequest(request, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
2228
|
+
const auth = await authenticateDevRequest(request, workspaceRoot, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
2133
2229
|
const envStore = getRuntimeEnvStore(workspaceRoot);
|
|
2134
2230
|
const secretRegistry = loadSecretRegistry(workspaceRoot);
|
|
2135
2231
|
const bundle = createRuntimeSecretsBundle({
|
|
@@ -2544,7 +2640,7 @@ export async function startDevServer(
|
|
|
2544
2640
|
const externalInvoke = parseExternalInvoke(pathname);
|
|
2545
2641
|
if (externalInvoke) {
|
|
2546
2642
|
const args = await parseRequestArgs(request);
|
|
2547
|
-
const auth = await authenticateDevRequest(request, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
2643
|
+
const auth = await authenticateDevRequest(request, workspaceRoot, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
2548
2644
|
const result = await runExternalEntry(
|
|
2549
2645
|
workspaceRoot,
|
|
2550
2646
|
{
|
|
@@ -2612,7 +2708,7 @@ export async function startDevServer(
|
|
|
2612
2708
|
}
|
|
2613
2709
|
|
|
2614
2710
|
const args = await parseRequestArgs(request);
|
|
2615
|
-
const auth = await authenticateDevRequest(request, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
2711
|
+
const auth = await authenticateDevRequest(request, workspaceRoot, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
2616
2712
|
|
|
2617
2713
|
const result = await runQuery(
|
|
2618
2714
|
workspaceRoot,
|
|
@@ -2685,7 +2781,7 @@ export async function startDevServer(
|
|
|
2685
2781
|
}
|
|
2686
2782
|
|
|
2687
2783
|
const args = await parseRequestArgs(request);
|
|
2688
|
-
const auth = await authenticateDevRequest(request, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
2784
|
+
const auth = await authenticateDevRequest(request, workspaceRoot, authConfig, getRuntimeEnvStore(workspaceRoot));
|
|
2689
2785
|
|
|
2690
2786
|
await prepareRuntimeEnvironment(workspaceRoot, {
|
|
2691
2787
|
mock: options.mock,
|
package/src/forge/version.ts
CHANGED