@hyper.authio/client 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/README.md CHANGED
@@ -102,6 +102,11 @@ callback that is not for the transaction throws `AuthioCallbackError` (`reason`:
102
102
  `missing_issuer`, `missing_code`); an OAuth error Authio sent back (`login_required` after `prompt: "none"`, for one)
103
103
  throws `AuthioProtocolError`.
104
104
 
105
+ `acrValues` asks for a sign-in level; it does not guarantee one. Authio may sign the person in with less (no second
106
+ factor enrolled and none required by the client), as OpenID Connect allows (`acr_values` is a voluntary claim), so
107
+ read `idToken.acr` after the callback and refuse a sign-in below the level the app needs, as
108
+ [`examples/web-login`](../../examples/web-login/server.mjs) does.
109
+
105
110
  ### ID tokens and sign-out
106
111
 
107
112
  ```ts
@@ -238,7 +243,7 @@ const user = asUserEvent(event); // { id, username, emailConfirmed, … } for us
238
243
 
239
244
  Every event type has its accessor (`asLoginEvent`, `asUserLockedOutEvent`, `asTokenRevokedEvent`,
240
245
  `asKeysRotatedEvent`, `asTokenReuseDetectedEvent`, `asAdminResourceEvent`, `asUserSuspensionEvent`,
241
- `asAttackDetectedEvent`, `asCredentialCompromisedEvent`, `asPingEvent`), which exposes every member of `data` in
246
+ `asAttackDetectedEvent`, `asCredentialCompromisedEvent`, `asAuthenticatorAppEvent`, `asPingEvent`), which exposes every member of `data` in
242
247
  camelCase with its value unchanged; `event.data` stays as delivered (snake_case). An unknown type still parses
243
248
  (`isKnownWebhookEventType` is `false`, `event.data` is the raw JSON).
244
249
 
package/dist/index.cjs CHANGED
@@ -129,6 +129,10 @@ var AuthioWebhookEventTypes = {
129
129
  UserSuspensionEnded: "user_suspension_ended",
130
130
  AttackDetected: "attack_detected",
131
131
  CredentialCompromised: "credential_compromised",
132
+ UserAuthenticatorAppEnrolled: "user_authenticator_app_enrolled",
133
+ UserAuthenticatorAppRemoved: "user_authenticator_app_removed",
134
+ UserRecoveryCodesRegenerated: "user_recovery_codes_regenerated",
135
+ UserRecoveryCodeUsed: "user_recovery_code_used",
132
136
  Ping: "ping"
133
137
  };
134
138
  var AuthioWebhookHeaders = {
@@ -164,6 +168,8 @@ var AuthioDefaults = {
164
168
  CredentialClientIdPrefix: "cred_",
165
169
  ApiKeyHeader: "x-api-key",
166
170
  RequestIdHeader: "X-Request-Id",
171
+ /** The header `typ` of an access token (RFC 9068 §2.1). */
172
+ AccessTokenType: "at+jwt",
167
173
  /** The header `typ` of a back-channel logout token. */
168
174
  LogoutTokenType: "logout+jwt",
169
175
  /** The event a back-channel logout token carries. */
@@ -962,6 +968,14 @@ async function validateAuthioIdToken(token, context, expectations = {}) {
962
968
  }
963
969
  return payload;
964
970
  }
971
+ function requireAccessTokenType(header) {
972
+ const type = typeof header.typ === "string" ? header.typ.toLowerCase() : void 0;
973
+ if (type !== AuthioDefaults.AccessTokenType && type !== `application/${AuthioDefaults.AccessTokenType}`)
974
+ throw new AuthioTokenValidationError(
975
+ "not_access_token",
976
+ `Only access tokens are accepted; this token's header typ is '${header.typ ?? ""}', not '${AuthioDefaults.AccessTokenType}'.`
977
+ );
978
+ }
965
979
  async function validateAuthioLogoutToken(token, context) {
966
980
  const signed = isAuthioTokenEncrypted(token) ? await context.decrypt(token) : token;
967
981
  const invalid = (message) => new AuthioTokenValidationError("invalid_logout_token", message);
@@ -1293,12 +1307,13 @@ function createAuthioClient(options) {
1293
1307
  const signed = await decrypt(accessToken);
1294
1308
  if (signed.split(".").length !== 3) return void 0;
1295
1309
  const document = await discovery.get(callOptions.signal);
1296
- const { payload } = await verifyAuthioJwt(signed, withClientSecret(await signingKeys(callOptions.signal), clientSecret), {
1310
+ const { payload, header } = await verifyAuthioJwt(signed, withClientSecret(await signingKeys(callOptions.signal), clientSecret), {
1297
1311
  issuer: connection.issuer,
1298
1312
  algorithms: acceptedSigningAlgorithms(accessTokenAlgorithm, document.id_token_signing_alg_values_supported),
1299
1313
  clockSkew,
1300
1314
  now: connection.clock.now()
1301
1315
  });
1316
+ requireAccessTokenType(header);
1302
1317
  return payload;
1303
1318
  },
1304
1319
  async createEndSessionUrl(request = {}) {
@@ -1711,7 +1726,7 @@ function createAuthioAccountCredential(options) {
1711
1726
  if (cached && clock.now() < cached.refreshAt) return cached;
1712
1727
  pending ??= (async () => {
1713
1728
  const issuedAt = clock.now();
1714
- const answer = kind === "client_secret" ? await client.tokens.clientCredentials({ scopes: scope, signal }) : await client.tokens.personalAccessToken({ token: value, scopes: scope, signal });
1729
+ const answer = kind === "client_secret" ? await client.tokens.clientCredentials({ scopes: scope, signal }) : await client.tokens.personalAccessToken({ token: value, clientId: AuthioDefaults.PlatformClientId, scopes: scope, signal });
1715
1730
  cached = toClientCredentialsToken(answer, issuedAt, refreshSkewMs);
1716
1731
  return cached;
1717
1732
  })().finally(() => {
@@ -2024,6 +2039,13 @@ var asAdminResourceEvent = (event) => dataWhen(
2024
2039
  var asUserSuspensionEvent = (event) => dataWhen(event, AuthioWebhookEventTypes.UserSuspended, AuthioWebhookEventTypes.UserSuspensionEnded);
2025
2040
  var asAttackDetectedEvent = (event) => dataWhen(event, AuthioWebhookEventTypes.AttackDetected);
2026
2041
  var asCredentialCompromisedEvent = (event) => dataWhen(event, AuthioWebhookEventTypes.CredentialCompromised);
2042
+ var asAuthenticatorAppEvent = (event) => dataWhen(
2043
+ event,
2044
+ AuthioWebhookEventTypes.UserAuthenticatorAppEnrolled,
2045
+ AuthioWebhookEventTypes.UserAuthenticatorAppRemoved,
2046
+ AuthioWebhookEventTypes.UserRecoveryCodesRegenerated,
2047
+ AuthioWebhookEventTypes.UserRecoveryCodeUsed
2048
+ );
2027
2049
  var asPingEvent = (event) => dataWhen(event, AuthioWebhookEventTypes.Ping);
2028
2050
  var knownEventTypes = new Set(Object.values(AuthioWebhookEventTypes));
2029
2051
  function isKnownWebhookEventType(type) {
@@ -2108,6 +2130,7 @@ exports.AuthioWebhookEventTypes = AuthioWebhookEventTypes;
2108
2130
  exports.AuthioWebhookHeaders = AuthioWebhookHeaders;
2109
2131
  exports.asAdminResourceEvent = asAdminResourceEvent;
2110
2132
  exports.asAttackDetectedEvent = asAttackDetectedEvent;
2133
+ exports.asAuthenticatorAppEvent = asAuthenticatorAppEvent;
2111
2134
  exports.asCredentialCompromisedEvent = asCredentialCompromisedEvent;
2112
2135
  exports.asKeysRotatedEvent = asKeysRotatedEvent;
2113
2136
  exports.asLoginEvent = asLoginEvent;
@@ -2140,6 +2163,7 @@ exports.randomValue = randomValue;
2140
2163
  exports.readAuthioAdminResponse = readAuthioAdminResponse;
2141
2164
  exports.readAuthioFrontChannelLogout = readAuthioFrontChannelLogout;
2142
2165
  exports.readWebhookEventData = readWebhookEventData;
2166
+ exports.requireAccessTokenType = requireAccessTokenType;
2143
2167
  exports.requireHmacSecretLength = requireHmacSecretLength;
2144
2168
  exports.resolveConnection = resolveConnection;
2145
2169
  exports.resolveIssuer = resolveIssuer;