@passflow/node 0.0.0-bootstrap.0 → 0.0.0-stage
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 +2 -179
- package/package.json +4 -104
- package/LICENSE +0 -21
- package/dist/claims-BHJhnzch.d.cts +0 -51
- package/dist/claims-BHJhnzch.d.mts +0 -51
- package/dist/evaluate-BTuol-Oa.mjs +0 -348
- package/dist/evaluate-C20MecTf.cjs +0 -491
- package/dist/express.cjs +0 -52
- package/dist/express.d.cts +0 -29
- package/dist/express.d.mts +0 -29
- package/dist/express.mjs +0 -49
- package/dist/fastify.cjs +0 -67
- package/dist/fastify.d.cts +0 -40
- package/dist/fastify.d.mts +0 -40
- package/dist/fastify.mjs +0 -63
- package/dist/hono.cjs +0 -51
- package/dist/hono.d.cts +0 -24
- package/dist/hono.d.mts +0 -24
- package/dist/hono.mjs +0 -48
- package/dist/index.cjs +0 -558
- package/dist/index.d.cts +0 -141
- package/dist/index.d.mts +0 -141
- package/dist/index.mjs +0 -539
package/README.md
CHANGED
|
@@ -1,180 +1,3 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Temporary Holding Version
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Requires Node.js 22.12 or newer, and Express, Fastify and Hono are optional peers, so you install only the framework you use.
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @passflow/node
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
```js
|
|
12
|
-
import { AAL2, createVerifier } from '@passflow/node';
|
|
13
|
-
import { getAuth, passflowExpress } from '@passflow/node/express';
|
|
14
|
-
import express from 'express';
|
|
15
|
-
|
|
16
|
-
const verifier = createVerifier({ issuer: 'https://auth.example.com', audience: '7f3a9c21d4b8e6' });
|
|
17
|
-
const passflow = passflowExpress(verifier);
|
|
18
|
-
|
|
19
|
-
const app = express();
|
|
20
|
-
app.get('/invoices', passflow.requireACR(AAL2), (req, res) => res.json({ sub: getAuth(req).sub }));
|
|
21
|
-
app.post('/transfers', passflow.requireFresh(300), (_req, res) => res.json({ ok: true }));
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
`audience` is the ID of a Passflow application, or a list of them. The Passflow documentation has step-by-step tutorials for Express, Fastify and Hono (`docs/tutorials/node-backend.md`) and a runnable API in `examples/resource-server`.
|
|
25
|
-
|
|
26
|
-
## Use it with a framework
|
|
27
|
-
|
|
28
|
-
### Express
|
|
29
|
-
|
|
30
|
-
`passflowExpress(verifier)` returns `authenticate`, `requireACR(min)` and `requireFresh(seconds)`, and the last two work alone without `authenticate` in front. Claims live on `req.passflow`, which `getAuth(req)` reads, and that name cannot collide with the global `auth` type that `express-jwt` declares.
|
|
31
|
-
|
|
32
|
-
```js
|
|
33
|
-
app.get('/me', passflow.authenticate, (req, res) => res.json({ sub: getAuth(req).sub }));
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Fastify
|
|
37
|
-
|
|
38
|
-
Register the plugin once per instance, because it only decorates: routes opt in one at a time and public routes are untouched.
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
import { AAL2, createVerifier } from '@passflow/node';
|
|
42
|
-
import { getAuth, passflowFastify } from '@passflow/node/fastify';
|
|
43
|
-
import Fastify from 'fastify';
|
|
44
|
-
|
|
45
|
-
const verifier = createVerifier({ issuer: 'https://auth.example.com', audience: '7f3a9c21d4b8e6' });
|
|
46
|
-
|
|
47
|
-
const app = Fastify();
|
|
48
|
-
await app.register(passflowFastify, { verifier });
|
|
49
|
-
|
|
50
|
-
app.get('/invoices', { preHandler: app.passflow.requireACR(AAL2) }, async (request) => ({
|
|
51
|
-
subject: getAuth(request).sub,
|
|
52
|
-
}));
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
A second registration throws Fastify's own `FST_ERR_DEC_ALREADY_PRESENT`, so use `createFastifyHooks(verifier)` for every further verifier. It returns the same three hooks with no plugin and no decoration.
|
|
56
|
-
|
|
57
|
-
### Hono
|
|
58
|
-
|
|
59
|
-
`@passflow/node` and `@passflow/node/hono` import no Node built-ins, so they run on any runtime with WebCrypto and `fetch`.
|
|
60
|
-
|
|
61
|
-
```js
|
|
62
|
-
import { AAL2, createVerifier } from '@passflow/node';
|
|
63
|
-
import { getAuth, passflowHono } from '@passflow/node/hono';
|
|
64
|
-
import { Hono } from 'hono';
|
|
65
|
-
|
|
66
|
-
const passflow = passflowHono(createVerifier({ issuer: 'https://auth.example.com', audience: '7f3a9c21d4b8e6' }));
|
|
67
|
-
const app = new Hono();
|
|
68
|
-
|
|
69
|
-
app.get('/invoices', passflow.requireACR(AAL2), (c) => c.json({ subject: getAuth(c).sub }));
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Responses and CORS
|
|
73
|
-
|
|
74
|
-
| Situation | Status | `WWW-Authenticate` |
|
|
75
|
-
|---|---|---|
|
|
76
|
-
| No token, or not a `Bearer` token | 401 | `Bearer` |
|
|
77
|
-
| Invalid or expired token | 401 | `Bearer error="invalid_token"` |
|
|
78
|
-
| Token below the required level | 401 | `Bearer error="insufficient_user_authentication", acr_values="2"` |
|
|
79
|
-
| Authentication too old | 401 | `Bearer error="insufficient_user_authentication", max_age=300` |
|
|
80
|
-
| Key set unavailable | 503 | none |
|
|
81
|
-
|
|
82
|
-
Every rejection has `Cache-Control: no-store` and an empty body, and the middleware writes it directly, so your own error handler cannot drop the challenge. The browser SDK acts only on the step-up challenge, and the library never sends one for an authentication failure. Nothing from the token, the request or the key set URL appears in any error or response, and the package writes no logs.
|
|
83
|
-
|
|
84
|
-
A browser hides `WWW-Authenticate` from a cross-origin page unless the response names it in `Access-Control-Expose-Headers`. The SDK then sees a bare 401 and cannot start the step-up, and a `*` entry does not cover the header on a credentialed request.
|
|
85
|
-
|
|
86
|
-
Each adapter exports `exposeChallengeHeader`, which adds the header to your CORS options and keeps the entries you already have, under the option name that framework's middleware uses: `exposedHeaders` for `cors` and `@fastify/cors`, `exposeHeaders` for `hono/cors`.
|
|
87
|
-
|
|
88
|
-
```js
|
|
89
|
-
import { exposeChallengeHeader } from '@passflow/node/express';
|
|
90
|
-
import cors from 'cors';
|
|
91
|
-
|
|
92
|
-
app.use(cors(exposeChallengeHeader({ origin: 'https://app.example.com', credentials: true })));
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
`CHALLENGE_HEADER` and `CORS_EXPOSED_HEADERS` are exported if you build the list yourself.
|
|
96
|
-
|
|
97
|
-
## Claims, configuration and composition
|
|
98
|
-
|
|
99
|
-
### Reading the claims
|
|
100
|
-
|
|
101
|
-
`getAuth(req)` throws `PassflowUsageError` when no Passflow middleware ran, which your framework turns into a 500.
|
|
102
|
-
|
|
103
|
-
```ts
|
|
104
|
-
type PassflowClaims = {
|
|
105
|
-
sub: string; iss: string; aud: string[]; exp: number; iat?: number;
|
|
106
|
-
authTime?: number; // whole Unix seconds; absent when the claim is absent, not a number or beyond ±2^53
|
|
107
|
-
acr: string; // '0' when the claim is absent or not a string
|
|
108
|
-
amr: string[]; scopes: string[]; sid?: string; type: string;
|
|
109
|
-
tenants: Record<string, TenantMembership>;
|
|
110
|
-
verifiedAt: number; // the verifier's clock at verification, in Unix seconds
|
|
111
|
-
raw: Record<string, unknown>;
|
|
112
|
-
};
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
`rolesOf(claims, tenantId)` returns the roles of the tenant's root group. Compare levels with `acrSatisfies(acr, min)`, which is exact over the whole 64-bit range and never satisfied by a required level that parses to 0. `acrLevel(acr)` returns a `number` for display only. It is exact up to 2^53, and the library never decides with it.
|
|
116
|
-
|
|
117
|
-
### Configuration
|
|
118
|
-
|
|
119
|
-
```js
|
|
120
|
-
createVerifier({
|
|
121
|
-
issuer: 'https://auth.example.com', // required, exact match
|
|
122
|
-
audience: ['7f3a9c21d4b8e6', 'b81e4d07a39c52'], // required: a string or a list; the token must name at least one
|
|
123
|
-
jwksUrl: 'https://auth.example.com/.well-known/jwks.json', // this is the default; OIDC discovery is not fetched
|
|
124
|
-
allowInsecureJwksUrl: false, // https is required except on loopback hosts, unless this is set
|
|
125
|
-
leewaySeconds: 30, // exp and nbf only
|
|
126
|
-
tokenTypes: ['access'],
|
|
127
|
-
now: () => new Date(),
|
|
128
|
-
jwks: {
|
|
129
|
-
maxAge: 600000, // ms; a set older than this keeps answering while it reloads in the background
|
|
130
|
-
minRefreshInterval: 30000, // ms; at most one attempt per interval for a background reload, after a failure or for an unknown kid
|
|
131
|
-
maxStale: 3600000, // ms; how long after the last successful fetch a stale set is trusted
|
|
132
|
-
maxBytes: 1048576, // response body cap
|
|
133
|
-
timeout: 5000, // ms, an integer up to 2147483647
|
|
134
|
-
fetch, // replaces the global fetch, for proxies
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
An invalid configuration throws `TypeError` naming the option, at startup. `requireACR('gold')` throws too, because 'gold' has no level above 0. A freshness limit must truncate to a whole number of seconds from 1 to `Number.MAX_SAFE_INTEGER`. Leaving a `jwks` option out gives the default. An explicit `0` is a value: `maxAge: 0` starts a background reload on every verification, gated by `minRefreshInterval`, and `minRefreshInterval: 0` removes the gate, both legal and both discouraged. `maxBytes: 0`, `maxStale: 0` and `timeout: 0` throw.
|
|
140
|
-
|
|
141
|
-
Build one verifier per trust policy, at startup, because each owns a key cache.
|
|
142
|
-
|
|
143
|
-
### Stacking verifiers
|
|
144
|
-
|
|
145
|
-
An adapter's `authenticate` always verifies the presented token and replaces any claims on the request. `requireACR` and `requireFresh` act on existing claims only when their own verifier produced that exact object. Otherwise they verify the presented token themselves. A broad verifier upstream therefore cannot let a token outside a route's issuer, audiences or token types through on its level alone:
|
|
146
|
-
|
|
147
|
-
```js
|
|
148
|
-
app.use(broad.authenticate);
|
|
149
|
-
app.get('/restricted', narrow.requireACR('2'), handler);
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
A token outside `narrow`'s policy gets `invalid_token`, never a step-up. The identity rule is strict: two verifiers built from the same configuration re-verify each other's claims, and so does a spread or cloned claims object. A wrapper object that delegates to `verifier.verify` is a different identity for the same reason, and each case costs one more signature check and never a bypass. Keep one verifier per trust policy, and put the narrowest verifier's method directly on the route.
|
|
153
|
-
|
|
154
|
-
In Fastify, the broad hook goes on an encapsulated scope and the narrow one on the route:
|
|
155
|
-
|
|
156
|
-
```js
|
|
157
|
-
await app.register(async (scope) => {
|
|
158
|
-
scope.addHook('preHandler', scope.passflow.authenticate);
|
|
159
|
-
scope.get('/restricted', { preHandler: createFastifyHooks(narrowVerifier).requireACR('2') }, handler);
|
|
160
|
-
});
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## Key set behaviour
|
|
164
|
-
|
|
165
|
-
The key set is fetched and cached by this package, with the same rules and defaults as the Go library `github.com/madappgang/passflow-go/auth`, though the Go options are `time.Duration` values and these are milliseconds.
|
|
166
|
-
|
|
167
|
-
- A token's key is looked up by `kid` and the key type its algorithm requires. An unknown `kid` causes at most one refetch per `minRefreshInterval`, and a bad signature never causes one. The request with the unknown `kid` waits for that refetch, or joins one already running, and one verification never makes two fetches.
|
|
168
|
-
- A set older than `maxAge` answers immediately and starts a background reload, unless one is running or `minRefreshInterval` has not passed since the last attempt. A request waits for a reload only when there is no set, or the set is past `maxStale`.
|
|
169
|
-
- A reload counts only when all of it succeeds: status 200, a body within `maxBytes`, a JSON key set, and every usable key imported and checked. A usable key must be public, with no private member, RSA of at least 2048 bits with an odd exponent from 3 to 2^31 - 1, or P-256. Each key member must be canonical unpadded base64url. A key that carries private material fails the reload.
|
|
170
|
-
- Anything short of a full success is one failed attempt, and a working set stays in place.
|
|
171
|
-
- While reloads fail, a stale set is trusted until `maxStale` after the last successful fetch. Then every request gets a 503 until a reload succeeds.
|
|
172
|
-
- A 503 never carries a challenge. A key rotation during an outage therefore does not look like a bad token.
|
|
173
|
-
|
|
174
|
-
**Serverless and edge runtimes.** A runtime that freezes or cancels work after a response may never finish a background reload. The set is then refreshed by the waiting path once it is past `maxStale`, or by an unknown `kid`.
|
|
175
|
-
|
|
176
|
-
**Rotation window.** A token signed by a key published less than `minRefreshInterval` after the previous fetch attempt is rejected as `key_unknown` until the gate opens, at most 30 seconds per instance with the defaults. Passflow signs with one key today, so only a deployment that rotates keys needs to call `await verifier.refresh()` from its rotation hook. It ignores the gate and reloads from a fetch that starts after the call, waiting out any fetch already running, so its success proves the set is newer than the call. It can therefore take up to twice `timeout`.
|
|
177
|
-
|
|
178
|
-
## License
|
|
179
|
-
|
|
180
|
-
Released under the MIT license, which is separate from the Passflow server's license.
|
|
3
|
+
This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
|
package/package.json
CHANGED
|
@@ -1,106 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@passflow/node",
|
|
3
|
-
"version": "0.0.0-
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/MadAppGang/passflow.git",
|
|
9
|
-
"directory": "sdk/node"
|
|
10
|
-
},
|
|
11
|
-
"type": "module",
|
|
12
|
-
"sideEffects": false,
|
|
13
|
-
"engines": {
|
|
14
|
-
"node": ">=22.12"
|
|
15
|
-
},
|
|
16
|
-
"files": [
|
|
17
|
-
"dist",
|
|
18
|
-
"README.md",
|
|
19
|
-
"LICENSE"
|
|
20
|
-
],
|
|
21
|
-
"exports": {
|
|
22
|
-
".": {
|
|
23
|
-
"import": {
|
|
24
|
-
"types": "./dist/index.d.mts",
|
|
25
|
-
"default": "./dist/index.mjs"
|
|
26
|
-
},
|
|
27
|
-
"require": {
|
|
28
|
-
"types": "./dist/index.d.cts",
|
|
29
|
-
"default": "./dist/index.cjs"
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
"./express": {
|
|
33
|
-
"import": {
|
|
34
|
-
"types": "./dist/express.d.mts",
|
|
35
|
-
"default": "./dist/express.mjs"
|
|
36
|
-
},
|
|
37
|
-
"require": {
|
|
38
|
-
"types": "./dist/express.d.cts",
|
|
39
|
-
"default": "./dist/express.cjs"
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
"./fastify": {
|
|
43
|
-
"import": {
|
|
44
|
-
"types": "./dist/fastify.d.mts",
|
|
45
|
-
"default": "./dist/fastify.mjs"
|
|
46
|
-
},
|
|
47
|
-
"require": {
|
|
48
|
-
"types": "./dist/fastify.d.cts",
|
|
49
|
-
"default": "./dist/fastify.cjs"
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
"./hono": {
|
|
53
|
-
"import": {
|
|
54
|
-
"types": "./dist/hono.d.mts",
|
|
55
|
-
"default": "./dist/hono.mjs"
|
|
56
|
-
},
|
|
57
|
-
"require": {
|
|
58
|
-
"types": "./dist/hono.d.cts",
|
|
59
|
-
"default": "./dist/hono.cjs"
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
"./package.json": "./package.json"
|
|
63
|
-
},
|
|
64
|
-
"publishConfig": {
|
|
65
|
-
"access": "public"
|
|
66
|
-
},
|
|
67
|
-
"dependencies": {
|
|
68
|
-
"jose": "^6.2.14"
|
|
69
|
-
},
|
|
70
|
-
"peerDependencies": {
|
|
71
|
-
"express": "^4.18.0 || ^5.0.0",
|
|
72
|
-
"fastify": "^5.0.0",
|
|
73
|
-
"hono": "^4.0.0"
|
|
74
|
-
},
|
|
75
|
-
"peerDependenciesMeta": {
|
|
76
|
-
"express": {
|
|
77
|
-
"optional": true
|
|
78
|
-
},
|
|
79
|
-
"fastify": {
|
|
80
|
-
"optional": true
|
|
81
|
-
},
|
|
82
|
-
"hono": {
|
|
83
|
-
"optional": true
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
"devDependencies": {
|
|
87
|
-
"@arethetypeswrong/core": "0.18.5",
|
|
88
|
-
"@biomejs/biome": "1.9.4",
|
|
89
|
-
"@types/express": "5.0.6",
|
|
90
|
-
"@types/node": "^22.18.0",
|
|
91
|
-
"express": "5.3.0",
|
|
92
|
-
"fastify": "5.12.7",
|
|
93
|
-
"hono": "4.13.14",
|
|
94
|
-
"publint": "0.3.25",
|
|
95
|
-
"tsdown": "0.23.4",
|
|
96
|
-
"typescript": "^5.9.3",
|
|
97
|
-
"vitest": "^3.2.4"
|
|
98
|
-
},
|
|
99
|
-
"scripts": {
|
|
100
|
-
"build": "tsdown",
|
|
101
|
-
"lint": "biome ci .",
|
|
102
|
-
"format": "biome format . --write",
|
|
103
|
-
"typecheck": "tsc --noEmit",
|
|
104
|
-
"test": "vitest run"
|
|
105
|
-
}
|
|
106
|
-
}
|
|
3
|
+
"version": "0.0.0-stage",
|
|
4
|
+
"stub": true,
|
|
5
|
+
"description": "Temporary package placeholder for staged publishing"
|
|
6
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 MadAppGang Pty Ltd
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/** The `passflow_tm` wire shape, camelCased. `tenantRoles` is decoded for raw access; `rolesOf` does not read it. */
|
|
2
|
-
type TenantMembership = {
|
|
3
|
-
tenantId: string;
|
|
4
|
-
tenantName?: string;
|
|
5
|
-
tenantRoles?: string[];
|
|
6
|
-
rootGroupId?: string;
|
|
7
|
-
groups?: Record<string, string[]>;
|
|
8
|
-
groupNames?: Record<string, string>;
|
|
9
|
-
};
|
|
10
|
-
type PassflowClaims = {
|
|
11
|
-
sub: string;
|
|
12
|
-
iss: string;
|
|
13
|
-
aud: string[];
|
|
14
|
-
exp: number;
|
|
15
|
-
iat?: number;
|
|
16
|
-
/** Whole Unix seconds. Absent when the claim is absent, not a number or beyond ±2^53. */
|
|
17
|
-
authTime?: number;
|
|
18
|
-
/** `'0'` when the claim is absent or not a string. */
|
|
19
|
-
acr: string;
|
|
20
|
-
amr: string[];
|
|
21
|
-
scopes: string[];
|
|
22
|
-
sid?: string;
|
|
23
|
-
type: string;
|
|
24
|
-
tenants: Record<string, TenantMembership>;
|
|
25
|
-
/** The verifier's clock at verification, in Unix seconds. `requireFresh` measures from it. */
|
|
26
|
-
verifiedAt: number;
|
|
27
|
-
raw: Record<string, unknown>;
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* The structural part of a verifier. Declared here, with `verifiedBy`, so that provenance needs nothing
|
|
31
|
-
* from the verifier module.
|
|
32
|
-
*/
|
|
33
|
-
interface Verifier {
|
|
34
|
-
verify(token: string): Promise<PassflowClaims>;
|
|
35
|
-
refresh(): Promise<void>;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* True only for the exact object `verifier.verify` returned. A spread or clone loses provenance, which
|
|
39
|
-
* costs one more verification and never a bypass. So does any wrapper or decorator object that delegates
|
|
40
|
-
* to `verifier.verify`: it is a different identity, and this verifier's adapter re-verifies claims it
|
|
41
|
-
* returns. Two verifiers built from the same configuration are different identities too.
|
|
42
|
-
*/
|
|
43
|
-
declare function verifiedBy(claims: PassflowClaims, verifier: Verifier): boolean;
|
|
44
|
-
/**
|
|
45
|
-
* The roles a user holds in a tenant: `groups[rootGroupId]`, the rule the server's `CalcTenantRoles` and
|
|
46
|
-
* `@passflow/core`'s `parseMembership` apply. A missing tenant, root group or map gives an empty list.
|
|
47
|
-
* So does an empty `rootGroupId`, unlike the server: no role is granted from a group keyed by "".
|
|
48
|
-
* `tenantRoles` is a copy the server derives from the same root group, and is not consulted.
|
|
49
|
-
*/
|
|
50
|
-
declare function rolesOf(claims: PassflowClaims, tenantId: string): string[];
|
|
51
|
-
export { verifiedBy as a, rolesOf as i, TenantMembership as n, Verifier as r, PassflowClaims as t };
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/** The `passflow_tm` wire shape, camelCased. `tenantRoles` is decoded for raw access; `rolesOf` does not read it. */
|
|
2
|
-
type TenantMembership = {
|
|
3
|
-
tenantId: string;
|
|
4
|
-
tenantName?: string;
|
|
5
|
-
tenantRoles?: string[];
|
|
6
|
-
rootGroupId?: string;
|
|
7
|
-
groups?: Record<string, string[]>;
|
|
8
|
-
groupNames?: Record<string, string>;
|
|
9
|
-
};
|
|
10
|
-
type PassflowClaims = {
|
|
11
|
-
sub: string;
|
|
12
|
-
iss: string;
|
|
13
|
-
aud: string[];
|
|
14
|
-
exp: number;
|
|
15
|
-
iat?: number;
|
|
16
|
-
/** Whole Unix seconds. Absent when the claim is absent, not a number or beyond ±2^53. */
|
|
17
|
-
authTime?: number;
|
|
18
|
-
/** `'0'` when the claim is absent or not a string. */
|
|
19
|
-
acr: string;
|
|
20
|
-
amr: string[];
|
|
21
|
-
scopes: string[];
|
|
22
|
-
sid?: string;
|
|
23
|
-
type: string;
|
|
24
|
-
tenants: Record<string, TenantMembership>;
|
|
25
|
-
/** The verifier's clock at verification, in Unix seconds. `requireFresh` measures from it. */
|
|
26
|
-
verifiedAt: number;
|
|
27
|
-
raw: Record<string, unknown>;
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* The structural part of a verifier. Declared here, with `verifiedBy`, so that provenance needs nothing
|
|
31
|
-
* from the verifier module.
|
|
32
|
-
*/
|
|
33
|
-
interface Verifier {
|
|
34
|
-
verify(token: string): Promise<PassflowClaims>;
|
|
35
|
-
refresh(): Promise<void>;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* True only for the exact object `verifier.verify` returned. A spread or clone loses provenance, which
|
|
39
|
-
* costs one more verification and never a bypass. So does any wrapper or decorator object that delegates
|
|
40
|
-
* to `verifier.verify`: it is a different identity, and this verifier's adapter re-verifies claims it
|
|
41
|
-
* returns. Two verifiers built from the same configuration are different identities too.
|
|
42
|
-
*/
|
|
43
|
-
declare function verifiedBy(claims: PassflowClaims, verifier: Verifier): boolean;
|
|
44
|
-
/**
|
|
45
|
-
* The roles a user holds in a tenant: `groups[rootGroupId]`, the rule the server's `CalcTenantRoles` and
|
|
46
|
-
* `@passflow/core`'s `parseMembership` apply. A missing tenant, root group or map gives an empty list.
|
|
47
|
-
* So does an empty `rootGroupId`, unlike the server: no role is granted from a group keyed by "".
|
|
48
|
-
* `tenantRoles` is a copy the server derives from the same root group, and is not consulted.
|
|
49
|
-
*/
|
|
50
|
-
declare function rolesOf(claims: PassflowClaims, tenantId: string): string[];
|
|
51
|
-
export { verifiedBy as a, rolesOf as i, TenantMembership as n, Verifier as r, PassflowClaims as t };
|