import { BaseProvider } from '../base-provider.js'
/**
* AtprotoProvider — AT-Protocol (did:plc) authentication + binding.
*
* Binds a user's atproto identity (did:plc + secp256k1 signing key + handle) to
* the registry. The binding is seed-anchored: one BIP39 mnemonic yields both the
* keet Ed25519 identity (our P2P world) and the atproto secp256k1 key (bsky world)
* — see atproto-bridge/identity.
*
* Verification of did:plc control is delegated to an injected `verify` fn (mirrors
* how KeetIdentityProvider injects IdentityKey), so this package stays dep-light and
* the atproto crypto lives in one place. `verify(proof)` must return a verified
* binding `{ did, handle, didKey, ed25519PublicKey }` (or throw/return false).
*/
export class AtprotoProvider extends BaseProvider {
constructor (identityRegistry, opts = {}) {
super('atproto')
this.registry = identityRegistry
this._verify = opts.verify || null
}
async extractCredentials (req) {
const raw = req.headers['x-atproto-proof']
if (!raw) return null
try {
return { proof: JSON.parse(raw) }
} catch {
return null
}
}
async authenticate (credentials) {
if (!this._verify) throw new Error('atproto verifier not configured')
const { proof } = credentials
const binding = await this._verify(proof)
if (!binding || !binding.did) throw new Error('Invalid atproto proof')
const { did, handle, didKey, ed25519PublicKey } = binding
let identity = await this.registry.getIdentityByDid(did)
if (!identity) {
// Auto-register. identityId = the did:plc (the atproto identity's own ID);
// the ed25519 binding link + handle live in credentials.
await this.registry.registerIdentity({
identityId: did,
method: 'atproto',
credentials: { did, handle, didKey, ed25519PublicKey },
createdAt: Date.now()
})
identity = { identityId: did, method: 'atproto' }
}
return {
identityId: identity.identityId,
method: 'atproto',
credentials: { did, handle, didKey }
}
}
}
export default AtprotoProvider