@qauth-labs/plugin-sdk 0.0.0-stage → 0.2.0-rc.1
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/LICENSE +201 -0
- package/README.md +148 -2
- package/dist/host.d.ts +24 -0
- package/dist/host.js +24 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +31 -0
- package/dist/lib/auth-method.d.ts +424 -0
- package/dist/lib/auth-method.js +1 -0
- package/dist/lib/context.d.ts +163 -0
- package/dist/lib/context.js +390 -0
- package/dist/lib/contract.d.ts +274 -0
- package/dist/lib/contract.js +285 -0
- package/dist/lib/credential-provider.d.ts +86 -0
- package/dist/lib/credential-provider.js +15 -0
- package/dist/lib/engine.d.ts +125 -0
- package/dist/lib/engine.js +367 -0
- package/dist/lib/errors.d.ts +49 -0
- package/dist/lib/errors.js +63 -0
- package/dist/lib/parameters.d.ts +21 -0
- package/dist/lib/parameters.js +62 -0
- package/dist/lib/validate.d.ts +55 -0
- package/dist/lib/validate.js +265 -0
- package/dist/testing/contract-kit.d.ts +116 -0
- package/dist/testing/contract-kit.js +526 -0
- package/dist/testing/fetch-guard.d.ts +38 -0
- package/dist/testing/fetch-guard.js +49 -0
- package/dist/testing/index.d.ts +12 -0
- package/dist/testing/index.js +1 -0
- package/package.json +60 -3
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fixed values of the AuthMethod contract (#599, ADR-003 as amended,
|
|
3
|
+
* ADR-018 §4, ADR-019 Decision 12).
|
|
4
|
+
*
|
|
5
|
+
* EXPERIMENTAL. The AuthMethod contract is the plugin API of ADR-018 §4: it is
|
|
6
|
+
* public and documented, so others can write methods against it, but it stays
|
|
7
|
+
* outside the 1.0 stability promise and may change in a minor release. It
|
|
8
|
+
* becomes stable in 1.x. QAuth's experimental-feature registry lists it as
|
|
9
|
+
* `plugin-api`.
|
|
10
|
+
*
|
|
11
|
+
* This package imports no other QAuth package, so the few values it shares
|
|
12
|
+
* with the server's other contracts (the Interaction API's version label and
|
|
13
|
+
* display rule, the credential types QAuth writes itself) are written out
|
|
14
|
+
* here. The server checks at boot, and in its tests, that each still equals
|
|
15
|
+
* its source.
|
|
16
|
+
*
|
|
17
|
+
* @packageDocumentation
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The contract's name, as it appears in {@link AUTH_METHOD_CONTRACT}.
|
|
21
|
+
*
|
|
22
|
+
* @experimental
|
|
23
|
+
*/
|
|
24
|
+
export const AUTH_METHOD_CONTRACT_NAME = 'qauth-auth-method';
|
|
25
|
+
/**
|
|
26
|
+
* The contract's version: a release candidate until QAuth 1.0.0
|
|
27
|
+
* (ADR-018 §5). A breaking change raises the release-candidate number, so one
|
|
28
|
+
* identifier never names two contracts.
|
|
29
|
+
*
|
|
30
|
+
* @experimental
|
|
31
|
+
*/
|
|
32
|
+
export const AUTH_METHOD_CONTRACT_VERSION = '1-rc.0';
|
|
33
|
+
/**
|
|
34
|
+
* The contract a method declares it is written against, as a format id:
|
|
35
|
+
* `qauth-auth-method/1-rc.0`. The registry refuses a method that declares
|
|
36
|
+
* another one, so a method built for another version fails the boot instead of
|
|
37
|
+
* running against a contract it does not know.
|
|
38
|
+
*
|
|
39
|
+
* @experimental
|
|
40
|
+
*/
|
|
41
|
+
export const AUTH_METHOD_CONTRACT = `${AUTH_METHOD_CONTRACT_NAME}/${AUTH_METHOD_CONTRACT_VERSION}`;
|
|
42
|
+
/**
|
|
43
|
+
* The export a plugin package names its method by: `authMethod`, an
|
|
44
|
+
* AuthMethod object. A CommonJS package may put it under `default`.
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* // index.ts of @example/qauth-plugin-corp-sso
|
|
48
|
+
* export const authMethod: AuthMethod<CorpSsoConfig, CorpSsoTransaction> = { … };
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @experimental
|
|
52
|
+
*/
|
|
53
|
+
export const AUTH_METHOD_PLUGIN_EXPORT = 'authMethod';
|
|
54
|
+
/**
|
|
55
|
+
* The Interaction API's version label, which every step name carries
|
|
56
|
+
* (ADR-018 §5): `v1-rc.0` until QAuth 1.0.0.
|
|
57
|
+
*/
|
|
58
|
+
const INTERACTION_API_VERSION = 'v1-rc.0';
|
|
59
|
+
/**
|
|
60
|
+
* The upstream step: the Interaction API step through which a person signs in
|
|
61
|
+
* at an upstream identity provider. The method starts an upstream transaction
|
|
62
|
+
* (`AuthMethod.start`), the browser goes to the provider, and the
|
|
63
|
+
* method's callback route hands the provider's answer back to the engine.
|
|
64
|
+
*
|
|
65
|
+
* Step names are QAuth identifiers that carry the Interaction API's version
|
|
66
|
+
* (ADR-018 §5): `urn:qauth:ia:upstream:v1-rc.0` until 1.0.0. The step's params
|
|
67
|
+
* and its place in ADR-017's step table come with the upstream sign-in flow
|
|
68
|
+
* (#639).
|
|
69
|
+
*
|
|
70
|
+
* @experimental
|
|
71
|
+
*/
|
|
72
|
+
export const UPSTREAM_STEP_URN = `urn:qauth:ia:upstream:${INTERACTION_API_VERSION}`;
|
|
73
|
+
/**
|
|
74
|
+
* Every step a method may contribute in this version of the contract, by URN.
|
|
75
|
+
* Only the upstream step today; a later version may add others (an LDAP form,
|
|
76
|
+
* for instance).
|
|
77
|
+
*
|
|
78
|
+
* @experimental
|
|
79
|
+
*/
|
|
80
|
+
export const AUTH_METHOD_STEP_URNS = Object.freeze([UPSTREAM_STEP_URN]);
|
|
81
|
+
/**
|
|
82
|
+
* The root every method's routes are mounted under, on the realm host. A
|
|
83
|
+
* method's own prefix is `/upstream/<type>` ({@link authMethodRoutePrefix}),
|
|
84
|
+
* so each provider has its own callback URL, and a response meant for one
|
|
85
|
+
* provider cannot be replayed at another.
|
|
86
|
+
*
|
|
87
|
+
* No version in the path: the callback URL is registered at the upstream
|
|
88
|
+
* provider, and a path that changed at 1.0 would break every registration.
|
|
89
|
+
*
|
|
90
|
+
* @experimental
|
|
91
|
+
*/
|
|
92
|
+
export const AUTH_METHOD_ROUTE_ROOT = '/upstream';
|
|
93
|
+
/**
|
|
94
|
+
* The prefix a method's routes are mounted under: `/upstream/<type>`.
|
|
95
|
+
*
|
|
96
|
+
* @experimental
|
|
97
|
+
*/
|
|
98
|
+
export function authMethodRoutePrefix(type) {
|
|
99
|
+
if (!AUTH_METHOD_TYPE_PATTERN.test(type)) {
|
|
100
|
+
throw new TypeError('not an AuthMethod type');
|
|
101
|
+
}
|
|
102
|
+
return `${AUTH_METHOD_ROUTE_ROOT}/${type}`;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* An AuthMethod's `type`: lower-case ASCII letters, digits and `_`, starting
|
|
106
|
+
* with a letter, at most 63 characters. It is the registry key, the value
|
|
107
|
+
* written to `user_credentials.provider_type`, the `source` of the attributes
|
|
108
|
+
* the method extracts and a segment of its route prefix, so it must be safe in
|
|
109
|
+
* all four. A type with `:` or `/` (an ID-JAG issuer type, `oidc_<issuer URL>`)
|
|
110
|
+
* can never match.
|
|
111
|
+
*
|
|
112
|
+
* @experimental
|
|
113
|
+
*/
|
|
114
|
+
export const AUTH_METHOD_TYPE_PATTERN = /^[a-z][a-z0-9_]{0,62}$/;
|
|
115
|
+
/**
|
|
116
|
+
* An AuthMethod's `id`: the stable name of the method's implementation, such
|
|
117
|
+
* as `oidc` or `corp-sso`. Lower-case ASCII letters, digits, `_` and `-`,
|
|
118
|
+
* starting with a letter, at most 63 characters.
|
|
119
|
+
*
|
|
120
|
+
* @experimental
|
|
121
|
+
*/
|
|
122
|
+
export const AUTH_METHOD_ID_PATTERN = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
123
|
+
/**
|
|
124
|
+
* Types an AuthMethod may not take: the credential types and the attribute
|
|
125
|
+
* source QAuth writes itself, built or planned. A method under one of them
|
|
126
|
+
* would read as that built-in credential, and `wallet` would outrank every
|
|
127
|
+
* other attribute source in claim resolution (`rankAttributeSource`).
|
|
128
|
+
*
|
|
129
|
+
* @experimental
|
|
130
|
+
*/
|
|
131
|
+
export const RESERVED_AUTH_METHOD_TYPES = Object.freeze([
|
|
132
|
+
'password',
|
|
133
|
+
'wallet',
|
|
134
|
+
'webauthn',
|
|
135
|
+
'self_reported',
|
|
136
|
+
'totp',
|
|
137
|
+
'recovery_code',
|
|
138
|
+
'email_code',
|
|
139
|
+
]);
|
|
140
|
+
/**
|
|
141
|
+
* A route path below a method's prefix: one to four segments of lower-case
|
|
142
|
+
* ASCII letters, digits, `_` and `-`, such as `/callback`. No dot segment, no
|
|
143
|
+
* encoded character, no route parameter and no wildcard can match, so a path
|
|
144
|
+
* cannot leave the prefix it is mounted under.
|
|
145
|
+
*
|
|
146
|
+
* @experimental
|
|
147
|
+
*/
|
|
148
|
+
export const AUTH_METHOD_ROUTE_PATH_PATTERN = /^(?:\/[a-z0-9][a-z0-9_-]{0,62}){1,4}$/;
|
|
149
|
+
/**
|
|
150
|
+
* The display rule of the Interaction API (ADR-019 Decision 6), which a
|
|
151
|
+
* method's default name follows: no markup bracket, no control, format,
|
|
152
|
+
* private-use or unassigned character, no line separator and no space at
|
|
153
|
+
* either end.
|
|
154
|
+
*
|
|
155
|
+
* @experimental
|
|
156
|
+
*/
|
|
157
|
+
export const AUTH_METHOD_DISPLAY_NAME_PATTERN = /^[^<>\p{C}\p{Z}\u115F\u1160\u3164\uFFA0](?:[^<>\p{C}\p{Zl}\p{Zp}\u115F\u1160\u3164\uFFA0]*[^<>\p{C}\p{Z}\u115F\u1160\u3164\uFFA0])?$/u;
|
|
158
|
+
/**
|
|
159
|
+
* The longest default display name, in UTF-16 code units.
|
|
160
|
+
*
|
|
161
|
+
* @experimental
|
|
162
|
+
*/
|
|
163
|
+
export const MAX_AUTH_METHOD_DISPLAY_NAME_LENGTH = 64;
|
|
164
|
+
/**
|
|
165
|
+
* The largest default icon, in bytes.
|
|
166
|
+
*
|
|
167
|
+
* @experimental
|
|
168
|
+
*/
|
|
169
|
+
export const MAX_AUTH_METHOD_ICON_BYTES = 16 * 1024;
|
|
170
|
+
/**
|
|
171
|
+
* The media types a default icon may have: raster PNG only. An SVG can carry
|
|
172
|
+
* script, and the icon is served from the realm origin.
|
|
173
|
+
*
|
|
174
|
+
* @experimental
|
|
175
|
+
*/
|
|
176
|
+
export const AUTH_METHOD_ICON_MEDIA_TYPES = Object.freeze(['image/png']);
|
|
177
|
+
/**
|
|
178
|
+
* The largest upstream transaction a method may ask the engine to keep, in
|
|
179
|
+
* bytes of its JSON form. The engine stores it server-side, bound to the
|
|
180
|
+
* interaction and its flow cookie, for the lifetime of the interaction.
|
|
181
|
+
*
|
|
182
|
+
* @experimental
|
|
183
|
+
*/
|
|
184
|
+
export const MAX_UPSTREAM_TRANSACTION_BYTES = 4096;
|
|
185
|
+
/**
|
|
186
|
+
* A binding (#599): the value `start()` answers beside its transaction, which
|
|
187
|
+
* the authorization request carries to the provider (as `state`, usually)
|
|
188
|
+
* and the provider's callback carries back. 22 to 512 base64url characters:
|
|
189
|
+
* 22 is the length of 128 random bits in base64url. The pattern checks the
|
|
190
|
+
* length and the alphabet only; the method makes the value random.
|
|
191
|
+
*
|
|
192
|
+
* @experimental
|
|
193
|
+
*/
|
|
194
|
+
export const AUTH_METHOD_BINDING_PATTERN = /^[A-Za-z0-9_-]{22,512}$/;
|
|
195
|
+
/**
|
|
196
|
+
* The parameter a callback route carries the binding back in when the route
|
|
197
|
+
* names none: `state` (RFC 6749 §4.1.2).
|
|
198
|
+
*
|
|
199
|
+
* @experimental
|
|
200
|
+
*/
|
|
201
|
+
export const DEFAULT_AUTH_METHOD_BINDING_PARAMETER = 'state';
|
|
202
|
+
/**
|
|
203
|
+
* A parameter name a callback route may name as its binding parameter:
|
|
204
|
+
* ASCII letters, digits, `_`, `.` and `-`, at most 64 characters.
|
|
205
|
+
*
|
|
206
|
+
* @experimental
|
|
207
|
+
*/
|
|
208
|
+
export const AUTH_METHOD_BINDING_PARAMETER_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
209
|
+
/**
|
|
210
|
+
* The longest URL a method may send the browser to when it starts.
|
|
211
|
+
*
|
|
212
|
+
* @experimental
|
|
213
|
+
*/
|
|
214
|
+
export const MAX_UPSTREAM_REDIRECT_LENGTH = 8192;
|
|
215
|
+
/**
|
|
216
|
+
* The longest `externalSub` a method may resolve: 255 characters, the limit
|
|
217
|
+
* OpenID Connect Core 1.0 §2 sets on `sub`.
|
|
218
|
+
*
|
|
219
|
+
* @experimental
|
|
220
|
+
*/
|
|
221
|
+
export const MAX_AUTH_METHOD_EXTERNAL_SUB_LENGTH = 255;
|
|
222
|
+
/**
|
|
223
|
+
* The largest `rawClaims` a method's `verify()` may resolve, in bytes of its
|
|
224
|
+
* JSON form. The engine keeps a frozen copy.
|
|
225
|
+
*
|
|
226
|
+
* @experimental
|
|
227
|
+
*/
|
|
228
|
+
export const MAX_AUTH_METHOD_RAW_CLAIMS_BYTES = 64 * 1024;
|
|
229
|
+
/**
|
|
230
|
+
* An attribute key a method may extract: lower-case ASCII letters, digits
|
|
231
|
+
* and `_`, starting with a letter, at most 64 characters (`email`,
|
|
232
|
+
* `given_name`).
|
|
233
|
+
*
|
|
234
|
+
* @experimental
|
|
235
|
+
*/
|
|
236
|
+
export const AUTH_METHOD_ATTRIBUTE_KEY_PATTERN = /^[a-z][a-z0-9_]{0,63}$/;
|
|
237
|
+
/**
|
|
238
|
+
* The longest attribute value a method may extract, in UTF-16 code units.
|
|
239
|
+
*
|
|
240
|
+
* @experimental
|
|
241
|
+
*/
|
|
242
|
+
export const MAX_AUTH_METHOD_ATTRIBUTE_VALUE_LENGTH = 8192;
|
|
243
|
+
/**
|
|
244
|
+
* The most parameters a method's route handler sees from one query or one
|
|
245
|
+
* form; the rest are dropped.
|
|
246
|
+
*
|
|
247
|
+
* @experimental
|
|
248
|
+
*/
|
|
249
|
+
export const MAX_AUTH_METHOD_PARAMETERS = 64;
|
|
250
|
+
/**
|
|
251
|
+
* The longest parameter value a method's route handler sees, in UTF-16 code
|
|
252
|
+
* units; a parameter with a longer value is dropped.
|
|
253
|
+
*
|
|
254
|
+
* @experimental
|
|
255
|
+
*/
|
|
256
|
+
export const MAX_AUTH_METHOD_PARAMETER_LENGTH = 8192;
|
|
257
|
+
/**
|
|
258
|
+
* The most values of one repeated parameter a method's route handler sees;
|
|
259
|
+
* a parameter repeated more often is dropped.
|
|
260
|
+
*
|
|
261
|
+
* @experimental
|
|
262
|
+
*/
|
|
263
|
+
export const MAX_AUTH_METHOD_PARAMETER_VALUES = 16;
|
|
264
|
+
/**
|
|
265
|
+
* Why a method's callback did not produce a result, other than the person
|
|
266
|
+
* declining at the provider:
|
|
267
|
+
*
|
|
268
|
+
* - `transaction_mismatch`: no transaction is pending for this browser, or the
|
|
269
|
+
* callback does not belong to it (its binding, `state` by default, is
|
|
270
|
+
* missing, repeated or another's). A callback replayed in another browser,
|
|
271
|
+
* or forged, ends here; the host answers it before the method runs.
|
|
272
|
+
* - `provider_error`: the provider answered with an error, other than the
|
|
273
|
+
* person declining (`access_denied`).
|
|
274
|
+
* - `invalid_response`: the provider's answer failed a check, such as the ID
|
|
275
|
+
* token's signature, `iss`, `aud` or `nonce`.
|
|
276
|
+
* - `upstream_unavailable`: the provider could not be reached in time.
|
|
277
|
+
*
|
|
278
|
+
* @experimental
|
|
279
|
+
*/
|
|
280
|
+
export const UPSTREAM_FAILURE_REASONS = Object.freeze([
|
|
281
|
+
'transaction_mismatch',
|
|
282
|
+
'provider_error',
|
|
283
|
+
'invalid_response',
|
|
284
|
+
'upstream_unavailable',
|
|
285
|
+
]);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The verification half of a sign-in method (ADR-003): what every provider
|
|
3
|
+
* QAuth registers implements, and what an AuthMethod extends.
|
|
4
|
+
*
|
|
5
|
+
* The auth engine depends only on this contract: it resolves a provider by
|
|
6
|
+
* `type`, calls `verify()`, and upserts the attributes from
|
|
7
|
+
* `extractAttributes()`. It contains no provider-specific logic, so new
|
|
8
|
+
* providers are added without changing the engine.
|
|
9
|
+
*
|
|
10
|
+
* EXPERIMENTAL as part of this package, the plugin API of ADR-018 §4: it may
|
|
11
|
+
* change in a minor release.
|
|
12
|
+
*
|
|
13
|
+
* @see https://docs.qauth.dev/reference/records/adr/003-credential-provider-interface/
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Level of assurance for a verified identity, aligned with eIDAS Levels of
|
|
17
|
+
* Assurance (LoA) and ISO/IEC 29115. Propagated downstream as the OIDC `acr`
|
|
18
|
+
* claim; `'low'` credentials (e.g. self-asserted email/password) carry no `acr`
|
|
19
|
+
* claim per ADR-003.
|
|
20
|
+
*
|
|
21
|
+
* @experimental
|
|
22
|
+
*/
|
|
23
|
+
export type AssuranceLevel = 'low' | 'substantial' | 'high';
|
|
24
|
+
/**
|
|
25
|
+
* The result of a successful {@link CredentialProvider.verify} call: a
|
|
26
|
+
* provider-scoped subject identifier, the assurance level of the verification,
|
|
27
|
+
* and the raw claims the provider observed.
|
|
28
|
+
*
|
|
29
|
+
* @experimental
|
|
30
|
+
*/
|
|
31
|
+
export interface VerifiedIdentity {
|
|
32
|
+
/**
|
|
33
|
+
* Subject identifier as known to the upstream provider (e.g. the normalized
|
|
34
|
+
* email for password, a DID for a wallet). Never used directly as the token
|
|
35
|
+
* `sub` — the engine maps it to the internal `users.id`.
|
|
36
|
+
*/
|
|
37
|
+
externalSub: string;
|
|
38
|
+
/** Assurance level of this verification (eIDAS LoA / ISO 29115). */
|
|
39
|
+
assuranceLevel: AssuranceLevel;
|
|
40
|
+
/** Unprocessed claims exactly as returned by the provider, before extraction. */
|
|
41
|
+
rawClaims: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A single normalized user attribute derived from a {@link VerifiedIdentity},
|
|
45
|
+
* ready to be upserted into `user_attributes`. Trust ordering during claim
|
|
46
|
+
* resolution is driven by `source` and `verified`.
|
|
47
|
+
*
|
|
48
|
+
* @experimental
|
|
49
|
+
*/
|
|
50
|
+
export interface UserAttribute {
|
|
51
|
+
/**
|
|
52
|
+
* Origin of the attribute (e.g. `'self_reported'`, `'wallet'`,
|
|
53
|
+
* `'oidc_google'`). Drives trust ordering during claim resolution.
|
|
54
|
+
*/
|
|
55
|
+
source: string;
|
|
56
|
+
/** Attribute key (e.g. `'email'`). */
|
|
57
|
+
attrKey: string;
|
|
58
|
+
/** Attribute value. */
|
|
59
|
+
attrValue: string;
|
|
60
|
+
/** Whether the provider verified this attribute. */
|
|
61
|
+
verified: boolean;
|
|
62
|
+
/** Optional expiry after which the attribute must be re-verified. */
|
|
63
|
+
expiresAt?: Date;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Strategy interface implemented by every authentication method (ADR-003).
|
|
67
|
+
*
|
|
68
|
+
* @experimental
|
|
69
|
+
*/
|
|
70
|
+
export interface CredentialProvider {
|
|
71
|
+
/**
|
|
72
|
+
* Stable discriminator the server's provider registry registers and
|
|
73
|
+
* resolves this provider by (e.g. `'password'`, `'wallet'`).
|
|
74
|
+
*/
|
|
75
|
+
readonly type: string;
|
|
76
|
+
/**
|
|
77
|
+
* Verify the provider-specific `input` and resolve a normalized
|
|
78
|
+
* {@link VerifiedIdentity}. Implementations reject on verification failure.
|
|
79
|
+
*/
|
|
80
|
+
verify(input: unknown): Promise<VerifiedIdentity>;
|
|
81
|
+
/**
|
|
82
|
+
* Derive normalized {@link UserAttribute} rows from a successful verification
|
|
83
|
+
* result, ready to be upserted into `user_attributes`.
|
|
84
|
+
*/
|
|
85
|
+
extractAttributes(result: VerifiedIdentity): UserAttribute[];
|
|
86
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The verification half of a sign-in method (ADR-003): what every provider
|
|
3
|
+
* QAuth registers implements, and what an AuthMethod extends.
|
|
4
|
+
*
|
|
5
|
+
* The auth engine depends only on this contract: it resolves a provider by
|
|
6
|
+
* `type`, calls `verify()`, and upserts the attributes from
|
|
7
|
+
* `extractAttributes()`. It contains no provider-specific logic, so new
|
|
8
|
+
* providers are added without changing the engine.
|
|
9
|
+
*
|
|
10
|
+
* EXPERIMENTAL as part of this package, the plugin API of ADR-018 §4: it may
|
|
11
|
+
* change in a minor release.
|
|
12
|
+
*
|
|
13
|
+
* @see https://docs.qauth.dev/reference/records/adr/003-credential-provider-interface/
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { AuthMethod, AuthMethodContext, AuthMethodRoute, AuthMethodRouteRequest, UpstreamStart, UpstreamStartRequest } from './auth-method.js';
|
|
2
|
+
import { type UpstreamFailureReason } from './contract.js';
|
|
3
|
+
import type { AssuranceLevel, UserAttribute, VerifiedIdentity } from './credential-provider.js';
|
|
4
|
+
/**
|
|
5
|
+
* Why the upstream step ended without an identity: a callback's reason, or
|
|
6
|
+
* `verification_refused` when `verify()` rejected the callback's result.
|
|
7
|
+
*
|
|
8
|
+
* @experimental
|
|
9
|
+
*/
|
|
10
|
+
export type UpstreamStepFailureReason = UpstreamFailureReason | 'verification_refused';
|
|
11
|
+
/**
|
|
12
|
+
* How the upstream step ended, as the sign-in flow acts on it:
|
|
13
|
+
*
|
|
14
|
+
* - `identity`: a checked `VerifiedIdentity` and its attributes, each with
|
|
15
|
+
* `source` equal to the method's `type`. The flow looks the credential up by
|
|
16
|
+
* `(realm, type, externalSub)`; it never matches an account by an attribute.
|
|
17
|
+
* - `declined`: the person cancelled or declined at the provider.
|
|
18
|
+
* - `failed`: anything else. `cause` is `verify()`'s rejection, for the log.
|
|
19
|
+
*
|
|
20
|
+
* @experimental
|
|
21
|
+
*/
|
|
22
|
+
export type UpstreamStepCompletion = {
|
|
23
|
+
readonly kind: 'identity';
|
|
24
|
+
readonly identity: VerifiedIdentity;
|
|
25
|
+
readonly attributes: readonly UserAttribute[];
|
|
26
|
+
} | {
|
|
27
|
+
readonly kind: 'declined';
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: 'failed';
|
|
30
|
+
readonly reason: UpstreamStepFailureReason;
|
|
31
|
+
readonly cause?: unknown;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Start an upstream transaction: call `method.start()` and check its answer.
|
|
35
|
+
* The redirect must be an absolute `https` URL with no credentials and no
|
|
36
|
+
* fragment, the transaction JSON data within its cap, and the binding 22 to
|
|
37
|
+
* 512 base64url characters. The returned transaction is a frozen copy; the
|
|
38
|
+
* host stores it with the binding (a `PendingUpstreamTransaction`).
|
|
39
|
+
*
|
|
40
|
+
* A rejection of `start()` (the provider's discovery document cannot be read,
|
|
41
|
+
* for instance) propagates unchanged.
|
|
42
|
+
*
|
|
43
|
+
* @throws AuthMethodFaultError when the answer fails a check
|
|
44
|
+
* @experimental
|
|
45
|
+
*/
|
|
46
|
+
export declare function startUpstreamStep<TConfig, TTransaction>(method: AuthMethod<TConfig, TTransaction>, request: UpstreamStartRequest, context: AuthMethodContext<TConfig>): Promise<UpstreamStart<TTransaction>>;
|
|
47
|
+
/**
|
|
48
|
+
* The binding a request to `route` carries: the single value of the route's
|
|
49
|
+
* binding parameter (`state` unless the route names another), from the
|
|
50
|
+
* query of a `GET` route and the form of a `POST` one. `undefined` when the
|
|
51
|
+
* parameter is missing or repeated, or `route` is not a callback route.
|
|
52
|
+
*
|
|
53
|
+
* @experimental
|
|
54
|
+
*/
|
|
55
|
+
export declare function presentedUpstreamBinding(route: AuthMethodRoute, request: Pick<AuthMethodRouteRequest, 'query' | 'form'>): string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the binding a callback carried is the pending transaction's, in
|
|
58
|
+
* constant time (`timingSafeEqual` over the UTF-8 bytes). `false` unless both are strings and
|
|
59
|
+
* the expected one is a binding: a missing or repeated parameter never
|
|
60
|
+
* matches, and neither does an empty one.
|
|
61
|
+
*
|
|
62
|
+
* The host runs it before it takes the transaction, so a callback from
|
|
63
|
+
* another site cannot spend or cancel a pending sign-in.
|
|
64
|
+
*
|
|
65
|
+
* @experimental
|
|
66
|
+
*/
|
|
67
|
+
export declare function upstreamBindingMatches(expected: unknown, presented: unknown): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* The lowest assurance level, the ceiling of a method's identity when the
|
|
70
|
+
* caller names none.
|
|
71
|
+
*
|
|
72
|
+
* @experimental
|
|
73
|
+
*/
|
|
74
|
+
export declare const DEFAULT_UPSTREAM_MAX_ASSURANCE: AssuranceLevel;
|
|
75
|
+
/**
|
|
76
|
+
* Options of {@link completeUpstreamStep}.
|
|
77
|
+
*
|
|
78
|
+
* @experimental
|
|
79
|
+
*/
|
|
80
|
+
export interface UpstreamStepCompletionOptions {
|
|
81
|
+
/**
|
|
82
|
+
* The highest assurance level the identity may carry: a method that
|
|
83
|
+
* resolves a higher one is lowered to it. Default
|
|
84
|
+
* {@link DEFAULT_UPSTREAM_MAX_ASSURANCE} (`low`).
|
|
85
|
+
*
|
|
86
|
+
* The assurance level reaches `acr` and step-up, so a method does not get
|
|
87
|
+
* to set it alone. The owner's decision of 23 Oct 2026 ranks an upstream
|
|
88
|
+
* sign-in with a password, so by default it counts as `low` whatever the
|
|
89
|
+
* method claims; an operator raises the ceiling for a configured provider
|
|
90
|
+
* it trusts more (the plugin runtime and the upstream sign-in flow, #600
|
|
91
|
+
* and #639, pass it).
|
|
92
|
+
*/
|
|
93
|
+
readonly maxAssurance?: AssuranceLevel;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Turn what a method's callback handed back into the end of the upstream
|
|
97
|
+
* step. A `result` goes to `method.verify()`; the identity it resolves is
|
|
98
|
+
* checked and its assurance level lowered to `options.maxAssurance`, then
|
|
99
|
+
* `method.extractAttributes()` gives its attributes, which are checked too.
|
|
100
|
+
*
|
|
101
|
+
* @param outcome - what the callback route's handler returned, unchecked, or
|
|
102
|
+
* the {@link AuthMethodFaultError} the route mount made of a handler that
|
|
103
|
+
* threw or a context that could not be built
|
|
104
|
+
* @param options - the assurance ceiling
|
|
105
|
+
* @throws AuthMethodFaultError when the outcome is a fault or outside the
|
|
106
|
+
* closed set, or the identity or an attribute fails a check
|
|
107
|
+
* @throws TypeError when `options.maxAssurance` is not an assurance level
|
|
108
|
+
* @experimental
|
|
109
|
+
*/
|
|
110
|
+
export declare function completeUpstreamStep(method: AuthMethod, outcome: unknown, options?: UpstreamStepCompletionOptions): Promise<UpstreamStepCompletion>;
|
|
111
|
+
/**
|
|
112
|
+
* Why `value` cannot be where an upstream step sends the browser, or
|
|
113
|
+
* `undefined` when it can.
|
|
114
|
+
*
|
|
115
|
+
* @experimental
|
|
116
|
+
*/
|
|
117
|
+
export declare function upstreamRedirectProblem(value: unknown): string | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* A frozen copy of `value` when it is JSON data within the transaction cap:
|
|
120
|
+
* plain objects, arrays, strings, finite numbers, booleans and `null`, nested
|
|
121
|
+
* at most 16 deep. `undefined` otherwise.
|
|
122
|
+
*
|
|
123
|
+
* @experimental
|
|
124
|
+
*/
|
|
125
|
+
export declare function storableTransaction(value: unknown): unknown;
|