@c9up/visa 0.1.3 → 0.1.4
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 +82 -11
- package/dist/VisaManager.d.ts +47 -0
- package/dist/VisaManager.d.ts.map +1 -1
- package/dist/VisaManager.js +169 -7
- package/dist/VisaManager.js.map +1 -1
- package/dist/VisaProvider.d.ts +4 -0
- package/dist/VisaProvider.d.ts.map +1 -1
- package/dist/VisaProvider.js +87 -5
- package/dist/VisaProvider.js.map +1 -1
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +16 -1
- package/dist/configure.js.map +1 -1
- package/dist/guard.d.ts +2 -1
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +12 -13
- package/dist/guard.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/register.d.ts.map +1 -1
- package/dist/register.js +9 -4
- package/dist/register.js.map +1 -1
- package/dist/store.d.ts +10 -0
- package/dist/store.d.ts.map +1 -1
- package/dist/stores/atlas.d.ts +1 -1
- package/dist/stores/atlas.d.ts.map +1 -1
- package/dist/stores/atlas.js +6 -1
- package/dist/stores/atlas.js.map +1 -1
- package/dist/stores/memory.d.ts +1 -2
- package/dist/stores/memory.d.ts.map +1 -1
- package/dist/stores/memory.js +1 -3
- package/dist/stores/memory.js.map +1 -1
- package/dist/token.js +17 -5
- package/dist/token.js.map +1 -1
- package/package.json +1 -1
- package/src/VisaManager.ts +212 -11
- package/src/VisaProvider.ts +108 -6
- package/src/configure.ts +17 -1
- package/src/guard.ts +12 -13
- package/src/index.ts +1 -0
- package/src/register.ts +11 -4
- package/src/store.ts +11 -0
- package/src/stores/atlas.ts +6 -1
- package/src/stores/memory.ts +1 -3
- package/src/token.ts +30 -11
- package/stubs/database/migrations/9000_create_visa_tables.stub +2 -0
- package/stubs/database/migrations/9001_add_visa_client_secret_expiry.stub +29 -0
package/src/token.ts
CHANGED
|
@@ -224,6 +224,13 @@ async function exchangeRefresh(
|
|
|
224
224
|
const requested = asString(request.scope);
|
|
225
225
|
const scopes =
|
|
226
226
|
requested === undefined ? record.scopes : narrow(requested, record.scopes);
|
|
227
|
+
const resources = narrowResources(
|
|
228
|
+
validateResources(
|
|
229
|
+
readResourceParameter(request.resource),
|
|
230
|
+
options.resourcesSupported,
|
|
231
|
+
),
|
|
232
|
+
record.resources ?? [],
|
|
233
|
+
);
|
|
227
234
|
|
|
228
235
|
if (!(await store.consumeRefreshToken(record.tokenHash, now))) throw invalid;
|
|
229
236
|
|
|
@@ -233,13 +240,18 @@ async function exchangeRefresh(
|
|
|
233
240
|
...(record.userId === undefined ? {} : { userId: record.userId }),
|
|
234
241
|
scopes,
|
|
235
242
|
familyId: record.familyId,
|
|
236
|
-
resources
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
+
resources,
|
|
244
|
+
// Only the ACCESS token is narrowed. RFC 6749 §6: a rotated refresh
|
|
245
|
+
// token's scope "MUST be identical to that of the refresh token
|
|
246
|
+
// included by the client in the request" — otherwise asking for a
|
|
247
|
+
// down-scoped token for one call would cost the rest of the user's
|
|
248
|
+
// consent for the whole session. Resources follow the same rule:
|
|
249
|
+
// RFC 8707 has a client refresh once per resource it calls, which
|
|
250
|
+
// only works if a token for one leaves the others reachable.
|
|
251
|
+
refresh: {
|
|
252
|
+
scopes: record.scopes,
|
|
253
|
+
resources: record.resources ?? [],
|
|
254
|
+
},
|
|
243
255
|
},
|
|
244
256
|
store,
|
|
245
257
|
options,
|
|
@@ -306,6 +318,13 @@ interface Issued {
|
|
|
306
318
|
familyId?: string;
|
|
307
319
|
/** RFC 8707 — what the token is for. Empty means unbound. */
|
|
308
320
|
resources?: string[];
|
|
321
|
+
/**
|
|
322
|
+
* What the refresh token carries, when it differs from the access token.
|
|
323
|
+
*
|
|
324
|
+
* Absent, the refresh token carries the access token's scopes and
|
|
325
|
+
* resources — the case of a code exchange, where both halves are the grant.
|
|
326
|
+
*/
|
|
327
|
+
refresh?: { scopes: string[]; resources: string[] };
|
|
309
328
|
}
|
|
310
329
|
|
|
311
330
|
/** Mint the pair and store both, hashed. */
|
|
@@ -347,18 +366,18 @@ async function issue(
|
|
|
347
366
|
const refreshTtl =
|
|
348
367
|
options.refreshTokenTtlSeconds ?? DEFAULTS.refreshTokenTtlSeconds;
|
|
349
368
|
const refreshToken = randomToken();
|
|
369
|
+
const refreshScopes = grant.refresh?.scopes ?? grant.scopes;
|
|
370
|
+
const refreshResources = grant.refresh?.resources ?? grant.resources ?? [];
|
|
350
371
|
await store.saveRefreshToken({
|
|
351
372
|
tokenHash: hashSecret(refreshToken),
|
|
352
373
|
familyId: grant.familyId,
|
|
353
374
|
clientId: grant.client.id,
|
|
354
375
|
...(grant.userId === undefined ? {} : { userId: grant.userId }),
|
|
355
|
-
scopes:
|
|
376
|
+
scopes: refreshScopes,
|
|
356
377
|
expiresAt: new Date(now.getTime() + refreshTtl * 1000),
|
|
357
378
|
// Carried so a rotation can narrow from the same list the code bound —
|
|
358
379
|
// without it, the second token of a session would be unbound.
|
|
359
|
-
...(
|
|
360
|
-
? {}
|
|
361
|
-
: { resources: grant.resources }),
|
|
380
|
+
...(refreshResources.length === 0 ? {} : { resources: refreshResources }),
|
|
362
381
|
});
|
|
363
382
|
response.refresh_token = refreshToken;
|
|
364
383
|
return response;
|
|
@@ -26,6 +26,8 @@ export default class CreateVisaTables extends Migration {
|
|
|
26
26
|
t.string("name", 255).notNullable();
|
|
27
27
|
// Null for a public client — a SPA or a native app has no secret to keep.
|
|
28
28
|
t.string("secret_hash", 255).nullable();
|
|
29
|
+
// `secret_expires_at` is added by 9001, so a table created before it
|
|
30
|
+
// existed gets it the same way a new one does.
|
|
29
31
|
t.json("redirect_uris").notNullable();
|
|
30
32
|
t.json("grant_types").notNullable();
|
|
31
33
|
t.json("scopes").notNullable();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
to: database/migrations/9001_add_visa_client_secret_expiry.ts
|
|
3
|
+
---
|
|
4
|
+
import { Migration } from "@c9up/atlas";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* When a client's secret stops working (RFC 7591 §3.2.1).
|
|
8
|
+
*
|
|
9
|
+
* Its own migration rather than a line in 9000, so an application whose
|
|
10
|
+
* tables already exist gets the column by running `ream configure @c9up/visa`
|
|
11
|
+
* again: 9000 is already there and is left alone, this one lands and adds it.
|
|
12
|
+
*
|
|
13
|
+
* Null means the secret does not expire. The store reads it at every client
|
|
14
|
+
* authentication: an expiry announced at registration and not stored here
|
|
15
|
+
* would never be enforced.
|
|
16
|
+
*/
|
|
17
|
+
export default class AddVisaClientSecretExpiry extends Migration {
|
|
18
|
+
async up() {
|
|
19
|
+
this.schema.alterTable("visa_clients", (t) => {
|
|
20
|
+
t.timestamp("secret_expires_at").nullable();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async down() {
|
|
25
|
+
this.schema.alterTable("visa_clients", (t) => {
|
|
26
|
+
t.dropColumn("secret_expires_at");
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|