companion-token-custody-integration.test.mjs
54 lines 3.2 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * Tier 2 — INTEGRATION: custody composed with the PKCE core across the acquire → use → refresh →
3 * logout lifecycle, over an async keychain adapter (the realistic shape).
4 */
5 import { describe, it } from 'node:test';
6 import assert from 'node:assert/strict';
7 import { validateTokenResponse } from '../lib/companion-oauth-pkce.mjs';
8 import { buildSessionMeta, createTokenCustody } from '../lib/companion-token-custody.mjs';
9 import { makeAsyncKeychain } from './helpers/companion-keychain-fake.mjs';
10
11 describe('Integration — token response → custody → refresh decision', () => {
12 it('stores a validated token response and decides correctly over time', async () => {
13 const custody = createTokenCustody(makeAsyncKeychain());
14 const tr = validateTokenResponse({ access_token: 'jwt', token_type: 'Bearer', expires_in: 3600, refresh_token: 'r', scope: 'vault:read vault:write' });
15 assert.equal(tr.ok, true);
16
17 const now = 1_000_000;
18 const meta = buildSessionMeta(tr, { now, refreshTtlMs: 86_400_000, issuer: 'https://knowtation.store' });
19 await custody.storeSession({ accessToken: tr.accessToken, refreshToken: tr.refreshToken, meta });
20
21 assert.equal(await custody.decide({ now, skewMs: 30_000 }), 'valid');
22 assert.equal(await custody.decide({ now: meta.expiresAt - 10, skewMs: 30_000 }), 'refresh');
23 assert.equal(await custody.decide({ now: meta.refreshExpiresAt + 1 }), 'reauth');
24 });
25 });
26
27 describe('Integration — refresh rotation lifecycle', () => {
28 it('a refreshed token response rotates the stored secrets', async () => {
29 const custody = createTokenCustody(makeAsyncKeychain());
30 const first = validateTokenResponse({ access_token: 'jwt1', token_type: 'Bearer', expires_in: 60, refresh_token: 'r1' });
31 await custody.storeSession({ accessToken: first.accessToken, refreshToken: first.refreshToken, meta: buildSessionMeta(first, { now: 0, refreshTtlMs: 1_000_000 }) });
32
33 const refreshed = validateTokenResponse({ access_token: 'jwt2', token_type: 'Bearer', expires_in: 60, refresh_token: 'r2' });
34 await custody.updateAccessToken({ accessToken: refreshed.accessToken, refreshToken: refreshed.refreshToken, meta: buildSessionMeta(refreshed, { now: 1000, refreshTtlMs: 1_000_000 }) });
35
36 const loaded = await custody.loadSession();
37 assert.equal(loaded.accessToken, 'jwt2');
38 assert.equal(loaded.refreshToken, 'r2');
39 });
40 });
41
42 describe('Integration — invalid_grant on refresh forces a full logout', () => {
43 it('an invalid_grant token response leads the caller to clearSession → reauth', async () => {
44 const custody = createTokenCustody(makeAsyncKeychain());
45 const first = validateTokenResponse({ access_token: 'jwt1', token_type: 'Bearer', expires_in: 60, refresh_token: 'r1' });
46 await custody.storeSession({ accessToken: first.accessToken, refreshToken: first.refreshToken, meta: buildSessionMeta(first, { now: 0, refreshTtlMs: 1_000_000 }) });
47
48 const refreshAttempt = validateTokenResponse({ error: 'invalid_grant' });
49 assert.equal(refreshAttempt.ok, false);
50 // Caller policy on refresh failure: clear and re-auth.
51 await custody.clearSession();
52 assert.equal(await custody.decide({ now: 0 }), 'reauth');
53 });
54 });
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago