@hyper.authio/server 0.1.1 → 0.7.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/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { AuthioResourceAccess, AuthioResourceAccessSource, AuthioError, AuthioConnection, AuthioDiscoveryClient, AuthioClient, AuthioConnectionOptions, AuthioClientOptions, AuthioWebhookEvent, AuthioClock, AuthioWebhookDataByType, AuthioTypedWebhookEvent, AuthioClientCredentialsToken } from '@hyper.authio/client';
1
+ import { AuthioCallerKind, AuthioCredentialForm, AuthioResourceAccess, AuthioIntrospectionResult, AuthioResourceAccessSource, AuthioClock, AuthioError, AuthioTokenFailure, AuthioConnection, AuthioDiscoveryClient, AuthioClient, AuthioConnectionOptions, AuthioClientOptions, AuthioEncryptionKeySource, AuthioWebhookEvent, AuthioWebhookDataByType, AuthioTypedWebhookEvent, AuthioClientCredentialsToken } from '@hyper.authio/client';
2
2
 
3
3
  /**
4
4
  * The party acting for the subject, from the `act` claim (RFC 8693): who exchanged the token, nested when the chain
@@ -7,8 +7,17 @@ import { AuthioResourceAccess, AuthioResourceAccessSource, AuthioError, AuthioCo
7
7
  interface AuthioActor {
8
8
  readonly clientId: string | undefined;
9
9
  readonly subject: string | undefined;
10
+ /** `name`, on an impersonation: the administrator's display name. */
11
+ readonly name: string | undefined;
10
12
  readonly actor: AuthioActor | undefined;
11
13
  }
14
+ /**
15
+ * The administrator who impersonates the subject (an `act` with `sub` and no `client_id`).
16
+ */
17
+ interface AuthioImpersonator {
18
+ readonly subject: string;
19
+ readonly name: string | undefined;
20
+ }
12
21
  /**
13
22
  * Where the user reads flat (non-`*_resource_access`) roles and groups from.
14
23
  */
@@ -31,6 +40,41 @@ interface AuthioUserClaimSettings {
31
40
  declare class AuthioUser {
32
41
  /** `sub`: the person's id, or the credential's for a machine caller. */
33
42
  readonly subject: string;
43
+ /**
44
+ * Who called: `user` (a person signed in), `client` (a client as itself), `consumer` (a machine consumer of a
45
+ * client) or `account_credential` (a credential of an Authio account).
46
+ */
47
+ readonly kind: AuthioCallerKind;
48
+ /**
49
+ * How a machine caller proved who it is (`client_secret`, `personal_access_token`, `api_key`,
50
+ * `consumer_api_key`); `undefined` for a person or a client.
51
+ */
52
+ readonly credentialForm: AuthioCredentialForm | undefined;
53
+ /** `consumer_name`, for a consumer. */
54
+ readonly consumerName: string | undefined;
55
+ /** `credential_name`, for an account credential. */
56
+ readonly credentialName: string | undefined;
57
+ /** `owner_user_id`: the person who made the credential. */
58
+ readonly ownerUserId: string | undefined;
59
+ /**
60
+ * `owner_username`: the person who made the credential. On an account credential Authio wrote it to
61
+ * `consumer_name` before, which is read when `owner_username` is absent.
62
+ */
63
+ readonly ownerUsername: string | undefined;
64
+ /** `credential_realm`: the one realm an account credential is limited to. */
65
+ readonly credentialRealm: string | undefined;
66
+ /** `credential_client`: the one client an account credential is limited to. */
67
+ readonly credentialClient: string | undefined;
68
+ /** Whether an administrator impersonates the subject. */
69
+ readonly impersonated: boolean;
70
+ /** The administrator impersonating the subject. */
71
+ readonly impersonator: AuthioImpersonator | undefined;
72
+ /** Whether a client acts for the subject through a delegation (`act` with `client_id`). */
73
+ readonly delegated: boolean;
74
+ /** The clients acting for the subject, outermost first. */
75
+ readonly actorChain: readonly string[];
76
+ /** `auth_time`, in seconds since the epoch. */
77
+ readonly authTime: number | undefined;
34
78
  /** The client the token was issued to (`azp`, else `client_id`). */
35
79
  readonly clientId: string | undefined;
36
80
  /** `sid`. */
@@ -86,10 +130,16 @@ declare class AuthioUser {
86
130
  * @param claims The token's payload, or the introspection answer's members.
87
131
  * @param settings Where flat roles and groups are read from.
88
132
  * @param token The access token, kept for forwarding and on-behalf-of.
133
+ * @param source Where the claims come from: a JWT (default) or an introspection answer. An introspection answer for
134
+ * a consumer that reports no `exp` and no `actor_type` is the consumer's API key (Authio reports no lifetime for a
135
+ * key), so its `credentialForm` is `consumer_api_key`.
136
+ */
137
+ constructor(claims: Readonly<Record<string, unknown>>, settings?: AuthioUserClaimSettings, token?: string, source?: "token" | "introspection");
138
+ /**
139
+ * Reads the caller from an introspection answer: `client_id` becomes `azp`, `username` becomes
140
+ * `preferred_username`, and `token_type` becomes `typ` unless it is `api_key` (a consumer's API key).
89
141
  */
90
- constructor(claims: Readonly<Record<string, unknown>>, settings?: AuthioUserClaimSettings, token?: string);
91
- /** Whether an actor exchanged the token on the subject's behalf. */
92
- get isDelegated(): boolean;
142
+ static fromIntrospection(result: AuthioIntrospectionResult, settings?: AuthioUserClaimSettings, token?: string): AuthioUser;
93
143
  /** Whether the scope was granted. */
94
144
  hasScope(scope: string): boolean;
95
145
  /**
@@ -138,6 +188,10 @@ interface AuthioRuleFailure {
138
188
  readonly error?: "insufficient_scope" | "insufficient_user_authentication";
139
189
  /** The required scopes, for `insufficient_scope`. */
140
190
  readonly scope?: string;
191
+ /** The context classes to ask for again, for `insufficient_user_authentication` (RFC 9470 `acr_values`). */
192
+ readonly acrValues?: string;
193
+ /** How old the sign-in may be, in seconds, for `insufficient_user_authentication` (RFC 9470 `max_age`). */
194
+ readonly maxAge?: number;
141
195
  }
142
196
  /**
143
197
  * A condition on the caller.
@@ -150,7 +204,8 @@ interface AuthioRule {
150
204
  }
151
205
  /**
152
206
  * Checks every rule. On refusal the message joins every rule's, and the error is the one asking for a stronger
153
- * sign-in, else the first one that has an error.
207
+ * sign-in (with the strongest `acr_values` and the shortest `max_age` the failing rules ask for), else the first one
208
+ * that has an error.
154
209
  */
155
210
  declare function authorize(user: AuthioUser, ...rules: AuthioRule[]): AuthioRuleFailure | undefined;
156
211
  /**
@@ -184,13 +239,30 @@ declare function requireClaim(type: string, ...values: string[]): AuthioRule;
184
239
  */
185
240
  declare function requireOrganization(organization?: string): AuthioRule;
186
241
  /**
187
- * Requires a minimum authentication level (`acr`): 1 one factor, 2 several, 3 one of them phishing-resistant.
242
+ * Requires a minimum authentication level (`acr`): 1 one factor, 2 several, 3 one of them phishing-resistant. The
243
+ * refusal asks for that level again (`acr_values`), and for a sign-in at most `maxAge` seconds old when given.
244
+ */
245
+ declare function requireAuthenticationLevel(minimumLevel: number, options?: {
246
+ maxAge?: number;
247
+ clock?: AuthioClock;
248
+ }): AuthioRule;
249
+ /**
250
+ * Requires authentication methods (`amr`, case-insensitive), such as `otp`, `swk` or `fed`. The refusal asks for the
251
+ * context class the methods imply (`2` for `mfa`/`otp`/`mca`, `3` for `swk`/`hwk`), or for `acrValues` when given.
252
+ */
253
+ declare function requireAuthenticationMethod(methods: string | readonly string[], match?: AuthioMatch, options?: {
254
+ acrValues?: string;
255
+ }): AuthioRule;
256
+ /**
257
+ * Requires a sign-in at most `maxAge` seconds old (`auth_time`); the refusal asks for it with `max_age` (RFC 9470).
188
258
  */
189
- declare function requireAuthenticationLevel(minimumLevel: number): AuthioRule;
259
+ declare function requireRecentAuthentication(maxAge: number, options?: {
260
+ clock?: AuthioClock;
261
+ }): AuthioRule;
190
262
  /**
191
- * Requires authentication methods (`amr`, case-insensitive), such as `otp`, `swk` or `fed`.
263
+ * Requires the caller to be of one of the kinds: `user`, `client`, `consumer`, `account_credential`.
192
264
  */
193
- declare function requireAuthenticationMethod(methods: string | readonly string[], match?: AuthioMatch): AuthioRule;
265
+ declare function requireCallerKind(kinds: AuthioCallerKind | readonly AuthioCallerKind[]): AuthioRule;
194
266
  /**
195
267
  * Requires the token to have been issued to one of the clients (`azp`, else `client_id`).
196
268
  */
@@ -213,10 +285,18 @@ declare class AuthioBearerError extends AuthioError {
213
285
  readonly error: "invalid_token" | undefined;
214
286
  /** Why the token was refused, safe to send to the caller. */
215
287
  readonly description: string | undefined;
288
+ /** Why the token was refused, as a stable code; `undefined` when there was no token or Authio could not be asked. */
289
+ readonly reason: AuthioTokenFailure | "inactive" | "unconfirmed" | undefined;
290
+ /** The API's protected resource metadata URL (RFC 9728), sent as `resource_metadata` when configured. */
291
+ readonly resourceMetadata: string | undefined;
216
292
  constructor(description: string | undefined, options?: {
217
293
  error?: "invalid_token" | undefined;
218
294
  cause?: unknown;
295
+ reason?: AuthioTokenFailure | "inactive" | "unconfirmed";
296
+ resourceMetadata?: string;
219
297
  });
298
+ /** The same refusal, pointing at the API's protected resource metadata. */
299
+ withResourceMetadata(resourceMetadata: string | undefined): AuthioBearerError;
220
300
  /** The `WWW-Authenticate` value. */
221
301
  get wwwAuthenticate(): string;
222
302
  }
@@ -231,8 +311,8 @@ declare class AuthioForbiddenError extends AuthioError {
231
311
  get wwwAuthenticate(): string | undefined;
232
312
  }
233
313
  /**
234
- * The `WWW-Authenticate` value of a refusal: `insufficient_scope` (with `scope`) or
235
- * `insufficient_user_authentication`.
314
+ * The `WWW-Authenticate` value of a refusal: `insufficient_scope` (with `scope`) or `insufficient_user_authentication`
315
+ * (with `acr_values` and `max_age`, RFC 9470).
236
316
  */
237
317
  declare function toForbiddenChallenge(failure: AuthioRuleFailure): string | undefined;
238
318
  /**
@@ -246,15 +326,27 @@ declare function toProblemDetails(error: AuthioBearerError | AuthioForbiddenErro
246
326
  * - `none`: the JWT is validated locally (signature, issuer, audience, lifetime). Fastest; a revoked token stays
247
327
  * valid until it expires.
248
328
  * - `activeCheck`: validated locally, then confirmed active by introspection (cached per `jti`).
249
- * - `introspectionOnly`: introspection alone, for opaque (reference) or encrypted tokens.
329
+ * - `introspectionOnly`: introspection alone, for opaque (reference) tokens, or encrypted ones without the key.
250
330
  */
251
331
  type AuthioIntrospectionMode = "none" | "activeCheck" | "introspectionOnly";
252
332
  /**
253
333
  * How an API validates Authio access tokens.
254
334
  */
255
335
  interface AuthioVerifierOptions extends AuthioConnectionOptions, AuthioUserClaimSettings {
256
- /** The audiences this API accepts: its resource identifier (RFC 8707) or client id. Required. */
257
- audiences: string | readonly string[];
336
+ /** The audiences this API accepts: its resource identifier (RFC 8707) or client id. Required unless `resource` is set. */
337
+ audiences?: string | readonly string[];
338
+ /**
339
+ * The API's own resource identifier (RFC 8707), such as `https://orders.example.com`. Added to `audiences`, and
340
+ * the `resource` of its protected resource metadata (RFC 9728).
341
+ */
342
+ resource?: string;
343
+ /**
344
+ * Points `401` challenges at the API's protected resource metadata (RFC 9728) with `resource_metadata`: `true`
345
+ * for `{origin of resource}/.well-known/oauth-protected-resource{path of resource}`, or the URL itself. Off by default.
346
+ */
347
+ resourceMetadata?: boolean | string;
348
+ /** Scopes every request must carry (all of them); `authioBearer` answers `403 insufficient_scope` otherwise. */
349
+ requiredScopes?: readonly string[];
258
350
  /** Defaults to `none`. */
259
351
  introspection?: AuthioIntrospectionMode;
260
352
  /**
@@ -271,6 +363,15 @@ interface AuthioVerifierOptions extends AuthioConnectionOptions, AuthioUserClaim
271
363
  validAlgorithms?: readonly string[];
272
364
  /** Refuses a token whose `typ` claim is present and not `Bearer` (an ID token, for one). Defaults to `true`. */
273
365
  rejectNonAccessTokens?: boolean;
366
+ /**
367
+ * The private half of the encryption key Authio encrypts this API's access tokens to (the resource server's or its
368
+ * client's), so encrypted tokens are decrypted and validated locally (RSA-OAEP, RSA-OAEP-256).
369
+ */
370
+ clientEncryptionKey?: AuthioEncryptionKeySource;
371
+ /** A PEM or JWK file holding `clientEncryptionKey` (Node only). */
372
+ clientEncryptionKeyPath?: string;
373
+ /** Accepted for parity with the other Authio SDKs; RSA1_5 stays refused, because Node cannot decrypt it. */
374
+ allowLegacyEncryptionAlgorithms?: boolean;
274
375
  }
275
376
  /**
276
377
  * Validates Authio access tokens and reads the caller.
@@ -282,6 +383,12 @@ interface AuthioVerifier {
282
383
  readonly discovery: AuthioDiscoveryClient;
283
384
  /** The accepted audiences. */
284
385
  readonly audiences: readonly string[];
386
+ /** The API's resource identifier, when configured. */
387
+ readonly resource: string | undefined;
388
+ /** The protected resource metadata URL sent in challenges, when turned on. */
389
+ readonly resourceMetadataUrl: string | undefined;
390
+ /** Scopes every request must carry. */
391
+ readonly requiredScopes: readonly string[];
285
392
  /** The introspection mode. */
286
393
  readonly introspection: AuthioIntrospectionMode;
287
394
  /** The client used for introspection, when there is one. */
@@ -307,7 +414,7 @@ declare const AuthioBearerErrorDescriptions: {
307
414
  readonly Inactive: "Authio reports the token as not active to this API's client. It was revoked or has expired or its session ended; or this API's client is not the introspection client of the resource server the token is for.";
308
415
  readonly Unconfirmed: "Authio could not be reached to confirm that the token is active.";
309
416
  readonly Empty: "The token is empty.";
310
- readonly Encrypted: "The token is encrypted; validate it with introspection ('introspectionOnly').";
417
+ readonly Encrypted: "The token is encrypted; set clientEncryptionKey to decrypt it, or validate it with introspection ('introspectionOnly').";
311
418
  readonly SigningKeysUnavailable: "Authio could not be reached to read its signing keys.";
312
419
  };
313
420
  /**
@@ -321,6 +428,48 @@ declare function createAuthioVerifier(options: AuthioVerifierOptions): AuthioVer
321
428
  */
322
429
  declare function readBearerToken(header: string | null | undefined): string | undefined;
323
430
 
431
+ /**
432
+ * What an API publishes about itself as a protected resource (RFC 9728).
433
+ */
434
+ interface AuthioProtectedResourceMetadataOptions extends Pick<AuthioConnectionOptions, "issuer" | "authority" | "realm"> {
435
+ /** The API's resource identifier (RFC 8707), such as `https://orders.example.com/api`. Required. */
436
+ resource: string;
437
+ /** Scopes a client can ask for to call the API. */
438
+ scopesSupported?: readonly string[];
439
+ /** A name to show to people. */
440
+ resourceName?: string;
441
+ /** A page documenting the API. */
442
+ resourceDocumentation?: string;
443
+ /** Further members, sent as given. */
444
+ extra?: Record<string, unknown>;
445
+ }
446
+ /**
447
+ * The protected resource metadata document (RFC 9728 §2).
448
+ */
449
+ interface AuthioProtectedResourceMetadata {
450
+ resource: string;
451
+ authorization_servers: string[];
452
+ bearer_methods_supported: string[];
453
+ scopes_supported?: string[];
454
+ resource_name?: string;
455
+ resource_documentation?: string;
456
+ [member: string]: unknown;
457
+ }
458
+ /**
459
+ * Where a resource's metadata is published: `{origin}/.well-known/oauth-protected-resource{path}` (RFC 9728 §3.1),
460
+ * the path being the resource's own, without a trailing slash.
461
+ */
462
+ declare function protectedResourceMetadataUrl(resource: string): string;
463
+ /**
464
+ * The path a resource's metadata is served at on the API itself: `/.well-known/oauth-protected-resource{path}`.
465
+ */
466
+ declare function protectedResourceMetadataPath(resource: string): string;
467
+ /**
468
+ * Builds the API's protected resource metadata: the resource, the realm's issuer as its authorization server, bearer
469
+ * tokens in the `Authorization` header, and the optional scopes, name and documentation.
470
+ */
471
+ declare function createProtectedResourceMetadata(options: AuthioProtectedResourceMetadataOptions): AuthioProtectedResourceMetadata;
472
+
324
473
  /**
325
474
  * Remembers delivered event ids, so a redelivery is acknowledged without running the handlers twice.
326
475
  */
@@ -436,4 +585,4 @@ declare function createOnBehalfOf(options: {
436
585
  tokenRefreshSkew?: number;
437
586
  }): AuthioOnBehalfOf;
438
587
 
439
- export { type AuthioActor, AuthioBearerError, AuthioBearerErrorDescriptions, type AuthioDownstreamSubject, AuthioForbiddenError, type AuthioIntrospectionMode, type AuthioMatch, type AuthioOnBehalfOf, type AuthioRule, type AuthioRuleFailure, AuthioUser, type AuthioUserClaimSettings, type AuthioVerifier, type AuthioVerifierOptions, type AuthioWebhookDeduplicationStore, type AuthioWebhookHandler, type AuthioWebhookOptions, type AuthioWebhookOutcome, type AuthioWebhooks, allOf, anyOf, authorize, createAuthioVerifier, createAuthioWebhooks, createForwardingFetch, createMemoryDeduplicationStore, createOnBehalfOf, readBearerToken, requireAuthenticationLevel, requireAuthenticationMethod, requireClaim, requireClient, requireGroup, requireOrganization, requirePermission, requireRole, requireScope, toForbiddenChallenge, toProblemDetails };
588
+ export { type AuthioActor, AuthioBearerError, AuthioBearerErrorDescriptions, type AuthioDownstreamSubject, AuthioForbiddenError, type AuthioImpersonator, type AuthioIntrospectionMode, type AuthioMatch, type AuthioOnBehalfOf, type AuthioProtectedResourceMetadata, type AuthioProtectedResourceMetadataOptions, type AuthioRule, type AuthioRuleFailure, AuthioUser, type AuthioUserClaimSettings, type AuthioVerifier, type AuthioVerifierOptions, type AuthioWebhookDeduplicationStore, type AuthioWebhookHandler, type AuthioWebhookOptions, type AuthioWebhookOutcome, type AuthioWebhooks, allOf, anyOf, authorize, createAuthioVerifier, createAuthioWebhooks, createForwardingFetch, createMemoryDeduplicationStore, createOnBehalfOf, createProtectedResourceMetadata, protectedResourceMetadataPath, protectedResourceMetadataUrl, readBearerToken, requireAuthenticationLevel, requireAuthenticationMethod, requireCallerKind, requireClaim, requireClient, requireGroup, requireOrganization, requirePermission, requireRecentAuthentication, requireRole, requireScope, toForbiddenChallenge, toProblemDetails };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AuthioResourceAccess, AuthioResourceAccessSource, AuthioError, AuthioConnection, AuthioDiscoveryClient, AuthioClient, AuthioConnectionOptions, AuthioClientOptions, AuthioWebhookEvent, AuthioClock, AuthioWebhookDataByType, AuthioTypedWebhookEvent, AuthioClientCredentialsToken } from '@hyper.authio/client';
1
+ import { AuthioCallerKind, AuthioCredentialForm, AuthioResourceAccess, AuthioIntrospectionResult, AuthioResourceAccessSource, AuthioClock, AuthioError, AuthioTokenFailure, AuthioConnection, AuthioDiscoveryClient, AuthioClient, AuthioConnectionOptions, AuthioClientOptions, AuthioEncryptionKeySource, AuthioWebhookEvent, AuthioWebhookDataByType, AuthioTypedWebhookEvent, AuthioClientCredentialsToken } from '@hyper.authio/client';
2
2
 
3
3
  /**
4
4
  * The party acting for the subject, from the `act` claim (RFC 8693): who exchanged the token, nested when the chain
@@ -7,8 +7,17 @@ import { AuthioResourceAccess, AuthioResourceAccessSource, AuthioError, AuthioCo
7
7
  interface AuthioActor {
8
8
  readonly clientId: string | undefined;
9
9
  readonly subject: string | undefined;
10
+ /** `name`, on an impersonation: the administrator's display name. */
11
+ readonly name: string | undefined;
10
12
  readonly actor: AuthioActor | undefined;
11
13
  }
14
+ /**
15
+ * The administrator who impersonates the subject (an `act` with `sub` and no `client_id`).
16
+ */
17
+ interface AuthioImpersonator {
18
+ readonly subject: string;
19
+ readonly name: string | undefined;
20
+ }
12
21
  /**
13
22
  * Where the user reads flat (non-`*_resource_access`) roles and groups from.
14
23
  */
@@ -31,6 +40,41 @@ interface AuthioUserClaimSettings {
31
40
  declare class AuthioUser {
32
41
  /** `sub`: the person's id, or the credential's for a machine caller. */
33
42
  readonly subject: string;
43
+ /**
44
+ * Who called: `user` (a person signed in), `client` (a client as itself), `consumer` (a machine consumer of a
45
+ * client) or `account_credential` (a credential of an Authio account).
46
+ */
47
+ readonly kind: AuthioCallerKind;
48
+ /**
49
+ * How a machine caller proved who it is (`client_secret`, `personal_access_token`, `api_key`,
50
+ * `consumer_api_key`); `undefined` for a person or a client.
51
+ */
52
+ readonly credentialForm: AuthioCredentialForm | undefined;
53
+ /** `consumer_name`, for a consumer. */
54
+ readonly consumerName: string | undefined;
55
+ /** `credential_name`, for an account credential. */
56
+ readonly credentialName: string | undefined;
57
+ /** `owner_user_id`: the person who made the credential. */
58
+ readonly ownerUserId: string | undefined;
59
+ /**
60
+ * `owner_username`: the person who made the credential. On an account credential Authio wrote it to
61
+ * `consumer_name` before, which is read when `owner_username` is absent.
62
+ */
63
+ readonly ownerUsername: string | undefined;
64
+ /** `credential_realm`: the one realm an account credential is limited to. */
65
+ readonly credentialRealm: string | undefined;
66
+ /** `credential_client`: the one client an account credential is limited to. */
67
+ readonly credentialClient: string | undefined;
68
+ /** Whether an administrator impersonates the subject. */
69
+ readonly impersonated: boolean;
70
+ /** The administrator impersonating the subject. */
71
+ readonly impersonator: AuthioImpersonator | undefined;
72
+ /** Whether a client acts for the subject through a delegation (`act` with `client_id`). */
73
+ readonly delegated: boolean;
74
+ /** The clients acting for the subject, outermost first. */
75
+ readonly actorChain: readonly string[];
76
+ /** `auth_time`, in seconds since the epoch. */
77
+ readonly authTime: number | undefined;
34
78
  /** The client the token was issued to (`azp`, else `client_id`). */
35
79
  readonly clientId: string | undefined;
36
80
  /** `sid`. */
@@ -86,10 +130,16 @@ declare class AuthioUser {
86
130
  * @param claims The token's payload, or the introspection answer's members.
87
131
  * @param settings Where flat roles and groups are read from.
88
132
  * @param token The access token, kept for forwarding and on-behalf-of.
133
+ * @param source Where the claims come from: a JWT (default) or an introspection answer. An introspection answer for
134
+ * a consumer that reports no `exp` and no `actor_type` is the consumer's API key (Authio reports no lifetime for a
135
+ * key), so its `credentialForm` is `consumer_api_key`.
136
+ */
137
+ constructor(claims: Readonly<Record<string, unknown>>, settings?: AuthioUserClaimSettings, token?: string, source?: "token" | "introspection");
138
+ /**
139
+ * Reads the caller from an introspection answer: `client_id` becomes `azp`, `username` becomes
140
+ * `preferred_username`, and `token_type` becomes `typ` unless it is `api_key` (a consumer's API key).
89
141
  */
90
- constructor(claims: Readonly<Record<string, unknown>>, settings?: AuthioUserClaimSettings, token?: string);
91
- /** Whether an actor exchanged the token on the subject's behalf. */
92
- get isDelegated(): boolean;
142
+ static fromIntrospection(result: AuthioIntrospectionResult, settings?: AuthioUserClaimSettings, token?: string): AuthioUser;
93
143
  /** Whether the scope was granted. */
94
144
  hasScope(scope: string): boolean;
95
145
  /**
@@ -138,6 +188,10 @@ interface AuthioRuleFailure {
138
188
  readonly error?: "insufficient_scope" | "insufficient_user_authentication";
139
189
  /** The required scopes, for `insufficient_scope`. */
140
190
  readonly scope?: string;
191
+ /** The context classes to ask for again, for `insufficient_user_authentication` (RFC 9470 `acr_values`). */
192
+ readonly acrValues?: string;
193
+ /** How old the sign-in may be, in seconds, for `insufficient_user_authentication` (RFC 9470 `max_age`). */
194
+ readonly maxAge?: number;
141
195
  }
142
196
  /**
143
197
  * A condition on the caller.
@@ -150,7 +204,8 @@ interface AuthioRule {
150
204
  }
151
205
  /**
152
206
  * Checks every rule. On refusal the message joins every rule's, and the error is the one asking for a stronger
153
- * sign-in, else the first one that has an error.
207
+ * sign-in (with the strongest `acr_values` and the shortest `max_age` the failing rules ask for), else the first one
208
+ * that has an error.
154
209
  */
155
210
  declare function authorize(user: AuthioUser, ...rules: AuthioRule[]): AuthioRuleFailure | undefined;
156
211
  /**
@@ -184,13 +239,30 @@ declare function requireClaim(type: string, ...values: string[]): AuthioRule;
184
239
  */
185
240
  declare function requireOrganization(organization?: string): AuthioRule;
186
241
  /**
187
- * Requires a minimum authentication level (`acr`): 1 one factor, 2 several, 3 one of them phishing-resistant.
242
+ * Requires a minimum authentication level (`acr`): 1 one factor, 2 several, 3 one of them phishing-resistant. The
243
+ * refusal asks for that level again (`acr_values`), and for a sign-in at most `maxAge` seconds old when given.
244
+ */
245
+ declare function requireAuthenticationLevel(minimumLevel: number, options?: {
246
+ maxAge?: number;
247
+ clock?: AuthioClock;
248
+ }): AuthioRule;
249
+ /**
250
+ * Requires authentication methods (`amr`, case-insensitive), such as `otp`, `swk` or `fed`. The refusal asks for the
251
+ * context class the methods imply (`2` for `mfa`/`otp`/`mca`, `3` for `swk`/`hwk`), or for `acrValues` when given.
252
+ */
253
+ declare function requireAuthenticationMethod(methods: string | readonly string[], match?: AuthioMatch, options?: {
254
+ acrValues?: string;
255
+ }): AuthioRule;
256
+ /**
257
+ * Requires a sign-in at most `maxAge` seconds old (`auth_time`); the refusal asks for it with `max_age` (RFC 9470).
188
258
  */
189
- declare function requireAuthenticationLevel(minimumLevel: number): AuthioRule;
259
+ declare function requireRecentAuthentication(maxAge: number, options?: {
260
+ clock?: AuthioClock;
261
+ }): AuthioRule;
190
262
  /**
191
- * Requires authentication methods (`amr`, case-insensitive), such as `otp`, `swk` or `fed`.
263
+ * Requires the caller to be of one of the kinds: `user`, `client`, `consumer`, `account_credential`.
192
264
  */
193
- declare function requireAuthenticationMethod(methods: string | readonly string[], match?: AuthioMatch): AuthioRule;
265
+ declare function requireCallerKind(kinds: AuthioCallerKind | readonly AuthioCallerKind[]): AuthioRule;
194
266
  /**
195
267
  * Requires the token to have been issued to one of the clients (`azp`, else `client_id`).
196
268
  */
@@ -213,10 +285,18 @@ declare class AuthioBearerError extends AuthioError {
213
285
  readonly error: "invalid_token" | undefined;
214
286
  /** Why the token was refused, safe to send to the caller. */
215
287
  readonly description: string | undefined;
288
+ /** Why the token was refused, as a stable code; `undefined` when there was no token or Authio could not be asked. */
289
+ readonly reason: AuthioTokenFailure | "inactive" | "unconfirmed" | undefined;
290
+ /** The API's protected resource metadata URL (RFC 9728), sent as `resource_metadata` when configured. */
291
+ readonly resourceMetadata: string | undefined;
216
292
  constructor(description: string | undefined, options?: {
217
293
  error?: "invalid_token" | undefined;
218
294
  cause?: unknown;
295
+ reason?: AuthioTokenFailure | "inactive" | "unconfirmed";
296
+ resourceMetadata?: string;
219
297
  });
298
+ /** The same refusal, pointing at the API's protected resource metadata. */
299
+ withResourceMetadata(resourceMetadata: string | undefined): AuthioBearerError;
220
300
  /** The `WWW-Authenticate` value. */
221
301
  get wwwAuthenticate(): string;
222
302
  }
@@ -231,8 +311,8 @@ declare class AuthioForbiddenError extends AuthioError {
231
311
  get wwwAuthenticate(): string | undefined;
232
312
  }
233
313
  /**
234
- * The `WWW-Authenticate` value of a refusal: `insufficient_scope` (with `scope`) or
235
- * `insufficient_user_authentication`.
314
+ * The `WWW-Authenticate` value of a refusal: `insufficient_scope` (with `scope`) or `insufficient_user_authentication`
315
+ * (with `acr_values` and `max_age`, RFC 9470).
236
316
  */
237
317
  declare function toForbiddenChallenge(failure: AuthioRuleFailure): string | undefined;
238
318
  /**
@@ -246,15 +326,27 @@ declare function toProblemDetails(error: AuthioBearerError | AuthioForbiddenErro
246
326
  * - `none`: the JWT is validated locally (signature, issuer, audience, lifetime). Fastest; a revoked token stays
247
327
  * valid until it expires.
248
328
  * - `activeCheck`: validated locally, then confirmed active by introspection (cached per `jti`).
249
- * - `introspectionOnly`: introspection alone, for opaque (reference) or encrypted tokens.
329
+ * - `introspectionOnly`: introspection alone, for opaque (reference) tokens, or encrypted ones without the key.
250
330
  */
251
331
  type AuthioIntrospectionMode = "none" | "activeCheck" | "introspectionOnly";
252
332
  /**
253
333
  * How an API validates Authio access tokens.
254
334
  */
255
335
  interface AuthioVerifierOptions extends AuthioConnectionOptions, AuthioUserClaimSettings {
256
- /** The audiences this API accepts: its resource identifier (RFC 8707) or client id. Required. */
257
- audiences: string | readonly string[];
336
+ /** The audiences this API accepts: its resource identifier (RFC 8707) or client id. Required unless `resource` is set. */
337
+ audiences?: string | readonly string[];
338
+ /**
339
+ * The API's own resource identifier (RFC 8707), such as `https://orders.example.com`. Added to `audiences`, and
340
+ * the `resource` of its protected resource metadata (RFC 9728).
341
+ */
342
+ resource?: string;
343
+ /**
344
+ * Points `401` challenges at the API's protected resource metadata (RFC 9728) with `resource_metadata`: `true`
345
+ * for `{origin of resource}/.well-known/oauth-protected-resource{path of resource}`, or the URL itself. Off by default.
346
+ */
347
+ resourceMetadata?: boolean | string;
348
+ /** Scopes every request must carry (all of them); `authioBearer` answers `403 insufficient_scope` otherwise. */
349
+ requiredScopes?: readonly string[];
258
350
  /** Defaults to `none`. */
259
351
  introspection?: AuthioIntrospectionMode;
260
352
  /**
@@ -271,6 +363,15 @@ interface AuthioVerifierOptions extends AuthioConnectionOptions, AuthioUserClaim
271
363
  validAlgorithms?: readonly string[];
272
364
  /** Refuses a token whose `typ` claim is present and not `Bearer` (an ID token, for one). Defaults to `true`. */
273
365
  rejectNonAccessTokens?: boolean;
366
+ /**
367
+ * The private half of the encryption key Authio encrypts this API's access tokens to (the resource server's or its
368
+ * client's), so encrypted tokens are decrypted and validated locally (RSA-OAEP, RSA-OAEP-256).
369
+ */
370
+ clientEncryptionKey?: AuthioEncryptionKeySource;
371
+ /** A PEM or JWK file holding `clientEncryptionKey` (Node only). */
372
+ clientEncryptionKeyPath?: string;
373
+ /** Accepted for parity with the other Authio SDKs; RSA1_5 stays refused, because Node cannot decrypt it. */
374
+ allowLegacyEncryptionAlgorithms?: boolean;
274
375
  }
275
376
  /**
276
377
  * Validates Authio access tokens and reads the caller.
@@ -282,6 +383,12 @@ interface AuthioVerifier {
282
383
  readonly discovery: AuthioDiscoveryClient;
283
384
  /** The accepted audiences. */
284
385
  readonly audiences: readonly string[];
386
+ /** The API's resource identifier, when configured. */
387
+ readonly resource: string | undefined;
388
+ /** The protected resource metadata URL sent in challenges, when turned on. */
389
+ readonly resourceMetadataUrl: string | undefined;
390
+ /** Scopes every request must carry. */
391
+ readonly requiredScopes: readonly string[];
285
392
  /** The introspection mode. */
286
393
  readonly introspection: AuthioIntrospectionMode;
287
394
  /** The client used for introspection, when there is one. */
@@ -307,7 +414,7 @@ declare const AuthioBearerErrorDescriptions: {
307
414
  readonly Inactive: "Authio reports the token as not active to this API's client. It was revoked or has expired or its session ended; or this API's client is not the introspection client of the resource server the token is for.";
308
415
  readonly Unconfirmed: "Authio could not be reached to confirm that the token is active.";
309
416
  readonly Empty: "The token is empty.";
310
- readonly Encrypted: "The token is encrypted; validate it with introspection ('introspectionOnly').";
417
+ readonly Encrypted: "The token is encrypted; set clientEncryptionKey to decrypt it, or validate it with introspection ('introspectionOnly').";
311
418
  readonly SigningKeysUnavailable: "Authio could not be reached to read its signing keys.";
312
419
  };
313
420
  /**
@@ -321,6 +428,48 @@ declare function createAuthioVerifier(options: AuthioVerifierOptions): AuthioVer
321
428
  */
322
429
  declare function readBearerToken(header: string | null | undefined): string | undefined;
323
430
 
431
+ /**
432
+ * What an API publishes about itself as a protected resource (RFC 9728).
433
+ */
434
+ interface AuthioProtectedResourceMetadataOptions extends Pick<AuthioConnectionOptions, "issuer" | "authority" | "realm"> {
435
+ /** The API's resource identifier (RFC 8707), such as `https://orders.example.com/api`. Required. */
436
+ resource: string;
437
+ /** Scopes a client can ask for to call the API. */
438
+ scopesSupported?: readonly string[];
439
+ /** A name to show to people. */
440
+ resourceName?: string;
441
+ /** A page documenting the API. */
442
+ resourceDocumentation?: string;
443
+ /** Further members, sent as given. */
444
+ extra?: Record<string, unknown>;
445
+ }
446
+ /**
447
+ * The protected resource metadata document (RFC 9728 §2).
448
+ */
449
+ interface AuthioProtectedResourceMetadata {
450
+ resource: string;
451
+ authorization_servers: string[];
452
+ bearer_methods_supported: string[];
453
+ scopes_supported?: string[];
454
+ resource_name?: string;
455
+ resource_documentation?: string;
456
+ [member: string]: unknown;
457
+ }
458
+ /**
459
+ * Where a resource's metadata is published: `{origin}/.well-known/oauth-protected-resource{path}` (RFC 9728 §3.1),
460
+ * the path being the resource's own, without a trailing slash.
461
+ */
462
+ declare function protectedResourceMetadataUrl(resource: string): string;
463
+ /**
464
+ * The path a resource's metadata is served at on the API itself: `/.well-known/oauth-protected-resource{path}`.
465
+ */
466
+ declare function protectedResourceMetadataPath(resource: string): string;
467
+ /**
468
+ * Builds the API's protected resource metadata: the resource, the realm's issuer as its authorization server, bearer
469
+ * tokens in the `Authorization` header, and the optional scopes, name and documentation.
470
+ */
471
+ declare function createProtectedResourceMetadata(options: AuthioProtectedResourceMetadataOptions): AuthioProtectedResourceMetadata;
472
+
324
473
  /**
325
474
  * Remembers delivered event ids, so a redelivery is acknowledged without running the handlers twice.
326
475
  */
@@ -436,4 +585,4 @@ declare function createOnBehalfOf(options: {
436
585
  tokenRefreshSkew?: number;
437
586
  }): AuthioOnBehalfOf;
438
587
 
439
- export { type AuthioActor, AuthioBearerError, AuthioBearerErrorDescriptions, type AuthioDownstreamSubject, AuthioForbiddenError, type AuthioIntrospectionMode, type AuthioMatch, type AuthioOnBehalfOf, type AuthioRule, type AuthioRuleFailure, AuthioUser, type AuthioUserClaimSettings, type AuthioVerifier, type AuthioVerifierOptions, type AuthioWebhookDeduplicationStore, type AuthioWebhookHandler, type AuthioWebhookOptions, type AuthioWebhookOutcome, type AuthioWebhooks, allOf, anyOf, authorize, createAuthioVerifier, createAuthioWebhooks, createForwardingFetch, createMemoryDeduplicationStore, createOnBehalfOf, readBearerToken, requireAuthenticationLevel, requireAuthenticationMethod, requireClaim, requireClient, requireGroup, requireOrganization, requirePermission, requireRole, requireScope, toForbiddenChallenge, toProblemDetails };
588
+ export { type AuthioActor, AuthioBearerError, AuthioBearerErrorDescriptions, type AuthioDownstreamSubject, AuthioForbiddenError, type AuthioImpersonator, type AuthioIntrospectionMode, type AuthioMatch, type AuthioOnBehalfOf, type AuthioProtectedResourceMetadata, type AuthioProtectedResourceMetadataOptions, type AuthioRule, type AuthioRuleFailure, AuthioUser, type AuthioUserClaimSettings, type AuthioVerifier, type AuthioVerifierOptions, type AuthioWebhookDeduplicationStore, type AuthioWebhookHandler, type AuthioWebhookOptions, type AuthioWebhookOutcome, type AuthioWebhooks, allOf, anyOf, authorize, createAuthioVerifier, createAuthioWebhooks, createForwardingFetch, createMemoryDeduplicationStore, createOnBehalfOf, createProtectedResourceMetadata, protectedResourceMetadataPath, protectedResourceMetadataUrl, readBearerToken, requireAuthenticationLevel, requireAuthenticationMethod, requireCallerKind, requireClaim, requireClient, requireGroup, requireOrganization, requirePermission, requireRecentAuthentication, requireRole, requireScope, toForbiddenChallenge, toProblemDetails };