@atlasauth/pca-ciba 0.2.0 → 0.4.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.
package/README.md CHANGED
@@ -29,7 +29,7 @@ const a = agent({
29
29
  riskPolicy: { theta1: 0.3, theta2: 0.6 },
30
30
  });
31
31
 
32
- const review = reviewAction(a, 'stripe.payout', 'acct:1', { amount: 80 }, { goal: 'reconcile October refunds' });
32
+ const review = reviewAction(a, 'stripe.payout', 'acct:1', { amount: 80, currency: 'USD', payee: 'acct:supplier-9' }, { goal: 'reconcile October refunds' });
33
33
 
34
34
  if (review.kind === 'step_up' && requiresCiba(review)) {
35
35
  const broker = createCibaBroker({ onComplete: (session) => console.log(session.status) }); // omit onComplete for POLL
@@ -51,13 +51,49 @@ if (review.kind === 'step_up' && requiresCiba(review)) {
51
51
  }
52
52
  ```
53
53
 
54
+ ### Against a real OpenID Provider
55
+
56
+ `createCibaClient` talks to a provider's backchannel authentication and token endpoints (client authentication `client_secret_basic`) and implements the client side of POLL and PING:
57
+
58
+ ```ts
59
+ import { createCibaClient, verifyPingCallback } from '@atlasauth/pca-ciba';
60
+
61
+ const ciba = createCibaClient({
62
+ backchannelAuthenticationEndpoint: 'https://op.example/ciba/auth',
63
+ tokenEndpoint: 'https://op.example/token',
64
+ clientId: 'pca-agent',
65
+ clientSecret: process.env.CIBA_SECRET!,
66
+ });
67
+
68
+ const flow = await ciba.initiate(review.request, {
69
+ loginHint: 'owner@acme',
70
+ bindingMessage: { tokenOnly: true }, // for providers that restrict binding_message (see below)
71
+ stepUpScope: false, // for providers that only accept registered scopes
72
+ });
73
+ const result = await ciba.poller(flow).wait(); // 'approved' (with tokens) | 'denied' | 'expired'
74
+ ```
75
+
76
+ The poller never polls faster than the provider's `interval`, adds 5 seconds on every `slow_down` (CIBA Core 11), stops at `expires_in`, and never re-sends a settled `auth_req_id`. For PING delivery pass `delivery: 'ping'`, check the provider's callback with `verifyPingCallback`, then call `poller.notify()` (which also wakes a `poll()`/`wait()` that is sleeping) and `poll()`. Concurrent `poll()` calls share one token request; thrown fetches, 5xx replies and non-JSON replies back the next poll off (the error has `retryable: true`, and `wait()` retries up to five in a row); every request has a timeout (`timeoutMs`, default 30 s) and `wait({ signal })` / `poll({ signal })` cancel the in-flight fetch. Endpoints must be `https://`; `allowInsecureHttp: true` exists for a local test provider only. A caller-supplied `clientNotificationToken` must be at least 22 characters (128 bits). The returned tokens are the provider's response and are not verified by this package.
77
+
78
+ The message is rendered ONLY from a canonical structured action (`cibaActionFromStepUp`) in a fixed order, `Approve <verb> <amount> <CUR> to "<payee>" on <resource>` (tokenOnly: `Approve-<verb>-<amount>-<CUR>-to-<payee>-<resource>`), and fails loud rather than misstating the action (R3-05, breaking). `params` may hold only `amount` (number or plain decimal string), `currency` (3 letters, REQUIRED next to an amount and always shown; there is no `$` shorthand or USD default) and one payee field (`payee`, `to`, `recipient`, `beneficiary` or `destination`; REQUIRED next to an amount). Every other `params` key makes `toCibaAuthRequest` throw, so a hidden `amt`, `total` or `memo` can never ride along unseen. The payee is shown in full inside double quotes (at most 64 characters; control, bidirectional and invisible characters, quotes, backslashes and a "because you asked" clause are refused, not altered; tokenOnly needs an ASCII payee). The verb and resource are plain identifiers (`[A-Za-z0-9][A-Za-z0-9._:/#@+-]{0,63}`), never free text. With `bindingMessage.maxChars`, only the optional goal clause shrinks, and if the head alone does not fit it throws. Not covered: look-alike (homoglyph) characters inside an otherwise printable payee.
79
+
80
+ Provider limits to know about: Keycloak accepts a `binding_message` of at most 50 characters from a narrow ASCII set without spaces, so the default rich message is refused with `invalid_binding_message` there; use `bindingMessage: { tokenOnly: true }`. In that mode the message is all-or-nothing: the amount must be a plain non-negative decimal (no sign or exponent), the currency a 3-letter code, and if `maxChars` cannot hold the verb, amount, currency and resource in full, `toCibaAuthRequest` throws rather than sending a shortened message. A provider that does not know the per-verb scope `urn:atlas:pca:stepup:<verb>` answers `invalid_scope`; pass `stepUpScope: false`.
81
+
54
82
  `toCibaAuthRequest(stepUp, opts?)` is the pure mapping from a step-up to a CIBA request, if you want to drive your own broker or a real OpenID provider.
55
83
 
56
84
  ## Status
57
85
 
58
- The broker is in-memory and models the CIBA lifecycle without any network; a production deployment would swap in a real OpenID provider. The package computes approval envelopes and the derived trust budget and authorises nothing on its own: `cibaDecisionToCosign` is only the budget-level effect. The cryptographic cosign (FROST) is produced and verified separately, and the resource server's verifier remains the authority. Cryptography in PCA is unaudited.
86
+ Experimental. The broker is in-memory and models the CIBA lifecycle without any network; a production deployment would swap in a real OpenID provider. The package computes approval envelopes and the derived trust budget and authorises nothing on its own: `cibaDecisionToCosign` is only the budget-level effect. The cryptographic cosign (FROST) is produced and verified separately, and the resource server's verifier remains the authority. Cryptography in PCA is unaudited.
87
+
88
+ The human decision is **unauthenticated data**. `resolve` and `cibaDecisionToCosign` take the decision string as given: the client does not verify the ID token, and the approval recharges the trust budget generically without being cryptographically bound to the action the human was shown. Do not treat a CIBA approval as an authorisation of a specific action; only the verified cosign (a signature over `thresholdMessage` of that PCActn) is. Verify the provider's ID token yourself, and bind the `binding_message` to the action digest, before you let a decision recharge a budget.
89
+
90
+ What has been checked against OpenID Connect CIBA Core 1.0 (Final): the specification's own examples (committed with source URL and sha256) are replayed through the independent `openid-client` library (6.8.2), which also drives the broker end to end through a small HTTP provider written for the tests (initiate, poll through `authorization_pending`, approve, deny, expiry). The values the package emits are asserted against the specification's requirements: `auth_req_id` is unguessable (160 random bits, restricted character set), `requested_expiry` is a positive integer, `scope` carries `openid` and only valid scope-token characters, and `binding_message` is a single short line of plain text.
91
+
92
+ Validated against a real provider: Keycloak 26.8.0 (official release distribution on OpenJDK 21, CIBA enabled, a mock authentication-channel device playing the human). Ten end-to-end checks run POLL approve, deny (cancelled and unauthorized), expiry (including a late approval that the provider refuses), PING delivery with a notification-token callback, `login_hint` by username, e-mail and different case, unknown-user, bad-secret, bad-scope, `user_code` and malformed `requested_expiry` errors, `slow_down` enforcement (an early poll is answered `slow_down`; the client backs off by 5 seconds), single-use of an approved `auth_req_id`, and the `binding_message` and scope limits above. The real request and response transcripts (tokens and credentials redacted) are committed as a fixture and replayed offline on every run. The live run is gated behind `PCA_CIBA_KEYCLOAK_URL`, and the repository's `scripts/run-keycloak-ciba.sh` reproduces it end to end and tears the provider down.
93
+
94
+ Observed provider behaviour that differs from the specification: Keycloak does not honour `requested_expiry` (it returns its own `expires_in`), does not require `openid` in `scope`, reports an unknown user as `invalid_request` with description `invalid_user` rather than `unknown_user_id`, reports a bad client secret as `unauthorized_client`, and its stock user resolver supports `login_hint` only (`login_hint_token` and `id_token_hint` are refused with `invalid_user` unless a custom resolver is installed).
59
95
 
60
- Source and issues: https://github.com/Atlas-Authorization/pca
96
+ Not validated: other providers (Auth0, Descope, Stytch and others were not tried), PUSH mode, signed authentication requests, `user_code`, `login_hint_token` and `id_token_hint` against a provider that supports them, and the in-memory broker's own token-endpoint behaviour against any real client. The in-memory broker remains a test double.
61
97
 
62
98
  ## License
63
99
 
@@ -0,0 +1,120 @@
1
+ /**
2
+ * A network client for a real OpenID Provider's CIBA endpoints (OpenID CIBA Core 1.0): start a
3
+ * backchannel authentication request and wait for the human's decision by POLL or PING. It is the
4
+ * relying-party side that `createCibaBroker` (in-memory, test-double) models.
5
+ *
6
+ * Poll discipline follows CIBA Core 7.3 / 11: the client never polls faster than the `interval` the OP
7
+ * returned, adds at least 5 seconds to it on every `slow_down`, and stops at `expires_in`. A terminal
8
+ * answer (approved / denied / expired) is cached, so a settled flow is never re-sent to the token
9
+ * endpoint (an approved `auth_req_id` is single-use at real providers).
10
+ *
11
+ * The client authenticates with `client_secret_basic`. The returned tokens are the OP's response, not
12
+ * verified here: a caller that relies on the ID token must verify it with its own JOSE library.
13
+ */
14
+ import { type CibaAuthRequest, type CibaRequestOptions } from './index';
15
+ import type { StepUpRequest } from '@atlasauth/pca';
16
+ export type CibaDelivery = 'poll' | 'ping';
17
+ /** An error response from the OP (RFC 6749 5.2 / CIBA Core 13), or a malformed reply. */
18
+ export declare class CibaProviderError extends Error {
19
+ readonly error: string;
20
+ readonly errorDescription: string | undefined;
21
+ readonly status: number;
22
+ /** True for failures worth retrying (network error, timeout, 5xx, non-JSON reply); not for a definitive OP answer. */
23
+ readonly retryable: boolean;
24
+ constructor(status: number, error: string, errorDescription?: string, retryable?: boolean);
25
+ }
26
+ export interface CibaClientConfig {
27
+ backchannelAuthenticationEndpoint: string;
28
+ tokenEndpoint: string;
29
+ clientId: string;
30
+ clientSecret: string;
31
+ /** Injectable for tests. Default global `fetch`. */
32
+ fetch?: typeof fetch;
33
+ /** Injectable for tests. Default `setTimeout`. */
34
+ sleep?: (ms: number) => Promise<void>;
35
+ /** Clock in ms. Default `Date.now`. */
36
+ now?: () => number;
37
+ /** Per-request timeout in ms for every call to the OP. Default 30000. */
38
+ timeoutMs?: number;
39
+ /**
40
+ * Permit `http://` endpoints. The client secret and the `auth_req_id` travel on these requests, so this
41
+ * is for local test providers only. Default false: a non-https endpoint is rejected.
42
+ */
43
+ allowInsecureHttp?: boolean;
44
+ }
45
+ export interface CibaInitiateOptions extends CibaRequestOptions {
46
+ /** Exactly one identifying hint is allowed (CIBA Core 7.1). `loginHint` comes from {@link CibaRequestOptions}. */
47
+ loginHintToken?: string;
48
+ idTokenHint?: string;
49
+ /** `ping` sends a `client_notification_token` (generated if not given); `poll` (default) sends none. */
50
+ delivery?: CibaDelivery;
51
+ clientNotificationToken?: string;
52
+ acrValues?: string;
53
+ userCode?: string;
54
+ }
55
+ export interface CibaFlow {
56
+ /** The OP-issued `auth_req_id`. */
57
+ authReqId: string;
58
+ expiresInSec: number;
59
+ /** Interval the OP asked for, in seconds. */
60
+ intervalSec: number;
61
+ /** ms epoch the OP answered. */
62
+ startedAt: number;
63
+ delivery: CibaDelivery;
64
+ clientNotificationToken?: string;
65
+ /** The request PCA derived (scope, binding message, ...) that was sent. */
66
+ request: CibaAuthRequest;
67
+ }
68
+ export interface CibaTokenResponse {
69
+ access_token: string;
70
+ token_type: string;
71
+ expires_in?: number;
72
+ id_token?: string;
73
+ refresh_token?: string;
74
+ scope?: string;
75
+ }
76
+ export type CibaPollResult = {
77
+ status: 'pending';
78
+ intervalSec: number;
79
+ } | {
80
+ status: 'approved';
81
+ tokens: CibaTokenResponse;
82
+ } | {
83
+ status: 'denied';
84
+ error: string;
85
+ } | {
86
+ status: 'expired';
87
+ };
88
+ export interface CibaPoller {
89
+ /** Current poll interval in seconds (grows on `slow_down`). */
90
+ intervalSec(): number;
91
+ /** Sleep until the interval allows it, then ask the token endpoint once. */
92
+ poll(opts?: {
93
+ signal?: AbortSignal;
94
+ }): Promise<CibaPollResult>;
95
+ /** Poll until the flow settles; honours `signal`. */
96
+ wait(opts?: {
97
+ signal?: AbortSignal;
98
+ }): Promise<Exclude<CibaPollResult, {
99
+ status: 'pending';
100
+ }>>;
101
+ /** A PING arrived: the next `poll()` may go out immediately, and a `poll()`/`wait()` that is sleeping wakes now. */
102
+ notify(): void;
103
+ }
104
+ export interface CibaClient {
105
+ initiate(stepUp: StepUpRequest, opts?: CibaInitiateOptions): Promise<CibaFlow>;
106
+ poller(flow: CibaFlow): CibaPoller;
107
+ }
108
+ /** A fresh `client_notification_token` (CIBA Core 7.1: at least 128 bits of entropy, at most 1024 chars). */
109
+ export declare function newClientNotificationToken(): string;
110
+ export declare function createCibaClient(cfg: CibaClientConfig): CibaClient;
111
+ /**
112
+ * Validate a PING callback (CIBA Core 10.2): `Authorization: Bearer <client_notification_token>` and a JSON
113
+ * body `{ "auth_req_id": ... }`. Returns the `auth_req_id` only when the bearer matches the token sent for
114
+ * that flow (constant-time comparison); throws otherwise. The callback carries no decision: call
115
+ * `poller.notify()` and then `poll()` to fetch it.
116
+ */
117
+ export declare function verifyPingCallback(input: {
118
+ authorization: string | undefined;
119
+ body: unknown;
120
+ }, flow: Pick<CibaFlow, 'authReqId' | 'clientNotificationToken'>): string;
package/dist/client.js ADDED
@@ -0,0 +1,330 @@
1
+ "use strict";
2
+ /**
3
+ * A network client for a real OpenID Provider's CIBA endpoints (OpenID CIBA Core 1.0): start a
4
+ * backchannel authentication request and wait for the human's decision by POLL or PING. It is the
5
+ * relying-party side that `createCibaBroker` (in-memory, test-double) models.
6
+ *
7
+ * Poll discipline follows CIBA Core 7.3 / 11: the client never polls faster than the `interval` the OP
8
+ * returned, adds at least 5 seconds to it on every `slow_down`, and stops at `expires_in`. A terminal
9
+ * answer (approved / denied / expired) is cached, so a settled flow is never re-sent to the token
10
+ * endpoint (an approved `auth_req_id` is single-use at real providers).
11
+ *
12
+ * The client authenticates with `client_secret_basic`. The returned tokens are the OP's response, not
13
+ * verified here: a caller that relies on the ID token must verify it with its own JOSE library.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.CibaProviderError = void 0;
17
+ exports.newClientNotificationToken = newClientNotificationToken;
18
+ exports.createCibaClient = createCibaClient;
19
+ exports.verifyPingCallback = verifyPingCallback;
20
+ const node_crypto_1 = require("node:crypto");
21
+ const index_1 = require("./index");
22
+ const CIBA_GRANT = 'urn:openid:params:grant-type:ciba';
23
+ /** CIBA Core 11: on `slow_down` the interval grows by at least 5 seconds. */
24
+ const SLOW_DOWN_STEP_SEC = 5;
25
+ /** An error response from the OP (RFC 6749 5.2 / CIBA Core 13), or a malformed reply. */
26
+ class CibaProviderError extends Error {
27
+ error;
28
+ errorDescription;
29
+ status;
30
+ /** True for failures worth retrying (network error, timeout, 5xx, non-JSON reply); not for a definitive OP answer. */
31
+ retryable;
32
+ constructor(status, error, errorDescription, retryable = false) {
33
+ super(`ciba: provider returned ${String(status)} ${error}${errorDescription !== undefined ? ` (${errorDescription})` : ''}`);
34
+ this.name = 'CibaProviderError';
35
+ this.status = status;
36
+ this.error = error;
37
+ this.errorDescription = errorDescription;
38
+ this.retryable = retryable;
39
+ }
40
+ }
41
+ exports.CibaProviderError = CibaProviderError;
42
+ async function readJson(res) {
43
+ const text = await res.text();
44
+ let parsed;
45
+ try {
46
+ parsed = JSON.parse(text);
47
+ }
48
+ catch {
49
+ throw new CibaProviderError(res.status, 'invalid_response', 'the provider reply was not JSON', true);
50
+ }
51
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
52
+ throw new CibaProviderError(res.status, 'invalid_response', 'the provider reply was not a JSON object');
53
+ }
54
+ return parsed;
55
+ }
56
+ function providerError(status, body) {
57
+ const error = typeof body['error'] === 'string' ? body['error'] : 'invalid_response';
58
+ const desc = typeof body['error_description'] === 'string' ? body['error_description'] : undefined;
59
+ return new CibaProviderError(status, error, desc);
60
+ }
61
+ function positiveInt(value, name, status) {
62
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) {
63
+ throw new CibaProviderError(status, 'invalid_response', `${name} must be a positive integer`);
64
+ }
65
+ return value;
66
+ }
67
+ const MIN_NOTIFICATION_TOKEN_CHARS = 22; // 22 base64url chars = 132 bits
68
+ const MAX_NOTIFICATION_TOKEN_CHARS = 1024;
69
+ const DEFAULT_TIMEOUT_MS = 30_000;
70
+ const MAX_BACKOFF_MS = 60_000;
71
+ const MAX_CONSECUTIVE_TRANSIENT = 5;
72
+ function checkEndpoint(name, value, allowInsecure) {
73
+ let u;
74
+ try {
75
+ u = new URL(value);
76
+ }
77
+ catch {
78
+ throw new RangeError(`ciba: ${name} is not a valid URL`);
79
+ }
80
+ if (u.protocol === 'https:')
81
+ return;
82
+ if (u.protocol === 'http:' && allowInsecure)
83
+ return;
84
+ throw new RangeError(`ciba: ${name} must be an https:// URL (set allowInsecureHttp only for a local test provider)`);
85
+ }
86
+ /** A fresh `client_notification_token` (CIBA Core 7.1: at least 128 bits of entropy, at most 1024 chars). */
87
+ function newClientNotificationToken() {
88
+ return (0, node_crypto_1.randomBytes)(32).toString('base64url');
89
+ }
90
+ function createCibaClient(cfg) {
91
+ const doFetch = cfg.fetch ?? fetch;
92
+ const clock = cfg.now ?? Date.now;
93
+ const timeoutMs = cfg.timeoutMs ?? DEFAULT_TIMEOUT_MS;
94
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
95
+ throw new RangeError('ciba: timeoutMs must be a positive number');
96
+ checkEndpoint('backchannelAuthenticationEndpoint', cfg.backchannelAuthenticationEndpoint, cfg.allowInsecureHttp === true);
97
+ checkEndpoint('tokenEndpoint', cfg.tokenEndpoint, cfg.allowInsecureHttp === true);
98
+ const basic = `Basic ${Buffer.from(`${encodeURIComponent(cfg.clientId)}:${encodeURIComponent(cfg.clientSecret)}`).toString('base64')}`;
99
+ /** One request with a hard timeout; the caller's signal also cancels the fetch itself. */
100
+ async function post(url, form, signal) {
101
+ if (signal?.aborted === true)
102
+ throw new Error('ciba: wait aborted');
103
+ const ac = new AbortController();
104
+ const timer = setTimeout(() => ac.abort(new Error('timeout')), timeoutMs);
105
+ const onAbort = () => ac.abort(new Error('aborted'));
106
+ signal?.addEventListener('abort', onAbort, { once: true });
107
+ try {
108
+ return await doFetch(url, {
109
+ method: 'POST',
110
+ headers: { authorization: basic, 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' },
111
+ body: form.toString(),
112
+ signal: ac.signal,
113
+ });
114
+ }
115
+ catch (e) {
116
+ if (signal !== undefined && signal.aborted)
117
+ throw new Error('ciba: wait aborted');
118
+ throw new CibaProviderError(0, 'network_error', e instanceof Error ? e.message : 'request failed', true);
119
+ }
120
+ finally {
121
+ clearTimeout(timer);
122
+ signal?.removeEventListener('abort', onAbort);
123
+ }
124
+ }
125
+ return {
126
+ async initiate(stepUp, opts = {}) {
127
+ const hints = [opts.loginHint, opts.loginHintToken, opts.idTokenHint].filter((h) => h !== undefined);
128
+ if (hints.length !== 1) {
129
+ throw new RangeError('ciba: exactly one of loginHint, loginHintToken, idTokenHint is required (CIBA Core 7.1)');
130
+ }
131
+ const delivery = opts.delivery ?? 'poll';
132
+ const notificationToken = delivery === 'ping' ? (opts.clientNotificationToken ?? newClientNotificationToken()) : undefined;
133
+ if (opts.clientNotificationToken !== undefined &&
134
+ (opts.clientNotificationToken.length < MIN_NOTIFICATION_TOKEN_CHARS ||
135
+ opts.clientNotificationToken.length > MAX_NOTIFICATION_TOKEN_CHARS ||
136
+ !/^[\x21-\x7E]+$/.test(opts.clientNotificationToken))) {
137
+ throw new RangeError(`ciba: clientNotificationToken must be ${String(MIN_NOTIFICATION_TOKEN_CHARS)}..${String(MAX_NOTIFICATION_TOKEN_CHARS)} printable characters (>= 128 bits, CIBA Core 7.1)`);
138
+ }
139
+ const request = (0, index_1.toCibaAuthRequest)(stepUp, opts);
140
+ const form = new URLSearchParams({
141
+ scope: request.scope,
142
+ binding_message: request.binding_message,
143
+ requested_expiry: String(request.requested_expiry),
144
+ });
145
+ if (opts.loginHint !== undefined)
146
+ form.set('login_hint', opts.loginHint);
147
+ if (opts.loginHintToken !== undefined)
148
+ form.set('login_hint_token', opts.loginHintToken);
149
+ if (opts.idTokenHint !== undefined)
150
+ form.set('id_token_hint', opts.idTokenHint);
151
+ if (opts.acrValues !== undefined)
152
+ form.set('acr_values', opts.acrValues);
153
+ if (opts.userCode !== undefined)
154
+ form.set('user_code', opts.userCode);
155
+ if (notificationToken !== undefined)
156
+ form.set('client_notification_token', notificationToken);
157
+ const res = await post(cfg.backchannelAuthenticationEndpoint, form);
158
+ const body = await readJson(res);
159
+ if (!res.ok)
160
+ throw providerError(res.status, body);
161
+ const authReqId = body['auth_req_id'];
162
+ if (typeof authReqId !== 'string' || authReqId.length === 0) {
163
+ throw new CibaProviderError(res.status, 'invalid_response', 'auth_req_id missing');
164
+ }
165
+ const expiresInSec = positiveInt(body['expires_in'], 'expires_in', res.status);
166
+ // CIBA Core 7.3: `interval` is optional and defaults to 5 seconds.
167
+ const intervalSec = body['interval'] === undefined ? 5 : positiveInt(body['interval'], 'interval', res.status);
168
+ return {
169
+ authReqId,
170
+ expiresInSec,
171
+ intervalSec,
172
+ startedAt: clock(),
173
+ delivery,
174
+ ...(notificationToken !== undefined ? { clientNotificationToken: notificationToken } : {}),
175
+ request,
176
+ };
177
+ },
178
+ poller(flow) {
179
+ let intervalMs = flow.intervalSec * 1000;
180
+ let nextAt = flow.startedAt + intervalMs;
181
+ const deadline = flow.startedAt + flow.expiresInSec * 1000;
182
+ let settled;
183
+ let inflight;
184
+ let wake;
185
+ let failures = 0;
186
+ /** Sleep `ms`, ending early on notify() or on the caller's abort. */
187
+ async function pause(ms, signal) {
188
+ let timer;
189
+ let onAbort;
190
+ const racers = [
191
+ cfg.sleep !== undefined ? cfg.sleep(ms) : new Promise((r) => { timer = setTimeout(r, ms); }),
192
+ new Promise((r) => { wake = r; }),
193
+ ];
194
+ if (signal !== undefined) {
195
+ racers.push(new Promise((r) => { onAbort = () => r(); signal.addEventListener('abort', onAbort, { once: true }); }));
196
+ }
197
+ try {
198
+ await Promise.race(racers);
199
+ }
200
+ finally {
201
+ wake = undefined;
202
+ if (timer !== undefined)
203
+ clearTimeout(timer);
204
+ if (signal !== undefined && onAbort !== undefined)
205
+ signal.removeEventListener('abort', onAbort);
206
+ }
207
+ if (signal?.aborted === true)
208
+ throw new Error('ciba: wait aborted');
209
+ }
210
+ function backoff() {
211
+ failures += 1;
212
+ const delay = Math.min(MAX_BACKOFF_MS, intervalMs * 2 ** Math.min(failures, 6));
213
+ nextAt = clock() + delay;
214
+ }
215
+ async function doPoll(signal) {
216
+ if (settled !== undefined)
217
+ return settled;
218
+ const wait = nextAt - clock();
219
+ if (wait > 0)
220
+ await pause(wait, signal);
221
+ if (settled !== undefined)
222
+ return settled;
223
+ if (clock() >= deadline) {
224
+ settled = { status: 'expired' };
225
+ return settled;
226
+ }
227
+ let res;
228
+ let body;
229
+ try {
230
+ res = await post(cfg.tokenEndpoint, new URLSearchParams({ grant_type: CIBA_GRANT, auth_req_id: flow.authReqId }), signal);
231
+ if (res.status >= 500) {
232
+ await res.text().catch(() => '');
233
+ throw new CibaProviderError(res.status, 'server_error', 'the provider answered with a server error', true);
234
+ }
235
+ body = await readJson(res);
236
+ }
237
+ catch (e) {
238
+ if (e instanceof CibaProviderError && e.retryable)
239
+ backoff();
240
+ throw e;
241
+ }
242
+ failures = 0;
243
+ nextAt = clock() + intervalMs;
244
+ if (res.ok) {
245
+ const at = body['access_token'];
246
+ const tt = body['token_type'];
247
+ if (typeof at !== 'string' || typeof tt !== 'string') {
248
+ throw new CibaProviderError(res.status, 'invalid_response', 'token response lacks access_token / token_type');
249
+ }
250
+ settled = { status: 'approved', tokens: body };
251
+ return settled;
252
+ }
253
+ const err = providerError(res.status, body);
254
+ switch (err.error) {
255
+ case 'authorization_pending':
256
+ return { status: 'pending', intervalSec: intervalMs / 1000 };
257
+ case 'slow_down':
258
+ intervalMs += SLOW_DOWN_STEP_SEC * 1000;
259
+ nextAt = clock() + intervalMs;
260
+ return { status: 'pending', intervalSec: intervalMs / 1000 };
261
+ case 'access_denied':
262
+ settled = { status: 'denied', error: err.error };
263
+ return settled;
264
+ case 'expired_token':
265
+ settled = { status: 'expired' };
266
+ return settled;
267
+ default:
268
+ throw err;
269
+ }
270
+ }
271
+ const poller = {
272
+ intervalSec: () => intervalMs / 1000,
273
+ notify() {
274
+ nextAt = clock();
275
+ wake?.();
276
+ },
277
+ // Concurrent callers share one in-flight request: an approved auth_req_id is single-use at real OPs.
278
+ poll(opts = {}) {
279
+ if (inflight !== undefined)
280
+ return inflight;
281
+ const p = doPoll(opts.signal).finally(() => {
282
+ inflight = undefined;
283
+ });
284
+ inflight = p;
285
+ return p;
286
+ },
287
+ async wait(opts = {}) {
288
+ let transient = 0;
289
+ for (;;) {
290
+ if (opts.signal?.aborted === true)
291
+ throw new Error('ciba: wait aborted');
292
+ try {
293
+ const r = await poller.poll(opts.signal !== undefined ? { signal: opts.signal } : {});
294
+ transient = 0;
295
+ if (r.status !== 'pending')
296
+ return r;
297
+ }
298
+ catch (e) {
299
+ if (!(e instanceof CibaProviderError) || !e.retryable || ++transient >= MAX_CONSECUTIVE_TRANSIENT)
300
+ throw e;
301
+ }
302
+ }
303
+ },
304
+ };
305
+ return poller;
306
+ },
307
+ };
308
+ }
309
+ /**
310
+ * Validate a PING callback (CIBA Core 10.2): `Authorization: Bearer <client_notification_token>` and a JSON
311
+ * body `{ "auth_req_id": ... }`. Returns the `auth_req_id` only when the bearer matches the token sent for
312
+ * that flow (constant-time comparison); throws otherwise. The callback carries no decision: call
313
+ * `poller.notify()` and then `poll()` to fetch it.
314
+ */
315
+ function verifyPingCallback(input, flow) {
316
+ if (flow.clientNotificationToken === undefined)
317
+ throw new Error('ciba: flow was not started in ping mode');
318
+ const m = /^bearer +(\S+)$/i.exec(input.authorization ?? '');
319
+ if (m === null)
320
+ throw new Error('ciba: ping callback lacks a bearer token');
321
+ const digest = (v) => (0, node_crypto_1.createHash)('sha256').update(v).digest();
322
+ if (!(0, node_crypto_1.timingSafeEqual)(digest(m[1]), digest(flow.clientNotificationToken))) {
323
+ throw new Error('ciba: ping callback bearer does not match the client_notification_token');
324
+ }
325
+ const id = typeof input.body === 'object' && input.body !== null ? input.body['auth_req_id'] : undefined;
326
+ if (typeof id !== 'string' || id !== flow.authReqId)
327
+ throw new Error('ciba: ping callback auth_req_id does not match this flow');
328
+ return id;
329
+ }
330
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -53,16 +53,66 @@ export interface CibaAuthRequest {
53
53
  }
54
54
  /** Options shared by `toCibaAuthRequest` and the broker's `start`. */
55
55
  export interface CibaRequestOptions {
56
- /** Override the `requested_expiry` (seconds). Default 300. */
56
+ /** Override the `requested_expiry` (seconds). Must be a positive integer (CIBA Core 7.1). Default 300. */
57
57
  expiresInSec?: number;
58
+ /**
59
+ * Use this `auth_req_id` instead of the deterministic derivation. CIBA Core 7.3 requires an
60
+ * `auth_req_id` to carry at least 128 bits of entropy and use only `A-Za-z0-9.-_`; the default
61
+ * derivation hashes PUBLIC step-up fields and is therefore NOT secret. {@link createCibaBroker}
62
+ * always supplies a fresh random value here.
63
+ */
64
+ authReqId?: string;
65
+ /**
66
+ * Include the per-verb step-up scope `urn:atlas:pca:stepup:<verb>` next to `openid`. Default `true`.
67
+ * Real providers reject scopes that are not registered for the client (Keycloak answers `invalid_scope`),
68
+ * and per-verb scopes are open-ended, so set `false` when the OP has no such scope; the verb is then
69
+ * carried by the `binding_message` alone.
70
+ */
71
+ stepUpScope?: boolean;
58
72
  /** CIBA `login_hint` for the OP: the identifier of the human to notify. */
59
73
  loginHint?: string;
74
+ /**
75
+ * Constrain the `binding_message` for providers that restrict it. CIBA Core 7.1 only says the message
76
+ * SHOULD be short and plain text; real providers enforce more (Keycloak 26.8 accepts at most 50
77
+ * characters from a narrow ASCII set and rejects spaces, `$`, `:`, `,`, `(`, `)` and `@` with
78
+ * `invalid_binding_message`). With `tokenOnly` the message is rendered as hyphen-joined tokens using only
79
+ * `A-Za-z0-9._-` (for example `Approve-stripe.payout-80-USD-acct-1`), cut at `maxChars` on a token
80
+ * boundary. Default: the rich one-line form, unchanged.
81
+ */
82
+ bindingMessage?: {
83
+ maxChars?: number;
84
+ tokenOnly?: boolean;
85
+ };
86
+ /** CIBA `interval` (seconds, positive integer): minimum poll spacing the OP/broker enforces. Default 5. */
87
+ interval?: number;
60
88
  /**
61
89
  * The agent's human-facing name used in the binding message ("because you asked <actsFor> to …"),
62
90
  * e.g. `"support-bot"`. Default `"your agent"`.
63
91
  */
64
92
  actsFor?: string;
65
93
  }
94
+ /**
95
+ * R3-05: the approval message is rendered ONLY from this canonical, fixed-order structure. Anything in the
96
+ * step-up that is not part of it is not shown, so it is REFUSED: the person must never approve something the
97
+ * message does not display.
98
+ */
99
+ export interface CibaAction {
100
+ verb: string;
101
+ /** Plain non-negative decimal, shown in full. Present together with `currency` and `payee`. */
102
+ amount?: string;
103
+ /** 3-letter currency code, upper-cased; always shown next to the amount. */
104
+ currency?: string;
105
+ /** Who receives the value. Required whenever `amount` is present. */
106
+ payee?: string;
107
+ resource: string;
108
+ }
109
+ /** Plain-ASCII rendering of a payee: itself when ASCII, else the punycode of each word (or \u escapes when IDNA refuses). */
110
+ export declare function asciiForm(payee: string): string;
111
+ /**
112
+ * Build the canonical action from a step-up. Fail loud (R3-05): unknown `params` keys, a nested or malformed amount,
113
+ * a missing currency or payee next to an amount, two different payee fields, and non-identifier verb / resource all throw.
114
+ */
115
+ export declare function cibaActionFromStepUp(stepUp: StepUpRequest): CibaAction;
66
116
  /**
67
117
  * Map a PCA {@link StepUpRequest} to a {@link CibaAuthRequest}. Pure: derives a stable `auth_req_id`
68
118
  * from the step-up id, renders the `binding_message`, and carries the scope / expiry / poll interval.
@@ -97,6 +147,19 @@ export interface CibaBrokerOptions {
97
147
  */
98
148
  onComplete?: (session: CibaSession) => void;
99
149
  }
150
+ /**
151
+ * What a CIBA token request (CIBA Core 10.1 / 11) gets back from the broker: `ok` exactly once on
152
+ * approval; otherwise the OAuth `error` the token endpoint must return. After `slow_down`, `interval`
153
+ * is the new minimum spacing in seconds (the old one plus 5, CIBA Core 11).
154
+ */
155
+ export type CibaTokenOutcome = {
156
+ ok: true;
157
+ session: CibaSession;
158
+ } | {
159
+ ok: false;
160
+ error: 'authorization_pending' | 'slow_down' | 'access_denied' | 'expired_token' | 'invalid_grant';
161
+ interval: number;
162
+ };
100
163
  /** The in-memory CIBA broker returned by {@link createCibaBroker}. */
101
164
  export interface CibaBroker {
102
165
  /** Begin a backchannel flow for a step-up; returns the CIBA auth request (status starts `pending`). */
@@ -105,6 +168,13 @@ export interface CibaBroker {
105
168
  poll(auth_req_id: string): {
106
169
  status: CibaStatus;
107
170
  };
171
+ /**
172
+ * Token-endpoint semantics for POLL mode (CIBA Core 11): enforces the poll `interval` (a request that
173
+ * arrives sooner than `interval` seconds after the previous one gets `slow_down` and the interval grows
174
+ * by 5 s), and makes a successful approval single-use (a replayed `auth_req_id` gets `invalid_grant`).
175
+ * Unlike {@link CibaBroker.poll}, which is a side-effect-free status read.
176
+ */
177
+ token(auth_req_id: string): CibaTokenOutcome;
108
178
  /** The human acts out-of-band: approve or deny. Fires `onComplete` (PING/PUSH) if configured. */
109
179
  resolve(auth_req_id: string, decision: 'approve' | 'deny', by: string): CibaSession;
110
180
  /** Read a full session snapshot (status computed at read time), or `undefined` if unknown. */
@@ -145,3 +215,4 @@ export interface CibaCosignResult {
145
215
  export declare function cibaDecisionToCosign(decision: 'approve' | 'deny', budget: TrustBudget, policy: RiskPolicy, now: number, opts?: {
146
216
  full?: boolean;
147
217
  }): CibaCosignResult;
218
+ export * from './client';
package/dist/index.js CHANGED
@@ -26,41 +26,227 @@
26
26
  * Everything here is pure or in-memory and authorises nothing on its own: the resource server's verifier
27
27
  * (`requirePCA` / the adjudicator) and the FROST cosigners remain the run-time authority.
28
28
  */
29
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
30
+ if (k2 === undefined) k2 = k;
31
+ var desc = Object.getOwnPropertyDescriptor(m, k);
32
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
33
+ desc = { enumerable: true, get: function() { return m[k]; } };
34
+ }
35
+ Object.defineProperty(o, k2, desc);
36
+ }) : (function(o, m, k, k2) {
37
+ if (k2 === undefined) k2 = k;
38
+ o[k2] = m[k];
39
+ }));
40
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
41
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
42
+ };
29
43
  Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.asciiForm = asciiForm;
45
+ exports.cibaActionFromStepUp = cibaActionFromStepUp;
30
46
  exports.toCibaAuthRequest = toCibaAuthRequest;
31
47
  exports.createCibaBroker = createCibaBroker;
32
48
  exports.requiresCiba = requiresCiba;
33
49
  exports.cibaDecisionToCosign = cibaDecisionToCosign;
50
+ const node_crypto_1 = require("node:crypto");
51
+ const node_url_1 = require("node:url");
34
52
  const pca_1 = require("@atlasauth/pca");
35
53
  const DEFAULT_EXPIRY_SEC = 300;
36
54
  const DEFAULT_INTERVAL_SEC = 5;
37
- /** A short "$480" / "480 EUR" fragment from a step-up's params, or "" when there's no amount. */
38
- function amountFragment(params) {
39
- if (!params)
40
- return '';
41
- const amt = params['amount'];
42
- if (typeof amt !== 'number' || !Number.isFinite(amt))
43
- return '';
44
- const cur = params['currency'];
45
- if (typeof cur === 'string' && cur.length > 0 && cur.toLowerCase() !== 'usd') {
46
- return ` ${amt} ${cur.toUpperCase()}`;
55
+ /** CIBA Core 7.3: `auth_req_id` characters are restricted to A-Z a-z 0-9 . - _ . */
56
+ const AUTH_REQ_ID_RE = /^[A-Za-z0-9._-]+$/;
57
+ /** 128 bits of base64url is 22 characters; reject anything shorter (CIBA Core 7.3). */
58
+ const MIN_AUTH_REQ_ID_LEN = 22;
59
+ /** RFC 6749 3.3 `scope-token`: %x21 / %x23-5B / %x5D-7E. */
60
+ const SCOPE_TOKEN_RE = /^[\x21\x23-\x5B\x5D-\x7E]+$/;
61
+ /** The goal text is free-form (agent / user supplied); cap what is shown to a person on a small screen. */
62
+ const MAX_GOAL_CHARS = 100;
63
+ /**
64
+ * Make untrusted text safe for a one-line, plain-text approval prompt (CIBA Core 7.1: `binding_message`
65
+ * SHOULD be short and use a limited set of plain-text characters): control characters, line breaks and
66
+ * bidi / zero-width formatting characters become spaces, runs of whitespace collapse.
67
+ */
68
+ function plainText(value) {
69
+ return value
70
+ .replace(/[\p{Cc}\p{Cf}\u2028\u2029]/gu, ' ')
71
+ .replace(/\s+/gu, ' ')
72
+ .trim();
73
+ }
74
+ function clip(value, max) {
75
+ const chars = Array.from(value);
76
+ return chars.length <= max ? value : `${chars.slice(0, max - 1).join('')}…`;
77
+ }
78
+ const FORGED_CLAUSE_RE = /because\s+you\s+asked/i;
79
+ /** Keys of `params` the renderer displays. Every other key makes the request unrenderable. */
80
+ const PAYEE_KEYS = ['payee', 'to', 'recipient', 'beneficiary', 'destination'];
81
+ const DISPLAYED_PARAM_KEYS = new Set(['amount', 'currency', ...PAYEE_KEYS]);
82
+ /** verb / resource are identifiers, never free text: this class cannot hold quotes, spaces, bidi or control characters. */
83
+ const IDENT_RE = /^[A-Za-z0-9][A-Za-z0-9._:/#@+-]{0,63}$/;
84
+ const MAX_PAYEE_CHARS = 64;
85
+ /** Control, format (bidi, zero-width, joiners), line/paragraph separators, specials and unpaired surrogates. */
86
+ const UNDISPLAYABLE_RE = /[\p{Cc}\p{Cf}\p{Cs}\p{Co}\p{Cn}\u2028\u2029\uFFF9-\uFFFD]/u;
87
+ function identifier(value, what) {
88
+ if (typeof value !== 'string' || !IDENT_RE.test(value)) {
89
+ throw new RangeError(`ciba: the step-up ${what} must be a plain identifier (letters, digits and . _ : / # @ + -, at most 64 characters); refusing to render free text in the approval message`);
90
+ }
91
+ if (FORGED_CLAUSE_RE.test(value))
92
+ throw new RangeError(`ciba: the step-up ${what} contains an approval-explanation clause`);
93
+ return value;
94
+ }
95
+ function payeeText(value) {
96
+ if (typeof value !== 'string')
97
+ throw new RangeError('ciba: the payee must be a string (a nested value cannot be shown to the approver)');
98
+ const t = value.normalize('NFC');
99
+ if (t.length === 0 || t.trim() !== t || /\s{2,}/u.test(t))
100
+ throw new RangeError('ciba: the payee must be non-empty without leading, trailing or repeated whitespace');
101
+ if (UNDISPLAYABLE_RE.test(t) || /[\r\n\t]/.test(t))
102
+ throw new RangeError('ciba: the payee contains control, bidirectional or invisible characters and cannot be displayed');
103
+ if (/["\\]/.test(t))
104
+ throw new RangeError('ciba: the payee must not contain quote or backslash characters');
105
+ if (Array.from(t).length > MAX_PAYEE_CHARS)
106
+ throw new RangeError(`ciba: the payee is longer than ${String(MAX_PAYEE_CHARS)} characters; refusing to truncate what the person approves`);
107
+ if (FORGED_CLAUSE_RE.test(t))
108
+ throw new RangeError('ciba: the payee contains an approval-explanation clause');
109
+ // R4-13 lookalikes. NFC already composed what can be composed, so any combining mark left is a stacked/decorative one;
110
+ // compatibility forms (fullwidth, ligatures, circled letters) differ from their plain look-alike under NFKC; and a payee
111
+ // that mixes scripts ("Pаypal", Cyrillic а) is the classic homograph. All are refused. A payee in ONE non-Latin script is
112
+ // legitimate, so it is allowed but rendered WITH its ASCII form (see `asciiForm`) so a whole-script look-alike
113
+ // ("аррlе" in Cyrillic) is never shown as just the text it imitates.
114
+ if (/\p{M}/u.test(t))
115
+ throw new RangeError('ciba: the payee contains combining marks and cannot be shown unambiguously');
116
+ if (t.normalize('NFKC') !== t)
117
+ throw new RangeError('ciba: the payee contains compatibility/fullwidth characters that imitate plain ones');
118
+ if (scriptsOf(t).size > 1)
119
+ throw new RangeError('ciba: the payee mixes writing systems (a homograph risk); refusing so the person never approves a look-alike');
120
+ return t;
121
+ }
122
+ /** Writing-system groups present in `t` (letters only; digits, punctuation and spaces are script-neutral). */
123
+ const SCRIPT_GROUPS = [
124
+ ['latin', /\p{Script=Latin}/u],
125
+ ['cyrillic', /\p{Script=Cyrillic}/u],
126
+ ['greek', /\p{Script=Greek}/u],
127
+ ['armenian', /\p{Script=Armenian}/u],
128
+ ['hebrew', /\p{Script=Hebrew}/u],
129
+ ['arabic', /\p{Script=Arabic}/u],
130
+ ['cjk', /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Bopomofo}]/u],
131
+ ['indic-thai', /[\p{Script=Devanagari}\p{Script=Bengali}\p{Script=Tamil}\p{Script=Thai}\p{Script=Georgian}]/u],
132
+ ];
133
+ function scriptsOf(t) {
134
+ const found = new Set();
135
+ for (const ch of t) {
136
+ if (!/\p{L}/u.test(ch))
137
+ continue;
138
+ const g = SCRIPT_GROUPS.find(([, re]) => re.test(ch));
139
+ found.add(g ? g[0] : 'other:' + ch);
140
+ }
141
+ return found;
142
+ }
143
+ /** Plain-ASCII rendering of a payee: itself when ASCII, else the punycode of each word (or \u escapes when IDNA refuses). */
144
+ function asciiForm(payee) {
145
+ if (/^[\x20-\x7E]*$/.test(payee))
146
+ return payee;
147
+ return payee
148
+ .split(' ')
149
+ .map((w) => (/^[\x20-\x7E]*$/.test(w) ? w : (0, node_url_1.domainToASCII)(w) || Array.from(w, (c) => (c.codePointAt(0) > 0x7e ? `\\u{${c.codePointAt(0).toString(16)}}` : c)).join('')))
150
+ .join(' ');
151
+ }
152
+ /**
153
+ * Build the canonical action from a step-up. Fail loud (R3-05): unknown `params` keys, a nested or malformed amount,
154
+ * a missing currency or payee next to an amount, two different payee fields, and non-identifier verb / resource all throw.
155
+ */
156
+ function cibaActionFromStepUp(stepUp) {
157
+ const params = (stepUp.params ?? {});
158
+ if (params === null || typeof params !== 'object' || Array.isArray(params))
159
+ throw new RangeError('ciba: step-up params must be an object');
160
+ const unknown = Object.keys(params).filter((k) => !DISPLAYED_PARAM_KEYS.has(k) && params[k] !== undefined);
161
+ if (unknown.length > 0) {
162
+ throw new RangeError(`ciba: step-up params field(s) ${unknown.join(', ')} are not displayed in the approval message (displayable: amount, currency, ${PAYEE_KEYS.join('/')}); refusing so the person never approves a hidden field`);
163
+ }
164
+ const present = (v) => v !== undefined && v !== null;
165
+ const action = { verb: identifier(stepUp.verb, 'verb'), resource: identifier(stepUp.resource, 'resource') };
166
+ const payeeKeys = PAYEE_KEYS.filter((k) => present(params[k]));
167
+ if (payeeKeys.length > 1)
168
+ throw new RangeError(`ciba: more than one payee field (${payeeKeys.join(', ')}) in params; the approver would see only one`);
169
+ const hasAmount = present(params['amount']);
170
+ if (hasAmount) {
171
+ const amt = params['amount'];
172
+ const text = typeof amt === 'number' && Number.isFinite(amt) ? String(amt) : typeof amt === 'string' ? amt : '';
173
+ if (!PLAIN_AMOUNT_RE.test(text))
174
+ throw new RangeError('ciba: the binding message needs a plain non-negative decimal amount (no sign or exponent)');
175
+ const cur = params['currency'];
176
+ if (typeof cur !== 'string' || !CURRENCY_RE.test(cur))
177
+ throw new RangeError('ciba: the binding message needs a 3-letter currency code next to the amount');
178
+ if (payeeKeys.length === 0)
179
+ throw new RangeError('ciba: an amount needs a payee (payee / to / recipient / beneficiary / destination) so the approver sees who receives it');
180
+ action.amount = text;
181
+ action.currency = cur.toUpperCase();
47
182
  }
48
- return ` $${amt}`;
183
+ else if (present(params['currency'])) {
184
+ throw new RangeError('ciba: a currency without an amount cannot be displayed');
185
+ }
186
+ if (payeeKeys.length === 1)
187
+ action.payee = payeeText(params[payeeKeys[0]]);
188
+ return action;
49
189
  }
190
+ /** A plain non-negative decimal: no sign, no exponent, no leading junk. */
191
+ const PLAIN_AMOUNT_RE = /^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/;
192
+ const CURRENCY_RE = /^[A-Za-z]{3}$/;
50
193
  /**
51
- * Build the CIBA `binding_message`: the one-line human-readable approval context. Combines the
52
- * verb + optional amount + resource with the goal-lineage ("because you asked <agent> to '<goal>'").
53
- * Falls back to the salted goal commitment when the plaintext goal isn't carried on the step-up, so
54
- * lineage is always present.
194
+ * Build the CIBA `binding_message`: the one-line human-readable approval context, in the FIXED order
195
+ * `Approve <verb> <amount> <CUR> to "<payee>" on <resource>`. The head is built from the canonical action and is NEVER
196
+ * truncated; only the optional goal-lineage clause ("because you asked <agent> to '<goal>'") may shrink or drop to fit
197
+ * `maxChars`. If the head alone does not fit, throw. Falls back to the salted goal commitment when the plaintext goal
198
+ * isn't carried on the step-up.
55
199
  */
56
- function bindingMessage(stepUp, actsFor) {
57
- const head = `Approve ${stepUp.verb}${amountFragment(stepUp.params)} on ${stepUp.resource}`;
58
- const goalText = stepUp.goal !== undefined && stepUp.goal.length > 0
59
- ? stepUp.goal
200
+ function bindingMessage(stepUp, actsFor, maxChars) {
201
+ const a = cibaActionFromStepUp(stepUp);
202
+ const head = `Approve ${a.verb}` +
203
+ (a.amount !== undefined ? ` ${a.amount} ${a.currency}` : '') +
204
+ (a.payee !== undefined ? ` to "${a.payee}"${/^[\x20-\x7E]*$/.test(a.payee) ? '' : ` [non-ASCII name; ASCII form: ${asciiForm(a.payee)}]`}` : '') +
205
+ ` on ${a.resource}`;
206
+ const limit = maxChars ?? Infinity;
207
+ if (Array.from(head).length > limit) {
208
+ throw new RangeError(`ciba: the binding message needs ${String(Array.from(head).length)} characters but bindingMessage.maxChars is ${String(maxChars)}; refusing to truncate what the person approves`);
209
+ }
210
+ const plainGoal = stepUp.goal !== undefined ? plainText(stepUp.goal).replace(/['\u2018\u2019]/g, '\u2032') : '';
211
+ const goalText = plainGoal.length > 0
212
+ ? clip(plainGoal, MAX_GOAL_CHARS)
60
213
  : stepUp.goalCommit.length > 0
61
214
  ? `goal ${stepUp.goalCommit.slice(0, 10)}…`
62
215
  : undefined;
63
- return goalText === undefined ? head : `${head} — because you asked ${actsFor} to '${goalText}'`;
216
+ if (goalText === undefined)
217
+ return head;
218
+ const prefix = ` — because you asked ${actsFor} to '`;
219
+ const room = limit - Array.from(head + prefix).length - 1;
220
+ if (room >= Array.from(goalText).length)
221
+ return `${head}${prefix}${goalText}'`;
222
+ if (room >= 8)
223
+ return `${head}${prefix}${clip(goalText, room)}'`;
224
+ return head;
225
+ }
226
+ function tokenise(value) {
227
+ return value.replace(/[^A-Za-z0-9._-]+/g, '-').replace(/-{2,}/g, '-').replace(/^[-.]+|[-.]+$/g, '');
228
+ }
229
+ /**
230
+ * The compact, provider-safe rendering used by `bindingMessage: { tokenOnly: true }`, same fixed order:
231
+ * `Approve-<verb>-<amount>-<CUR>-to-<payee>-<resource>`. The message is the human's only view of what they approve,
232
+ * so it is all-or-nothing: the same canonical action as the rich form, a payee that is not plain ASCII (tokenising
233
+ * would silently drop characters) or a limit that would cut any field throws instead of sending a different message.
234
+ */
235
+ function compactBindingMessage(stepUp, maxChars) {
236
+ const a = cibaActionFromStepUp(stepUp);
237
+ if (a.payee !== undefined && !/^[\x20-\x7E]+$/.test(a.payee)) {
238
+ throw new RangeError('ciba: tokenOnly binding message needs an ASCII payee (tokenising would drop characters the person must see)');
239
+ }
240
+ const raw = ['Approve', a.verb, ...(a.amount !== undefined ? [a.amount, a.currency] : []), ...(a.payee !== undefined ? ['to', a.payee] : []), a.resource];
241
+ const parts = raw.map(tokenise);
242
+ if (parts.some((p) => p.length === 0)) {
243
+ throw new RangeError('ciba: tokenOnly binding message cannot represent the verb, payee or resource');
244
+ }
245
+ const out = parts.join('-');
246
+ if (out.length > maxChars) {
247
+ throw new RangeError(`ciba: the binding message needs ${String(out.length)} characters but bindingMessage.maxChars is ${String(maxChars)}; refusing to truncate what the person approves`);
248
+ }
249
+ return out;
64
250
  }
65
251
  /**
66
252
  * Map a PCA {@link StepUpRequest} to a {@link CibaAuthRequest}. Pure: derives a stable `auth_req_id`
@@ -68,14 +254,39 @@ function bindingMessage(stepUp, actsFor) {
68
254
  * Does not start a flow or notify anyone — that's the broker.
69
255
  */
70
256
  function toCibaAuthRequest(stepUp, opts = {}) {
71
- const actsFor = opts.actsFor !== undefined && opts.actsFor.length > 0 ? opts.actsFor : 'your agent';
72
- const auth_req_id = (0, pca_1.hashCanonical)({ d: 'atlas-pca/ciba/auth_req_id/v1', stepUp: stepUp.id });
257
+ const actsFor = opts.actsFor !== undefined && plainText(opts.actsFor).length > 0 ? clip(plainText(opts.actsFor), 40) : 'your agent';
258
+ const requestedExpiry = opts.expiresInSec !== undefined ? opts.expiresInSec : DEFAULT_EXPIRY_SEC;
259
+ if (!Number.isSafeInteger(requestedExpiry) || requestedExpiry <= 0) {
260
+ throw new RangeError(`ciba: requested_expiry must be a positive integer number of seconds, got ${String(requestedExpiry)}`);
261
+ }
262
+ // The verb is spliced into ONE scope-token. A space in it would otherwise smuggle in extra scope tokens.
263
+ const stepUpScope = `urn:atlas:pca:stepup:${stepUp.verb}`;
264
+ if (!SCOPE_TOKEN_RE.test(stepUpScope)) {
265
+ throw new RangeError('ciba: the step-up verb cannot be carried as an OAuth scope-token (RFC 6749 3.3)');
266
+ }
267
+ const scope = opts.stepUpScope === false ? 'openid' : `openid ${stepUpScope}`;
268
+ const interval = opts.interval !== undefined ? opts.interval : DEFAULT_INTERVAL_SEC;
269
+ if (!Number.isSafeInteger(interval) || interval <= 0) {
270
+ throw new RangeError(`ciba: interval must be a positive integer number of seconds, got ${String(interval)}`);
271
+ }
272
+ const auth_req_id = opts.authReqId ?? (0, pca_1.hashCanonical)({ d: 'atlas-pca/ciba/auth_req_id/v1', stepUp: stepUp.id });
273
+ if (!AUTH_REQ_ID_RE.test(auth_req_id) || auth_req_id.length < MIN_AUTH_REQ_ID_LEN || auth_req_id.length > 1024) {
274
+ throw new RangeError('ciba: auth_req_id must use only A-Za-z0-9.-_ and be 22..1024 characters (CIBA Core 7.3)');
275
+ }
276
+ const bm = opts.bindingMessage;
277
+ if (bm?.maxChars !== undefined && (!Number.isSafeInteger(bm.maxChars) || bm.maxChars < 1)) {
278
+ throw new RangeError('ciba: bindingMessage.maxChars must be a positive integer');
279
+ }
280
+ const maxChars = bm?.maxChars ?? 50;
281
+ const binding_message = bm?.tokenOnly === true
282
+ ? compactBindingMessage(stepUp, maxChars)
283
+ : bindingMessage(stepUp, actsFor, bm?.maxChars);
73
284
  return {
74
285
  auth_req_id,
75
- binding_message: bindingMessage(stepUp, actsFor),
76
- scope: `openid urn:atlas:pca:stepup:${stepUp.verb}`,
77
- requested_expiry: opts.expiresInSec !== undefined ? opts.expiresInSec : DEFAULT_EXPIRY_SEC,
78
- interval: DEFAULT_INTERVAL_SEC,
286
+ binding_message,
287
+ scope,
288
+ requested_expiry: requestedExpiry,
289
+ interval,
79
290
  ...(opts.loginHint !== undefined ? { login_hint: opts.loginHint } : {}),
80
291
  stepUpId: stepUp.id,
81
292
  tier: stepUp.tier,
@@ -112,16 +323,30 @@ function snapshot(s, now) {
112
323
  function createCibaBroker(opts = {}) {
113
324
  const clock = opts.now ?? Date.now;
114
325
  const sessions = new Map();
326
+ /** step-up id -> the auth_req_id of its live (pending or approved) flow, so a repeated `start` is idempotent. */
327
+ const liveByStepUp = new Map();
115
328
  return {
116
329
  start(stepUp, reqOpts) {
117
- const request = toCibaAuthRequest(stepUp, reqOpts);
118
- const createdAt = clock();
330
+ const now = clock();
331
+ const existingId = liveByStepUp.get(stepUp.id);
332
+ const existing = existingId !== undefined ? sessions.get(existingId) : undefined;
333
+ if (existing !== undefined) {
334
+ const st = effectiveStatus(existing, now);
335
+ // Never reset a flow that is still waiting or already approved: that would silently discard a decision.
336
+ if (st === 'pending' || st === 'approved')
337
+ return existing.request;
338
+ }
339
+ // CIBA Core 7.3: an unguessable auth_req_id (160 random bits, base64url) per flow.
340
+ const request = toCibaAuthRequest(stepUp, { ...reqOpts, authReqId: (0, node_crypto_1.randomBytes)(20).toString('base64url') });
341
+ const createdAt = now;
119
342
  const stored = {
120
343
  request,
121
344
  createdAt,
122
345
  expiresAt: createdAt + request.requested_expiry * 1000,
346
+ interval: request.interval,
123
347
  };
124
348
  sessions.set(request.auth_req_id, stored);
349
+ liveByStepUp.set(stepUp.id, request.auth_req_id);
125
350
  return request;
126
351
  },
127
352
  poll(auth_req_id) {
@@ -130,6 +355,29 @@ function createCibaBroker(opts = {}) {
130
355
  throw new Error(`ciba: unknown auth_req_id ${auth_req_id}`);
131
356
  return { status: effectiveStatus(s, clock()) };
132
357
  },
358
+ token(auth_req_id) {
359
+ const s = sessions.get(auth_req_id);
360
+ if (!s)
361
+ return { ok: false, error: 'invalid_grant', interval: DEFAULT_INTERVAL_SEC };
362
+ const now = clock();
363
+ if (s.consumed)
364
+ return { ok: false, error: 'invalid_grant', interval: s.interval };
365
+ const tooSoon = s.lastPollAt !== undefined && now - s.lastPollAt < s.interval * 1000;
366
+ s.lastPollAt = now;
367
+ if (tooSoon) {
368
+ s.interval += 5;
369
+ return { ok: false, error: 'slow_down', interval: s.interval };
370
+ }
371
+ const st = effectiveStatus(s, now);
372
+ if (st === 'pending')
373
+ return { ok: false, error: 'authorization_pending', interval: s.interval };
374
+ if (st === 'denied')
375
+ return { ok: false, error: 'access_denied', interval: s.interval };
376
+ if (st === 'expired')
377
+ return { ok: false, error: 'expired_token', interval: s.interval };
378
+ s.consumed = true;
379
+ return { ok: true, session: snapshot(s, now) };
380
+ },
133
381
  resolve(auth_req_id, decision, by) {
134
382
  const s = sessions.get(auth_req_id);
135
383
  if (!s)
@@ -178,4 +426,5 @@ function cibaDecisionToCosign(decision, budget, policy, now, opts = {}) {
178
426
  return { budget: (0, pca_1.applyCosign)(budget, policy, now, opts), cosigned: true };
179
427
  return { budget, cosigned: false };
180
428
  }
429
+ __exportStar(require("./client"), exports);
181
430
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlasauth/pca-ciba",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "CIBA (Client-Initiated Backchannel Authentication) bridge for PCA FROST step-up: turn a step-up request into a backchannel human-approval flow with risk-adaptive gating.",
5
5
  "keywords": [
6
6
  "ciba",
@@ -32,9 +32,10 @@
32
32
  "./package.json": "./package.json"
33
33
  },
34
34
  "dependencies": {
35
- "@atlasauth/pca": "0.2.0"
35
+ "@atlasauth/pca": "0.4.0"
36
36
  },
37
37
  "devDependencies": {
38
+ "openid-client": "6.8.2",
38
39
  "typescript": "^5.6.3",
39
40
  "vitest": "^2.1.4"
40
41
  },