const assert = require('node:assert/strict'); const { createHmac } = require('node:crypto'); const { test } = require('node:test'); const { ApiCredentialUnavailableError, } = require('@qinglong/runtime-core/api-credential'); const { ClusterControlApiCredentialConfigurationError, ClusterControlApiCredentialUnavailableError, apiCredentialSecretDigest, createClusterControlApiCredentialAuthenticator, } = require('@qinglong/cluster-control/api-credential'); const NOW = 10_000; const PEPPER = Buffer.alloc(32, 1).toString('base64url'); const NEXT_PEPPER = Buffer.alloc(32, 3).toString('base64url'); const SECRET = Buffer.alloc(32, 2).toString('base64url'); const CREDENTIAL_ID = 'app_primary'; function metadata(authorization = `Bearer ql3c_${CREDENTIAL_ID}_${SECRET}`) { return { requestId: 'request-1', method: 'POST', path: '/api/v3/projects/default/runs', query: Object.freeze({}), headers: Object.freeze({ authorization }), signal: new AbortController().signal, }; } function credential(overrides = {}) { return { credentialId: CREDENTIAL_ID, version: 2, pepperKeyId: 'legacy-v1', state: 'active', subject: { type: 'api_app', id: 'app_primary' }, subjectStatus: 'active', secretDigest: apiCredentialSecretDigest(PEPPER, CREDENTIAL_ID, SECRET), createdAtMs: 1, notBeforeAtMs: 1, expiresAtMs: 100_000, ...overrides, }; } function authenticator(value = credential(), overrides = {}) { return createClusterControlApiCredentialAuthenticator( { async resolve(credentialId) { assert.equal(credentialId, CREDENTIAL_ID); return value; }, }, PEPPER, { now: () => NOW, ...overrides }, ); } test('authenticates a high-entropy service bearer as a short-lived principal', async () => { const principal = await authenticator().authenticate(metadata()); assert.deepEqual(principal, { subject: { type: 'api_app', id: 'app_primary' }, authenticationId: 'api_credential:app_primary:2', authenticatedAtMs: NOW, expiresAtMs: NOW + 60_000, assurance: 'service', }); assert.equal(Object.isFrozen(principal), true); assert.equal(JSON.stringify(principal).includes(SECRET), false); }); test('derives a domain-separated HMAC digest and user assurance', async () => { const expected = createHmac('sha256', Buffer.from(PEPPER, 'base64url')) .update(Buffer.from('qinglong-api-credential-v1\0', 'utf8')) .update(CREDENTIAL_ID, 'utf8') .update('\0', 'utf8') .update(Buffer.from(SECRET, 'base64url')) .digest('hex'); assert.equal( apiCredentialSecretDigest(PEPPER, CREDENTIAL_ID, SECRET), expected, ); const principal = await authenticator( credential({ subject: { type: 'user', id: 'usr_primary' } }), ).authenticate(metadata()); assert.equal(principal.assurance, 'single_factor'); }); test('authenticates overlap generations by exact stored key id without fallback', async () => { const keyring = { schemaVersion: 1, activePepperKeyId: 'rotation-2026-08', keys: [ { pepperKeyId: 'legacy-v1', pepper: PEPPER }, { pepperKeyId: 'rotation-2026-08', pepper: NEXT_PEPPER }, ], }; const records = new Map([ [ 'legacy', credential({ credentialId: 'legacy', secretDigest: apiCredentialSecretDigest(PEPPER, 'legacy', SECRET), }), ], [ 'next', credential({ credentialId: 'next', pepperKeyId: 'rotation-2026-08', secretDigest: apiCredentialSecretDigest( NEXT_PEPPER, 'next', SECRET, ), }), ], ['unknown', credential({ credentialId: 'unknown', pepperKeyId: 'missing' })], ]); const verifier = createClusterControlApiCredentialAuthenticator( { async resolve(credentialId) { return records.get(credentialId) ?? null; } }, keyring, { now: () => NOW }, ); const request = (credentialId) => metadata(`Bearer ql3c_${credentialId}_${SECRET}`); assert.equal((await verifier.authenticate(request('legacy'))).subject.id, 'app_primary'); assert.equal((await verifier.authenticate(request('next'))).subject.id, 'app_primary'); await assert.rejects( verifier.authenticate(request('unknown')), ClusterControlApiCredentialUnavailableError, ); }); test('rejects missing, malformed, wrong, inactive and disabled credentials', async () => { let repositoryCalls = 0; const strict = createClusterControlApiCredentialAuthenticator( { async resolve() { repositoryCalls += 1; return credential(); }, }, PEPPER, { now: () => NOW }, ); for (const header of [ undefined, 'bearer token', `Bearer ql3c_${CREDENTIAL_ID}_short`, `Bearer ql3c_${CREDENTIAL_ID}_${Buffer.alloc(32, 3).toString('base64url')}`, ]) { const request = metadata(); request.headers = Object.freeze( header === undefined ? {} : { authorization: header }, ); assert.equal(await strict.authenticate(request), null); } assert.equal( repositoryCalls, 1, 'only a structurally valid token reaches SQL', ); for (const value of [ credential({ state: 'revoked' }), credential({ subjectStatus: 'disabled' }), credential({ notBeforeAtMs: NOW + 1 }), credential({ expiresAtMs: NOW }), null, ]) { assert.equal(await authenticator(value).authenticate(metadata()), null); } }); test('maps storage, corrupt record and cancellation failures to unavailable', async () => { const unavailable = createClusterControlApiCredentialAuthenticator( { async resolve() { throw new ApiCredentialUnavailableError(); }, }, PEPPER, { now: () => NOW }, ); await assert.rejects( unavailable.authenticate(metadata()), ClusterControlApiCredentialUnavailableError, ); await assert.rejects( authenticator(credential({ secretDigest: 'corrupt' })).authenticate( metadata(), ), ClusterControlApiCredentialUnavailableError, ); await assert.rejects( authenticator(credential({ pepperKeyId: 'other-v1' })).authenticate( metadata(), ), ClusterControlApiCredentialUnavailableError, ); const controller = new AbortController(); controller.abort(); await assert.rejects( authenticator().authenticate({ ...metadata(), signal: controller.signal }), ClusterControlApiCredentialUnavailableError, ); }); test('rejects weak pepper and unbounded principal lifetime at construction', () => { const repository = { async resolve() {} }; assert.throws( () => createClusterControlApiCredentialAuthenticator(repository, 'weak'), ClusterControlApiCredentialConfigurationError, ); assert.throws( () => createClusterControlApiCredentialAuthenticator(repository, PEPPER, { principalTtlMs: 300_001, }), ClusterControlApiCredentialConfigurationError, ); });