@hyper.authio/testing 0.1.1 → 0.8.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
@@ -2,20 +2,86 @@ import { JWK } from 'jose';
2
2
  import { AuthioClock } from '@hyper.authio/client';
3
3
 
4
4
  /**
5
- * A client registered in the fake.
5
+ * How the fake encrypts what it issues to a client, as Authio does once the client registered an encryption key.
6
+ */
7
+ interface FakeAuthioClientEncryption {
8
+ /** The client's public encryption key (RSA). */
9
+ key: JWK;
10
+ /** Defaults to `RSA-OAEP-256`. */
11
+ algorithm?: "RSA-OAEP" | "RSA-OAEP-256";
12
+ /** Defaults to `A256CBC-HS512`. */
13
+ encryptionMethod?: "A128CBC-HS256" | "A192CBC-HS384" | "A256CBC-HS512";
14
+ /** Encrypts the client's access tokens (nested JWT). */
15
+ accessTokens?: boolean;
16
+ /** Encrypts the client's ID tokens. Defaults to `true`. */
17
+ idTokens?: boolean;
18
+ /** Encrypts UserInfo answered as a JWT. Defaults to `true`. */
19
+ userInfo?: boolean;
20
+ /** Encrypts back-channel logout tokens. */
21
+ logoutTokens?: boolean;
22
+ }
23
+ /**
24
+ * How a client authenticates to the token, introspection, revocation and device endpoints, as registered in Authio.
25
+ */
26
+ type FakeAuthioTokenEndpointAuthMethod = "client_secret_basic" | "client_secret_post" | "private_key_jwt" | "none";
27
+ /**
28
+ * The algorithm Authio signs every token of a client with: the realm's RSA key, or the client secret (HS*).
29
+ */
30
+ type FakeAuthioSigningAlgorithm = "RS256" | "HS256" | "HS384" | "HS512";
31
+ /**
32
+ * A client registered in the fake, configured as a client is in Authio's console. What the client requires is
33
+ * enforced as Authio enforces it: a request that does not match is refused with an error naming the setting, and
34
+ * what the client does not require is accepted (PKCE sent voluntarily, a secret sent by a public client).
6
35
  */
7
36
  interface FakeAuthioClient {
8
37
  clientId: string;
9
- /** For `client_secret_basic` and `client_secret_post`. */
38
+ /** For `client_secret_basic` and `client_secret_post`, and the key of HS* signatures. */
10
39
  clientSecret?: string;
11
40
  /** The public JWK `private_key_jwt` assertions are verified with. */
12
41
  publicKey?: JWK;
42
+ /**
43
+ * How the client must authenticate; another method is refused with `invalid_client`, except a secret sent by a
44
+ * `none` client, which is ignored. Defaults to `client_secret_basic` with a `clientSecret`, `private_key_jwt` with
45
+ * only a `publicKey`, and `none` with neither.
46
+ */
47
+ tokenEndpointAuthMethod?: FakeAuthioTokenEndpointAuthMethod;
48
+ /**
49
+ * Refuses an authorization request without `code_challenge` (Authio's "PKCE" switch). Defaults to `true`, as Authio
50
+ * creates clients; set `false` for a client with PKCE off.
51
+ */
52
+ requirePkce?: boolean;
53
+ /**
54
+ * The PKCE methods accepted, required or not: a `code_challenge` with another method is refused. Defaults to
55
+ * `["S256"]`, as Authio creates clients.
56
+ */
57
+ pkceMethods?: ("S256" | "plain")[];
58
+ /**
59
+ * The algorithm the client's ID tokens, signed UserInfo and logout tokens are signed with. Defaults to `RS256`. HS*
60
+ * needs a `clientSecret` of at least 32, 48 or 64 bytes (RFC 7518 §3.2), as Authio requires.
61
+ */
62
+ signingAlgorithm?: FakeAuthioSigningAlgorithm;
63
+ /** The algorithm the client's access tokens are signed with, Authio's second setting. Defaults to `RS256`. */
64
+ accessTokenSigningAlgorithm?: FakeAuthioSigningAlgorithm;
65
+ /** Issues a new ID token with every refresh. Defaults to `false`. */
66
+ issueIdTokenOnRefresh?: boolean;
13
67
  /** Scopes granted when a request names none. */
14
68
  defaultScopes?: string[];
15
69
  /** Extra claims put into every access token issued to the client. */
16
70
  claims?: Record<string, unknown>;
17
71
  /** The client this one's tokens are introspectable by, besides itself (a resource server's introspection client). */
18
72
  introspectionClients?: string[];
73
+ /** The redirect URIs the authorization endpoint accepts. Any when absent. */
74
+ redirectUris?: string[];
75
+ /** The post-logout redirect URIs the end-session endpoint accepts. Any when absent. */
76
+ postLogoutRedirectUris?: string[];
77
+ /** Where back-channel logout tokens are posted when a session of the client ends. */
78
+ backchannelLogoutUri?: string;
79
+ /** The page loaded (here: fetched) when a session of the client ends (front-channel logout). */
80
+ frontchannelLogoutUri?: string;
81
+ /** Adds `iss` and `sid` to the front-channel logout URI. Defaults to `false`. */
82
+ frontchannelLogoutSessionRequired?: boolean;
83
+ /** Encrypts what is issued to the client. */
84
+ encryption?: FakeAuthioClientEncryption;
19
85
  }
20
86
  /**
21
87
  * A person the fake knows, for the password grant, authorization codes and UserInfo.
@@ -28,6 +94,23 @@ interface FakeAuthioUser {
28
94
  claims?: Record<string, unknown>;
29
95
  /** What UserInfo answers besides `sub`. */
30
96
  userInfo?: Record<string, unknown>;
97
+ /** The sign-in's methods. Defaults to `["pwd"]`. */
98
+ amr?: string[];
99
+ /** The sign-in's level. Defaults to `1`, or the `acr_values` asked for. */
100
+ acr?: string;
101
+ }
102
+ /**
103
+ * A personal access token the fake accepts with the `personal_access_token` grant.
104
+ */
105
+ interface FakeAuthioPersonalAccessToken {
106
+ /** The `pat_…` value. */
107
+ token: string;
108
+ /** The credential's id, the token's `sub`. */
109
+ sub: string;
110
+ /** The consumer's client, for a consumer's token; the `authio` client of an account otherwise. */
111
+ clientId?: string;
112
+ /** Claims added to the access token. */
113
+ claims?: Record<string, unknown>;
31
114
  }
32
115
  /**
33
116
  * An error the fake answers a request with.
@@ -45,9 +128,40 @@ interface FakeAuthioRecordedRequest {
45
128
  path: string;
46
129
  headers: Record<string, string>;
47
130
  form: Record<string, string>;
131
+ /** The query parameters. */
132
+ query: Record<string, string>;
48
133
  /** The client the request authenticated as, when it did. */
49
134
  clientId?: string;
50
135
  }
136
+ /**
137
+ * A back-channel logout token the fake posted to a client.
138
+ */
139
+ interface FakeAuthioBackchannelDelivery {
140
+ clientId: string;
141
+ uri: string;
142
+ logoutToken: string;
143
+ /** The HTTP status the client answered, or 0 when it could not be reached. */
144
+ status: number;
145
+ }
146
+ /**
147
+ * A front-channel logout page the fake loaded for a client.
148
+ */
149
+ interface FakeAuthioFrontchannelDelivery {
150
+ clientId: string;
151
+ /** The URL loaded, with `iss` and `sid` when the client requires the session. */
152
+ url: string;
153
+ /** The HTTP status the client answered, or 0 when it could not be reached. */
154
+ status: number;
155
+ }
156
+ /**
157
+ * The parameters an authorization ended with, as the fake sent them back.
158
+ */
159
+ interface FakeAuthioAuthorizationOutcome {
160
+ /** `code`, `state` and `iss`, or `error`, `error_description`, `state` and `iss`. */
161
+ parameters: Record<string, string>;
162
+ /** The redirect URI with the parameters, for `response_mode=query`. */
163
+ callbackUrl: string | undefined;
164
+ }
51
165
  /**
52
166
  * What the device flow answers to successive polls.
53
167
  */
@@ -62,6 +176,14 @@ interface FakeAuthioOptions {
62
176
  accessTokenLifetime?: number;
63
177
  clients?: FakeAuthioClient[];
64
178
  users?: FakeAuthioUser[];
179
+ personalAccessTokens?: FakeAuthioPersonalAccessToken[];
180
+ /**
181
+ * The clock tokens are issued and checked with (milliseconds since the epoch), such as the `ManualClock` the
182
+ * library under test uses. Defaults to the system clock.
183
+ */
184
+ clock?: {
185
+ now(): number;
186
+ };
65
187
  }
66
188
  /**
67
189
  * Options of a token minted directly.
@@ -82,34 +204,56 @@ interface FakeAuthioTokenOptions {
82
204
  issuer?: string;
83
205
  /** Signs with another key, so the signature does not verify. */
84
206
  untrusted?: boolean;
207
+ /** Encrypts the token (nested JWT) to this public key, as Authio does for a client with an encryption key. */
208
+ encryptTo?: {
209
+ key: JWK;
210
+ algorithm?: "RSA-OAEP" | "RSA-OAEP-256";
211
+ encryptionMethod?: string;
212
+ };
85
213
  }
214
+ type Endpoint = "token" | "introspect" | "revoke" | "userinfo" | "device" | "discovery" | "authorize" | "logout";
86
215
  /**
87
- * An in-memory Authio realm served over HTTP on loopback: discovery, JWKS, token (client credentials,
88
- * authorization code, refresh, token exchange, password, device code), introspection, revocation, UserInfo and
89
- * device authorization.
216
+ * An in-memory Authio realm served over HTTP on loopback: discovery, JWKS, authorization (PKCE required or not and its
217
+ * allowed methods, `state`, `nonce`, `prompt`, `max_age`, `acr_values`, `response_mode` query or web_message, RFC 9207
218
+ * `iss`), token (client credentials, authorization code with ID tokens, refresh, token exchange, password, device code,
219
+ * personal access token) with the client's exact authentication method, introspection, revocation, UserInfo, device
220
+ * authorization, end session, back-channel and front-channel logout delivery, tokens signed RS256 or HS* with the
221
+ * client secret, and JWE (RSA-OAEP, RSA-OAEP-256) for clients that registered an encryption key.
90
222
  */
91
223
  declare class FakeAuthio {
92
224
  /** Every request received, in order. */
93
225
  readonly requests: FakeAuthioRecordedRequest[];
94
226
  /** Answers the next requests to an endpoint with these errors, in order, before answering normally. */
95
- readonly failures: Map<"token" | "introspect" | "revoke" | "userinfo" | "device" | "discovery", FakeAuthioError[]>;
227
+ readonly failures: Map<Endpoint, FakeAuthioError[]>;
228
+ /** Every back-channel logout token posted, in order. */
229
+ readonly backchannelDeliveries: FakeAuthioBackchannelDelivery[];
230
+ /** Every front-channel logout page loaded, in order. */
231
+ readonly frontchannelDeliveries: FakeAuthioFrontchannelDelivery[];
96
232
  /** What successive device-code polls answer; the last value repeats. Defaults to `["approved"]`. */
97
233
  deviceOutcomes: FakeAuthioDeviceOutcome[];
98
234
  /** Overrides the discovery document's issuer, to test the mismatch check. */
99
235
  discoveryIssuer: string | undefined;
100
- /** Answers UserInfo as `application/jwt`, signed by the realm key. */
236
+ /** Answers UserInfo as `application/jwt`, signed by the realm key (and encrypted for a client with a key). */
101
237
  userInfoAsJwt: boolean;
238
+ /** Leaves `iss` out of authorization responses, to test the RFC 9207 check. */
239
+ omitAuthorizationIssuer: boolean;
102
240
  /** Answers connected-account exchanges. Defaults to granting a token for the provider. */
103
241
  connectionResponder: ((form: Record<string, string>) => FakeAuthioError | Record<string, unknown>) | undefined;
242
+ /** The person the authorization endpoint signs in when the request names none with `login_hint`. Defaults to the first user. */
243
+ signInAs: string | undefined;
104
244
  private readonly clients;
105
245
  private readonly users;
246
+ private readonly personalAccessTokens;
106
247
  private readonly codes;
107
248
  private readonly refreshTokens;
108
249
  private readonly issued;
109
250
  private readonly devicePolls;
110
251
  private readonly lifetime;
252
+ private readonly clock;
253
+ private session;
111
254
  private server;
112
255
  private signingKey;
256
+ private verificationKey;
113
257
  private untrustedKey;
114
258
  private publicJwk;
115
259
  private baseUrl;
@@ -128,10 +272,20 @@ declare class FakeAuthio {
128
272
  get jwks(): {
129
273
  keys: JWK[];
130
274
  };
275
+ /** The session the authorization endpoint keeps (one browser), or `undefined` when nobody is signed in. */
276
+ get currentSession(): {
277
+ sub: string;
278
+ sid: string;
279
+ authTime: number;
280
+ } | undefined;
131
281
  /** Registers a client. */
132
282
  addClient(client: FakeAuthioClient): void;
133
283
  /** Registers a person. */
134
284
  addUser(user: FakeAuthioUser): void;
285
+ /** Registers a personal access token. */
286
+ addPersonalAccessToken(token: FakeAuthioPersonalAccessToken): void;
287
+ /** Ends the signed-in session without notifying anyone, as if it expired. */
288
+ endSession(): void;
135
289
  /**
136
290
  * Registers an authorization code for a person, as the authorization endpoint would after sign-in.
137
291
  */
@@ -142,13 +296,58 @@ declare class FakeAuthio {
142
296
  scope?: string;
143
297
  codeChallengeVerifier?: string;
144
298
  resource?: string;
299
+ nonce?: string;
145
300
  }): string;
301
+ /**
302
+ * Follows an authorization URL as a browser would, signing in `signInAs` (or the `login_hint`), and returns what
303
+ * Authio sent back: the callback URL for `response_mode=query`, the posted message's parameters for `web_message`.
304
+ */
305
+ authorize(authorizationUrl: string): Promise<FakeAuthioAuthorizationOutcome>;
146
306
  /**
147
307
  * Mints an access token signed by the realm key, recorded so introspection and revocation know it.
148
308
  */
149
309
  issueAccessToken(options?: FakeAuthioTokenOptions): Promise<string>;
150
- /** Marks a token revoked, as Authio would after a sign-out. */
310
+ /**
311
+ * Mints a back-channel logout token for a client, signed by the realm key. `overrides` replaces claims (`undefined`
312
+ * removes one) and `type` the header `typ`, to build invalid ones in tests.
313
+ */
314
+ createLogoutToken(options: {
315
+ clientId: string;
316
+ sub?: string;
317
+ sid?: string;
318
+ type?: string;
319
+ overrides?: Record<string, unknown>;
320
+ }): Promise<string>;
321
+ /**
322
+ * Posts a back-channel logout token to the client's `backchannelLogoutUri`, as Authio does when a session ends.
323
+ *
324
+ * @returns The HTTP status the client answered, or 0 when it could not be reached.
325
+ */
326
+ deliverBackchannelLogout(clientId: string, options?: {
327
+ sub?: string;
328
+ sid?: string;
329
+ }): Promise<number>;
330
+ /**
331
+ * The front-channel logout URL of a client for a session: its `frontchannelLogoutUri`, with `iss` and `sid` when the
332
+ * client requires the session.
333
+ */
334
+ frontchannelLogoutUrl(clientId: string, sid: string): string;
335
+ /**
336
+ * Loads the client's front-channel logout page, as the browser does in an iframe when Authio ends a session.
337
+ *
338
+ * @returns The HTTP status the client answered, or 0 when it could not be reached.
339
+ */
340
+ deliverFrontchannelLogout(clientId: string, options: {
341
+ sid: string;
342
+ headers?: Record<string, string>;
343
+ }): Promise<number>;
344
+ /** Marks a token revoked, as Authio would after a sign-out: an access token, or a refresh token, which is then forgotten. */
151
345
  revokeToken(token: string): void;
346
+ /** Revokes every refresh token of a person, or of one Authio session (`sid`), as Authio does when a session ends. */
347
+ revokeRefreshTokens(options: {
348
+ sub?: string;
349
+ sid?: string;
350
+ }): void;
152
351
  /** Forgets the recorded requests. */
153
352
  clearRequests(): void;
154
353
  /** The recorded requests to one endpoint path suffix, such as `/token`. */
@@ -158,6 +357,8 @@ declare class FakeAuthio {
158
357
  private listen;
159
358
  private handle;
160
359
  private discovery;
360
+ private authorizeEndpoint;
361
+ private endSessionEndpoint;
161
362
  private authenticate;
162
363
  private token;
163
364
  private tokenAnswer;
@@ -165,6 +366,9 @@ declare class FakeAuthio {
165
366
  private revoke;
166
367
  private device;
167
368
  private userInfo;
369
+ private nowSeconds;
370
+ private sign;
371
+ private verificationKeyFor;
168
372
  private requireUser;
169
373
  }
170
374
 
@@ -212,4 +416,4 @@ declare function createWebhookDelivery(type: string, data: unknown, secret: stri
212
416
  sendEventIdHeader?: boolean;
213
417
  }): Promise<FakeWebhookDelivery>;
214
418
 
215
- export { FakeAuthio, type FakeAuthioClient, type FakeAuthioDeviceOutcome, type FakeAuthioError, type FakeAuthioOptions, type FakeAuthioRecordedRequest, type FakeAuthioTokenOptions, type FakeAuthioUser, type FakeWebhookDelivery, ManualClock, createWebhookDelivery };
419
+ export { FakeAuthio, type FakeAuthioAuthorizationOutcome, type FakeAuthioBackchannelDelivery, type FakeAuthioClient, type FakeAuthioClientEncryption, type FakeAuthioDeviceOutcome, type FakeAuthioError, type FakeAuthioFrontchannelDelivery, type FakeAuthioOptions, type FakeAuthioPersonalAccessToken, type FakeAuthioRecordedRequest, type FakeAuthioSigningAlgorithm, type FakeAuthioTokenEndpointAuthMethod, type FakeAuthioTokenOptions, type FakeAuthioUser, type FakeWebhookDelivery, ManualClock, createWebhookDelivery };
package/dist/index.d.ts CHANGED
@@ -2,20 +2,86 @@ import { JWK } from 'jose';
2
2
  import { AuthioClock } from '@hyper.authio/client';
3
3
 
4
4
  /**
5
- * A client registered in the fake.
5
+ * How the fake encrypts what it issues to a client, as Authio does once the client registered an encryption key.
6
+ */
7
+ interface FakeAuthioClientEncryption {
8
+ /** The client's public encryption key (RSA). */
9
+ key: JWK;
10
+ /** Defaults to `RSA-OAEP-256`. */
11
+ algorithm?: "RSA-OAEP" | "RSA-OAEP-256";
12
+ /** Defaults to `A256CBC-HS512`. */
13
+ encryptionMethod?: "A128CBC-HS256" | "A192CBC-HS384" | "A256CBC-HS512";
14
+ /** Encrypts the client's access tokens (nested JWT). */
15
+ accessTokens?: boolean;
16
+ /** Encrypts the client's ID tokens. Defaults to `true`. */
17
+ idTokens?: boolean;
18
+ /** Encrypts UserInfo answered as a JWT. Defaults to `true`. */
19
+ userInfo?: boolean;
20
+ /** Encrypts back-channel logout tokens. */
21
+ logoutTokens?: boolean;
22
+ }
23
+ /**
24
+ * How a client authenticates to the token, introspection, revocation and device endpoints, as registered in Authio.
25
+ */
26
+ type FakeAuthioTokenEndpointAuthMethod = "client_secret_basic" | "client_secret_post" | "private_key_jwt" | "none";
27
+ /**
28
+ * The algorithm Authio signs every token of a client with: the realm's RSA key, or the client secret (HS*).
29
+ */
30
+ type FakeAuthioSigningAlgorithm = "RS256" | "HS256" | "HS384" | "HS512";
31
+ /**
32
+ * A client registered in the fake, configured as a client is in Authio's console. What the client requires is
33
+ * enforced as Authio enforces it: a request that does not match is refused with an error naming the setting, and
34
+ * what the client does not require is accepted (PKCE sent voluntarily, a secret sent by a public client).
6
35
  */
7
36
  interface FakeAuthioClient {
8
37
  clientId: string;
9
- /** For `client_secret_basic` and `client_secret_post`. */
38
+ /** For `client_secret_basic` and `client_secret_post`, and the key of HS* signatures. */
10
39
  clientSecret?: string;
11
40
  /** The public JWK `private_key_jwt` assertions are verified with. */
12
41
  publicKey?: JWK;
42
+ /**
43
+ * How the client must authenticate; another method is refused with `invalid_client`, except a secret sent by a
44
+ * `none` client, which is ignored. Defaults to `client_secret_basic` with a `clientSecret`, `private_key_jwt` with
45
+ * only a `publicKey`, and `none` with neither.
46
+ */
47
+ tokenEndpointAuthMethod?: FakeAuthioTokenEndpointAuthMethod;
48
+ /**
49
+ * Refuses an authorization request without `code_challenge` (Authio's "PKCE" switch). Defaults to `true`, as Authio
50
+ * creates clients; set `false` for a client with PKCE off.
51
+ */
52
+ requirePkce?: boolean;
53
+ /**
54
+ * The PKCE methods accepted, required or not: a `code_challenge` with another method is refused. Defaults to
55
+ * `["S256"]`, as Authio creates clients.
56
+ */
57
+ pkceMethods?: ("S256" | "plain")[];
58
+ /**
59
+ * The algorithm the client's ID tokens, signed UserInfo and logout tokens are signed with. Defaults to `RS256`. HS*
60
+ * needs a `clientSecret` of at least 32, 48 or 64 bytes (RFC 7518 §3.2), as Authio requires.
61
+ */
62
+ signingAlgorithm?: FakeAuthioSigningAlgorithm;
63
+ /** The algorithm the client's access tokens are signed with, Authio's second setting. Defaults to `RS256`. */
64
+ accessTokenSigningAlgorithm?: FakeAuthioSigningAlgorithm;
65
+ /** Issues a new ID token with every refresh. Defaults to `false`. */
66
+ issueIdTokenOnRefresh?: boolean;
13
67
  /** Scopes granted when a request names none. */
14
68
  defaultScopes?: string[];
15
69
  /** Extra claims put into every access token issued to the client. */
16
70
  claims?: Record<string, unknown>;
17
71
  /** The client this one's tokens are introspectable by, besides itself (a resource server's introspection client). */
18
72
  introspectionClients?: string[];
73
+ /** The redirect URIs the authorization endpoint accepts. Any when absent. */
74
+ redirectUris?: string[];
75
+ /** The post-logout redirect URIs the end-session endpoint accepts. Any when absent. */
76
+ postLogoutRedirectUris?: string[];
77
+ /** Where back-channel logout tokens are posted when a session of the client ends. */
78
+ backchannelLogoutUri?: string;
79
+ /** The page loaded (here: fetched) when a session of the client ends (front-channel logout). */
80
+ frontchannelLogoutUri?: string;
81
+ /** Adds `iss` and `sid` to the front-channel logout URI. Defaults to `false`. */
82
+ frontchannelLogoutSessionRequired?: boolean;
83
+ /** Encrypts what is issued to the client. */
84
+ encryption?: FakeAuthioClientEncryption;
19
85
  }
20
86
  /**
21
87
  * A person the fake knows, for the password grant, authorization codes and UserInfo.
@@ -28,6 +94,23 @@ interface FakeAuthioUser {
28
94
  claims?: Record<string, unknown>;
29
95
  /** What UserInfo answers besides `sub`. */
30
96
  userInfo?: Record<string, unknown>;
97
+ /** The sign-in's methods. Defaults to `["pwd"]`. */
98
+ amr?: string[];
99
+ /** The sign-in's level. Defaults to `1`, or the `acr_values` asked for. */
100
+ acr?: string;
101
+ }
102
+ /**
103
+ * A personal access token the fake accepts with the `personal_access_token` grant.
104
+ */
105
+ interface FakeAuthioPersonalAccessToken {
106
+ /** The `pat_…` value. */
107
+ token: string;
108
+ /** The credential's id, the token's `sub`. */
109
+ sub: string;
110
+ /** The consumer's client, for a consumer's token; the `authio` client of an account otherwise. */
111
+ clientId?: string;
112
+ /** Claims added to the access token. */
113
+ claims?: Record<string, unknown>;
31
114
  }
32
115
  /**
33
116
  * An error the fake answers a request with.
@@ -45,9 +128,40 @@ interface FakeAuthioRecordedRequest {
45
128
  path: string;
46
129
  headers: Record<string, string>;
47
130
  form: Record<string, string>;
131
+ /** The query parameters. */
132
+ query: Record<string, string>;
48
133
  /** The client the request authenticated as, when it did. */
49
134
  clientId?: string;
50
135
  }
136
+ /**
137
+ * A back-channel logout token the fake posted to a client.
138
+ */
139
+ interface FakeAuthioBackchannelDelivery {
140
+ clientId: string;
141
+ uri: string;
142
+ logoutToken: string;
143
+ /** The HTTP status the client answered, or 0 when it could not be reached. */
144
+ status: number;
145
+ }
146
+ /**
147
+ * A front-channel logout page the fake loaded for a client.
148
+ */
149
+ interface FakeAuthioFrontchannelDelivery {
150
+ clientId: string;
151
+ /** The URL loaded, with `iss` and `sid` when the client requires the session. */
152
+ url: string;
153
+ /** The HTTP status the client answered, or 0 when it could not be reached. */
154
+ status: number;
155
+ }
156
+ /**
157
+ * The parameters an authorization ended with, as the fake sent them back.
158
+ */
159
+ interface FakeAuthioAuthorizationOutcome {
160
+ /** `code`, `state` and `iss`, or `error`, `error_description`, `state` and `iss`. */
161
+ parameters: Record<string, string>;
162
+ /** The redirect URI with the parameters, for `response_mode=query`. */
163
+ callbackUrl: string | undefined;
164
+ }
51
165
  /**
52
166
  * What the device flow answers to successive polls.
53
167
  */
@@ -62,6 +176,14 @@ interface FakeAuthioOptions {
62
176
  accessTokenLifetime?: number;
63
177
  clients?: FakeAuthioClient[];
64
178
  users?: FakeAuthioUser[];
179
+ personalAccessTokens?: FakeAuthioPersonalAccessToken[];
180
+ /**
181
+ * The clock tokens are issued and checked with (milliseconds since the epoch), such as the `ManualClock` the
182
+ * library under test uses. Defaults to the system clock.
183
+ */
184
+ clock?: {
185
+ now(): number;
186
+ };
65
187
  }
66
188
  /**
67
189
  * Options of a token minted directly.
@@ -82,34 +204,56 @@ interface FakeAuthioTokenOptions {
82
204
  issuer?: string;
83
205
  /** Signs with another key, so the signature does not verify. */
84
206
  untrusted?: boolean;
207
+ /** Encrypts the token (nested JWT) to this public key, as Authio does for a client with an encryption key. */
208
+ encryptTo?: {
209
+ key: JWK;
210
+ algorithm?: "RSA-OAEP" | "RSA-OAEP-256";
211
+ encryptionMethod?: string;
212
+ };
85
213
  }
214
+ type Endpoint = "token" | "introspect" | "revoke" | "userinfo" | "device" | "discovery" | "authorize" | "logout";
86
215
  /**
87
- * An in-memory Authio realm served over HTTP on loopback: discovery, JWKS, token (client credentials,
88
- * authorization code, refresh, token exchange, password, device code), introspection, revocation, UserInfo and
89
- * device authorization.
216
+ * An in-memory Authio realm served over HTTP on loopback: discovery, JWKS, authorization (PKCE required or not and its
217
+ * allowed methods, `state`, `nonce`, `prompt`, `max_age`, `acr_values`, `response_mode` query or web_message, RFC 9207
218
+ * `iss`), token (client credentials, authorization code with ID tokens, refresh, token exchange, password, device code,
219
+ * personal access token) with the client's exact authentication method, introspection, revocation, UserInfo, device
220
+ * authorization, end session, back-channel and front-channel logout delivery, tokens signed RS256 or HS* with the
221
+ * client secret, and JWE (RSA-OAEP, RSA-OAEP-256) for clients that registered an encryption key.
90
222
  */
91
223
  declare class FakeAuthio {
92
224
  /** Every request received, in order. */
93
225
  readonly requests: FakeAuthioRecordedRequest[];
94
226
  /** Answers the next requests to an endpoint with these errors, in order, before answering normally. */
95
- readonly failures: Map<"token" | "introspect" | "revoke" | "userinfo" | "device" | "discovery", FakeAuthioError[]>;
227
+ readonly failures: Map<Endpoint, FakeAuthioError[]>;
228
+ /** Every back-channel logout token posted, in order. */
229
+ readonly backchannelDeliveries: FakeAuthioBackchannelDelivery[];
230
+ /** Every front-channel logout page loaded, in order. */
231
+ readonly frontchannelDeliveries: FakeAuthioFrontchannelDelivery[];
96
232
  /** What successive device-code polls answer; the last value repeats. Defaults to `["approved"]`. */
97
233
  deviceOutcomes: FakeAuthioDeviceOutcome[];
98
234
  /** Overrides the discovery document's issuer, to test the mismatch check. */
99
235
  discoveryIssuer: string | undefined;
100
- /** Answers UserInfo as `application/jwt`, signed by the realm key. */
236
+ /** Answers UserInfo as `application/jwt`, signed by the realm key (and encrypted for a client with a key). */
101
237
  userInfoAsJwt: boolean;
238
+ /** Leaves `iss` out of authorization responses, to test the RFC 9207 check. */
239
+ omitAuthorizationIssuer: boolean;
102
240
  /** Answers connected-account exchanges. Defaults to granting a token for the provider. */
103
241
  connectionResponder: ((form: Record<string, string>) => FakeAuthioError | Record<string, unknown>) | undefined;
242
+ /** The person the authorization endpoint signs in when the request names none with `login_hint`. Defaults to the first user. */
243
+ signInAs: string | undefined;
104
244
  private readonly clients;
105
245
  private readonly users;
246
+ private readonly personalAccessTokens;
106
247
  private readonly codes;
107
248
  private readonly refreshTokens;
108
249
  private readonly issued;
109
250
  private readonly devicePolls;
110
251
  private readonly lifetime;
252
+ private readonly clock;
253
+ private session;
111
254
  private server;
112
255
  private signingKey;
256
+ private verificationKey;
113
257
  private untrustedKey;
114
258
  private publicJwk;
115
259
  private baseUrl;
@@ -128,10 +272,20 @@ declare class FakeAuthio {
128
272
  get jwks(): {
129
273
  keys: JWK[];
130
274
  };
275
+ /** The session the authorization endpoint keeps (one browser), or `undefined` when nobody is signed in. */
276
+ get currentSession(): {
277
+ sub: string;
278
+ sid: string;
279
+ authTime: number;
280
+ } | undefined;
131
281
  /** Registers a client. */
132
282
  addClient(client: FakeAuthioClient): void;
133
283
  /** Registers a person. */
134
284
  addUser(user: FakeAuthioUser): void;
285
+ /** Registers a personal access token. */
286
+ addPersonalAccessToken(token: FakeAuthioPersonalAccessToken): void;
287
+ /** Ends the signed-in session without notifying anyone, as if it expired. */
288
+ endSession(): void;
135
289
  /**
136
290
  * Registers an authorization code for a person, as the authorization endpoint would after sign-in.
137
291
  */
@@ -142,13 +296,58 @@ declare class FakeAuthio {
142
296
  scope?: string;
143
297
  codeChallengeVerifier?: string;
144
298
  resource?: string;
299
+ nonce?: string;
145
300
  }): string;
301
+ /**
302
+ * Follows an authorization URL as a browser would, signing in `signInAs` (or the `login_hint`), and returns what
303
+ * Authio sent back: the callback URL for `response_mode=query`, the posted message's parameters for `web_message`.
304
+ */
305
+ authorize(authorizationUrl: string): Promise<FakeAuthioAuthorizationOutcome>;
146
306
  /**
147
307
  * Mints an access token signed by the realm key, recorded so introspection and revocation know it.
148
308
  */
149
309
  issueAccessToken(options?: FakeAuthioTokenOptions): Promise<string>;
150
- /** Marks a token revoked, as Authio would after a sign-out. */
310
+ /**
311
+ * Mints a back-channel logout token for a client, signed by the realm key. `overrides` replaces claims (`undefined`
312
+ * removes one) and `type` the header `typ`, to build invalid ones in tests.
313
+ */
314
+ createLogoutToken(options: {
315
+ clientId: string;
316
+ sub?: string;
317
+ sid?: string;
318
+ type?: string;
319
+ overrides?: Record<string, unknown>;
320
+ }): Promise<string>;
321
+ /**
322
+ * Posts a back-channel logout token to the client's `backchannelLogoutUri`, as Authio does when a session ends.
323
+ *
324
+ * @returns The HTTP status the client answered, or 0 when it could not be reached.
325
+ */
326
+ deliverBackchannelLogout(clientId: string, options?: {
327
+ sub?: string;
328
+ sid?: string;
329
+ }): Promise<number>;
330
+ /**
331
+ * The front-channel logout URL of a client for a session: its `frontchannelLogoutUri`, with `iss` and `sid` when the
332
+ * client requires the session.
333
+ */
334
+ frontchannelLogoutUrl(clientId: string, sid: string): string;
335
+ /**
336
+ * Loads the client's front-channel logout page, as the browser does in an iframe when Authio ends a session.
337
+ *
338
+ * @returns The HTTP status the client answered, or 0 when it could not be reached.
339
+ */
340
+ deliverFrontchannelLogout(clientId: string, options: {
341
+ sid: string;
342
+ headers?: Record<string, string>;
343
+ }): Promise<number>;
344
+ /** Marks a token revoked, as Authio would after a sign-out: an access token, or a refresh token, which is then forgotten. */
151
345
  revokeToken(token: string): void;
346
+ /** Revokes every refresh token of a person, or of one Authio session (`sid`), as Authio does when a session ends. */
347
+ revokeRefreshTokens(options: {
348
+ sub?: string;
349
+ sid?: string;
350
+ }): void;
152
351
  /** Forgets the recorded requests. */
153
352
  clearRequests(): void;
154
353
  /** The recorded requests to one endpoint path suffix, such as `/token`. */
@@ -158,6 +357,8 @@ declare class FakeAuthio {
158
357
  private listen;
159
358
  private handle;
160
359
  private discovery;
360
+ private authorizeEndpoint;
361
+ private endSessionEndpoint;
161
362
  private authenticate;
162
363
  private token;
163
364
  private tokenAnswer;
@@ -165,6 +366,9 @@ declare class FakeAuthio {
165
366
  private revoke;
166
367
  private device;
167
368
  private userInfo;
369
+ private nowSeconds;
370
+ private sign;
371
+ private verificationKeyFor;
168
372
  private requireUser;
169
373
  }
170
374
 
@@ -212,4 +416,4 @@ declare function createWebhookDelivery(type: string, data: unknown, secret: stri
212
416
  sendEventIdHeader?: boolean;
213
417
  }): Promise<FakeWebhookDelivery>;
214
418
 
215
- export { FakeAuthio, type FakeAuthioClient, type FakeAuthioDeviceOutcome, type FakeAuthioError, type FakeAuthioOptions, type FakeAuthioRecordedRequest, type FakeAuthioTokenOptions, type FakeAuthioUser, type FakeWebhookDelivery, ManualClock, createWebhookDelivery };
419
+ export { FakeAuthio, type FakeAuthioAuthorizationOutcome, type FakeAuthioBackchannelDelivery, type FakeAuthioClient, type FakeAuthioClientEncryption, type FakeAuthioDeviceOutcome, type FakeAuthioError, type FakeAuthioFrontchannelDelivery, type FakeAuthioOptions, type FakeAuthioPersonalAccessToken, type FakeAuthioRecordedRequest, type FakeAuthioSigningAlgorithm, type FakeAuthioTokenEndpointAuthMethod, type FakeAuthioTokenOptions, type FakeAuthioUser, type FakeWebhookDelivery, ManualClock, createWebhookDelivery };