// hpp delegate — the running device signs a delegation authorizing a browser sub-key for DID web
// sign-in, and favela's keyless web-auth verify accepts a login proof built from it. Proves the full
// atproto-native auth loop server-side (no browser / no PLC network). Run: bare test/delegate.test.js
import '../lib/bare-globals.js'
import test from 'brittle'
import { deriveAtprotoKey, keyFromPrivate } from 'atproto-identity/lib/atproto-identity.js'
import { signAssertion } from 'atproto-identity/lib/auth.js'
import { makeAtprotoWebVerify } from 'atproto-identity/lib/web-auth.js'
import { SwarmDevice } from '../lib/swarm-device.js'
import b4a from 'b4a'
// The browser uses generateAtprotoKey() (webcrypto) in prod; under Bare there is no
// crypto.getRandomValues, so tests use deterministic keyFromPrivate(fixed bytes).
const subKeyFrom = (h) => keyFromPrivate(b4a.from(h.repeat(32), 'hex'))
// delegate() only needs this.did + this.privateKey — a stub KV substrate is enough (no StateBase).
const stubSb = { set: async () => {}, del: async () => {}, get: async () => null, list: async () => ({}) }
const hex = (u8) => Array.from(u8).map((b) => b.toString(16).padStart(2, '0')).join('')
const nowSec = () => Math.floor(Date.now() / 1000)
test('hpp delegate → browser sign → favela verify (full DID web-auth loop)', async (t) => {
const key = deriveAtprotoKey('legal winner thank year wave sausage worth useful legal winner thank yellow') // the DID's #atproto key
const DID = 'did:plc:hppdelegate2222222222222'
const device = new SwarmDevice(stubSb, { did: DID, privateKey: key.privateKey, self: 'device-a' })
const sub = subKeyFrom('11') // the browser sub-key
const subHex = hex(sub.publicKey)
// 1. the hpp mints the delegation (signs with the shared #atproto key)
const { delegation, did, subKey, exp } = await device.delegate({ subKey: subHex })
t.is(did, DID, 'delegation is for this DID')
t.is(subKey, subHex, 'delegation names the sub-key')
t.ok(exp > nowSec(), 'delegation carries a future expiry')
// 2. the browser signs a login challenge with the sub-key; favela verifies the full proof
// (mock PLC returns the DID's #atproto pubkey, as the directory would).
const verify = makeAtprotoWebVerify({ resolveVerifyKey: async (d) => (d === DID ? key.publicKey : null) })
const challenge = 'c'.repeat(64)
const sig = signAssertion({ challenge, iat: nowSec(), exp: nowSec() + 300 }, sub.privateKey)
const binding = await verify({ did: DID, subKey: subHex, challenge, sig, delegation })
t.ok(binding && binding.did === DID && binding.subKey === subHex, 'favela accepts the hpp-delegated sub-key login')
// an UNDELEGATED sub-key still cannot log in (delegation is bound to the real sub-key)
const rogue = subKeyFrom('22')
const rogueSig = signAssertion({ challenge, exp: nowSec() + 300 }, rogue.privateKey)
t.absent(await verify({ did: DID, subKey: hex(rogue.publicKey), challenge, sig: rogueSig, delegation }), 'a sub-key the hpp did NOT delegate is rejected')
// input validation
await t.exception(async () => { await device.delegate({ subKey: 'nothex' }) }, /subKey/, 'bad subKey rejected')
await t.exception(async () => { await device.delegate({}) }, /subKey/, 'missing subKey rejected')
})