@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/README.md CHANGED
@@ -41,7 +41,7 @@ try {
41
41
  | `introspectionCacheDuration` | 30 s | Never past the token's expiry; only active answers are kept. |
42
42
  | `clockSkew` | 30 s | |
43
43
  | `accessTokenSignedResponseAlg` | RS*, PS*, ES* (never HS*) | The algorithm (or several) access tokens are signed with, as the client is registered in Authio. HS256/384/512 are verified with the `clientSecret` of `client: { clientId, clientSecret }` (at least 32, 48 or 64 bytes). |
44
- | `rejectNonAccessTokens` | `true` | Refuses a token whose `typ` is not `Bearer` (an ID token, for one). |
44
+ | `rejectNonAccessTokens` | `true` | Refuses a token whose `typ` claim is not `Bearer` (an ID token, for one). The header `typ` must be `at+jwt` (RFC 9068 §4) regardless. |
45
45
  | `roleClaims` | `["role"]` | Claims holding flat role names (a Role claim mapper). |
46
46
  | `groupClaims` | `["groups"]` | Claims holding group names (a Group claim mapper). |
47
47
 
@@ -134,6 +134,9 @@ if (refusal) {
134
134
  | Sign-in too weak or too old (step-up) | `401` | `Bearer error="insufficient_user_authentication", error_description="…", acr_values="…", max_age=…` |
135
135
  | Missing role, permission, group, claim, organization, client or caller kind | `403` | none |
136
136
 
137
+ When several rules fail at once, a step-up is asked for only if every failing rule is a step-up: a missing scope, role
138
+ or permission, which no stronger sign-in fixes, answers `403` (with `insufficient_scope` for a scope) and no step-up.
139
+
137
140
  `resource_metadata` (RFC 9728 §5.1) is added to every `401` when the verifier publishes the protected resource
138
141
  metadata (`resourceMetadata`): the verifier's own refusals carry it, and `new AuthioForbiddenError(refusal, {
139
142
  resourceMetadata: verifier.resourceMetadataUrl })` (or `withResourceMetadata`) adds it to a step-up; the guards of
package/dist/index.cjs CHANGED
@@ -43,7 +43,7 @@ var AuthioUser = class _AuthioUser {
43
43
  actorChain;
44
44
  /** `auth_time`, in seconds since the epoch. */
45
45
  authTime;
46
- /** The client the token was issued to (`azp`, else `client_id`). */
46
+ /** The client the token was issued to (`client_id`, RFC 9068 §2.2, else `azp`). */
47
47
  clientId;
48
48
  /** `sid`. */
49
49
  sessionId;
@@ -104,7 +104,7 @@ var AuthioUser = class _AuthioUser {
104
104
  const text = (name) => typeof claims[name] === "string" && claims[name] ? claims[name] : void 0;
105
105
  this.claims = Object.freeze({ ...claims });
106
106
  this.subject = text(client.AuthioClaimTypes.Subject) ?? "";
107
- this.clientId = text(client.AuthioClaimTypes.AuthorizedParty) ?? text(client.AuthioClaimTypes.ClientId);
107
+ this.clientId = text(client.AuthioClaimTypes.ClientId) ?? text(client.AuthioClaimTypes.AuthorizedParty);
108
108
  this.sessionId = text(client.AuthioClaimTypes.SessionId);
109
109
  this.tokenId = text(client.AuthioClaimTypes.JwtId);
110
110
  this.audiences = Object.freeze(distinct(valuesOf(claims[client.AuthioClaimTypes.Audience])));
@@ -277,8 +277,9 @@ function distinctIgnoringCase(values) {
277
277
  function authorize(user, ...rules) {
278
278
  const failures = rules.map((rule) => rule.check(user)).filter((failure) => failure !== void 0);
279
279
  if (failures.length === 0) return void 0;
280
- const stepUps = failures.filter((failure) => failure.error === "insufficient_user_authentication");
281
- const bearer = stepUps[0] ?? failures.find((failure) => failure.error);
280
+ const others = failures.filter((failure) => failure.error !== "insufficient_user_authentication");
281
+ const stepUps = others.length === 0 ? failures : [];
282
+ const bearer = stepUps[0] ?? others.find((failure) => failure.error);
282
283
  const acrValues = strongestAcr(stepUps.map((failure) => failure.acrValues));
283
284
  const maxAges = stepUps.map((failure) => failure.maxAge).filter((value) => value !== void 0);
284
285
  return {
@@ -638,13 +639,15 @@ function createAuthioVerifier(options) {
638
639
  }
639
640
  let payload;
640
641
  try {
641
- ({ payload } = await client.verifyAuthioJwt(signed, keySet, {
642
+ let header;
643
+ ({ payload, header } = await client.verifyAuthioJwt(signed, keySet, {
642
644
  issuer: connection.issuer,
643
645
  audiences,
644
646
  algorithms,
645
647
  clockSkew,
646
648
  now: connection.clock.now()
647
649
  }));
650
+ client.requireAccessTokenType(header);
648
651
  } catch (error) {
649
652
  if (error instanceof client.AuthioTokenValidationError && error.reason === "invalid_algorithm" && symmetric.length === 0 && isSymmetricToken(signed))
650
653
  throw new AuthioBearerError(AuthioBearerErrorDescriptions.Symmetric, { cause: error, reason: error.reason });