Something went wrong. Try again.
Monorepo for Tangled forked from tangled.org/core
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889import assert from 'node:assert/strict';import { test } from 'node:test';import type { webcrypto } from 'node:crypto';import { VerifiedAccessIdentity, type TeamCertKey } from './access.ts';
const TEAM = 'tangled.cloudflareaccess.com';const AUD = 'a'.repeat(32);const env = { ACCESS_TEAM_DOMAIN: TEAM, ACCESS_AUD: AUD };
const generated = (await crypto.subtle.generateKey( { name: 'RSASSA-PKCS1-v1_5', modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: 'SHA-256' }, true, ['sign', 'verify'],)) as webcrypto.CryptoKeyPair;const jwk = { private: (await crypto.subtle.exportKey('jwk', generated.privateKey)) as TeamCertKey, public: (await crypto.subtle.exportKey('jwk', generated.publicKey)) as TeamCertKey,};
let servedKeys: TeamCertKey[] = [{ ...jwk.public, kid: 'test-key', use: 'sig', alg: 'RS256' }];globalThis.fetch = (async () => Response.json({ keys: servedKeys })) as typeof fetch;
const b64url = (bytes: Uint8Array): string => Buffer.from(bytes).toString('base64url');
const signedToken = async (claims: Record<string, unknown>, kid = 'test-key'): Promise<string> => { const head = b64url(new TextEncoder().encode(JSON.stringify({ alg: 'RS256', kid, typ: 'JWT' }))); const payload = b64url(new TextEncoder().encode(JSON.stringify(claims))); const key = await crypto.subtle.importKey( 'jwk', jwk.private, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, false, ['sign'], ); const signature = await crypto.subtle.sign( 'RSASSA-PKCS1-v1_5', key, new TextEncoder().encode(`${head}.${payload}`) as unknown as ArrayBuffer, ); return `${head}.${payload}.${b64url(new Uint8Array(signature))}`;};
const now = Math.floor(Date.now() / 1000);const claims = (over: Record<string, unknown> = {}) => ({ iss: `https://${TEAM}`, aud: AUD, iat: now, exp: now + 600, email: 'lewis@cia.nel.pet', ...over,});
test('team-signed token gives bearer email', async () => { const identity = await VerifiedAccessIdentity.verify(await signedToken(claims()), env); assert.equal(identity?.email, 'lewis@cia.nel.pet'); const arrayAud = await VerifiedAccessIdentity.verify(await signedToken(claims({ aud: [AUD] })), env); assert.equal(arrayAud?.email, 'lewis@cia.nel.pet');});
test('a token outside policy isnot a full ident', async () => { const [head, payload] = (await signedToken(claims())).split('.'); const cases: Record<string, Promise<string>> = { 'tampered signature': Promise.resolve(`${head}.${payload}.AAAA`), 'another team': signedToken(claims({ iss: 'https://elsewhere.cloudflareaccess.com' })), 'another audience': signedToken(claims({ aud: 'b'.repeat(32) })), 'superstring audience': signedToken(claims({ aud: `${AUD}tail` })), 'prefixed audience': signedToken(claims({ aud: `head${AUD}` })), expired: signedToken(claims({ exp: now - 3600 })), 'issued in the future': signedToken(claims({ iat: now + 3600 })), 'without exp': signedToken(claims({ exp: undefined })), 'without iat': signedToken(claims({ iat: undefined })), }; for (const [name, token] of Object.entries(cases)) { assert.equal(await VerifiedAccessIdentity.verify(await token, env), null, name); }});
test('token that is in rotated key kid will still be verifiable if served by certs', async () => { servedKeys = [{ ...jwk.public, kid: 'rotated-key', use: 'sig', alg: 'RS256' }]; const token = await signedToken(claims(), 'rotated-key'); const identity = await VerifiedAccessIdentity.verify(token, env); assert.equal(identity?.email, 'lewis@cia.nel.pet');});
test('w/o team or aud configured should be closed', async () => { assert.equal(await VerifiedAccessIdentity.verify(await signedToken(claims()), {}), null); assert.equal(await VerifiedAccessIdentity.verify(null, env), null);});