@flytedesk/app-kit 0.5.0 → 0.6.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.
@@ -0,0 +1,76 @@
1
+ import type { IdpAppSwitcherEntry } from "../oidc-client.js";
2
+ /**
3
+ * A fake flytedesk-id (OIDC provider) for e2e/integration harnesses, generalized out of
4
+ * media-planner's `apps/web/e2e/fake-idp.ts` (AK-10 follow-up, R-2/R-10) so every
5
+ * `@flytedesk/app-kit/auth` consumer gets ONE maintained fixture instead of each app
6
+ * hand-rolling its own. It serves the exact endpoints `./oidc-client.ts` calls —
7
+ * discovery, JWKS, `/auth` (authorize), `/token` (authorization_code + refresh, PKCE
8
+ * S256, Basic client auth), `/me` (userinfo), `/apps/:appId/authorization` (roles +
9
+ * permissions) and `/me/apps` (the "All flytedesk apps" switcher feed, `fetchMyApps`) —
10
+ * so a consumer's REAL login → callback → refresh chain runs UNMODIFIED against it. No
11
+ * bypass ever lands in production code: the consuming app simply points
12
+ * `FLYTEDESK_ID_ISSUER` at this server in its own e2e setup.
13
+ *
14
+ * Unlike the media-planner original, this fixture takes NO opinion on what identities or
15
+ * permission catalog exist — the caller supplies a map of named identities up front
16
+ * (`identities`) and picks which one the next `/auth` grants with `setNextIdentity` (in-
17
+ * process, for a Vitest-style harness) or the cross-process `/__set-identity` HTTP hook
18
+ * (for a Playwright spec driving a real browser against a real server process).
19
+ */
20
+ export interface FakeIdpIdentity {
21
+ sub: string;
22
+ email: string;
23
+ name: string;
24
+ roles: Array<{
25
+ id: string;
26
+ key: string;
27
+ name: string;
28
+ }>;
29
+ /** UNPREFIXED permission keys — this fixture app-prefixes them on
30
+ * `/apps/:appId/authorization`, exactly as real flytedesk-id does (the consuming
31
+ * app's `flytedeskAuth` plugin strips the prefix back off). */
32
+ permissions: string[];
33
+ /** Fed to `/me/apps` for `fetchMyApps`/the design system's "All flytedesk apps"
34
+ * switcher. Defaults to an empty list. */
35
+ apps?: IdpAppSwitcherEntry[];
36
+ }
37
+ export interface FakeIdpOptions {
38
+ clientId: string;
39
+ /** Defaults to `clientId` — distinct because real flytedesk-id keys the authorization
40
+ * catalog on `appId`, not `clientId` (see `FlytedeskAuthOptions.appId`). */
41
+ appId?: string;
42
+ /** Every identity this fixture can grant, keyed by a caller-chosen name (e.g. role). */
43
+ identities: Record<string, FakeIdpIdentity>;
44
+ /** Which key in `identities` `/auth` grants until `setNextIdentity`/`/__set-identity`
45
+ * says otherwise. */
46
+ defaultIdentityKey: string;
47
+ }
48
+ export declare class FakeIdp {
49
+ private server?;
50
+ private baseUrl;
51
+ private readonly clientId;
52
+ private readonly appId;
53
+ private readonly identities;
54
+ private nextIdentityKey;
55
+ private idpSessionLive;
56
+ private keys;
57
+ private publicJwk;
58
+ private readonly kid;
59
+ private readonly codes;
60
+ private readonly grants;
61
+ constructor(opts: FakeIdpOptions);
62
+ /** Pick the identity the NEXT `/auth` grants. */
63
+ setNextIdentity(key: string): void;
64
+ /** See `idpSessionLive`'s doc comment. */
65
+ setIdpSessionLive(live: boolean): void;
66
+ get url(): string;
67
+ start(port?: number): Promise<string>;
68
+ stop(): Promise<void>;
69
+ private json;
70
+ private readForm;
71
+ private bearer;
72
+ private signIdToken;
73
+ private issueGrant;
74
+ private handle;
75
+ private tokenGrant;
76
+ }
@@ -0,0 +1,255 @@
1
+ import { createServer } from "node:http";
2
+ import { createHash, randomBytes } from "node:crypto";
3
+ import { generateKeyPair, exportJWK, SignJWT } from "jose";
4
+ export class FakeIdp {
5
+ server;
6
+ baseUrl = "";
7
+ clientId;
8
+ appId;
9
+ identities;
10
+ nextIdentityKey;
11
+ // Whether this browser/client still holds a live flytedesk-id end-user session cookie
12
+ // — real oidc-provider's `prompt=none` handling (OIDC Core 3.1.2.1) answers straight
13
+ // from that cookie with no interaction at all: a fresh code if it's live,
14
+ // `error=login_required` back to redirect_uri if it's not. Defaults to true.
15
+ idpSessionLive = true;
16
+ keys;
17
+ publicJwk;
18
+ kid = "fake-idp-key";
19
+ codes = new Map();
20
+ grants = new Map();
21
+ constructor(opts) {
22
+ this.clientId = opts.clientId;
23
+ this.appId = opts.appId ?? opts.clientId;
24
+ this.identities = opts.identities;
25
+ if (!(opts.defaultIdentityKey in this.identities)) {
26
+ throw new Error(`FakeIdp: defaultIdentityKey "${opts.defaultIdentityKey}" is not in identities`);
27
+ }
28
+ this.nextIdentityKey = opts.defaultIdentityKey;
29
+ }
30
+ /** Pick the identity the NEXT `/auth` grants. */
31
+ setNextIdentity(key) {
32
+ if (!(key in this.identities)) {
33
+ throw new Error(`FakeIdp: no identity registered for "${key}"`);
34
+ }
35
+ this.nextIdentityKey = key;
36
+ }
37
+ /** See `idpSessionLive`'s doc comment. */
38
+ setIdpSessionLive(live) {
39
+ this.idpSessionLive = live;
40
+ }
41
+ get url() {
42
+ if (!this.baseUrl)
43
+ throw new Error("FakeIdp not started");
44
+ return this.baseUrl;
45
+ }
46
+ async start(port = 0) {
47
+ this.keys = await generateKeyPair("RS256", { extractable: true });
48
+ this.publicJwk = {
49
+ ...(await exportJWK(this.keys.publicKey)),
50
+ kid: this.kid,
51
+ use: "sig",
52
+ alg: "RS256",
53
+ };
54
+ this.server = createServer((req, res) => {
55
+ this.handle(req, res).catch((err) => {
56
+ res.writeHead(500, { "content-type": "application/json" });
57
+ res.end(JSON.stringify({ error: "fake_idp_error", detail: String(err) }));
58
+ });
59
+ });
60
+ await new Promise((resolve) => this.server.listen(port, "127.0.0.1", resolve));
61
+ const addr = this.server.address();
62
+ if (!addr || typeof addr === "string")
63
+ throw new Error("FakeIdp failed to bind a port");
64
+ this.baseUrl = `http://127.0.0.1:${addr.port}`;
65
+ return this.baseUrl;
66
+ }
67
+ async stop() {
68
+ if (this.server)
69
+ await new Promise((resolve) => this.server.close(() => resolve()));
70
+ this.server = undefined;
71
+ }
72
+ json(res, status, body) {
73
+ res.writeHead(status, { "content-type": "application/json" });
74
+ res.end(JSON.stringify(body));
75
+ }
76
+ async readForm(req) {
77
+ const chunks = [];
78
+ for await (const c of req)
79
+ chunks.push(c);
80
+ return new URLSearchParams(Buffer.concat(chunks).toString("utf8"));
81
+ }
82
+ bearer(req) {
83
+ const h = req.headers.authorization;
84
+ return h?.startsWith("Bearer ") ? h.slice(7) : null;
85
+ }
86
+ async signIdToken(identity) {
87
+ return new SignJWT({})
88
+ .setProtectedHeader({ alg: "RS256", kid: this.kid })
89
+ .setIssuer(this.baseUrl)
90
+ .setAudience(this.clientId)
91
+ .setSubject(identity.sub)
92
+ .setIssuedAt()
93
+ .setExpirationTime("15m")
94
+ .sign(this.keys.privateKey);
95
+ }
96
+ issueGrant(identity) {
97
+ const accessToken = `at-${randomBytes(24).toString("base64url")}`;
98
+ const refreshToken = `rt-${randomBytes(24).toString("base64url")}`;
99
+ this.grants.set(accessToken, { identity });
100
+ this.grants.set(refreshToken, { identity });
101
+ return { accessToken, refreshToken };
102
+ }
103
+ async handle(req, res) {
104
+ const u = new URL(req.url ?? "/", this.baseUrl);
105
+ const path = u.pathname;
106
+ if (req.method === "GET" && path === "/.well-known/openid-configuration") {
107
+ return this.json(res, 200, {
108
+ issuer: this.baseUrl,
109
+ authorization_endpoint: `${this.baseUrl}/auth`,
110
+ token_endpoint: `${this.baseUrl}/token`,
111
+ jwks_uri: `${this.baseUrl}/jwks`,
112
+ userinfo_endpoint: `${this.baseUrl}/me`,
113
+ revocation_endpoint: `${this.baseUrl}/revoke`,
114
+ response_types_supported: ["code"],
115
+ id_token_signing_alg_values_supported: ["RS256"],
116
+ code_challenge_methods_supported: ["S256"],
117
+ });
118
+ }
119
+ if (req.method === "GET" && path === "/jwks") {
120
+ return this.json(res, 200, { keys: [this.publicJwk] });
121
+ }
122
+ // Authorize: auto-grant the currently-selected identity, redirect back with a code —
123
+ // UNLESS this is a `prompt=none` silent request and idpSessionLive has been turned
124
+ // off, in which case real oidc-provider answers `error=login_required` with no
125
+ // interaction UI at all, and this fake mirrors that exactly.
126
+ if (req.method === "GET" && path === "/auth") {
127
+ const redirectUri = u.searchParams.get("redirect_uri");
128
+ const state = u.searchParams.get("state") ?? "";
129
+ const codeChallenge = u.searchParams.get("code_challenge") ?? "";
130
+ const prompt = u.searchParams.get("prompt");
131
+ if (!redirectUri)
132
+ return this.json(res, 400, { error: "missing redirect_uri" });
133
+ const loc = new URL(redirectUri);
134
+ if (prompt === "none" && !this.idpSessionLive) {
135
+ loc.searchParams.set("error", "login_required");
136
+ loc.searchParams.set("state", state);
137
+ res.writeHead(302, { location: loc.toString() });
138
+ return void res.end();
139
+ }
140
+ const code = `code-${randomBytes(18).toString("base64url")}`;
141
+ this.codes.set(code, { identity: this.identities[this.nextIdentityKey], codeChallenge });
142
+ loc.searchParams.set("code", code);
143
+ loc.searchParams.set("state", state);
144
+ res.writeHead(302, { location: loc.toString() });
145
+ return void res.end();
146
+ }
147
+ if (req.method === "POST" && path === "/token") {
148
+ const form = await this.readForm(req);
149
+ const grantType = form.get("grant_type");
150
+ if (grantType === "authorization_code") {
151
+ const code = form.get("code") ?? "";
152
+ const verifier = form.get("code_verifier") ?? "";
153
+ const entry = this.codes.get(code);
154
+ if (!entry)
155
+ return this.json(res, 400, { error: "invalid_grant" });
156
+ this.codes.delete(code);
157
+ // PKCE S256: base64url(sha256(verifier)) must equal the stored challenge.
158
+ const challenge = createHash("sha256").update(verifier).digest("base64url");
159
+ if (entry.codeChallenge && challenge !== entry.codeChallenge) {
160
+ return this.json(res, 400, { error: "invalid_grant", detail: "pkce_mismatch" });
161
+ }
162
+ const { accessToken, refreshToken } = this.issueGrant(entry.identity);
163
+ return this.json(res, 200, {
164
+ access_token: accessToken,
165
+ id_token: await this.signIdToken(entry.identity),
166
+ refresh_token: refreshToken,
167
+ token_type: "Bearer",
168
+ expires_in: 900,
169
+ });
170
+ }
171
+ if (grantType === "refresh_token") {
172
+ const rt = form.get("refresh_token") ?? "";
173
+ const grant = this.grants.get(rt);
174
+ if (!grant)
175
+ return this.json(res, 400, { error: "invalid_grant" });
176
+ // rotateRefreshToken: true — issue a fresh pair for the same identity.
177
+ const { accessToken, refreshToken } = this.issueGrant(grant.identity);
178
+ return this.json(res, 200, {
179
+ access_token: accessToken,
180
+ id_token: await this.signIdToken(grant.identity),
181
+ refresh_token: refreshToken,
182
+ token_type: "Bearer",
183
+ expires_in: 900,
184
+ });
185
+ }
186
+ return this.json(res, 400, { error: "unsupported_grant_type" });
187
+ }
188
+ if (req.method === "GET" && path === "/me") {
189
+ const grant = this.tokenGrant(req);
190
+ if (!grant)
191
+ return this.json(res, 401, { error: "invalid_token" });
192
+ return this.json(res, 200, {
193
+ sub: grant.identity.sub,
194
+ email: grant.identity.email,
195
+ email_verified: true,
196
+ name: grant.identity.name,
197
+ });
198
+ }
199
+ if (req.method === "GET" && path === `/apps/${this.appId}/authorization`) {
200
+ const grant = this.tokenGrant(req);
201
+ if (!grant)
202
+ return this.json(res, 401, { error: "invalid_token" });
203
+ return this.json(res, 200, {
204
+ userId: grant.identity.sub,
205
+ appId: this.appId,
206
+ roles: grant.identity.roles,
207
+ // App-prefixed, exactly as real flytedesk-id returns (the consuming app's
208
+ // flytedeskAuth plugin strips it back off — see plugin.ts's loadAuthedUser).
209
+ permissions: grant.identity.permissions.map((p) => `${this.appId}:${p}`),
210
+ });
211
+ }
212
+ if (req.method === "GET" && path === "/me/apps") {
213
+ const grant = this.tokenGrant(req);
214
+ if (!grant)
215
+ return this.json(res, 401, { error: "invalid_token" });
216
+ return this.json(res, 200, { apps: grant.identity.apps ?? [] });
217
+ }
218
+ if (req.method === "POST" && path === "/revoke") {
219
+ const form = await this.readForm(req);
220
+ this.grants.delete(form.get("token") ?? "");
221
+ return this.json(res, 200, {});
222
+ }
223
+ // Test-control (out-of-band, not part of OIDC): pick the identity the next /auth
224
+ // grants. The in-process setNextIdentity() covers a Vitest-style harness; this HTTP
225
+ // hook lets a Playwright spec (a separate process from the server under test) choose
226
+ // the persona cross-process.
227
+ if (req.method === "POST" && path === "/__set-identity") {
228
+ const form = await this.readForm(req);
229
+ const key = form.get("key") ?? "";
230
+ if (!(key in this.identities)) {
231
+ return this.json(res, 400, { error: "unknown_identity", key });
232
+ }
233
+ this.setNextIdentity(key);
234
+ return this.json(res, 200, { key });
235
+ }
236
+ // Test-control: flip whether this fake IdP currently has a live end-user session
237
+ // (see idpSessionLive's doc comment) — same cross-process HTTP-hook pattern as
238
+ // /__set-identity.
239
+ if (req.method === "POST" && path === "/__set-idp-session") {
240
+ const form = await this.readForm(req);
241
+ const live = form.get("live");
242
+ if (live !== "true" && live !== "false") {
243
+ return this.json(res, 400, { error: "invalid_live", live });
244
+ }
245
+ this.setIdpSessionLive(live === "true");
246
+ return this.json(res, 200, { live: this.idpSessionLive });
247
+ }
248
+ return this.json(res, 404, { error: "not_found", path });
249
+ }
250
+ tokenGrant(req) {
251
+ const token = this.bearer(req);
252
+ return token ? (this.grants.get(token) ?? null) : null;
253
+ }
254
+ }
255
+ //# sourceMappingURL=fake-idp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-idp.js","sourceRoot":"","sources":["../../../src/auth/testing/fake-idp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Test-only fixtures for `@flytedesk/app-kit/auth` consumers — a separate subpath
3
+ * export so this never ships to a production bundle by accident. See fake-idp.ts.
4
+ */
5
+ export { FakeIdp, type FakeIdpIdentity, type FakeIdpOptions } from "./fake-idp.js";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Test-only fixtures for `@flytedesk/app-kit/auth` consumers — a separate subpath
3
+ * export so this never ships to a production bundle by accident. See fake-idp.ts.
4
+ */
5
+ export { FakeIdp } from "./fake-idp.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/testing/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,OAAO,EAA6C,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flytedesk/app-kit",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Shared platform kit for flytedesk apps: flytedesk-id auth (BFF/OIDC client) and a Postgres-native trace/audit layer.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -37,6 +37,10 @@
37
37
  "types": "./dist/auth/index.d.ts",
38
38
  "import": "./dist/auth/index.js"
39
39
  },
40
+ "./auth/testing": {
41
+ "types": "./dist/auth/testing/index.d.ts",
42
+ "import": "./dist/auth/testing/index.js"
43
+ },
40
44
  "./trace": {
41
45
  "types": "./dist/trace/index.d.ts",
42
46
  "import": "./dist/trace/index.js"