@camstack/types 1.2.430 → 1.2.432
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/CHANGELOG.md +12 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +436 -4
- package/dist/index.mjs +431 -5
- package/dist/interfaces/config-ui.d.ts +7 -0
- package/dist/vendor-login-proxy/index.d.ts +3 -0
- package/dist/vendor-login-proxy/login-proxy.d.ts +53 -0
- package/dist/vendor-login-proxy/prefix.d.ts +11 -0
- package/dist/vendor-login-proxy/url-rewrite.d.ts +68 -0
- package/package.json +1 -1
|
@@ -996,6 +996,13 @@ export interface ConfigAccountSignInField extends ConfigFieldBase {
|
|
|
996
996
|
readonly providerLabel: string;
|
|
997
997
|
readonly beginAction: string;
|
|
998
998
|
readonly completeAction: string;
|
|
999
|
+
/**
|
|
1000
|
+
* Set when the hub captures the vendor redirect itself (the login runs through the hub's
|
|
1001
|
+
* login proxy): the form asks this action while the login tab is open. It answers
|
|
1002
|
+
* `{ pending: true }` until the sign-in finished, then the fields `completeAction` returns.
|
|
1003
|
+
* The paste box stays as the fallback when the page does not finish.
|
|
1004
|
+
*/
|
|
1005
|
+
readonly claimAction?: string;
|
|
999
1006
|
readonly signOutAction: string;
|
|
1000
1007
|
/** Non-secret settings key holding the account label. */
|
|
1001
1008
|
readonly accountKey: string;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { VendorLoginProxy, type LoginProxyAttemptSpec, type LoginProxyResponseView, type LoginProxyVerdict, type VendorLoginProxyOptions, } from './login-proxy.js';
|
|
2
|
+
export { rewriteBody, rewriteLocation, rewriteReferer, rewriteSetCookie, type LoginProxyRoute, type LoginProxyScheme, type LoginProxyUpstream, } from './url-rewrite.js';
|
|
3
|
+
export { VENDOR_LOGIN_PROXY_PREFIX } from './prefix.js';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { DataPlaneHandler } from '../interfaces/addon-data-plane.js';
|
|
2
|
+
import type { IScopedLogger } from '../interfaces/logging.js';
|
|
3
|
+
import { type OauthLandingOutcome } from '../utils/oauth-landing.js';
|
|
4
|
+
import { type LoginProxyUpstream } from './url-rewrite.js';
|
|
5
|
+
/** What the terminal function sees of one upstream response. Never logged. */
|
|
6
|
+
export interface LoginProxyResponseView {
|
|
7
|
+
readonly status: number;
|
|
8
|
+
/** The vendor URL that was requested. */
|
|
9
|
+
readonly url: string;
|
|
10
|
+
/** The raw `Location` the vendor sent (a custom app scheme is visible here), or null. */
|
|
11
|
+
readonly location: string | null;
|
|
12
|
+
readonly setCookies: readonly string[];
|
|
13
|
+
/** The body when it is small text (HTML/JSON/JS), else null. */
|
|
14
|
+
readonly bodyText: string | null;
|
|
15
|
+
}
|
|
16
|
+
export type LoginProxyVerdict<T> = {
|
|
17
|
+
readonly kind: 'continue';
|
|
18
|
+
} | {
|
|
19
|
+
readonly kind: 'done';
|
|
20
|
+
readonly payload: T;
|
|
21
|
+
};
|
|
22
|
+
export interface LoginProxyAttemptSpec<T> {
|
|
23
|
+
/** The attempt id the addon already minted (a sign-in attempt id); the path capability. */
|
|
24
|
+
readonly attemptId: string;
|
|
25
|
+
readonly expiresAtMs: number;
|
|
26
|
+
readonly upstreams: readonly LoginProxyUpstream[];
|
|
27
|
+
/** Called for every upstream response of this attempt. */
|
|
28
|
+
readonly terminal: (view: LoginProxyResponseView) => LoginProxyVerdict<T> | Promise<LoginProxyVerdict<T>>;
|
|
29
|
+
/** Finish the sign-in with the payload; the outcome picks the result page the browser lands on. */
|
|
30
|
+
readonly onDone: (payload: T) => Promise<OauthLandingOutcome>;
|
|
31
|
+
}
|
|
32
|
+
export interface VendorLoginProxyOptions {
|
|
33
|
+
readonly addonId: string;
|
|
34
|
+
readonly logger: IScopedLogger;
|
|
35
|
+
readonly now?: () => number;
|
|
36
|
+
/** Injectable for tests; defaults to the platform `fetch`. */
|
|
37
|
+
readonly fetchFn?: typeof fetch;
|
|
38
|
+
}
|
|
39
|
+
export declare class VendorLoginProxy {
|
|
40
|
+
#private;
|
|
41
|
+
constructor(options: VendorLoginProxyOptions);
|
|
42
|
+
/** The `DataPlaneHandler` to pass to `ctx.dataPlane.serve({ prefix: VENDOR_LOGIN_PROXY_PREFIX, access: 'admin', … })`. */
|
|
43
|
+
readonly handler: DataPlaneHandler;
|
|
44
|
+
/** Register an attempt. A one-shot capability: refused hosts are rejected up front. */
|
|
45
|
+
open<T>(spec: LoginProxyAttemptSpec<T>): void;
|
|
46
|
+
close(attemptId: string): void;
|
|
47
|
+
isOpen(attemptId: string): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* The hub-relative path that opens `vendorUrl` through the proxy for this attempt:
|
|
50
|
+
* the URL the browser is sent to. Null when the host is not in the attempt's list.
|
|
51
|
+
*/
|
|
52
|
+
entryPath(attemptId: string, vendorUrl: string): string | null;
|
|
53
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The data-plane prefix every `VendorLoginProxy` is served under
|
|
3
|
+
* (`/addon/<addonId>/vendor-login/<attemptId>/<vendorHost>/…`).
|
|
4
|
+
*
|
|
5
|
+
* The hub consults an addon's data-plane prefixes BEFORE its HTTP routes, so a
|
|
6
|
+
* prefix equal to the first segment of a route the addon serves swallows that
|
|
7
|
+
* route. A sign-in addon serves `/login/start|status|commit|cancel`; the proxy
|
|
8
|
+
* therefore lives on a segment no addon route uses. One constant, so the entry
|
|
9
|
+
* path, the mount and the docs cannot drift apart.
|
|
10
|
+
*/
|
|
11
|
+
export declare const VENDOR_LOGIN_PROXY_PREFIX = "vendor-login";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure rewriting for the vendor login proxy (D955).
|
|
3
|
+
*
|
|
4
|
+
* The browser talks to the hub under `<basePath>/<vendorHost>/<path>`; the vendor
|
|
5
|
+
* is reached on its real host. Everything here maps ONE way or the other and
|
|
6
|
+
* never touches the network, so the rules are testable without a socket.
|
|
7
|
+
*
|
|
8
|
+
* What is rewritten, and what is deliberately left alone:
|
|
9
|
+
* - a vendor URL that STARTS a value (an attribute, a JSON string, a header)
|
|
10
|
+
* becomes a proxy path;
|
|
11
|
+
* - a vendor URL that is the VALUE of a query parameter (`?return_to=https://…`,
|
|
12
|
+
* or its percent-encoded form) is left alone: the vendor reads it back as the
|
|
13
|
+
* place to return to, and a proxy path there breaks its own redirect check.
|
|
14
|
+
*/
|
|
15
|
+
export type LoginProxyScheme = 'https' | 'http';
|
|
16
|
+
export interface LoginProxyUpstream {
|
|
17
|
+
/** `host` or `host:port`, lower-case. The only hosts the proxy will ever dial. */
|
|
18
|
+
readonly host: string;
|
|
19
|
+
/** `http` is accepted for a loopback host only (a local helper, a test vendor). */
|
|
20
|
+
readonly scheme: LoginProxyScheme;
|
|
21
|
+
}
|
|
22
|
+
/** One attempt's view of the proxy: where it is mounted and which hosts it may reach. */
|
|
23
|
+
export interface LoginProxyRoute {
|
|
24
|
+
/** `<forwarded prefix>/addon/<addonId>/<prefix>/<attemptId>` — no trailing slash. */
|
|
25
|
+
readonly basePath: string;
|
|
26
|
+
readonly upstreams: readonly LoginProxyUpstream[];
|
|
27
|
+
}
|
|
28
|
+
export interface ParsedProxyPath {
|
|
29
|
+
readonly attemptId: string;
|
|
30
|
+
readonly host: string;
|
|
31
|
+
/** Path after the host, always starting with `/`. */
|
|
32
|
+
readonly pathname: string;
|
|
33
|
+
/** `?…` or the empty string. */
|
|
34
|
+
readonly search: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function hostWithoutPort(host: string): string;
|
|
37
|
+
export declare function isLoopbackHost(host: string): boolean;
|
|
38
|
+
/** An allow-list entry is valid when it is a bare host[:port] and `http` only targets loopback. */
|
|
39
|
+
export declare function isValidUpstream(upstream: LoginProxyUpstream): boolean;
|
|
40
|
+
export declare function findUpstream(route: LoginProxyRoute, host: string): LoginProxyUpstream | undefined;
|
|
41
|
+
/** `<basePath>/<host><pathname><search>` — what the browser is sent to. */
|
|
42
|
+
export declare function proxyPathFor(route: LoginProxyRoute, host: string, pathname: string, search?: string): string;
|
|
43
|
+
/** Split `/<attemptId>/<host>/<rest…>[?query]` (the data-plane view of a request). */
|
|
44
|
+
export declare function parseProxyPath(rawUrl: string): ParsedProxyPath | null;
|
|
45
|
+
/** The vendor URL a proxied request targets, or null when the host would not stay `host`. */
|
|
46
|
+
export declare function upstreamUrlFor(upstream: LoginProxyUpstream, parsed: ParsedProxyPath): URL | null;
|
|
47
|
+
/** A Location (or any URL value) to a vendor host becomes a proxy path; anything else is unchanged. */
|
|
48
|
+
export declare function rewriteLocation(location: string, currentUpstream: LoginProxyUpstream, route: LoginProxyRoute): string;
|
|
49
|
+
export type BodyKind = 'html' | 'css' | 'other';
|
|
50
|
+
/**
|
|
51
|
+
* Rewrite vendor URLs in a text body: absolute (`https://host/…`), protocol-relative
|
|
52
|
+
* (`//host/…`) and JSON-escaped (`https:\/\/host\/…`) when they start a value; and, in
|
|
53
|
+
* HTML/CSS, root-relative references (`href="/x"`, `url(/x)`) which would otherwise
|
|
54
|
+
* escape the proxy mount and land on the hub.
|
|
55
|
+
*/
|
|
56
|
+
export declare function rewriteBody(body: string, kind: BodyKind, currentUpstream: LoginProxyUpstream, route: LoginProxyRoute): string;
|
|
57
|
+
/**
|
|
58
|
+
* A vendor `Set-Cookie` for the browser: no `Domain` (the cookie belongs to the hub
|
|
59
|
+
* origin), `Path` narrowed to this attempt's prefix, `Secure` kept (the hub is HTTPS).
|
|
60
|
+
*/
|
|
61
|
+
export declare function rewriteSetCookie(cookie: string, route: LoginProxyRoute): string;
|
|
62
|
+
/**
|
|
63
|
+
* A browser `Referer` that points into this proxy, turned back into the vendor URL it
|
|
64
|
+
* stands for. A Referer that is not ours is dropped (null): it names the hub.
|
|
65
|
+
*/
|
|
66
|
+
export declare function rewriteReferer(referer: string, route: LoginProxyRoute): string | null;
|
|
67
|
+
/** Cookies the browser holds for the hub itself must never reach a vendor. */
|
|
68
|
+
export declare function filterForwardedCookies(cookieHeader: string): string;
|