@flytedesk/app-kit 0.7.1 → 1.0.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.
Files changed (37) hide show
  1. package/dist/auth/client/httpClient.d.ts +4 -2
  2. package/dist/auth/client/httpClient.js +20 -4
  3. package/dist/auth/client/httpClient.js.map +1 -1
  4. package/dist/auth/client/index.d.ts +4 -2
  5. package/dist/auth/client/index.js +3 -1
  6. package/dist/auth/client/index.js.map +1 -1
  7. package/dist/auth/client/silentAuth.d.ts +17 -0
  8. package/dist/auth/client/silentAuth.js +106 -0
  9. package/dist/auth/client/silentAuth.js.map +1 -0
  10. package/dist/auth/client/useAuthSession.d.ts +41 -14
  11. package/dist/auth/client/useAuthSession.js +56 -40
  12. package/dist/auth/client/useAuthSession.js.map +1 -1
  13. package/dist/auth/errors.d.ts +12 -0
  14. package/dist/auth/errors.js +15 -0
  15. package/dist/auth/errors.js.map +1 -1
  16. package/dist/auth/index.d.ts +22 -6
  17. package/dist/auth/index.js +20 -4
  18. package/dist/auth/index.js.map +1 -1
  19. package/dist/auth/oidc-client.d.ts +29 -0
  20. package/dist/auth/oidc-client.js +50 -11
  21. package/dist/auth/oidc-client.js.map +1 -1
  22. package/dist/auth/plugin.js +194 -62
  23. package/dist/auth/plugin.js.map +1 -1
  24. package/dist/auth/shared-types.d.ts +57 -1
  25. package/dist/auth/shared-types.js +51 -1
  26. package/dist/auth/shared-types.js.map +1 -1
  27. package/dist/auth/testing/fake-idp.d.ts +12 -0
  28. package/dist/auth/testing/fake-idp.js +43 -0
  29. package/dist/auth/testing/fake-idp.js.map +1 -1
  30. package/dist/auth/types.d.ts +90 -2
  31. package/dist/spa-server/cli.d.ts +10 -0
  32. package/dist/spa-server/cli.js +60 -0
  33. package/dist/spa-server/cli.js.map +1 -0
  34. package/dist/spa-server/index.d.ts +53 -0
  35. package/dist/spa-server/index.js +50 -0
  36. package/dist/spa-server/index.js.map +1 -0
  37. package/package.json +14 -3
@@ -10,9 +10,11 @@
10
10
  * future change here ever needs one, it belongs in the server (`../plugin.js`) or in a
11
11
  * new Node-only module instead.
12
12
  */
13
- import type { ApiErrorBody } from "../shared-types.js";
13
+ import { type ApiErrorBody } from "../shared-types.js";
14
14
  /** `expires_in`-free deadline for every call this client makes, unless a caller passes
15
- * its own `deadlineMs` — matches media-planner's AUTH_REQUEST_DEADLINE_MS. */
15
+ * its own `deadlineMs` — the same `AUTH_REQUEST_DEADLINE_MS` (`../shared-types.js`) the
16
+ * server plugin's default `refreshReuseGraceMs` is sized around (2x this), so the two
17
+ * can never drift apart — see that option's doc comment in `../types.ts`. */
16
18
  export declare const DEFAULT_REQUEST_DEADLINE_MS = 10000;
17
19
  export declare class ApiRequestError extends Error {
18
20
  code: string;
@@ -1,6 +1,21 @@
1
+ /**
2
+ * Browser-safe BFF session client for `@flytedesk/app-kit/auth`'s cookie/bearer
3
+ * pattern (POST {api}/auth/refresh -> Bearer on every call, transparent 401 -> refresh
4
+ * -> retry-once). Extracted (AK-16) from media-planner's
5
+ * packages/design-system/src/apiClient.ts, which every @flytedesk/app-kit/auth
6
+ * consumer was otherwise on track to re-implement by hand (see sms-app's
7
+ * apps/web/src/api/client.ts before this card: no 401-retry, no deadline, a different
8
+ * single-flight mechanism). This module has ZERO Node imports — no `node:*`, no
9
+ * Fastify, nothing server-only — so it is safe to import from a browser bundle. If a
10
+ * future change here ever needs one, it belongs in the server (`../plugin.js`) or in a
11
+ * new Node-only module instead.
12
+ */
13
+ import { AUTH_REQUEST_DEADLINE_MS, AUTH_ROUTE_PREFIX, authRoutePath } from "../shared-types.js";
1
14
  /** `expires_in`-free deadline for every call this client makes, unless a caller passes
2
- * its own `deadlineMs` — matches media-planner's AUTH_REQUEST_DEADLINE_MS. */
3
- export const DEFAULT_REQUEST_DEADLINE_MS = 10_000;
15
+ * its own `deadlineMs` — the same `AUTH_REQUEST_DEADLINE_MS` (`../shared-types.js`) the
16
+ * server plugin's default `refreshReuseGraceMs` is sized around (2x this), so the two
17
+ * can never drift apart — see that option's doc comment in `../types.ts`. */
18
+ export const DEFAULT_REQUEST_DEADLINE_MS = AUTH_REQUEST_DEADLINE_MS;
4
19
  export class ApiRequestError extends Error {
5
20
  code;
6
21
  field;
@@ -84,8 +99,9 @@ async function withDeadline(what, ms, run) {
84
99
  * sendAndDecode treats exactly like a refused refresh. */
85
100
  async function refreshAccessToken() {
86
101
  if (!refreshPromise) {
87
- refreshPromise = withDeadline("POST /auth/refresh", DEFAULT_REQUEST_DEADLINE_MS, async (signal) => {
88
- const res = await fetch(`${baseUrl}/auth/refresh`, {
102
+ const refreshPath = authRoutePath(AUTH_ROUTE_PREFIX, "refresh");
103
+ refreshPromise = withDeadline(`POST ${refreshPath}`, DEFAULT_REQUEST_DEADLINE_MS, async (signal) => {
104
+ const res = await fetch(`${baseUrl}${refreshPath}`, {
89
105
  method: "POST",
90
106
  credentials: "include",
91
107
  signal,
@@ -1 +1 @@
1
- {"version":3,"file":"httpClient.js","sourceRoot":"","sources":["../../../src/auth/client/httpClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"httpClient.js","sourceRoot":"","sources":["../../../src/auth/client/httpClient.ts"],"names":[],"mappings":"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"}
@@ -5,5 +5,7 @@
5
5
  * was extracted from where and why (AK-16).
6
6
  */
7
7
  export { ApiRequestError, RequestDeadlineError, DEFAULT_REQUEST_DEADLINE_MS, configureApiClient, setAccessToken, getAccessToken, getBaseUrl, setAuthRecovery, apiRequest, apiUpload, type AuthRecovery, type RequestOptions, } from "./httpClient.js";
8
- export { useAuthSession, type AuthStatus, type AuthSessionState } from "./useAuthSession.js";
9
- export type { AppSwitcherEntry, AuthedUserDto, IdpRole, ApiErrorBody } from "../shared-types.js";
8
+ export { useAuthSession, type AuthStatus, type AuthSessionState, type LoadingSessionState, type AuthenticatedSessionState, type UnauthenticatedSessionState, type ErrorSessionState, } from "./useAuthSession.js";
9
+ export { attemptSilentResume, type SilentResumeResult, type AttemptSilentResumeOptions, } from "./silentAuth.js";
10
+ export type { AppSwitcherEntry, AuthedUserDto, IdpRole, ApiErrorBody, SilentAuthMessage } from "../shared-types.js";
11
+ export { AUTH_ROUTE_PREFIX, AUTH_ROUTE_SEGMENTS, authRoutePath, type AuthRouteSegment } from "../shared-types.js";
@@ -5,5 +5,7 @@
5
5
  * was extracted from where and why (AK-16).
6
6
  */
7
7
  export { ApiRequestError, RequestDeadlineError, DEFAULT_REQUEST_DEADLINE_MS, configureApiClient, setAccessToken, getAccessToken, getBaseUrl, setAuthRecovery, apiRequest, apiUpload, } from "./httpClient.js";
8
- export { useAuthSession } from "./useAuthSession.js";
8
+ export { useAuthSession, } from "./useAuthSession.js";
9
+ export { attemptSilentResume, } from "./silentAuth.js";
10
+ export { AUTH_ROUTE_PREFIX, AUTH_ROUTE_SEGMENTS, authRoutePath } from "../shared-types.js";
9
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/client/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,2BAA2B,EAC3B,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,UAAU,EACV,eAAe,EACf,UAAU,EACV,SAAS,GAGV,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,cAAc,EAA0C,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/client/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,2BAA2B,EAC3B,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,UAAU,EACV,eAAe,EACf,UAAU,EACV,SAAS,GAGV,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,cAAc,GAOf,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,GAGpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,aAAa,EAAyB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,17 @@
1
+ export type SilentResumeResult = "success" | "login_required" | "error";
2
+ export interface AttemptSilentResumeOptions {
3
+ /** Well past the typical case (a same-datacenter redirect chain — under a second) but
4
+ * short enough that a genuinely hung network doesn't leave the caller waiting
5
+ * forever. Default 8000ms, matching media-planner's `SILENT_RESUME_TIMEOUT_MS`.
6
+ * Treated as `"error"` (not `"login_required"`) — a timeout means we don't actually
7
+ * know whether the IdP-side session is alive or dead. */
8
+ timeoutMs?: number;
9
+ }
10
+ /**
11
+ * Single-flighted: a second call while one attempt is already running reuses that same
12
+ * attempt (and its eventual result) instead of opening a second hidden iframe — a
13
+ * session layer's reactive path (a request 401ed) and its own cold-start bootstrap can
14
+ * both end up calling this for the same underlying condition, and this keeps them from
15
+ * racing two independent iframes/silent-auth attempts against flytedesk-id.
16
+ */
17
+ export declare function attemptSilentResume(options?: AttemptSilentResumeOptions): Promise<SilentResumeResult>;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Browser-safe silent (`prompt=none`) re-auth for `@flytedesk/app-kit/auth`'s BFF
3
+ * pattern (AK-14, gap 2) — generalized from media-planner's
4
+ * `packages/design-system/src/silentAuth.ts`. ZERO Node imports, same rule as
5
+ * `./httpClient.ts`: this is a browser bundle, not a server module.
6
+ *
7
+ * `prompt=none` (OIDC Core 3.1.2.1) is a silent authorization request that answers from
8
+ * flytedesk-id's own existing end-user session cookie with no visible UI at all — the
9
+ * architectural equivalent of a "Continue as {name}" Google One Tap for an app whose IdP
10
+ * is flytedesk-id, not Google, so there's nothing for a literal One Tap widget to attach
11
+ * to. See `../plugin.ts`'s `GET {routePrefix}/login` (`?prompt=none`) and its
12
+ * `{routePrefix}/callback`'s silent branch for the server half this drives.
13
+ *
14
+ * Driven from a hidden iframe rather than a top-level navigation so the running app is
15
+ * never unmounted/reloaded for an attempt that, most of the time, resolves in well under
16
+ * a second: the iframe navigates login -> flytedesk-id -> callback entirely on its own,
17
+ * and the callback's silent branch answers with a tiny page whose only job is a single
18
+ * `window.parent.postMessage(...)` this module listens for (the `SilentAuthMessage`
19
+ * contract, `../shared-types.js` — shared with the server so the two sides can never
20
+ * disagree on its shape).
21
+ */
22
+ import { getBaseUrl } from "./httpClient.js";
23
+ import { AUTH_ROUTE_PREFIX, authRoutePath, SILENT_AUTH_MESSAGE_SOURCE, } from "../shared-types.js";
24
+ const DEFAULT_SILENT_RESUME_TIMEOUT_MS = 8_000;
25
+ function isSilentAuthMessage(data) {
26
+ return (typeof data === "object" &&
27
+ data !== null &&
28
+ data.source === SILENT_AUTH_MESSAGE_SOURCE);
29
+ }
30
+ function resolveLoginUrl() {
31
+ const base = getBaseUrl();
32
+ const absolute = /^[a-z][a-z\d+.-]*:\/\//i.test(base) ? base : `${window.location.origin}${base}`;
33
+ const origin = new URL(absolute).origin;
34
+ return { url: `${absolute}${authRoutePath(AUTH_ROUTE_PREFIX, "login")}?prompt=none`, origin };
35
+ }
36
+ let inFlight = null;
37
+ /**
38
+ * Single-flighted: a second call while one attempt is already running reuses that same
39
+ * attempt (and its eventual result) instead of opening a second hidden iframe — a
40
+ * session layer's reactive path (a request 401ed) and its own cold-start bootstrap can
41
+ * both end up calling this for the same underlying condition, and this keeps them from
42
+ * racing two independent iframes/silent-auth attempts against flytedesk-id.
43
+ */
44
+ export function attemptSilentResume(options = {}) {
45
+ if (typeof window === "undefined" || typeof document === "undefined") {
46
+ return Promise.resolve("error");
47
+ }
48
+ if (!inFlight) {
49
+ inFlight = runSilentResume(options).finally(() => {
50
+ inFlight = null;
51
+ });
52
+ }
53
+ return inFlight;
54
+ }
55
+ function runSilentResume(options) {
56
+ return new Promise((resolve) => {
57
+ const { url, origin } = resolveLoginUrl();
58
+ const timeoutMs = options.timeoutMs ?? DEFAULT_SILENT_RESUME_TIMEOUT_MS;
59
+ const iframe = document.createElement("iframe");
60
+ iframe.style.display = "none";
61
+ iframe.setAttribute("aria-hidden", "true");
62
+ iframe.setAttribute("tabindex", "-1");
63
+ let settled = false;
64
+ const finish = (result) => {
65
+ if (settled)
66
+ return;
67
+ settled = true;
68
+ window.removeEventListener("message", onMessage);
69
+ clearTimeout(timer);
70
+ iframe.remove();
71
+ resolve(result);
72
+ };
73
+ const onMessage = (event) => {
74
+ // Both checks matter: `source` confirms the message came from THIS iframe (not
75
+ // some other frame/extension), `origin` confirms it came from this app's own API
76
+ // origin specifically — the only origin GET {routePrefix}/login?prompt=none's
77
+ // callback ever posts from.
78
+ if (event.source !== iframe.contentWindow)
79
+ return;
80
+ if (!isSilentAuthMessage(event.data))
81
+ return;
82
+ if (event.origin !== origin) {
83
+ // Our own iframe answered, but from an origin other than the API base URL's:
84
+ // the OIDC redirect_uri is not on that origin. The message is still rejected —
85
+ // never trusted — but as a failure now, loudly, rather than ignored until the
86
+ // timeout: this can never succeed, and waiting on it only holds the app on its
87
+ // spinner.
88
+ console.error(`Silent sign-in callback posted from ${event.origin}, expected ${origin}: the OIDC ` +
89
+ `redirect_uri must be on the API base URL's origin.`);
90
+ finish("error");
91
+ return;
92
+ }
93
+ if (event.data.status === "success") {
94
+ finish("success");
95
+ }
96
+ else {
97
+ finish(event.data.detail === "login_required" ? "login_required" : "error");
98
+ }
99
+ };
100
+ window.addEventListener("message", onMessage);
101
+ const timer = setTimeout(() => finish("error"), timeoutMs);
102
+ iframe.src = url;
103
+ document.body.appendChild(iframe);
104
+ });
105
+ }
106
+ //# sourceMappingURL=silentAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silentAuth.js","sourceRoot":"","sources":["../../../src/auth/client/silentAuth.ts"],"names":[],"mappings":"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"}
@@ -1,25 +1,52 @@
1
- import type { AppSwitcherEntry } from "../shared-types.js";
1
+ import { type AppSwitcherEntry } from "../shared-types.js";
2
2
  export type AuthStatus = "loading" | "authenticated" | "unauthenticated" | "error";
3
- export interface AuthSessionState<TUser> {
4
- status: AuthStatus;
5
- user: TUser | null;
6
- apps: AppSwitcherEntry[];
7
- /** Set only when loading the session failed for a reason OTHER than being signed
8
- * out — the api errored, or a request it depends on missed its deadline. */
9
- error: Error | null;
3
+ interface AuthSessionActions {
10
4
  /** Surfaces a failed sign-out as a rejection (fail loudly, AK-16) — local state is
11
- * cleared ONLY once the api confirms it, never optimistically. A caller showing a
12
- * pending state on this promise (as a design system's AccountMenu typically does)
13
- * gets an honest failure instead of a UI that claims "signed out" while the refresh
14
- * cookie and its token family are still valid server-side. */
5
+ * cleared once the api confirms it, OR once a `logout_store_failed` response proves
6
+ * the server already revoked this session regardless (see the doc comment on the
7
+ * `logout_store_failed` branch below). A caller showing a pending state on this
8
+ * promise (as a design system's AccountMenu typically does) gets an honest failure
9
+ * instead of silence. */
15
10
  signOut: () => Promise<void>;
16
11
  /** Starts a new bootstrap load from scratch — e.g. a Retry action on the error state. */
17
12
  refetch: () => void;
18
13
  }
14
+ export interface LoadingSessionState extends AuthSessionActions {
15
+ status: "loading";
16
+ user: null;
17
+ apps: AppSwitcherEntry[];
18
+ error: null;
19
+ }
20
+ /** "authenticated" always carries a user — there is no way to construct this variant
21
+ * without one, unlike the pre-AK-18 `{ status: AuthStatus; user: TUser | null }` shape
22
+ * that forced every consumer to handle an impossible `authenticated && !user` state by
23
+ * hand (sms-app's App.tsx, before this card). */
24
+ export interface AuthenticatedSessionState<TUser> extends AuthSessionActions {
25
+ status: "authenticated";
26
+ user: TUser;
27
+ apps: AppSwitcherEntry[];
28
+ error: null;
29
+ }
30
+ export interface UnauthenticatedSessionState extends AuthSessionActions {
31
+ status: "unauthenticated";
32
+ user: null;
33
+ apps: AppSwitcherEntry[];
34
+ error: null;
35
+ }
36
+ /** Set only when loading the session failed for a reason OTHER than being signed out —
37
+ * the api errored, or a request it depends on missed its deadline. */
38
+ export interface ErrorSessionState extends AuthSessionActions {
39
+ status: "error";
40
+ user: null;
41
+ apps: AppSwitcherEntry[];
42
+ error: Error;
43
+ }
44
+ export type AuthSessionState<TUser> = LoadingSessionState | AuthenticatedSessionState<TUser> | UnauthenticatedSessionState | ErrorSessionState;
19
45
  /**
20
46
  * `TUser` defaults to `unknown` deliberately: a consuming app almost always has its
21
47
  * own richer local type (or the exact shape @flytedesk/app-kit/auth's AuthedUser
22
- * describes) it wants `GET {api}/me`'s `user` decoded as, and should pass it
23
- * explicitly (`useAuthSession<MyUser>(...)`) rather than this module guessing.
48
+ * describes) it wants `GET {api}{routePrefix}/me`'s `user` decoded as, and should pass
49
+ * it explicitly (`useAuthSession<MyUser>(...)`) rather than this module guessing.
24
50
  */
25
51
  export declare function useAuthSession<TUser = unknown>(): AuthSessionState<TUser>;
52
+ export {};
@@ -1,26 +1,33 @@
1
1
  /**
2
2
  * Generic browser session hook for `@flytedesk/app-kit/auth`'s BFF pattern: bootstraps
3
- * via POST {api}/auth/refresh (the httpOnly refresh cookie) -> GET {api}/me -> GET
4
- * {api}/apps (the "All flytedesk apps" switcher feed, now served by the plugin itself —
5
- * see ../plugin.js's GET {routePrefix}/apps). Every consuming app was on track to
6
- * re-implement this by hand (sms-app's apps/web/src/session/useAuthSession.ts, before
7
- * this card) — extracting it here (AK-16) means every one gets the StrictMode-safe
8
- * single-flight bootstrap and the fail-loudly sign-out for free, instead of each app
9
- * getting a slightly different (and possibly wrong) version of the same ~80 lines.
3
+ * via POST {api}{routePrefix}/refresh (the httpOnly refresh cookie) -> GET
4
+ * {api}{routePrefix}/me -> GET {api}{routePrefix}/apps (the "All flytedesk apps"
5
+ * switcher feed) — all three routes served by the plugin itself (see ../plugin.js) at
6
+ * the paths ../shared-types.js's route contract names, never a hand-typed string on
7
+ * either side. Every consuming app was on track to re-implement this by hand (sms-app's
8
+ * apps/web/src/session/useAuthSession.ts, before this card) — extracting it here (AK-16)
9
+ * means every one gets the StrictMode-safe single-flight bootstrap and the fail-loudly
10
+ * sign-out for free, instead of each app getting a slightly different (and possibly
11
+ * wrong) version of the same ~80 lines.
10
12
  *
11
13
  * Deliberately narrower than media-planner's SessionKit (packages/design-system/src/
12
14
  * session.ts): no silent OIDC resume, no cross-tab identity-switch reload, no RBAC
13
15
  * flag helpers. Those stay app-specific policy layered on top of this shared
14
- * mechanism — media-planner's own switch-over (the PLN-1 card this AK-16 card files)
16
+ * mechanism — media-planner's own switch-over (the PLN-1 card this AK-18 card files)
15
17
  * keeps SessionKit, just pointed at this module's apiRequest instead of its local copy.
16
18
  */
17
19
  import * as React from "react";
18
20
  import { apiRequest, ApiRequestError, setAccessToken } from "./httpClient.js";
21
+ import { AUTH_ROUTE_PREFIX, authRoutePath } from "../shared-types.js";
22
+ const REFRESH_PATH = authRoutePath(AUTH_ROUTE_PREFIX, "refresh");
23
+ const ME_PATH = authRoutePath(AUTH_ROUTE_PREFIX, "me");
24
+ const APPS_PATH = authRoutePath(AUTH_ROUTE_PREFIX, "apps");
25
+ const LOGOUT_PATH = authRoutePath(AUTH_ROUTE_PREFIX, "logout");
19
26
  /**
20
27
  * `TUser` defaults to `unknown` deliberately: a consuming app almost always has its
21
28
  * own richer local type (or the exact shape @flytedesk/app-kit/auth's AuthedUser
22
- * describes) it wants `GET {api}/me`'s `user` decoded as, and should pass it
23
- * explicitly (`useAuthSession<MyUser>(...)`) rather than this module guessing.
29
+ * describes) it wants `GET {api}{routePrefix}/me`'s `user` decoded as, and should pass
30
+ * it explicitly (`useAuthSession<MyUser>(...)`) rather than this module guessing.
24
31
  */
25
32
  export function useAuthSession() {
26
33
  const [status, setStatus] = React.useState("loading");
@@ -28,12 +35,12 @@ export function useAuthSession() {
28
35
  const [apps, setApps] = React.useState([]);
29
36
  const [error, setError] = React.useState(null);
30
37
  const [generation, setGeneration] = React.useState(0);
31
- // POST /auth/refresh ROTATES the refresh cookie on every call (reuse detection — see
32
- // @flytedesk/app-kit/auth's plugin.ts) — a second call presenting the same,
38
+ // POST {routePrefix}/refresh ROTATES the refresh cookie on every call (reuse detection
39
+ // — see @flytedesk/app-kit/auth's plugin.ts) — a second call presenting the same,
33
40
  // now-already-consumed cookie is treated as token replay and revokes the whole
34
41
  // session. React 18+'s <StrictMode> intentionally double-invokes this effect in dev
35
42
  // (mount -> cleanup -> mount, on the SAME fiber, so this ref survives it) with the
36
- // SAME `generation`, which without a guard fires two concurrent /auth/refresh calls
43
+ // SAME `generation`, which without a guard fires two concurrent refresh calls
37
44
  // against that one cookie: the loser gets "token_reuse_detected" and this hook
38
45
  // bounces to "unauthenticated" for no signed-out reason at all. Tracking which
39
46
  // generation has already started a load — rather than an unconditional `cancelled`
@@ -48,29 +55,12 @@ export function useAuthSession() {
48
55
  setStatus("loading");
49
56
  setError(null);
50
57
  try {
51
- const refreshed = await apiRequest("/auth/refresh", { method: "POST" });
58
+ const refreshed = await apiRequest(REFRESH_PATH, { method: "POST" });
52
59
  setAccessToken(refreshed.accessToken);
53
- const me = await apiRequest("/me");
60
+ const me = await apiRequest(ME_PATH);
54
61
  setUser(me.user);
55
- try {
56
- // Matches the plugin's own route (../plugin.js: `GET {routePrefix}/apps`,
57
- // routePrefix default "/auth") — the same "/auth" prefix "/auth/refresh"
58
- // above already uses. A bare "/apps" 404s against the real server (found by
59
- // sms-app's Playwright e2e, the first real consumer to hit this path with an
60
- // unmocked backend — this file's own tests mock fetch and never caught it).
61
- const appsRes = await apiRequest("/auth/apps");
62
- setApps(appsRes.apps);
63
- }
64
- catch (err) {
65
- // 403 (no permission to see the switcher) is an expected, non-error outcome
66
- // — every other failure is a real error and must propagate.
67
- if (err instanceof ApiRequestError && err.status === 403) {
68
- setApps([]);
69
- }
70
- else {
71
- throw err;
72
- }
73
- }
62
+ const appsRes = await apiRequest(APPS_PATH);
63
+ setApps(appsRes.apps);
74
64
  setStatus("authenticated");
75
65
  }
76
66
  catch (err) {
@@ -86,17 +76,43 @@ export function useAuthSession() {
86
76
  void load();
87
77
  }, [generation]);
88
78
  const signOut = React.useCallback(async () => {
89
- // Fail loudly: only clear local state once the api actually confirms sign-out —
90
- // swallowing this and clearing state regardless would leave the refresh cookie and
91
- // its token family valid server-side, silently signing the user back in on the
92
- // next load (exactly the SMS-5 code-quality gate finding this hook fixes for good).
93
- await apiRequest("/auth/logout", { method: "POST" });
79
+ try {
80
+ await apiRequest(LOGOUT_PATH, { method: "POST" });
81
+ }
82
+ catch (err) {
83
+ if (err instanceof ApiRequestError && err.code === "logout_store_failed") {
84
+ // The plugin revokes the refresh-token family FIRST and unconditionally, before
85
+ // it ever attempts the AuthStore cleanup that failed here (plugin.ts's POST
86
+ // {routePrefix}/logout) — so the server already has no valid session for this
87
+ // browser no matter how this one response is reported. Leaving local state at
88
+ // "authenticated" here would be exactly the bug the SMS-5 gate found (the design
89
+ // system's AccountMenu swallowing this rejection and showing nothing): a UI
90
+ // claiming a live session the server has already revoked. Clearing local state
91
+ // mirrors the server's actual state; re-throwing still fails loudly so a caller
92
+ // can surface "sign-out did not fully complete" rather than reporting a clean one.
93
+ setAccessToken(null);
94
+ setUser(null);
95
+ setApps([]);
96
+ setStatus("unauthenticated");
97
+ }
98
+ throw err;
99
+ }
94
100
  setAccessToken(null);
95
101
  setUser(null);
96
102
  setApps([]);
97
103
  setStatus("unauthenticated");
98
104
  }, []);
99
105
  const refetch = React.useCallback(() => setGeneration((g) => g + 1), []);
100
- return { status, user, apps, error, signOut, refetch };
106
+ const actions = { signOut, refetch };
107
+ if (status === "authenticated") {
108
+ // Invariant: the only place status flips to "authenticated" is the line right after
109
+ // setUser(me.user) above, in the same synchronous pass — so by the time a render
110
+ // observes status === "authenticated", `user` is never null.
111
+ return { ...actions, status, user: user, apps, error: null };
112
+ }
113
+ if (status === "error") {
114
+ return { ...actions, status, user: null, apps, error: error ?? new Error("unknown error") };
115
+ }
116
+ return { ...actions, status, user: null, apps, error: null };
101
117
  }
102
118
  //# sourceMappingURL=useAuthSession.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAuthSession.js","sourceRoot":"","sources":["../../../src/auth/client/useAuthSession.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useAuthSession.js","sourceRoot":"","sources":["../../../src/auth/client/useAuthSession.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,18 @@ export declare class IdpError extends Error {
16
16
  export declare class IdpUnavailableError extends IdpError {
17
17
  constructor(message: string, cause?: unknown);
18
18
  }
19
+ /**
20
+ * A call to flytedesk-id's `/token` endpoint missed its `tokenRequestTimeoutMs` deadline
21
+ * (AK-14, gap 5) — distinct from a generic `IdpUnavailableError` so a caller can tell "the
22
+ * IdP hung" apart from "the IdP answered with an error" without inspecting a message
23
+ * string. A subclass of `IdpUnavailableError`, not a sibling: every existing
24
+ * `classifyIdpError`/`instanceof IdpUnavailableError` call site already treats a timeout
25
+ * exactly like any other outage (safe to retry, not a reason to force re-login) — this
26
+ * only adds a more specific type for a caller that wants to distinguish the two.
27
+ */
28
+ export declare class IdpTimeoutError extends IdpUnavailableError {
29
+ constructor(message: string, cause?: unknown);
30
+ }
19
31
  /** flytedesk-id answered and said no: the access/refresh token it was given is no
20
32
  * longer valid — revoked, expired past recovery, or the account's access to this app
21
33
  * was pulled. The caller should force re-login, not retry. */
@@ -23,6 +23,21 @@ export class IdpUnavailableError extends IdpError {
23
23
  this.name = "IdpUnavailableError";
24
24
  }
25
25
  }
26
+ /**
27
+ * A call to flytedesk-id's `/token` endpoint missed its `tokenRequestTimeoutMs` deadline
28
+ * (AK-14, gap 5) — distinct from a generic `IdpUnavailableError` so a caller can tell "the
29
+ * IdP hung" apart from "the IdP answered with an error" without inspecting a message
30
+ * string. A subclass of `IdpUnavailableError`, not a sibling: every existing
31
+ * `classifyIdpError`/`instanceof IdpUnavailableError` call site already treats a timeout
32
+ * exactly like any other outage (safe to retry, not a reason to force re-login) — this
33
+ * only adds a more specific type for a caller that wants to distinguish the two.
34
+ */
35
+ export class IdpTimeoutError extends IdpUnavailableError {
36
+ constructor(message, cause) {
37
+ super(message, cause);
38
+ this.name = "IdpTimeoutError";
39
+ }
40
+ }
26
41
  /** flytedesk-id answered and said no: the access/refresh token it was given is no
27
42
  * longer valid — revoked, expired past recovery, or the account's access to this app
28
43
  * was pulled. The caller should force re-login, not retry. */
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/auth/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,mFAAmF;AACnF,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGf;IAFlB,YACE,OAAe,EACC,KAAe;QAE/B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,UAAK,GAAL,KAAK,CAAU;QAG/B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED;;sBAEsB;AACtB,MAAM,OAAO,mBAAoB,SAAQ,QAAQ;IAC/C,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED;;+DAE+D;AAC/D,MAAM,OAAO,qBAAsB,SAAQ,QAAQ;IACjD,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED;;uFAEuF;AACvF,MAAM,OAAO,cAAe,SAAQ,KAAK;IAGrB;IAFlB,YACE,OAAe,EACC,KAAe;QAE/B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,UAAK,GAAL,KAAK,CAAU;QAG/B,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED;;gFAEgF;AAChF,MAAM,OAAO,aAAc,SAAQ,KAAK;IAGpB;IACA;IAHlB,YACE,OAAe,EACC,MAAc,EACd,KAAe;QAE/B,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAQ;QACd,UAAK,GAAL,KAAK,CAAU;QAG/B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,GAAG,YAAY,qBAAqB,IAAI,GAAG,YAAY,mBAAmB;QAAE,OAAO,GAAG,CAAC;IAE3F,IAAI,GAAG,YAAY,aAAa,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC;QAC/E,OAAO,IAAI,qBAAqB,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,IAAI,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/auth/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,mFAAmF;AACnF,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGf;IAFlB,YACE,OAAe,EACC,KAAe;QAE/B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,UAAK,GAAL,KAAK,CAAU;QAG/B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED;;sBAEsB;AACtB,MAAM,OAAO,mBAAoB,SAAQ,QAAQ;IAC/C,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,OAAO,eAAgB,SAAQ,mBAAmB;IACtD,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED;;+DAE+D;AAC/D,MAAM,OAAO,qBAAsB,SAAQ,QAAQ;IACjD,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED;;uFAEuF;AACvF,MAAM,OAAO,cAAe,SAAQ,KAAK;IAGrB;IAFlB,YACE,OAAe,EACC,KAAe;QAE/B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,UAAK,GAAL,KAAK,CAAU;QAG/B,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED;;gFAEgF;AAChF,MAAM,OAAO,aAAc,SAAQ,KAAK;IAGpB;IACA;IAHlB,YACE,OAAe,EACC,MAAc,EACd,KAAe;QAE/B,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAQ;QACd,UAAK,GAAL,KAAK,CAAU;QAG/B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,GAAG,YAAY,qBAAqB,IAAI,GAAG,YAAY,mBAAmB;QAAE,OAAO,GAAG,CAAC;IAE3F,IAAI,GAAG,YAAY,aAAa,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC;QAC/E,OAAO,IAAI,qBAAqB,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACrD,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,IAAI,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC/C,CAAC"}
@@ -5,10 +5,26 @@
5
5
  * src/{routes/auth.ts,lib/flytedeskId.ts,plugins/auth.ts} so client id/secret, issuer,
6
6
  * redirect URI, and app id are config instead of hardcoded to one app.
7
7
  *
8
+ * This module (`@flytedesk/app-kit/auth`) is the standalone surface: types, the OIDC
9
+ * client, PKCE/token/error helpers, and the `requireAuth`/`requirePermission` guards —
10
+ * everything that does NOT need Fastify's `FastifyRequest` ambient-augmented with
11
+ * `authUser`. The plugin itself (`flytedeskAuth`, and the `declare module "fastify"`
12
+ * augmentation that comes with registering it) lives at the separate subpath
13
+ * `@flytedesk/app-kit/auth/plugin` (AK-14, gap 6): TypeScript applies a `declare module`
14
+ * augmentation program-wide the instant ANY file importing it is part of the
15
+ * compilation, even transitively — so as long as `flytedeskAuth` was re-exported from
16
+ * here too, importing just `generatePkce`/`randomState`/`signAccessToken` from this
17
+ * module forced the augmentation (and its `AuthedUser` shape) onto a consumer's WHOLE
18
+ * program, with no way to opt out — including a consumer with its own, differently
19
+ * shaped `request.authUser` who never even registers this plugin (`TS2717: Subsequent
20
+ * property declarations must have the same type`). Splitting the plugin into its own
21
+ * subpath means the augmentation only applies to a program that actually imports it.
22
+ *
8
23
  * Usage:
9
24
  *
10
25
  * import Fastify from "fastify";
11
- * import { flytedeskAuth, requireAuth, requirePermission } from "@flytedesk/app-kit/auth";
26
+ * import { flytedeskAuth } from "@flytedesk/app-kit/auth/plugin";
27
+ * import { requireAuth, requirePermission } from "@flytedesk/app-kit/auth";
12
28
  *
13
29
  * const app = Fastify();
14
30
  * await app.register(flytedeskAuth, {
@@ -26,12 +42,12 @@
26
42
  *
27
43
  * app.get("/me", { preHandler: requireAuth }, async (request) => request.authUser);
28
44
  */
29
- export { flytedeskAuth } from "./plugin.js";
30
45
  export { requireAuth, requirePermission } from "./guards.js";
31
46
  export type { AuthedUser, AuthStore, CreateIdpSessionInput, CreateRefreshTokenInput, FlytedeskAuthOptions, IdpRole, IdpSessionRow, RefreshTokenRow, UpdateIdpSessionInput, UserinfoClaims, } from "./types.js";
32
- export type { AppSwitcherEntry, IdpAuthorization, IdpTokenResponse, IdTokenClaims, OidcClient, } from "./oidc-client.js";
33
- export { createOidcClient } from "./oidc-client.js";
34
- export type { ApiErrorBody } from "./shared-types.js";
35
- export { IdpError, IdpUnavailableError, IdpAccessRevokedError, AuthStoreError, OidcHttpError, classifyIdpError, } from "./errors.js";
47
+ export type { AppSwitcherEntry, IdpAuthorization, IdpTokenResponse, IdTokenClaims, OidcClient, OidcClientOptions, } from "./oidc-client.js";
48
+ export { createOidcClient, DEFAULT_OIDC_SCOPES, DEFAULT_TOKEN_REQUEST_TIMEOUT_MS } from "./oidc-client.js";
49
+ export type { ApiErrorBody, SilentAuthMessage } from "./shared-types.js";
50
+ export { AUTH_REQUEST_DEADLINE_MS, AUTH_ROUTE_PREFIX, AUTH_ROUTE_SEGMENTS, authRoutePath, SILENT_AUTH_MESSAGE_SOURCE, type AuthRouteSegment, } from "./shared-types.js";
51
+ export { IdpError, IdpUnavailableError, IdpTimeoutError, IdpAccessRevokedError, AuthStoreError, OidcHttpError, classifyIdpError, } from "./errors.js";
36
52
  export { generatePkce, randomState, type PkcePair } from "./pkce.js";
37
53
  export { generateRefreshToken, hashRefreshToken, signAccessToken, verifyAccessToken, type AccessTokenClaims, } from "./tokens.js";