@hyper.authio/testing 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.ts CHANGED
@@ -1,6 +1,25 @@
1
1
  import { JWK } from 'jose';
2
2
  import { AuthioClock } from '@hyper.authio/client';
3
3
 
4
+ /**
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
+ }
4
23
  /**
5
24
  * A client registered in the fake.
6
25
  */
@@ -16,6 +35,14 @@ interface FakeAuthioClient {
16
35
  claims?: Record<string, unknown>;
17
36
  /** The client this one's tokens are introspectable by, besides itself (a resource server's introspection client). */
18
37
  introspectionClients?: string[];
38
+ /** The redirect URIs the authorization endpoint accepts. Any when absent. */
39
+ redirectUris?: string[];
40
+ /** The post-logout redirect URIs the end-session endpoint accepts. Any when absent. */
41
+ postLogoutRedirectUris?: string[];
42
+ /** Where back-channel logout tokens are posted when a session of the client ends. */
43
+ backchannelLogoutUri?: string;
44
+ /** Encrypts what is issued to the client. */
45
+ encryption?: FakeAuthioClientEncryption;
19
46
  }
20
47
  /**
21
48
  * A person the fake knows, for the password grant, authorization codes and UserInfo.
@@ -28,6 +55,23 @@ interface FakeAuthioUser {
28
55
  claims?: Record<string, unknown>;
29
56
  /** What UserInfo answers besides `sub`. */
30
57
  userInfo?: Record<string, unknown>;
58
+ /** The sign-in's methods. Defaults to `["pwd"]`. */
59
+ amr?: string[];
60
+ /** The sign-in's level. Defaults to `1`, or the `acr_values` asked for. */
61
+ acr?: string;
62
+ }
63
+ /**
64
+ * A personal access token the fake accepts with the `personal_access_token` grant.
65
+ */
66
+ interface FakeAuthioPersonalAccessToken {
67
+ /** The `pat_…` value. */
68
+ token: string;
69
+ /** The credential's id, the token's `sub`. */
70
+ sub: string;
71
+ /** The consumer's client, for a consumer's token; the `authio` client of an account otherwise. */
72
+ clientId?: string;
73
+ /** Claims added to the access token. */
74
+ claims?: Record<string, unknown>;
31
75
  }
32
76
  /**
33
77
  * An error the fake answers a request with.
@@ -45,9 +89,30 @@ interface FakeAuthioRecordedRequest {
45
89
  path: string;
46
90
  headers: Record<string, string>;
47
91
  form: Record<string, string>;
92
+ /** The query parameters. */
93
+ query: Record<string, string>;
48
94
  /** The client the request authenticated as, when it did. */
49
95
  clientId?: string;
50
96
  }
97
+ /**
98
+ * A back-channel logout token the fake posted to a client.
99
+ */
100
+ interface FakeAuthioBackchannelDelivery {
101
+ clientId: string;
102
+ uri: string;
103
+ logoutToken: string;
104
+ /** The HTTP status the client answered, or 0 when it could not be reached. */
105
+ status: number;
106
+ }
107
+ /**
108
+ * The parameters an authorization ended with, as the fake sent them back.
109
+ */
110
+ interface FakeAuthioAuthorizationOutcome {
111
+ /** `code`, `state` and `iss`, or `error`, `error_description`, `state` and `iss`. */
112
+ parameters: Record<string, string>;
113
+ /** The redirect URI with the parameters, for `response_mode=query`. */
114
+ callbackUrl: string | undefined;
115
+ }
51
116
  /**
52
117
  * What the device flow answers to successive polls.
53
118
  */
@@ -62,6 +127,7 @@ interface FakeAuthioOptions {
62
127
  accessTokenLifetime?: number;
63
128
  clients?: FakeAuthioClient[];
64
129
  users?: FakeAuthioUser[];
130
+ personalAccessTokens?: FakeAuthioPersonalAccessToken[];
65
131
  }
66
132
  /**
67
133
  * Options of a token minted directly.
@@ -82,34 +148,52 @@ interface FakeAuthioTokenOptions {
82
148
  issuer?: string;
83
149
  /** Signs with another key, so the signature does not verify. */
84
150
  untrusted?: boolean;
151
+ /** Encrypts the token (nested JWT) to this public key, as Authio does for a client with an encryption key. */
152
+ encryptTo?: {
153
+ key: JWK;
154
+ algorithm?: "RSA-OAEP" | "RSA-OAEP-256";
155
+ encryptionMethod?: string;
156
+ };
85
157
  }
158
+ type Endpoint = "token" | "introspect" | "revoke" | "userinfo" | "device" | "discovery" | "authorize" | "logout";
86
159
  /**
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.
160
+ * An in-memory Authio realm served over HTTP on loopback: discovery, JWKS, authorization (PKCE, `state`, `nonce`,
161
+ * `prompt`, `max_age`, `acr_values`, `response_mode=web_message`, RFC 9207 `iss`), token (client credentials,
162
+ * authorization code with ID tokens, refresh, token exchange, password, device code, personal access token),
163
+ * introspection, revocation, UserInfo, device authorization, end session and back-channel logout delivery, with JWE
164
+ * (RSA-OAEP, RSA-OAEP-256) for clients that registered an encryption key.
90
165
  */
91
166
  declare class FakeAuthio {
92
167
  /** Every request received, in order. */
93
168
  readonly requests: FakeAuthioRecordedRequest[];
94
169
  /** 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[]>;
170
+ readonly failures: Map<Endpoint, FakeAuthioError[]>;
171
+ /** Every back-channel logout token posted, in order. */
172
+ readonly backchannelDeliveries: FakeAuthioBackchannelDelivery[];
96
173
  /** What successive device-code polls answer; the last value repeats. Defaults to `["approved"]`. */
97
174
  deviceOutcomes: FakeAuthioDeviceOutcome[];
98
175
  /** Overrides the discovery document's issuer, to test the mismatch check. */
99
176
  discoveryIssuer: string | undefined;
100
- /** Answers UserInfo as `application/jwt`, signed by the realm key. */
177
+ /** Answers UserInfo as `application/jwt`, signed by the realm key (and encrypted for a client with a key). */
101
178
  userInfoAsJwt: boolean;
179
+ /** Leaves `iss` out of authorization responses, to test the RFC 9207 check. */
180
+ omitAuthorizationIssuer: boolean;
102
181
  /** Answers connected-account exchanges. Defaults to granting a token for the provider. */
103
182
  connectionResponder: ((form: Record<string, string>) => FakeAuthioError | Record<string, unknown>) | undefined;
183
+ /** The person the authorization endpoint signs in when the request names none with `login_hint`. Defaults to the first user. */
184
+ signInAs: string | undefined;
104
185
  private readonly clients;
105
186
  private readonly users;
187
+ private readonly personalAccessTokens;
106
188
  private readonly codes;
107
189
  private readonly refreshTokens;
108
190
  private readonly issued;
109
191
  private readonly devicePolls;
110
192
  private readonly lifetime;
193
+ private session;
111
194
  private server;
112
195
  private signingKey;
196
+ private verificationKey;
113
197
  private untrustedKey;
114
198
  private publicJwk;
115
199
  private baseUrl;
@@ -128,10 +212,20 @@ declare class FakeAuthio {
128
212
  get jwks(): {
129
213
  keys: JWK[];
130
214
  };
215
+ /** The session the authorization endpoint keeps (one browser), or `undefined` when nobody is signed in. */
216
+ get currentSession(): {
217
+ sub: string;
218
+ sid: string;
219
+ authTime: number;
220
+ } | undefined;
131
221
  /** Registers a client. */
132
222
  addClient(client: FakeAuthioClient): void;
133
223
  /** Registers a person. */
134
224
  addUser(user: FakeAuthioUser): void;
225
+ /** Registers a personal access token. */
226
+ addPersonalAccessToken(token: FakeAuthioPersonalAccessToken): void;
227
+ /** Ends the signed-in session without notifying anyone, as if it expired. */
228
+ endSession(): void;
135
229
  /**
136
230
  * Registers an authorization code for a person, as the authorization endpoint would after sign-in.
137
231
  */
@@ -142,13 +236,44 @@ declare class FakeAuthio {
142
236
  scope?: string;
143
237
  codeChallengeVerifier?: string;
144
238
  resource?: string;
239
+ nonce?: string;
145
240
  }): string;
241
+ /**
242
+ * Follows an authorization URL as a browser would, signing in `signInAs` (or the `login_hint`), and returns what
243
+ * Authio sent back: the callback URL for `response_mode=query`, the posted message's parameters for `web_message`.
244
+ */
245
+ authorize(authorizationUrl: string): Promise<FakeAuthioAuthorizationOutcome>;
146
246
  /**
147
247
  * Mints an access token signed by the realm key, recorded so introspection and revocation know it.
148
248
  */
149
249
  issueAccessToken(options?: FakeAuthioTokenOptions): Promise<string>;
150
- /** Marks a token revoked, as Authio would after a sign-out. */
250
+ /**
251
+ * Mints a back-channel logout token for a client, signed by the realm key. `overrides` replaces claims (`undefined`
252
+ * removes one) and `type` the header `typ`, to build invalid ones in tests.
253
+ */
254
+ createLogoutToken(options: {
255
+ clientId: string;
256
+ sub?: string;
257
+ sid?: string;
258
+ type?: string;
259
+ overrides?: Record<string, unknown>;
260
+ }): Promise<string>;
261
+ /**
262
+ * Posts a back-channel logout token to the client's `backchannelLogoutUri`, as Authio does when a session ends.
263
+ *
264
+ * @returns The HTTP status the client answered, or 0 when it could not be reached.
265
+ */
266
+ deliverBackchannelLogout(clientId: string, options?: {
267
+ sub?: string;
268
+ sid?: string;
269
+ }): Promise<number>;
270
+ /** Marks a token revoked, as Authio would after a sign-out: an access token, or a refresh token, which is then forgotten. */
151
271
  revokeToken(token: string): void;
272
+ /** Revokes every refresh token of a person, or of one Authio session (`sid`), as Authio does when a session ends. */
273
+ revokeRefreshTokens(options: {
274
+ sub?: string;
275
+ sid?: string;
276
+ }): void;
152
277
  /** Forgets the recorded requests. */
153
278
  clearRequests(): void;
154
279
  /** The recorded requests to one endpoint path suffix, such as `/token`. */
@@ -158,6 +283,8 @@ declare class FakeAuthio {
158
283
  private listen;
159
284
  private handle;
160
285
  private discovery;
286
+ private authorizeEndpoint;
287
+ private endSessionEndpoint;
161
288
  private authenticate;
162
289
  private token;
163
290
  private tokenAnswer;
@@ -212,4 +339,4 @@ declare function createWebhookDelivery(type: string, data: unknown, secret: stri
212
339
  sendEventIdHeader?: boolean;
213
340
  }): Promise<FakeWebhookDelivery>;
214
341
 
215
- export { FakeAuthio, type FakeAuthioClient, type FakeAuthioDeviceOutcome, type FakeAuthioError, type FakeAuthioOptions, type FakeAuthioRecordedRequest, type FakeAuthioTokenOptions, type FakeAuthioUser, type FakeWebhookDelivery, ManualClock, createWebhookDelivery };
342
+ export { FakeAuthio, type FakeAuthioAuthorizationOutcome, type FakeAuthioBackchannelDelivery, type FakeAuthioClient, type FakeAuthioClientEncryption, type FakeAuthioDeviceOutcome, type FakeAuthioError, type FakeAuthioOptions, type FakeAuthioPersonalAccessToken, type FakeAuthioRecordedRequest, type FakeAuthioTokenOptions, type FakeAuthioUser, type FakeWebhookDelivery, ManualClock, createWebhookDelivery };