@wtfalch/foundry 0.57.0 → 0.59.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.
|
@@ -11,15 +11,27 @@ export declare function desktopCallbackUri(host: string): string;
|
|
|
11
11
|
/** The URIs to add to an app's current list: current first, then each wanted one it lacks. */
|
|
12
12
|
export declare function unionRedirectUris(current: readonly string[], wanted: readonly string[]): string[];
|
|
13
13
|
/**
|
|
14
|
-
* The
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* The redirect URIs a company web app signs in with: `/auth/callback` and the
|
|
15
|
+
* desktop callback on each of its hosts. Agora's own client holds only its
|
|
16
|
+
* `/callback`. `zitadelApp` creates the app with this list, and
|
|
17
|
+
* `zitadelDesktopCallback` adds what an existing app lacks.
|
|
18
|
+
*/
|
|
19
|
+
export declare function signInRedirectUris(org: DeclaredOrg, appKey: string): string[];
|
|
20
|
+
/**
|
|
21
|
+
* The marker value for an app: its ZITADEL id and the URIs it must hold, in a
|
|
22
|
+
* fixed order. A changed hostname or a re-created app gives a different
|
|
23
|
+
* value, so the next plan names the app again.
|
|
24
|
+
*/
|
|
25
|
+
export declare function signInCallbackAppliedValue(appId: string, uris: readonly string[]): string;
|
|
26
|
+
/**
|
|
27
|
+
* Marker of company app `appKey`: `signInCallbackAppliedValue` once its
|
|
28
|
+
* sign-in callbacks were registered. A marker holding only the app id (the
|
|
29
|
+
* desktop-only form of #339) differs, so such an app is repaired once.
|
|
18
30
|
*/
|
|
19
31
|
export declare function zitadelAppDesktopCallbackAppliedScopeKey(appKey: string): string;
|
|
20
32
|
/**
|
|
21
33
|
* One `zitadel:desktop-callback` step names every live web sign-in app whose
|
|
22
|
-
*
|
|
34
|
+
* marker differs from the one its recorded id and wanted URIs give. Plans nothing when the host's ZITADEL
|
|
23
35
|
* client lacks the two redirect URI calls (`wired`), before the org and
|
|
24
36
|
* project are recorded, or while the platform's sign-in is not ready.
|
|
25
37
|
*/
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { agoraHostnameFor } from './agora.js';
|
|
2
|
+
import { bouleHostnameFor } from './boule.js';
|
|
3
|
+
import { appHostname, cmsStudioHostnameFor, entryHostnames } from './hostnames.js';
|
|
1
4
|
/**
|
|
2
5
|
* #339: the desktop app signs in through the company's web app, so every
|
|
3
6
|
* `@wtfalch/auth` web client foundry makes registers this path beside
|
|
@@ -12,19 +15,48 @@ export function unionRedirectUris(current, wanted) {
|
|
|
12
15
|
return [...new Set([...current, ...wanted])];
|
|
13
16
|
}
|
|
14
17
|
/**
|
|
15
|
-
* The
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
18
|
+
* The redirect URIs a company web app signs in with: `/auth/callback` and the
|
|
19
|
+
* desktop callback on each of its hosts. Agora's own client holds only its
|
|
20
|
+
* `/callback`. `zitadelApp` creates the app with this list, and
|
|
21
|
+
* `zitadelDesktopCallback` adds what an existing app lacks.
|
|
22
|
+
*/
|
|
23
|
+
export function signInRedirectUris(org, appKey) {
|
|
24
|
+
if (appKey === 'agora')
|
|
25
|
+
return [`https://${agoraHostnameFor(org)}/callback`];
|
|
26
|
+
const authEntry = org.hosting.entries.find((e) => e.key === appKey && e.uses?.auth);
|
|
27
|
+
const hosts = authEntry
|
|
28
|
+
? entryHostnames(org, authEntry)
|
|
29
|
+
: [
|
|
30
|
+
appKey === 'cms' && org.site.enabled
|
|
31
|
+
? cmsStudioHostnameFor(org)
|
|
32
|
+
: appKey === 'boule'
|
|
33
|
+
? bouleHostnameFor(org)
|
|
34
|
+
: appHostname(org),
|
|
35
|
+
];
|
|
36
|
+
return [
|
|
37
|
+
...hosts.map((host) => `https://${host}/auth/callback`),
|
|
38
|
+
...hosts.map(desktopCallbackUri),
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The marker value for an app: its ZITADEL id and the URIs it must hold, in a
|
|
43
|
+
* fixed order. A changed hostname or a re-created app gives a different
|
|
44
|
+
* value, so the next plan names the app again.
|
|
45
|
+
*/
|
|
46
|
+
export function signInCallbackAppliedValue(appId, uris) {
|
|
47
|
+
return `${appId} ${[...uris].sort().join(' ')}`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Marker of company app `appKey`: `signInCallbackAppliedValue` once its
|
|
51
|
+
* sign-in callbacks were registered. A marker holding only the app id (the
|
|
52
|
+
* desktop-only form of #339) differs, so such an app is repaired once.
|
|
19
53
|
*/
|
|
20
54
|
export function zitadelAppDesktopCallbackAppliedScopeKey(appKey) {
|
|
21
55
|
return `zitadel_app_desktop_callback_applied.${appKey}`;
|
|
22
56
|
}
|
|
23
|
-
/** The fixed Agora client keeps its own `/callback`; it is not built on `@wtfalch/auth`. */
|
|
24
|
-
const NO_DESKTOP_CALLBACK_APPS = new Set(['agora']);
|
|
25
57
|
/**
|
|
26
58
|
* One `zitadel:desktop-callback` step names every live web sign-in app whose
|
|
27
|
-
*
|
|
59
|
+
* marker differs from the one its recorded id and wanted URIs give. Plans nothing when the host's ZITADEL
|
|
28
60
|
* client lacks the two redirect URI calls (`wired`), before the org and
|
|
29
61
|
* project are recorded, or while the platform's sign-in is not ready.
|
|
30
62
|
*/
|
|
@@ -32,10 +64,7 @@ export function planDesktopCallbacks(org, live, recordedIds, plannedSteps, authR
|
|
|
32
64
|
if (!wired || !authReady || !recordedIds.zitadel_org || !recordedIds.zitadel_project)
|
|
33
65
|
return null;
|
|
34
66
|
const apps = live
|
|
35
|
-
.filter((r) => r.system === 'zitadel' &&
|
|
36
|
-
r.kind === 'app' &&
|
|
37
|
-
r.config.appType === 'web' &&
|
|
38
|
-
!NO_DESKTOP_CALLBACK_APPS.has(r.key))
|
|
67
|
+
.filter((r) => r.system === 'zitadel' && r.kind === 'app' && r.config.appType === 'web')
|
|
39
68
|
.map((r) => r.key)
|
|
40
69
|
// An app this plan recreates gets its marker when it is made.
|
|
41
70
|
.filter((appKey) => !plannedSteps.some((s) => s.action.system === 'zitadel' &&
|
|
@@ -44,14 +73,16 @@ export function planDesktopCallbacks(org, live, recordedIds, plannedSteps, authR
|
|
|
44
73
|
s.action.desired.recreate === true))
|
|
45
74
|
.filter((appKey) => {
|
|
46
75
|
const appId = recordedIds[`zitadel:app:${appKey}`];
|
|
47
|
-
return (Boolean(appId) &&
|
|
76
|
+
return (Boolean(appId) &&
|
|
77
|
+
recordedIds[zitadelAppDesktopCallbackAppliedScopeKey(appKey)] !==
|
|
78
|
+
signInCallbackAppliedValue(appId, signInRedirectUris(org, appKey)));
|
|
48
79
|
});
|
|
49
80
|
if (apps.length === 0)
|
|
50
81
|
return null;
|
|
51
82
|
return {
|
|
52
83
|
action: { system: 'zitadel', kind: 'desktop-callback', key: org.id, desired: { apps } },
|
|
53
84
|
step: {
|
|
54
|
-
description: `register the
|
|
85
|
+
description: `register the sign-in callbacks for ${org.slug}'s sign-in apps (${apps.join(', ')})`,
|
|
55
86
|
destroys: false,
|
|
56
87
|
migrates: false,
|
|
57
88
|
},
|
package/dist/plan/executor.d.ts
CHANGED
|
@@ -2212,20 +2212,16 @@ export declare class FoundryStepExecutor implements StepExecutor, StepDestroyer
|
|
|
2212
2212
|
*/
|
|
2213
2213
|
private zitadelPostLogoutUris;
|
|
2214
2214
|
/**
|
|
2215
|
-
* #339: registers
|
|
2216
|
-
*
|
|
2217
|
-
*
|
|
2218
|
-
* union
|
|
2215
|
+
* #339: registers the sign-in callbacks (`signInRedirectUris`: `/auth/callback`
|
|
2216
|
+
* and `/auth/desktop/callback` per host, Agora's `/callback`) on each live web
|
|
2217
|
+
* sign-in app the planner named, keeping every redirect URI the app already
|
|
2218
|
+
* holds (the union of the current list and the wanted ones; a write only when
|
|
2219
|
+
* the union is larger). An app's marker (its app id and wanted URIs) is recorded and
|
|
2219
2220
|
* persisted only after its read and write resolved, so a failure leaves no
|
|
2220
2221
|
* marker and the next plan names the app again. The error names ids only,
|
|
2221
2222
|
* never the cause.
|
|
2222
2223
|
*/
|
|
2223
2224
|
private zitadelDesktopCallback;
|
|
2224
|
-
/**
|
|
2225
|
-
* The hostnames a company web app signs in at: the same rule `zitadelApp`
|
|
2226
|
-
* uses for the redirect URIs it creates the app with.
|
|
2227
|
-
*/
|
|
2228
|
-
private signInHostnames;
|
|
2229
2225
|
/**
|
|
2230
2226
|
* `plan/diff.ts`'s `planLoginHost` (#267): adds the org's own company
|
|
2231
2227
|
* login host (`action.desired.host`) to the ZITADEL instance's trusted
|
package/dist/plan/executor.js
CHANGED
|
@@ -22,7 +22,7 @@ import { previewsOn } from './coolify-previews.js';
|
|
|
22
22
|
import { CORE_FILES_COLLECTION_NAME, FILES_CORE_COLLECTION_SCOPE_KEY, ZITADEL_FILES_PROJECT_SCOPE_KEY, } from './core-files.js';
|
|
23
23
|
import { waitForHealthyVersion, waitForNewDeployment, } from './deploy-health.js';
|
|
24
24
|
import { DESKTOP_AUTH_APP_SCOPE_KEY, DESKTOP_AUTH_DEPLOYED_SCOPE_KEY, DESKTOP_AUTH_DOCKERFILE, DESKTOP_AUTH_HOME, DESKTOP_AUTH_ORIGIN_SCOPE_KEY, DESKTOP_AUTH_READY_PATH, DESKTOP_AUTH_READY_SCOPE_KEY, DESKTOP_AUTH_REDIRECT_SCOPE_KEY, DESKTOP_AUTH_REVISION_SCOPE_KEY, desktopAuthCallbackUri, desktopAuthEnv, desktopAuthInputs, desktopAuthRedirectUnsupported, desktopAuthRevision, } from './desktop-auth.js';
|
|
25
|
-
import {
|
|
25
|
+
import { signInCallbackAppliedValue, signInRedirectUris, unionRedirectUris, zitadelAppDesktopCallbackAppliedScopeKey, } from './desktop-callback.js';
|
|
26
26
|
import { PLATFORM_NETWORK, companyNetworkName, ensureDestination, findDestination, onPlatformNetwork, placed, placedByScope, } from './destinations.js';
|
|
27
27
|
import { DEFAULT_AI_LIMIT_MICROS } from './diff.js';
|
|
28
28
|
import { createEdgeAccess, isNotFound as isEdgeNotFound, } from './edge-access.js';
|
|
@@ -1967,10 +1967,11 @@ export class FoundryStepExecutor {
|
|
|
1967
1967
|
return {};
|
|
1968
1968
|
}
|
|
1969
1969
|
/**
|
|
1970
|
-
* #339: registers
|
|
1971
|
-
*
|
|
1972
|
-
*
|
|
1973
|
-
* union
|
|
1970
|
+
* #339: registers the sign-in callbacks (`signInRedirectUris`: `/auth/callback`
|
|
1971
|
+
* and `/auth/desktop/callback` per host, Agora's `/callback`) on each live web
|
|
1972
|
+
* sign-in app the planner named, keeping every redirect URI the app already
|
|
1973
|
+
* holds (the union of the current list and the wanted ones; a write only when
|
|
1974
|
+
* the union is larger). An app's marker (its app id and wanted URIs) is recorded and
|
|
1974
1975
|
* persisted only after its read and write resolved, so a failure leaves no
|
|
1975
1976
|
* marker and the next plan names the app again. The error names ids only,
|
|
1976
1977
|
* never the cause.
|
|
@@ -1991,7 +1992,7 @@ export class FoundryStepExecutor {
|
|
|
1991
1992
|
const appId = recorded[`zitadel:app:${appKey}`];
|
|
1992
1993
|
if (!appId)
|
|
1993
1994
|
continue;
|
|
1994
|
-
const wanted =
|
|
1995
|
+
const wanted = signInRedirectUris(org, appKey);
|
|
1995
1996
|
try {
|
|
1996
1997
|
const current = await read(zitadelOrgId, projectId, appId);
|
|
1997
1998
|
const union = unionRedirectUris(current, wanted);
|
|
@@ -1999,27 +2000,13 @@ export class FoundryStepExecutor {
|
|
|
1999
2000
|
await write(zitadelOrgId, projectId, appId, union);
|
|
2000
2001
|
}
|
|
2001
2002
|
catch {
|
|
2002
|
-
throw new Error(`zitadel:desktop-callback for ${org.slug}: registering the
|
|
2003
|
+
throw new Error(`zitadel:desktop-callback for ${org.slug}: registering the sign-in callbacks failed for app '${appKey}' (${appId})`);
|
|
2003
2004
|
}
|
|
2004
|
-
context.recordScopeId(zitadelAppDesktopCallbackAppliedScopeKey(appKey), appId);
|
|
2005
|
+
context.recordScopeId(zitadelAppDesktopCallbackAppliedScopeKey(appKey), signInCallbackAppliedValue(appId, wanted));
|
|
2005
2006
|
await context.persistScopeIds();
|
|
2006
2007
|
}
|
|
2007
2008
|
return {};
|
|
2008
2009
|
}
|
|
2009
|
-
/**
|
|
2010
|
-
* The hostnames a company web app signs in at: the same rule `zitadelApp`
|
|
2011
|
-
* uses for the redirect URIs it creates the app with.
|
|
2012
|
-
*/
|
|
2013
|
-
signInHostnames(org, appKey) {
|
|
2014
|
-
const authEntry = org.hosting.entries.find((e) => e.key === appKey && e.uses?.auth);
|
|
2015
|
-
if (appKey === 'cms' && org.site.enabled)
|
|
2016
|
-
return [cmsHostnameFor(org)];
|
|
2017
|
-
if (appKey === 'boule')
|
|
2018
|
-
return [bouleHostnameFor(org)];
|
|
2019
|
-
if (authEntry)
|
|
2020
|
-
return entryHostnames(org, authEntry);
|
|
2021
|
-
return [appHostname(org)];
|
|
2022
|
-
}
|
|
2023
2010
|
/**
|
|
2024
2011
|
* `plan/diff.ts`'s `planLoginHost` (#267): adds the org's own company
|
|
2025
2012
|
* login host (`action.desired.host`) to the ZITADEL instance's trusted
|
|
@@ -2701,55 +2688,24 @@ export class FoundryStepExecutor {
|
|
|
2701
2688
|
await this.ports.deliverSecret(`zitadel:app:${org.slug}:${action.key}`, created.clientSecret);
|
|
2702
2689
|
return { resourceId: created.appId };
|
|
2703
2690
|
}
|
|
2704
|
-
// #139: the fixed `cms` client (`diff.ts`'s `planCmsClient`) signs
|
|
2705
|
-
// people into the cms studio at `cmsHostnameFor(org)` -- `cms.<domain>`,
|
|
2706
|
-
// or `<slug>-cms.oikos.host` for a company without a domain -- not the
|
|
2707
|
-
// org's own app hostname. #164: the fixed `agora` client (`diff.ts`'s
|
|
2708
|
-
// `planAgoraClient`) is the same shape, always -- there is no
|
|
2709
|
-
// `org.site.enabled`-style guard to check, since Agora is always on.
|
|
2710
2691
|
// The Agora client stays `appType: 'web'`: providers' `createOidcApp`
|
|
2711
2692
|
// makes every non-native app a public client (authorization code + PKCE,
|
|
2712
2693
|
// `OIDC_AUTH_METHOD_TYPE_NONE`, no secret), which is what Agora's browser
|
|
2713
2694
|
// SPA needs. Providers cannot create ZITADEL's `USER_AGENT` app kind.
|
|
2714
|
-
// #161: the fixed `boule` client signs people into the org's Boule at its
|
|
2715
|
-
// own hostname, on the standard `/auth/callback` path (Boule is built on
|
|
2716
|
-
// `@wtfalch/auth` like every other web app here).
|
|
2717
|
-
const hostname = isCmsClient
|
|
2718
|
-
? cmsHostnameFor(org)
|
|
2719
|
-
: isAgoraClient
|
|
2720
|
-
? agoraHostnameFor(org)
|
|
2721
|
-
: isBouleClient
|
|
2722
|
-
? bouleHostnameFor(org)
|
|
2723
|
-
: appHostname(org);
|
|
2724
2695
|
// Native apps sign in via device code, with no redirect of their own
|
|
2725
2696
|
// (auth/scripts/provision.mjs:475-497, prior art cited in the decisions
|
|
2726
2697
|
// file) -- empty arrays rather than a web app's callback/logout URLs.
|
|
2727
|
-
//
|
|
2728
|
-
//
|
|
2729
|
-
// client needs `https://<agora address>/callback` as a redirect URI") --
|
|
2730
|
-
// Agora is not built on `@wtfalch/auth`'s `NextAuth` mount the way every
|
|
2731
|
-
// other web app this file provisions is.
|
|
2732
|
-
const redirectUri = isAgoraClient
|
|
2733
|
-
? `https://${hostname}/callback`
|
|
2734
|
-
: `https://${hostname}/auth/callback`;
|
|
2735
|
-
// An entry with `uses.auth` signs in at its own exact hostnames, one pair
|
|
2736
|
-
// of addresses each; a `*` adds none (`entryHostnames`).
|
|
2737
|
-
const hosts = authEntry ? entryHostnames(org, authEntry) : null;
|
|
2698
|
+
// `signInRedirectUris` holds each web app's hosts and callback paths (the
|
|
2699
|
+
// cms, Agora, Boule and entry hostnames; Agora's `/callback`).
|
|
2738
2700
|
const postLogoutUris = appType === 'native' ? [] : companyAppPostLogoutUris(org, action.key);
|
|
2739
|
-
//
|
|
2740
|
-
const
|
|
2701
|
+
// The same rule the repair step uses (`signInRedirectUris`).
|
|
2702
|
+
const signInUris = appType === 'web' ? signInRedirectUris(org, action.key) : [];
|
|
2741
2703
|
const created = await this.ports.zitadel.createOidcApp(zitadelOrgId, projectId, {
|
|
2742
2704
|
name: action.key,
|
|
2743
2705
|
appType,
|
|
2744
2706
|
redirectUris: appType === 'native'
|
|
2745
2707
|
? []
|
|
2746
|
-
:
|
|
2747
|
-
? [
|
|
2748
|
-
...hosts.map((host) => `https://${host}/auth/callback`),
|
|
2749
|
-
...desktopCallbacks,
|
|
2750
|
-
...(authEntry?.uses?.auth?.mcp ? [mcpCallbackUri(org)] : []),
|
|
2751
|
-
]
|
|
2752
|
-
: [redirectUri, ...desktopCallbacks],
|
|
2708
|
+
: [...signInUris, ...(authEntry?.uses?.auth?.mcp ? [mcpCallbackUri(org)] : [])],
|
|
2753
2709
|
postLogoutRedirectUris: postLogoutUris,
|
|
2754
2710
|
...(authEntry?.uses?.auth?.mcp ? { accessTokenType: 'jwt' } : {}),
|
|
2755
2711
|
});
|
|
@@ -2769,8 +2725,8 @@ export class FoundryStepExecutor {
|
|
|
2769
2725
|
if (appType === 'web') {
|
|
2770
2726
|
context.recordScopeId(zitadelAppPostLogoutAppliedScopeKey(action.key), postLogoutAppliedValue(created.appId, postLogoutUris));
|
|
2771
2727
|
}
|
|
2772
|
-
if (
|
|
2773
|
-
context.recordScopeId(zitadelAppDesktopCallbackAppliedScopeKey(action.key), created.appId);
|
|
2728
|
+
if (appType === 'web') {
|
|
2729
|
+
context.recordScopeId(zitadelAppDesktopCallbackAppliedScopeKey(action.key), signInCallbackAppliedValue(created.appId, signInUris));
|
|
2774
2730
|
}
|
|
2775
2731
|
return { resourceId: created.appId };
|
|
2776
2732
|
}
|
|
@@ -107,6 +107,11 @@ export interface WorkerAppUpload {
|
|
|
107
107
|
migrations?: WorkerAppMigrations;
|
|
108
108
|
/** Durable Object classes the upload marks container-enabled; absent when the entry declares no container. */
|
|
109
109
|
containers?: string[];
|
|
110
|
+
/** Workers Logs: always on, every request sampled. The adapter sends it as the upload metadata's `observability`. */
|
|
111
|
+
observability: {
|
|
112
|
+
enabled: true;
|
|
113
|
+
head_sampling_rate: 1;
|
|
114
|
+
};
|
|
110
115
|
}
|
|
111
116
|
export interface WorkerAppContainer {
|
|
112
117
|
className: string;
|
package/dist/plan/worker-app.js
CHANGED
|
@@ -526,6 +526,7 @@ export async function applyWorkerApp(deps, org, action, context) {
|
|
|
526
526
|
? [{ type: 'hyperdrive', name: WORKER_APP_HYPERDRIVE_BINDING, id: hyperdriveId }]
|
|
527
527
|
: []),
|
|
528
528
|
],
|
|
529
|
+
observability: { enabled: true, head_sampling_rate: 1 },
|
|
529
530
|
...(migrations ? { migrations } : {}),
|
|
530
531
|
...(want.containers.length > 0
|
|
531
532
|
? { containers: want.containers.map((c) => c.className) }
|