@hyper.authio/server 0.1.1 → 0.8.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
@@ -30,19 +30,56 @@ try {
30
30
 
31
31
  | Option | Default | |
32
32
  | --- | --- | --- |
33
- | `audiences` | | Required. |
34
- | `introspection` | `none` | `none`: local JWT validation. `activeCheck`: then confirmed active by introspection (cached per `jti`), so a revoked token, an ended session or a suspension is refused before the token expires. `introspectionOnly`: introspection alone, for opaque or encrypted tokens. |
35
- | `client` | | The API's confidential client, needed by introspection: an `AuthioClient` or `{ clientId, clientSecret }` (or a private key). It must be the resource server's introspection client. |
33
+ | `audiences` | | Required, unless `resource` is set. |
34
+ | `resource` | | The API's own resource identifier (RFC 8707); added to `audiences` and used for RFC 9728. |
35
+ | `resourceMetadata` | off | `true` (or the URL) adds `resource_metadata="{origin}/.well-known/oauth-protected-resource{path}"` to `401` challenges (RFC 9728). |
36
+ | `requiredScopes` | | Scopes every request must carry (all); `authioBearer` answers `403 insufficient_scope`. |
37
+ | `clientEncryptionKey`, `clientEncryptionKeyPath` | | Decrypts access tokens Authio encrypts to this key and validates them locally. Without it nothing is decrypted. RSA1_5 is refused (`legacy_algorithm`), even with `allowLegacyEncryptionAlgorithms`. |
38
+ | `encryptionAlgorithms`, `encryptionMethods` | `RSA-OAEP`, `RSA-OAEP-256`; the CBC-HS methods | The JWE `alg` and `enc` accepted, as registered in Authio (AES-GCM may be listed; RSA1_5 cannot: Node cannot decrypt it). |
39
+ | `introspection` | `none` | `none`: local JWT validation. `activeCheck`: then confirmed active by introspection (cached per `jti`), so a revoked token, an ended session or a suspension is refused before the token expires. `introspectionOnly`: introspection alone, for opaque tokens (or encrypted ones without the key). |
40
+ | `client` | | The API's confidential client, needed by introspection: an `AuthioClient` or `{ clientId, clientSecret }` (or a private key), with `tokenEndpointAuthMethod` as the client is registered. It must be the resource server's introspection client. |
36
41
  | `introspectionCacheDuration` | 30 s | Never past the token's expiry; only active answers are kept. |
37
42
  | `clockSkew` | 30 s | |
38
- | `validAlgorithms` | RS*, PS*, ES* | |
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). |
39
44
  | `rejectNonAccessTokens` | `true` | Refuses a token whose `typ` is not `Bearer` (an ID token, for one). |
40
45
  | `roleClaims` | `["role"]` | Claims holding flat role names (a Role claim mapper). |
41
46
  | `groupClaims` | `["groups"]` | Claims holding group names (a Group claim mapper). |
42
47
 
43
48
  A refusal is an `AuthioBearerError` whose `wwwAuthenticate` is `Bearer` when the request had no token, and
44
- `Bearer error="invalid_token", error_description="…"` otherwise. A token addressed elsewhere is described with the
45
- audience the API accepts; introspection failures fail closed.
49
+ `Bearer error="invalid_token", error_description="…"` otherwise (plus `resource_metadata` when turned on). Its
50
+ `reason` names why, with the outcome names of `conformance/tokens.json` (`expired`, `invalid_audience`,
51
+ `not_access_token`, `reference_token`, `decryption_failed`, …, and `inactive` / `unconfirmed` for introspection). A
52
+ token addressed elsewhere is described with the audience the API accepts; introspection failures fail closed.
53
+
54
+ ### Protected resource metadata (RFC 9728)
55
+
56
+ ```ts
57
+ import { createProtectedResourceMetadata, protectedResourceMetadataPath } from "@hyper.authio/server";
58
+
59
+ const verifier = createAuthioVerifier({ issuer, resource: "https://orders.example.com/api", resourceMetadata: true });
60
+ const path = protectedResourceMetadataPath(verifier.resource!); // /.well-known/oauth-protected-resource/api
61
+ const document = createProtectedResourceMetadata({ issuer, resource: verifier.resource!, scopesSupported: ["orders:read"] });
62
+ ```
63
+
64
+ ### Access tokens signed with the client secret (HS*)
65
+
66
+ Authio signs a client's access tokens with the algorithm of its access token setting. When that is HS256, HS384 or
67
+ HS512, the signature is made with the client secret: only who holds the secret validates the token locally, so that is
68
+ the client's own API.
69
+
70
+ ```ts
71
+ const verifier = createAuthioVerifier({
72
+ issuer,
73
+ audiences: "storefront",
74
+ accessTokenSignedResponseAlg: "HS256",
75
+ client: { clientId: "storefront", clientSecret: process.env.AUTHIO_CLIENT_SECRET! }, // 32+ bytes for HS256
76
+ });
77
+ ```
78
+
79
+ Third-party APIs never get the secret: they validate such tokens with introspection (`introspection:
80
+ "introspectionOnly"` and their own introspection client). Without `accessTokenSignedResponseAlg`, an HS* token is
81
+ refused (`invalid_algorithm`) with a description naming both ways. `authioBearer` and the MCP packages take the same
82
+ option.
46
83
 
47
84
  ## The caller
48
85
 
@@ -50,6 +87,13 @@ audience the API accepts; introspection failures fail closed.
50
87
 
51
88
  ```ts
52
89
  user.subject; user.clientId; user.scopes;
90
+ user.kind; // "user" | "client" | "consumer" | "account_credential"
91
+ user.credentialForm; // "client_secret" | "personal_access_token" | "api_key" | "consumer_api_key" (machines only)
92
+ user.consumerName; user.credentialId; user.credentialName; user.ownerUserId; user.ownerUsername; user.accountId;
93
+ user.credentialRealm; user.credentialClient; // what an account credential is limited to
94
+ user.impersonated; user.impersonator; // an administrator acting as the person (act.sub)
95
+ user.delegated; user.delegationId; user.actorChain; // clients acting for the person (act.client_id), outermost first
96
+ user.authTime; // auth_time, in seconds
53
97
  user.roles; // *_resource_access roles, then flat role claims
54
98
  user.permissions; // { orders: ["read", "write"] }
55
99
  user.resourceAccess; // per-source entries: user, realm, client, plan, consumer, secret, system
@@ -64,7 +108,8 @@ user.hasGroup("finance");
64
108
  user.hasClaim("department", "finance", "sales");
65
109
  ```
66
110
 
67
- The raw token is kept as `user.token`, not enumerable, so it stays out of logs and JSON.
111
+ The raw token is kept as `user.token`, not enumerable, so it stays out of logs and JSON. The caller fields follow
112
+ `conformance/callers.json`; `AuthioUser.fromIntrospection(answer)` reads an introspection answer the same way.
68
113
 
69
114
  ## Authorize
70
115
 
@@ -81,8 +126,14 @@ if (refusal) {
81
126
  ```
82
127
 
83
128
  Rules: `requireScope`, `requireRole`, `requirePermission`, `requireGroup`, `requireClaim`, `requireOrganization`,
84
- `requireAuthenticationLevel`, `requireAuthenticationMethod`, `requireClient`, combined with `allOf` and `anyOf`.
85
- `authorize` checks every rule and joins their messages.
129
+ `requireAuthenticationLevel`, `requireAuthenticationMethod`, `requireRecentAuthentication`, `requireCallerKind`,
130
+ `requireClient`, combined with `allOf` and `anyOf`. `authorize` checks every rule and joins their messages.
131
+
132
+ **Step-up (RFC 9470)**: `requireAuthenticationLevel(2, { maxAge: 300 })`, `requireAuthenticationMethod("otp")` and
133
+ `requireRecentAuthentication(300)` refuse with `insufficient_user_authentication`, `acrValues` (the level asked for;
134
+ `2` for `mfa`/`otp`/`mca`, `3` for `swk`/`hwk`) and `maxAge`, so the challenge reads
135
+ `Bearer error="insufficient_user_authentication", error_description="…", acr_values="2", max_age=300`. A client
136
+ answers it with `createStepUpAuthorizationUrl` from `@hyper.authio/client`.
86
137
 
87
138
  ## Webhooks
88
139