@toa.io/extensions.exposition 1.0.0-alpha.149 → 1.0.0-alpha.150
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/components/identity.bans/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.basic/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.federation/manifest.toa.yaml +22 -25
- package/components/identity.federation/operations/authenticate.d.ts +6 -4
- package/components/identity.federation/operations/authenticate.js +18 -8
- package/components/identity.federation/operations/authenticate.js.map +1 -1
- package/components/identity.federation/operations/decode.d.ts +3 -2
- package/components/identity.federation/operations/decode.js +9 -29
- package/components/identity.federation/operations/decode.js.map +1 -1
- package/components/identity.federation/operations/incept.d.ts +3 -2
- package/components/identity.federation/operations/incept.js +15 -5
- package/components/identity.federation/operations/incept.js.map +1 -1
- package/components/identity.federation/operations/lib/Configuration.d.ts +39 -0
- package/components/identity.federation/operations/lib/Configuration.js +3 -0
- package/components/identity.federation/operations/lib/Configuration.js.map +1 -0
- package/components/identity.federation/operations/lib/Ctx.d.ts +7 -0
- package/components/identity.federation/operations/lib/Ctx.js +3 -0
- package/components/identity.federation/operations/lib/Ctx.js.map +1 -0
- package/components/identity.federation/operations/lib/Payload.d.ts +5 -0
- package/components/identity.federation/operations/lib/Payload.js +3 -0
- package/components/identity.federation/operations/lib/Payload.js.map +1 -0
- package/components/identity.federation/operations/lib/decode.d.ts +3 -0
- package/components/identity.federation/operations/lib/decode.js +59 -0
- package/components/identity.federation/operations/lib/decode.js.map +1 -0
- package/components/identity.federation/operations/lib/discovery.d.ts +4 -0
- package/components/identity.federation/operations/lib/{assertions-as-values.js → discovery.js} +23 -21
- package/components/identity.federation/operations/lib/discovery.js.map +1 -0
- package/components/identity.federation/operations/lib/errors.d.ts +11 -0
- package/components/identity.federation/operations/lib/errors.js +15 -0
- package/components/identity.federation/operations/lib/errors.js.map +1 -0
- package/components/identity.federation/operations/lib/exchange.d.ts +3 -0
- package/components/identity.federation/operations/lib/exchange.js +107 -0
- package/components/identity.federation/operations/lib/exchange.js.map +1 -0
- package/components/identity.federation/operations/lib/index.d.ts +3 -0
- package/components/identity.federation/operations/lib/index.js +8 -0
- package/components/identity.federation/operations/lib/index.js.map +1 -0
- package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.federation/operations/types/Scheme.d.ts +1 -0
- package/components/identity.federation/operations/types/Scheme.js +3 -0
- package/components/identity.federation/operations/types/Scheme.js.map +1 -0
- package/components/identity.federation/operations/types/configuration.d.ts +9 -4
- package/components/identity.federation/operations/types/context.d.ts +2 -18
- package/components/identity.federation/operations/types/index.d.ts +1 -0
- package/components/identity.federation/operations/types/index.js +1 -0
- package/components/identity.federation/operations/types/index.js.map +1 -1
- package/components/identity.federation/source/authenticate.ts +27 -11
- package/components/identity.federation/source/decode.ts +9 -7
- package/components/identity.federation/source/incept.ts +19 -7
- package/components/identity.federation/source/lib/Configuration.ts +39 -0
- package/components/identity.federation/source/lib/Ctx.ts +8 -0
- package/components/identity.federation/source/lib/Payload.ts +6 -0
- package/components/identity.federation/source/lib/decode.ts +48 -0
- package/components/identity.federation/source/lib/discovery.ts +30 -0
- package/components/identity.federation/source/lib/errors.ts +12 -0
- package/components/identity.federation/source/lib/exchange.ts +116 -0
- package/components/identity.federation/source/lib/index.ts +3 -0
- package/components/identity.federation/source/types/Scheme.ts +1 -0
- package/components/identity.federation/source/types/configuration.ts +9 -4
- package/components/identity.federation/source/types/context.ts +3 -20
- package/components/identity.federation/source/types/index.ts +1 -0
- package/components/identity.keys/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.otp/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.passkeys/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.roles/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.tokens/operations/tsconfig.tsbuildinfo +1 -1
- package/documentation/identity.md +41 -2
- package/features/auth.claims.feature +0 -1
- package/features/authorities.federation.feature +0 -1
- package/features/identity.federation.feature +53 -34
- package/features/map.feature +1 -2
- package/features/steps/{IdP.ts → IDP.ts} +141 -23
- package/package.json +10 -12
- package/source/HTTP/Server.ts +3 -2
- package/source/directives/auth/Authorization.ts +1 -0
- package/source/directives/auth/schemes.ts +1 -0
- package/source/directives/auth/types.ts +1 -1
- package/transpiled/HTTP/Server.js +3 -2
- package/transpiled/HTTP/Server.js.map +1 -1
- package/transpiled/directives/auth/Authorization.js +1 -0
- package/transpiled/directives/auth/Authorization.js.map +1 -1
- package/transpiled/directives/auth/schemes.js +1 -0
- package/transpiled/directives/auth/schemes.js.map +1 -1
- package/transpiled/directives/auth/types.d.ts +1 -1
- package/transpiled/tsconfig.tsbuildinfo +1 -1
- package/components/identity.federation/operations/lib/assertions-as-values.d.ts +0 -4
- package/components/identity.federation/operations/lib/assertions-as-values.js.map +0 -1
- package/components/identity.federation/operations/lib/get.d.ts +0 -1
- package/components/identity.federation/operations/lib/get.js +0 -64
- package/components/identity.federation/operations/lib/get.js.map +0 -1
- package/components/identity.federation/operations/lib/jwt.d.ts +0 -20
- package/components/identity.federation/operations/lib/jwt.js +0 -152
- package/components/identity.federation/operations/lib/jwt.js.map +0 -1
- package/components/identity.federation/source/lib/assertions-as-values.ts +0 -22
- package/components/identity.federation/source/lib/get.ts +0 -82
- package/components/identity.federation/source/lib/jwt.test.ts +0 -179
- package/components/identity.federation/source/lib/jwt.ts +0 -198
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* eslint-disable max-depth */
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
-
};
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.get = void 0;
|
|
8
|
-
const node_stream_1 = require("node:stream");
|
|
9
|
-
const consumers_1 = require("node:stream/consumers");
|
|
10
|
-
const ttlcache_1 = __importDefault(require("@isaacs/ttlcache"));
|
|
11
|
-
const http_cache_semantics_1 = __importDefault(require("http-cache-semantics"));
|
|
12
|
-
const cache = new ttlcache_1.default();
|
|
13
|
-
async function get(url, init) {
|
|
14
|
-
const headers = toRecord(init?.headers);
|
|
15
|
-
const request = { url, method: 'GET', headers };
|
|
16
|
-
const cached = cache.get(url);
|
|
17
|
-
if (cached?.policy.satisfiesWithoutRevalidation(request) === true) {
|
|
18
|
-
const headers = toHeaders(cached.policy.responseHeaders());
|
|
19
|
-
return new Response(cached.body, { headers });
|
|
20
|
-
}
|
|
21
|
-
const response = await fetch(url, init);
|
|
22
|
-
const policy = new http_cache_semantics_1.default(request, { status: response.status, headers: toRecord(response.headers) });
|
|
23
|
-
const ttl = policy.timeToLive();
|
|
24
|
-
if (policy.storable() && ttl > 0) {
|
|
25
|
-
const body = await toBuffer(response.body);
|
|
26
|
-
cache.set(url, { policy, body }, { ttl });
|
|
27
|
-
return new Response(body, { headers: response.headers });
|
|
28
|
-
}
|
|
29
|
-
else
|
|
30
|
-
return response;
|
|
31
|
-
}
|
|
32
|
-
exports.get = get;
|
|
33
|
-
function toRecord(headers) {
|
|
34
|
-
if (!(headers instanceof Headers))
|
|
35
|
-
headers = toHeaders(headers);
|
|
36
|
-
return Object.fromEntries(headers.entries());
|
|
37
|
-
}
|
|
38
|
-
function toHeaders(values) {
|
|
39
|
-
const headers = new Headers();
|
|
40
|
-
if (values === undefined)
|
|
41
|
-
return headers;
|
|
42
|
-
if (Array.isArray(values))
|
|
43
|
-
for (const [name, value] of values)
|
|
44
|
-
headers.append(name, value);
|
|
45
|
-
else
|
|
46
|
-
for (const name in values) {
|
|
47
|
-
const value = values[name];
|
|
48
|
-
if (value === undefined)
|
|
49
|
-
continue;
|
|
50
|
-
if (Array.isArray(value))
|
|
51
|
-
for (const v of value)
|
|
52
|
-
headers.append(name, v);
|
|
53
|
-
else
|
|
54
|
-
headers.append(name, value);
|
|
55
|
-
}
|
|
56
|
-
return headers;
|
|
57
|
-
}
|
|
58
|
-
async function toBuffer(body) {
|
|
59
|
-
if (body === null)
|
|
60
|
-
return null;
|
|
61
|
-
const buf = await (0, consumers_1.buffer)(node_stream_1.Readable.fromWeb(body));
|
|
62
|
-
return Buffer.from(buf);
|
|
63
|
-
}
|
|
64
|
-
//# sourceMappingURL=get.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"get.js","sourceRoot":"","sources":["../../source/lib/get.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { JwtHeader, IdToken, Trust } from '../types';
|
|
2
|
-
import type { Stash } from '@toa.io/types';
|
|
3
|
-
export declare function decodeJwt(token: string): {
|
|
4
|
-
header: unknown;
|
|
5
|
-
payload: unknown;
|
|
6
|
-
rawHeader: string;
|
|
7
|
-
rawPayload: string;
|
|
8
|
-
signature: string;
|
|
9
|
-
};
|
|
10
|
-
export declare function validateJwtHeader(header: unknown): asserts header is JwtHeader;
|
|
11
|
-
export declare function validateJwtPayload(payload: unknown, trusted: Trust[], header: JwtHeader): asserts payload is IdToken;
|
|
12
|
-
export declare function validateSignature({ header: { kid, alg }, payload: { iss }, rawHeader, rawPayload, signature, trusted }: {
|
|
13
|
-
readonly header: JwtHeader;
|
|
14
|
-
rawHeader: string;
|
|
15
|
-
readonly payload: IdToken;
|
|
16
|
-
rawPayload: string;
|
|
17
|
-
signature: string;
|
|
18
|
-
trusted?: Trust[];
|
|
19
|
-
}): Promise<void>;
|
|
20
|
-
export declare function decode(token: string, trusted: Trust[] | undefined, stash: Stash): Promise<IdToken>;
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.decode = exports.validateSignature = exports.validateJwtPayload = exports.validateJwtHeader = exports.decodeJwt = void 0;
|
|
30
|
-
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
31
|
-
const assert = __importStar(require("node:assert"));
|
|
32
|
-
const get_1 = require("./get");
|
|
33
|
-
function decodeJwt(token) {
|
|
34
|
-
const [rawHeader, rawPayload, signature] = token.split('.', 3);
|
|
35
|
-
const header = JSON.parse(Buffer.from(rawHeader, 'base64url').toString());
|
|
36
|
-
const payload = JSON.parse(Buffer.from(rawPayload, 'base64url').toString());
|
|
37
|
-
return { header, payload, rawHeader, rawPayload, signature };
|
|
38
|
-
}
|
|
39
|
-
exports.decodeJwt = decodeJwt;
|
|
40
|
-
function validateJwtHeader(header) {
|
|
41
|
-
assert.ok(header !== null && typeof header === 'object', 'Header is not an object');
|
|
42
|
-
assert.ok('alg' in header, 'Header is missing alg');
|
|
43
|
-
assert.ok(typeof header.alg === 'string', 'Header alg is not a string');
|
|
44
|
-
assert.match(header.alg, /^RS256|HS\d{3}$/, `Unknown algorithm ${header.alg}`);
|
|
45
|
-
assert.ok(!('kid' in header) || typeof header.kid === 'string', 'kid must be a string if present');
|
|
46
|
-
}
|
|
47
|
-
exports.validateJwtHeader = validateJwtHeader;
|
|
48
|
-
function validateJwtPayload(payload, trusted, header) {
|
|
49
|
-
assert.ok(trusted.length > 0, 'No trusted issuers provided');
|
|
50
|
-
// the full list of validations is
|
|
51
|
-
// at https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
|
|
52
|
-
assert.ok(payload !== null && typeof payload === 'object', 'Payload is not an object');
|
|
53
|
-
assert.ok('iss' in payload, 'Payload is missing iss');
|
|
54
|
-
assert.ok(typeof payload.iss === 'string', 'Payload iss is not a string');
|
|
55
|
-
assert.ok('aud' in payload, 'Payload is missing aud');
|
|
56
|
-
assert.ok(typeof payload.aud === 'string' ||
|
|
57
|
-
(Array.isArray(payload.aud) && payload.aud.every((e) => typeof e === 'string')), 'Payload aud is not a string nor an array of strings');
|
|
58
|
-
const issuer = trusted.find((config) => config.iss === payload.iss);
|
|
59
|
-
assert.ok(issuer, `Unknown issuer: ${payload.iss}`);
|
|
60
|
-
if (Array.isArray(issuer.aud)) {
|
|
61
|
-
const tokenAud = payload.aud;
|
|
62
|
-
if (typeof tokenAud === 'string')
|
|
63
|
-
assert.ok(issuer.aud.some((a) => a === tokenAud), `Unknown audience: ${tokenAud}`);
|
|
64
|
-
else
|
|
65
|
-
assert.ok(issuer.aud.some((a) => tokenAud.includes(a)), `Unknown audiences: ${tokenAud.join(', ')}`);
|
|
66
|
-
}
|
|
67
|
-
if (header.alg.startsWith('HS')) {
|
|
68
|
-
const secrets = issuer.secrets;
|
|
69
|
-
assert.ok(secrets, `We don't have known secrets for ${payload.iss}`);
|
|
70
|
-
const keys = secrets[header.alg];
|
|
71
|
-
assert.ok(keys, `No known secrets for ${header.alg}`);
|
|
72
|
-
if (typeof header.kid === 'string')
|
|
73
|
-
assert.ok(header.kid in keys, `No secret ${header.kid} provided for ${header.alg}`);
|
|
74
|
-
}
|
|
75
|
-
assert.ok('sub' in payload, 'Payload is missing sub');
|
|
76
|
-
assert.ok(typeof payload.sub === 'string', 'Payload sub is not a string');
|
|
77
|
-
assert.ok('exp' in payload, 'Payload is missing exp');
|
|
78
|
-
assert.ok(typeof payload.exp === 'number', 'Payload exp is not a number');
|
|
79
|
-
assert.ok(Date.now() < payload.exp * 1000, 'Token is expired');
|
|
80
|
-
assert.ok('iat' in payload, 'Payload is missing iat');
|
|
81
|
-
assert.ok(typeof payload.iat === 'number', 'Payload iat is not a number');
|
|
82
|
-
assert.ok(Date.now() >= payload.iat * 1000, 'Token was issued in the future');
|
|
83
|
-
assert.ok(payload.exp >= payload.iat, 'Payload exp is before iat');
|
|
84
|
-
if ('nbf' in payload) {
|
|
85
|
-
assert.ok(typeof payload.nbf === 'number', 'Payload nbf is not a number');
|
|
86
|
-
assert.ok(Date.now() >= payload.nbf * 1000, 'Token is not valid yet');
|
|
87
|
-
}
|
|
88
|
-
if ('jti' in payload)
|
|
89
|
-
assert.ok(typeof payload.jti === 'string', 'Payload jti is not a string');
|
|
90
|
-
}
|
|
91
|
-
exports.validateJwtPayload = validateJwtPayload;
|
|
92
|
-
async function validateSignature({ header: { kid, alg }, payload: { iss }, rawHeader, rawPayload, signature, trusted = [] }) {
|
|
93
|
-
if (alg.startsWith('HS')) {
|
|
94
|
-
// symmetric algorithm, issuer is validated at this point
|
|
95
|
-
const secrets = trusted.find((c) => c.iss === iss).secrets[alg];
|
|
96
|
-
const secret = kid !== undefined ? secrets[kid] : Object.values(secrets)[0];
|
|
97
|
-
const algorithm = alg.replace(/^HS(\d{3})$/, 'sha$1'); // HS256 -> sha256
|
|
98
|
-
const hmac = node_crypto_1.default.createHmac(algorithm, secret);
|
|
99
|
-
hmac.update(rawHeader);
|
|
100
|
-
hmac.update('.');
|
|
101
|
-
hmac.update(rawPayload);
|
|
102
|
-
assert.strictEqual(signature, hmac.digest('base64url'), 'Signature does not match');
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
// Getting issuer public keys
|
|
106
|
-
const oidcRequest = await (0, get_1.get)(new URL('/.well-known/openid-configuration', iss).href);
|
|
107
|
-
assert.ok(oidcRequest.ok, `Failed to fetch OpenID configuration: ${oidcRequest.statusText}`);
|
|
108
|
-
const { jwks_uri: jwksUri } = (await oidcRequest.json());
|
|
109
|
-
const jwkRequest = await (0, get_1.get)(jwksUri);
|
|
110
|
-
assert.ok(jwkRequest.ok, `Failed to fetch issuer keys: ${jwkRequest.statusText}`);
|
|
111
|
-
const { keys } = (await jwkRequest.json());
|
|
112
|
-
// getting the corresponding signing key
|
|
113
|
-
const signingKeys = keys.filter((k) => k.use === 'sig' && k.alg === alg);
|
|
114
|
-
assert.ok(signingKeys.length > 0, 'No acceptable signing keys found');
|
|
115
|
-
assert.ok(kid !== undefined || signingKeys.length === 1, 'Signing key selection is not deterministic');
|
|
116
|
-
const signingKey = kid === undefined ? keys[0] : signingKeys.find((k) => k.kid === kid);
|
|
117
|
-
assert.ok(signingKey, 'Signing key was not found in issuer keys');
|
|
118
|
-
const verifyFunction = node_crypto_1.default.createVerify('RSA-SHA256');
|
|
119
|
-
verifyFunction.write(rawHeader);
|
|
120
|
-
verifyFunction.write('.');
|
|
121
|
-
verifyFunction.write(rawPayload);
|
|
122
|
-
verifyFunction.end();
|
|
123
|
-
const signatureValid = verifyFunction.verify({ format: 'jwk', key: signingKey }, signature, 'base64url');
|
|
124
|
-
assert.ok(signatureValid, 'Failed to validate signature');
|
|
125
|
-
}
|
|
126
|
-
exports.validateSignature = validateSignature;
|
|
127
|
-
async function validateJti(token, stash) {
|
|
128
|
-
const key = `identity:federation:jti:${token.jti}`;
|
|
129
|
-
const used = await stash.exists(key);
|
|
130
|
-
assert.ok(used === 0, 'Token has already been used');
|
|
131
|
-
const ttl = token.exp - Math.floor(Date.now() / 1000);
|
|
132
|
-
await stash.set(key, '1', 'EX', ttl);
|
|
133
|
-
}
|
|
134
|
-
async function decode(token, trusted, stash) {
|
|
135
|
-
assert.ok(trusted !== undefined, 'No trusted issuers provided');
|
|
136
|
-
const { header, payload, rawHeader, rawPayload, signature } = decodeJwt(token);
|
|
137
|
-
validateJwtHeader(header);
|
|
138
|
-
validateJwtPayload(payload, trusted, header);
|
|
139
|
-
if (payload.jti !== undefined)
|
|
140
|
-
await validateJti(payload, stash);
|
|
141
|
-
await validateSignature({
|
|
142
|
-
header,
|
|
143
|
-
rawHeader,
|
|
144
|
-
payload,
|
|
145
|
-
rawPayload,
|
|
146
|
-
signature,
|
|
147
|
-
trusted
|
|
148
|
-
});
|
|
149
|
-
return payload;
|
|
150
|
-
}
|
|
151
|
-
exports.decode = decode;
|
|
152
|
-
//# sourceMappingURL=jwt.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../source/lib/jwt.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import * as assert from 'node:assert'
|
|
2
|
-
import { console } from 'openspan'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Wrapping function that returns assertion errors as function return value
|
|
6
|
-
*/
|
|
7
|
-
export function assertionsAsValues<T extends (...args: any[]) => Promise<any>> (
|
|
8
|
-
fn: T) {
|
|
9
|
-
return async (...args: Parameters<T>): Promise<ReturnType<T> | Error> => {
|
|
10
|
-
try {
|
|
11
|
-
return await fn(...args)
|
|
12
|
-
} catch (err) {
|
|
13
|
-
if (err instanceof assert.AssertionError) {
|
|
14
|
-
console.error('OIDC Authentication exception', { message: err.message })
|
|
15
|
-
|
|
16
|
-
return err
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
throw err
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/* eslint-disable max-depth */
|
|
2
|
-
|
|
3
|
-
import { Readable } from 'node:stream'
|
|
4
|
-
import { buffer } from 'node:stream/consumers'
|
|
5
|
-
|
|
6
|
-
import Cache from '@isaacs/ttlcache'
|
|
7
|
-
import Policy from 'http-cache-semantics'
|
|
8
|
-
import type { ReadableStream } from 'node:stream/web'
|
|
9
|
-
|
|
10
|
-
const cache = new Cache<string, Entry>()
|
|
11
|
-
|
|
12
|
-
export async function get (url: string, init?: RequestInit): Promise<Response> {
|
|
13
|
-
const headers = toRecord(init?.headers)
|
|
14
|
-
const request = { url, method: 'GET', headers }
|
|
15
|
-
const cached = cache.get(url)
|
|
16
|
-
|
|
17
|
-
if (cached?.policy.satisfiesWithoutRevalidation(request) === true) {
|
|
18
|
-
const headers = toHeaders(cached.policy.responseHeaders())
|
|
19
|
-
|
|
20
|
-
return new Response(cached.body, { headers })
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const response = await fetch(url, init)
|
|
24
|
-
const policy = new Policy(request, { status: response.status, headers: toRecord(response.headers) })
|
|
25
|
-
const ttl = policy.timeToLive()
|
|
26
|
-
|
|
27
|
-
if (policy.storable() && ttl > 0) {
|
|
28
|
-
const body = await toBuffer(response.body as ReadableStream)
|
|
29
|
-
|
|
30
|
-
cache.set(url, { policy, body }, { ttl })
|
|
31
|
-
|
|
32
|
-
return new Response(body, { headers: response.headers })
|
|
33
|
-
} else
|
|
34
|
-
return response
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function toRecord (headers: RequestInit['headers']): Record<string, string> {
|
|
38
|
-
if (!(headers instanceof Headers))
|
|
39
|
-
headers = toHeaders(headers)
|
|
40
|
-
|
|
41
|
-
return Object.fromEntries(headers.entries())
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function toHeaders (values?: Array<[string, string]> | Record<string, string | string[] | undefined>): Headers {
|
|
45
|
-
const headers = new Headers()
|
|
46
|
-
|
|
47
|
-
if (values === undefined)
|
|
48
|
-
return headers
|
|
49
|
-
|
|
50
|
-
if (Array.isArray(values))
|
|
51
|
-
for (const [name, value] of values)
|
|
52
|
-
headers.append(name, value)
|
|
53
|
-
else
|
|
54
|
-
for (const name in values) {
|
|
55
|
-
const value = values[name]
|
|
56
|
-
|
|
57
|
-
if (value === undefined)
|
|
58
|
-
continue
|
|
59
|
-
|
|
60
|
-
if (Array.isArray(value))
|
|
61
|
-
for (const v of value)
|
|
62
|
-
headers.append(name, v)
|
|
63
|
-
else
|
|
64
|
-
headers.append(name, value)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return headers
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function toBuffer (body: ReadableStream<Uint8Array> | null): Promise<Buffer | null> {
|
|
71
|
-
if (body === null)
|
|
72
|
-
return null
|
|
73
|
-
|
|
74
|
-
const buf = await buffer(Readable.fromWeb(body as ReadableStream))
|
|
75
|
-
|
|
76
|
-
return Buffer.from(buf)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
interface Entry {
|
|
80
|
-
policy: Policy
|
|
81
|
-
body: Buffer | null
|
|
82
|
-
}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import * as http from 'node:http'
|
|
2
|
-
|
|
3
|
-
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
|
4
|
-
import { once } from 'node:events'
|
|
5
|
-
import { validateSignature, decodeJwt, validateJwtPayload } from './jwt'
|
|
6
|
-
import { get } from './get'
|
|
7
|
-
import type { AddressInfo } from 'node:net'
|
|
8
|
-
import type { IdToken, JwtHeader, Trust } from '../types'
|
|
9
|
-
|
|
10
|
-
describe('jwt', () => {
|
|
11
|
-
describe('validateJwtPayload', () => {
|
|
12
|
-
const jwtHeader: JwtHeader = { alg: 'HS256', kid: 'k1' }
|
|
13
|
-
|
|
14
|
-
const trusted: Trust[] = [{
|
|
15
|
-
iss: 'test-iss',
|
|
16
|
-
aud: ['test-aud1', 'test-aud2'],
|
|
17
|
-
secrets: { [jwtHeader.alg]: { [jwtHeader.kid!]: 'test-secrete' } }
|
|
18
|
-
}]
|
|
19
|
-
|
|
20
|
-
const validJwtPayload: IdToken = {
|
|
21
|
-
iss: trusted[0].iss,
|
|
22
|
-
sub: 'test-sub',
|
|
23
|
-
iat: Date.now() / 1000,
|
|
24
|
-
exp: Date.now() / 1000 + 2000,
|
|
25
|
-
aud: trusted[0].aud![1]
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
describe('aud', () => {
|
|
29
|
-
test('throws without it', () => {
|
|
30
|
-
const { aud, ...noAudPayload } = validJwtPayload
|
|
31
|
-
|
|
32
|
-
expect(() => validateJwtPayload(noAudPayload, trusted, jwtHeader)).toThrow('Payload is missing aud')
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
test('passes with a single string', () => {
|
|
36
|
-
expect(validJwtPayload.aud).toEqual(expect.any(String))
|
|
37
|
-
expect(() => validateJwtPayload(validJwtPayload, trusted, jwtHeader)).not.toThrow()
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
test('passes with an array', () => {
|
|
41
|
-
const arrayAudPayload = { ...validJwtPayload, aud: trusted[0].aud }
|
|
42
|
-
|
|
43
|
-
expect(arrayAudPayload.aud).toEqual(expect.any(Array))
|
|
44
|
-
expect(() => validateJwtPayload(arrayAudPayload, trusted, jwtHeader)).not.toThrow()
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test('throws with an array that does not intersects', () => {
|
|
48
|
-
const arrayAudPayload = { ...validJwtPayload, aud: ['boo', 'foo'] }
|
|
49
|
-
|
|
50
|
-
expect(arrayAudPayload.aud).toEqual(expect.any(Array))
|
|
51
|
-
expect(() => validateJwtPayload(arrayAudPayload, trusted, jwtHeader))
|
|
52
|
-
.toThrow('Unknown audiences: boo, foo')
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
test('decode', () => {
|
|
58
|
-
const { header, payload } = decodeJwt('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb21lIjoicGF5bG9hZCJ9.4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg')
|
|
59
|
-
|
|
60
|
-
expect(header).toMatchObject({ alg: 'HS256' })
|
|
61
|
-
expect(payload).toEqual({ some: 'payload' })
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
test('symmetric pass', async () => {
|
|
65
|
-
// example from
|
|
66
|
-
// https://pyjwt.readthedocs.io/en/latest/usage.html#encoding-decoding-tokens-with-hs256
|
|
67
|
-
|
|
68
|
-
await expect(validateSignature({
|
|
69
|
-
header: { alg: 'HS256', kid: 'k2' },
|
|
70
|
-
payload: { iss: 'test-issuer', aud: 'test', sub: '0', exp: 0, iat: 0 },
|
|
71
|
-
rawHeader: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
|
|
72
|
-
rawPayload: 'eyJzb21lIjoicGF5bG9hZCJ9',
|
|
73
|
-
signature: '4twFt5NiznN84AWoo1d7KO1T_yoc0Z6XOpOVswacPZg',
|
|
74
|
-
trusted: [
|
|
75
|
-
{
|
|
76
|
-
iss: 'test-issuer',
|
|
77
|
-
secrets: {
|
|
78
|
-
HS256: {
|
|
79
|
-
k1: 'old-secret',
|
|
80
|
-
k2: 'secret'
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
} as Parameters<typeof validateSignature>[0])).resolves.toBeUndefined()
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
test('symmetric fail', async () => {
|
|
89
|
-
await expect(validateSignature({
|
|
90
|
-
header: { alg: 'HS256' },
|
|
91
|
-
payload: { iss: 'test-issuer', aud: 'test', sub: '0', exp: 0, iat: 0 },
|
|
92
|
-
rawHeader: 'header',
|
|
93
|
-
rawPayload: 'payload',
|
|
94
|
-
signature: 'signature',
|
|
95
|
-
trusted: [
|
|
96
|
-
{
|
|
97
|
-
iss: 'test-issuer',
|
|
98
|
-
secrets: {
|
|
99
|
-
HS256: {
|
|
100
|
-
theKey: 'secret'
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
]
|
|
105
|
-
} as Parameters<typeof validateSignature>[0])).rejects.toThrow('Signature does not match')
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
describe('cache', () => {
|
|
109
|
-
let server: http.Server
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
111
|
-
const handler = jest.fn<void, [http.IncomingMessage, http.ServerResponse]>()
|
|
112
|
-
let endpoint: string
|
|
113
|
-
|
|
114
|
-
beforeAll(async () => {
|
|
115
|
-
server = http.createServer(handler)
|
|
116
|
-
|
|
117
|
-
server.listen(0, 'localhost')
|
|
118
|
-
await once(server, 'listening')
|
|
119
|
-
|
|
120
|
-
const { port } = server.address() as AddressInfo
|
|
121
|
-
|
|
122
|
-
endpoint = `http://localhost:${port}`
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
afterEach(() => {
|
|
126
|
-
handler.mockReset()
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
afterAll(async () => {
|
|
130
|
-
server.close()
|
|
131
|
-
await once(server, 'close')
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
test('fetches only once', async () => {
|
|
135
|
-
handler.mockImplementationOnce((req, res) => {
|
|
136
|
-
res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'public, max-age=3600' })
|
|
137
|
-
res.end(JSON.stringify({ foo: 'bar' }))
|
|
138
|
-
}).mockImplementationOnce((req, res) => {
|
|
139
|
-
res.writeHead(500)
|
|
140
|
-
res.end()
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
const firstRequest = await get(endpoint)
|
|
144
|
-
|
|
145
|
-
expect(firstRequest.ok).toBe(true)
|
|
146
|
-
|
|
147
|
-
const json = await firstRequest.json()
|
|
148
|
-
|
|
149
|
-
expect(json).toEqual({ foo: 'bar' })
|
|
150
|
-
|
|
151
|
-
const secondRequest = await get(endpoint)
|
|
152
|
-
|
|
153
|
-
expect(secondRequest.ok).toBe(true)
|
|
154
|
-
await expect(secondRequest.json()).resolves.toEqual({ foo: 'bar' })
|
|
155
|
-
|
|
156
|
-
expect(handler).toHaveBeenCalledTimes(1)
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
test('respects caching headers', async () => {
|
|
160
|
-
handler.mockImplementation((req, res) => {
|
|
161
|
-
res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-cache' })
|
|
162
|
-
res.end(JSON.stringify({ foo: 'bar' }))
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
const firstRequest = await get(endpoint + '/no-cache')
|
|
166
|
-
|
|
167
|
-
expect(firstRequest.headers.get('cache-control')).toBe('no-cache')
|
|
168
|
-
expect(firstRequest.ok).toBe(true)
|
|
169
|
-
await expect(firstRequest.json()).resolves.toEqual({ foo: 'bar' })
|
|
170
|
-
|
|
171
|
-
const secondRequest = await get(endpoint + '/no-cache')
|
|
172
|
-
|
|
173
|
-
expect(secondRequest.ok).toBe(true)
|
|
174
|
-
await expect(secondRequest.json()).resolves.toEqual({ foo: 'bar' })
|
|
175
|
-
|
|
176
|
-
expect(handler).toHaveBeenCalledTimes(2)
|
|
177
|
-
})
|
|
178
|
-
})
|
|
179
|
-
})
|