@hyper.authio/server 0.8.1 → 0.9.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
@@ -75,7 +75,7 @@ declare class AuthioUser {
75
75
  readonly actorChain: readonly string[];
76
76
  /** `auth_time`, in seconds since the epoch. */
77
77
  readonly authTime: number | undefined;
78
- /** The client the token was issued to (`azp`, else `client_id`). */
78
+ /** The client the token was issued to (`client_id`, RFC 9068 §2.2, else `azp`). */
79
79
  readonly clientId: string | undefined;
80
80
  /** `sid`. */
81
81
  readonly sessionId: string | undefined;
@@ -203,9 +203,11 @@ interface AuthioRule {
203
203
  check(user: AuthioUser): AuthioRuleFailure | undefined;
204
204
  }
205
205
  /**
206
- * Checks every rule. On refusal the message joins every rule's, and the error is the one asking for a stronger
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.
206
+ * Checks every rule. On refusal the message joins every rule's. When a rule other than a step-up fails (a scope, role,
207
+ * permission, group, claim, organization, client or caller kind), no stronger sign-in can satisfy the request: the
208
+ * refusal is a `403`, with the first such rule's error (`insufficient_scope`) or none, and never asks for a step-up.
209
+ * Only when every failing rule asks for a stronger or more recent sign-in is the error `insufficient_user_authentication`,
210
+ * with the strongest `acr_values` and the shortest `max_age` they ask for.
209
211
  */
210
212
  declare function authorize(user: AuthioUser, ...rules: AuthioRule[]): AuthioRuleFailure | undefined;
211
213
  /**
@@ -264,7 +266,7 @@ declare function requireRecentAuthentication(maxAge: number, options?: {
264
266
  */
265
267
  declare function requireCallerKind(kinds: AuthioCallerKind | readonly AuthioCallerKind[]): AuthioRule;
266
268
  /**
267
- * Requires the token to have been issued to one of the clients (`azp`, else `client_id`).
269
+ * Requires the token to have been issued to one of the clients (`client_id`, else `azp`).
268
270
  */
269
271
  declare function requireClient(clientIds: string | readonly string[]): AuthioRule;
270
272
  /**
@@ -381,7 +383,10 @@ interface AuthioVerifierOptions extends AuthioConnectionOptions, AuthioUserClaim
381
383
  * realm's keys are accepted, and never HS*.
382
384
  */
383
385
  accessTokenSignedResponseAlg?: string | readonly string[];
384
- /** Refuses a token whose `typ` claim is present and not `Bearer` (an ID token, for one). Defaults to `true`. */
386
+ /**
387
+ * Refuses a token whose `typ` claim is present and not `Bearer` (an ID token, for one). Defaults to `true`. A JWT
388
+ * whose header `typ` is not `at+jwt` (RFC 9068 §4) is refused whatever this says.
389
+ */
385
390
  rejectNonAccessTokens?: boolean;
386
391
  /**
387
392
  * The private half of the encryption key Authio encrypts this API's access tokens to (the resource server's or its
package/dist/index.d.ts CHANGED
@@ -75,7 +75,7 @@ declare class AuthioUser {
75
75
  readonly actorChain: readonly string[];
76
76
  /** `auth_time`, in seconds since the epoch. */
77
77
  readonly authTime: number | undefined;
78
- /** The client the token was issued to (`azp`, else `client_id`). */
78
+ /** The client the token was issued to (`client_id`, RFC 9068 §2.2, else `azp`). */
79
79
  readonly clientId: string | undefined;
80
80
  /** `sid`. */
81
81
  readonly sessionId: string | undefined;
@@ -203,9 +203,11 @@ interface AuthioRule {
203
203
  check(user: AuthioUser): AuthioRuleFailure | undefined;
204
204
  }
205
205
  /**
206
- * Checks every rule. On refusal the message joins every rule's, and the error is the one asking for a stronger
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.
206
+ * Checks every rule. On refusal the message joins every rule's. When a rule other than a step-up fails (a scope, role,
207
+ * permission, group, claim, organization, client or caller kind), no stronger sign-in can satisfy the request: the
208
+ * refusal is a `403`, with the first such rule's error (`insufficient_scope`) or none, and never asks for a step-up.
209
+ * Only when every failing rule asks for a stronger or more recent sign-in is the error `insufficient_user_authentication`,
210
+ * with the strongest `acr_values` and the shortest `max_age` they ask for.
209
211
  */
210
212
  declare function authorize(user: AuthioUser, ...rules: AuthioRule[]): AuthioRuleFailure | undefined;
211
213
  /**
@@ -264,7 +266,7 @@ declare function requireRecentAuthentication(maxAge: number, options?: {
264
266
  */
265
267
  declare function requireCallerKind(kinds: AuthioCallerKind | readonly AuthioCallerKind[]): AuthioRule;
266
268
  /**
267
- * Requires the token to have been issued to one of the clients (`azp`, else `client_id`).
269
+ * Requires the token to have been issued to one of the clients (`client_id`, else `azp`).
268
270
  */
269
271
  declare function requireClient(clientIds: string | readonly string[]): AuthioRule;
270
272
  /**
@@ -381,7 +383,10 @@ interface AuthioVerifierOptions extends AuthioConnectionOptions, AuthioUserClaim
381
383
  * realm's keys are accepted, and never HS*.
382
384
  */
383
385
  accessTokenSignedResponseAlg?: string | readonly string[];
384
- /** Refuses a token whose `typ` claim is present and not `Bearer` (an ID token, for one). Defaults to `true`. */
386
+ /**
387
+ * Refuses a token whose `typ` claim is present and not `Bearer` (an ID token, for one). Defaults to `true`. A JWT
388
+ * whose header `typ` is not `at+jwt` (RFC 9068 §4) is refused whatever this says.
389
+ */
385
390
  rejectNonAccessTokens?: boolean;
386
391
  /**
387
392
  * The private half of the encryption key Authio encrypts this API's access tokens to (the resource server's or its
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { systemClock, AuthioClaimTypes, parseResourceAccess, AuthioError, AuthioDefaults, resolveIssuer, AuthioConfigurationError, resolveConnection, createAuthioDiscoveryClient, seconds, AuthioHmacAlgorithms, createAuthioSigningKeys, encryptionKeyFromFile, createAuthioTokenDecrypter, AuthioAsymmetricAlgorithms, requireHmacSecretLength, createAuthioClient, toScopeString, parseAuthioBearerChallenge, AuthioOAuthErrors, AuthioWebhookHeaders, verifyWebhookSignature, tryParseWebhookEvent, toClientCredentialsToken, isAuthioTokenEncrypted, AuthioTokenValidationError, withClientSecret, verifyAuthioJwt, AuthioTokenTypes } from '@hyper.authio/client';
1
+ import { systemClock, AuthioClaimTypes, parseResourceAccess, AuthioError, AuthioDefaults, resolveIssuer, AuthioConfigurationError, resolveConnection, createAuthioDiscoveryClient, seconds, AuthioHmacAlgorithms, createAuthioSigningKeys, encryptionKeyFromFile, createAuthioTokenDecrypter, AuthioAsymmetricAlgorithms, requireHmacSecretLength, createAuthioClient, toScopeString, parseAuthioBearerChallenge, AuthioOAuthErrors, AuthioWebhookHeaders, verifyWebhookSignature, tryParseWebhookEvent, toClientCredentialsToken, isAuthioTokenEncrypted, AuthioTokenValidationError, withClientSecret, verifyAuthioJwt, requireAccessTokenType, AuthioTokenTypes } from '@hyper.authio/client';
2
2
  import { decodeProtectedHeader } from 'jose';
3
3
 
4
4
  // src/user.ts
@@ -41,7 +41,7 @@ var AuthioUser = class _AuthioUser {
41
41
  actorChain;
42
42
  /** `auth_time`, in seconds since the epoch. */
43
43
  authTime;
44
- /** The client the token was issued to (`azp`, else `client_id`). */
44
+ /** The client the token was issued to (`client_id`, RFC 9068 §2.2, else `azp`). */
45
45
  clientId;
46
46
  /** `sid`. */
47
47
  sessionId;
@@ -102,7 +102,7 @@ var AuthioUser = class _AuthioUser {
102
102
  const text = (name) => typeof claims[name] === "string" && claims[name] ? claims[name] : void 0;
103
103
  this.claims = Object.freeze({ ...claims });
104
104
  this.subject = text(AuthioClaimTypes.Subject) ?? "";
105
- this.clientId = text(AuthioClaimTypes.AuthorizedParty) ?? text(AuthioClaimTypes.ClientId);
105
+ this.clientId = text(AuthioClaimTypes.ClientId) ?? text(AuthioClaimTypes.AuthorizedParty);
106
106
  this.sessionId = text(AuthioClaimTypes.SessionId);
107
107
  this.tokenId = text(AuthioClaimTypes.JwtId);
108
108
  this.audiences = Object.freeze(distinct(valuesOf(claims[AuthioClaimTypes.Audience])));
@@ -275,8 +275,9 @@ function distinctIgnoringCase(values) {
275
275
  function authorize(user, ...rules) {
276
276
  const failures = rules.map((rule) => rule.check(user)).filter((failure) => failure !== void 0);
277
277
  if (failures.length === 0) return void 0;
278
- const stepUps = failures.filter((failure) => failure.error === "insufficient_user_authentication");
279
- const bearer = stepUps[0] ?? failures.find((failure) => failure.error);
278
+ const others = failures.filter((failure) => failure.error !== "insufficient_user_authentication");
279
+ const stepUps = others.length === 0 ? failures : [];
280
+ const bearer = stepUps[0] ?? others.find((failure) => failure.error);
280
281
  const acrValues = strongestAcr(stepUps.map((failure) => failure.acrValues));
281
282
  const maxAges = stepUps.map((failure) => failure.maxAge).filter((value) => value !== void 0);
282
283
  return {
@@ -636,13 +637,15 @@ function createAuthioVerifier(options) {
636
637
  }
637
638
  let payload;
638
639
  try {
639
- ({ payload } = await verifyAuthioJwt(signed, keySet, {
640
+ let header;
641
+ ({ payload, header } = await verifyAuthioJwt(signed, keySet, {
640
642
  issuer: connection.issuer,
641
643
  audiences,
642
644
  algorithms,
643
645
  clockSkew,
644
646
  now: connection.clock.now()
645
647
  }));
648
+ requireAccessTokenType(header);
646
649
  } catch (error) {
647
650
  if (error instanceof AuthioTokenValidationError && error.reason === "invalid_algorithm" && symmetric.length === 0 && isSymmetricToken(signed))
648
651
  throw new AuthioBearerError(AuthioBearerErrorDescriptions.Symmetric, { cause: error, reason: error.reason });