@hyper.authio/testing 0.8.1 → 0.10.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
@@ -28,6 +28,14 @@ type FakeAuthioTokenEndpointAuthMethod = "client_secret_basic" | "client_secret_
28
28
  * The algorithm Authio signs every token of a client with: the realm's RSA key, or the client secret (HS*).
29
29
  */
30
30
  type FakeAuthioSigningAlgorithm = "RS256" | "HS256" | "HS384" | "HS512";
31
+ /**
32
+ * The form a client's access tokens are handed out in, as Authio's access token format setting: `Jwt`, the
33
+ * self-contained JWT; `Reference`, an opaque handle of 43 characters that only introspection resolves; `Lightweight`, a
34
+ * JWT that carries only the identity and session claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`, `typ`,
35
+ * `client_id`, `azp`, `scope`, `sid`, `auth_time`, `acr`, `amr`, `idp`, `act`, `delegation_id`, `session_client`,
36
+ * `org_id`, `org_alias`). Introspection answers with every claim of the full token, whatever the form.
37
+ */
38
+ type FakeAuthioAccessTokenFormat = "Jwt" | "Reference" | "Lightweight";
31
39
  /**
32
40
  * A client registered in the fake, configured as a client is in Authio's console. What the client requires is
33
41
  * enforced as Authio enforces it: a request that does not match is refused with an error naming the setting, and
@@ -62,6 +70,8 @@ interface FakeAuthioClient {
62
70
  signingAlgorithm?: FakeAuthioSigningAlgorithm;
63
71
  /** The algorithm the client's access tokens are signed with, Authio's second setting. Defaults to `RS256`. */
64
72
  accessTokenSigningAlgorithm?: FakeAuthioSigningAlgorithm;
73
+ /** The form the client's access tokens are handed out in. Defaults to `Jwt`. */
74
+ accessTokenFormat?: FakeAuthioAccessTokenFormat;
65
75
  /** Issues a new ID token with every refresh. Defaults to `false`. */
66
76
  issueIdTokenOnRefresh?: boolean;
67
77
  /** Scopes granted when a request names none. */
@@ -198,6 +208,8 @@ interface FakeAuthioTokenOptions {
198
208
  expiresIn?: number;
199
209
  /** The `typ` claim. Defaults to `Bearer`. */
200
210
  type?: string;
211
+ /** The header `typ`. Defaults to `at+jwt` (RFC 9068 §2.1); `null` leaves it out. */
212
+ headerType?: string | null;
201
213
  /** Claims added to the payload. */
202
214
  claims?: Record<string, unknown>;
203
215
  /** Signs with another issuer. */
@@ -210,6 +222,12 @@ interface FakeAuthioTokenOptions {
210
222
  algorithm?: "RSA-OAEP" | "RSA-OAEP-256";
211
223
  encryptionMethod?: string;
212
224
  };
225
+ /**
226
+ * The form of the token: `Reference` mints an opaque handle (never encrypted) and `Lightweight` a JWT with only the
227
+ * identity and session claims; introspection answers with every claim either way. Defaults to the client's
228
+ * `accessTokenFormat`, else `Jwt`.
229
+ */
230
+ format?: FakeAuthioAccessTokenFormat;
213
231
  }
214
232
  type Endpoint = "token" | "introspect" | "revoke" | "userinfo" | "device" | "discovery" | "authorize" | "logout";
215
233
  /**
@@ -218,7 +236,8 @@ type Endpoint = "token" | "introspect" | "revoke" | "userinfo" | "device" | "dis
218
236
  * `iss`), token (client credentials, authorization code with ID tokens, refresh, token exchange, password, device code,
219
237
  * personal access token) with the client's exact authentication method, introspection, revocation, UserInfo, device
220
238
  * 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.
239
+ * client secret, access tokens as a JWT, an opaque reference or a lightweight JWT (`accessTokenFormat`), and JWE
240
+ * (RSA-OAEP, RSA-OAEP-256) for clients that registered an encryption key.
222
241
  */
223
242
  declare class FakeAuthio {
224
243
  /** Every request received, in order. */
@@ -416,4 +435,4 @@ declare function createWebhookDelivery(type: string, data: unknown, secret: stri
416
435
  sendEventIdHeader?: boolean;
417
436
  }): Promise<FakeWebhookDelivery>;
418
437
 
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 };
438
+ export { FakeAuthio, type FakeAuthioAccessTokenFormat, 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
@@ -28,6 +28,14 @@ type FakeAuthioTokenEndpointAuthMethod = "client_secret_basic" | "client_secret_
28
28
  * The algorithm Authio signs every token of a client with: the realm's RSA key, or the client secret (HS*).
29
29
  */
30
30
  type FakeAuthioSigningAlgorithm = "RS256" | "HS256" | "HS384" | "HS512";
31
+ /**
32
+ * The form a client's access tokens are handed out in, as Authio's access token format setting: `Jwt`, the
33
+ * self-contained JWT; `Reference`, an opaque handle of 43 characters that only introspection resolves; `Lightweight`, a
34
+ * JWT that carries only the identity and session claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`, `typ`,
35
+ * `client_id`, `azp`, `scope`, `sid`, `auth_time`, `acr`, `amr`, `idp`, `act`, `delegation_id`, `session_client`,
36
+ * `org_id`, `org_alias`). Introspection answers with every claim of the full token, whatever the form.
37
+ */
38
+ type FakeAuthioAccessTokenFormat = "Jwt" | "Reference" | "Lightweight";
31
39
  /**
32
40
  * A client registered in the fake, configured as a client is in Authio's console. What the client requires is
33
41
  * enforced as Authio enforces it: a request that does not match is refused with an error naming the setting, and
@@ -62,6 +70,8 @@ interface FakeAuthioClient {
62
70
  signingAlgorithm?: FakeAuthioSigningAlgorithm;
63
71
  /** The algorithm the client's access tokens are signed with, Authio's second setting. Defaults to `RS256`. */
64
72
  accessTokenSigningAlgorithm?: FakeAuthioSigningAlgorithm;
73
+ /** The form the client's access tokens are handed out in. Defaults to `Jwt`. */
74
+ accessTokenFormat?: FakeAuthioAccessTokenFormat;
65
75
  /** Issues a new ID token with every refresh. Defaults to `false`. */
66
76
  issueIdTokenOnRefresh?: boolean;
67
77
  /** Scopes granted when a request names none. */
@@ -198,6 +208,8 @@ interface FakeAuthioTokenOptions {
198
208
  expiresIn?: number;
199
209
  /** The `typ` claim. Defaults to `Bearer`. */
200
210
  type?: string;
211
+ /** The header `typ`. Defaults to `at+jwt` (RFC 9068 §2.1); `null` leaves it out. */
212
+ headerType?: string | null;
201
213
  /** Claims added to the payload. */
202
214
  claims?: Record<string, unknown>;
203
215
  /** Signs with another issuer. */
@@ -210,6 +222,12 @@ interface FakeAuthioTokenOptions {
210
222
  algorithm?: "RSA-OAEP" | "RSA-OAEP-256";
211
223
  encryptionMethod?: string;
212
224
  };
225
+ /**
226
+ * The form of the token: `Reference` mints an opaque handle (never encrypted) and `Lightweight` a JWT with only the
227
+ * identity and session claims; introspection answers with every claim either way. Defaults to the client's
228
+ * `accessTokenFormat`, else `Jwt`.
229
+ */
230
+ format?: FakeAuthioAccessTokenFormat;
213
231
  }
214
232
  type Endpoint = "token" | "introspect" | "revoke" | "userinfo" | "device" | "discovery" | "authorize" | "logout";
215
233
  /**
@@ -218,7 +236,8 @@ type Endpoint = "token" | "introspect" | "revoke" | "userinfo" | "device" | "dis
218
236
  * `iss`), token (client credentials, authorization code with ID tokens, refresh, token exchange, password, device code,
219
237
  * personal access token) with the client's exact authentication method, introspection, revocation, UserInfo, device
220
238
  * 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.
239
+ * client secret, access tokens as a JWT, an opaque reference or a lightweight JWT (`accessTokenFormat`), and JWE
240
+ * (RSA-OAEP, RSA-OAEP-256) for clients that registered an encryption key.
222
241
  */
223
242
  declare class FakeAuthio {
224
243
  /** Every request received, in order. */
@@ -416,4 +435,4 @@ declare function createWebhookDelivery(type: string, data: unknown, secret: stri
416
435
  sendEventIdHeader?: boolean;
417
436
  }): Promise<FakeWebhookDelivery>;
418
437
 
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 };
438
+ export { FakeAuthio, type FakeAuthioAccessTokenFormat, 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.js CHANGED
@@ -1,9 +1,32 @@
1
1
  import { createServer } from 'http';
2
- import { randomUUID, createHash } from 'crypto';
2
+ import { randomUUID, randomBytes, createHash } from 'crypto';
3
3
  import { SignJWT, decodeJwt, generateKeyPair, exportJWK, jwtVerify, importJWK, decodeProtectedHeader, CompactEncrypt } from 'jose';
4
4
  import { AuthioWebhookHeaders, computeWebhookSignatureHeader } from '@hyper.authio/client';
5
5
 
6
6
  // src/fake-authio.ts
7
+ var lightweightAccessTokenClaims = /* @__PURE__ */ new Set([
8
+ "iss",
9
+ "sub",
10
+ "aud",
11
+ "exp",
12
+ "nbf",
13
+ "iat",
14
+ "jti",
15
+ "typ",
16
+ "client_id",
17
+ "azp",
18
+ "scope",
19
+ "sid",
20
+ "auth_time",
21
+ "acr",
22
+ "amr",
23
+ "idp",
24
+ "act",
25
+ "delegation_id",
26
+ "session_client",
27
+ "org_id",
28
+ "org_alias"
29
+ ]);
7
30
  var FakeAuthio = class _FakeAuthio {
8
31
  /** Every request received, in order. */
9
32
  requests = [];
@@ -130,6 +153,7 @@ var FakeAuthio = class _FakeAuthio {
130
153
  async issueAccessToken(options = {}) {
131
154
  const now = this.nowSeconds();
132
155
  const clientId = options.clientId ?? "fake-client";
156
+ const headerType = options.headerType === null ? void 0 : options.headerType ?? "at+jwt";
133
157
  const jti = randomUUID();
134
158
  const payload = {
135
159
  typ: options.type ?? "Bearer",
@@ -140,9 +164,17 @@ var FakeAuthio = class _FakeAuthio {
140
164
  ...options.claims
141
165
  };
142
166
  const builder = new SignJWT(payload).setIssuer(options.issuer ?? this.issuer).setAudience(options.audience ?? clientId).setIssuedAt(now).setNotBefore(now).setExpirationTime(now + (options.expiresIn ?? this.lifetime)).setJti(jti);
143
- const signed = options.untrusted ? await builder.setProtectedHeader({ alg: "RS256", kid: this.keyId, typ: "JWT" }).sign(this.untrustedKey) : await this.sign(builder, this.clients.get(clientId), "JWT", this.clients.get(clientId)?.accessTokenSigningAlgorithm ?? "RS256");
144
- const token = options.encryptTo ? await encryptTo(signed, options.encryptTo) : signed;
145
- this.issued.set(token, { claims: decodeJwt(signed), clientId, revoked: false });
167
+ const sign = (jwt) => options.untrusted ? jwt.setProtectedHeader({ alg: "RS256", kid: this.keyId, typ: headerType }).sign(this.untrustedKey) : this.sign(jwt, this.clients.get(clientId), headerType, this.clients.get(clientId)?.accessTokenSigningAlgorithm ?? "RS256");
168
+ const format = options.format ?? this.clients.get(clientId)?.accessTokenFormat ?? "Jwt";
169
+ const full = await sign(builder);
170
+ const claims = decodeJwt(full);
171
+ let token;
172
+ if (format === "Reference") token = randomBytes(32).toString("base64url");
173
+ else {
174
+ const signed = format === "Lightweight" ? await sign(new SignJWT(Object.fromEntries(Object.entries(claims).filter(([name]) => lightweightAccessTokenClaims.has(name))))) : full;
175
+ token = options.encryptTo ? await encryptTo(signed, options.encryptTo) : signed;
176
+ }
177
+ this.issued.set(token, { claims, clientId, revoked: false });
146
178
  return token;
147
179
  }
148
180
  /**