/**
* NoiseKeyProvider
*
* Authenticates P2P connections by their Noise protocol public key.
* The Noise key is verified at the transport layer (Hyperswarm),
* so by the time we see it, it's already authenticated — we just
* need to look up the User it maps to.
*
* This provider is NOT used for HTTP requests (no extractCredentials).
* Instead, it's called directly by the P2P admin handler.
*
* All reads/writes go through IdentityRegistry methods (Autobase-backed).
*/
export class NoiseKeyProvider {
constructor (registry) {
this.registry = registry
this.name = 'noise-key'
}
/**
* Authenticate by Noise public key (hex string)
* Called directly by P2P handlers, not via HTTP middleware.
*
* @param {string} publicKeyHex - Noise public key as hex
* @returns {Promise<Object|null>} Auth result or null
*/
async authenticateByKey (publicKeyHex) {
const identity = await this.registry.getIdentityByNoiseKey(publicKeyHex)
if (!identity) return null
// Check if identity is revoked
if (identity.revoked) return null
const user = await this.registry.getUser(identity.userId)
if (!user) return null
return {
userId: user.userId,
user,
identityId: identity.identityId,
method: 'noise-key',
verified: true
}
}
/**
* Register a Noise key for a user
*
* @param {string} publicKeyHex - Noise public key as hex
* @param {string} userId - User ID to link to
* @returns {Promise<Object>} Created identity
*/
async register (publicKeyHex, userId) {
// Check for collision — key already linked to a different user
const existing = await this.registry.getIdentityByNoiseKey(publicKeyHex)
if (existing) {
if (existing.userId && existing.userId !== userId) {
throw new Error('Noise key is already linked to a different user')
}
// Already linked to this user — return existing
if (existing.userId === userId) {
return existing
}
}
const identityId = `noise:${publicKeyHex}`
// Create identity via registry (goes through Autobase)
const identity = await this.registry.createIdentity({
identityId,
method: 'noise-key',
userId,
credentials: { publicKey: publicKeyHex }
})
// Link to user (adds to user.identities array)
await this.registry.linkIdentity(identityId, userId)
console.log(`[NoiseKey] Registered ${publicKeyHex.slice(0, 16)}... for user ${userId}`)
return identity
}
/**
* Unregister a Noise key
*
* @param {string} publicKeyHex - Noise public key as hex
* @returns {Promise<boolean>} True if removed
*/
async unregister (publicKeyHex) {
const identity = await this.registry.getIdentityByNoiseKey(publicKeyHex)
if (!identity) return false
// Unlink from user and revoke
try {
await this.registry.unlinkIdentity(identity.identityId)
} catch {
// May already be unlinked
}
try {
await this.registry.revokeIdentity(identity.identityId, {
reason: 'Noise key unregistered',
revokedBy: 'system'
})
} catch {
// May already be revoked
}
console.log(`[NoiseKey] Unregistered ${publicKeyHex.slice(0, 16)}...`)
return true
}
}