Support CAT token in bunker URLs for NIP-46 connections (v0.2.2)

- Add 'bunker' to SignerType with isRemote getter and displayName
- Parse CAT token from bunker URL (?cat= parameter)
- Pass CAT token to BunkerSigner constructor
- Store bunkerCatToken in account for reconnection
- Add deploy command documentation

Files modified:
- src/domain/identity/SignerType.ts: Add bunker signer type
- src/providers/NostrProvider/bunker.signer.ts: Parse and use CAT tokens
- src/providers/NostrProvider/index.tsx: Pass CAT to login/reconnect
- src/types/index.d.ts: Add bunkerCatToken to TAccount

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
woikos
2025-12-29 13:02:34 +01:00
parent 12e02dd05b
commit cdfd034c68
7 changed files with 372 additions and 20 deletions

View File

@@ -59,12 +59,13 @@ function generateRequestId(): string {
}
/**
* Parse a bunker URL (bunker://<pubkey>?relay=<url>&secret=<secret>).
* Parse a bunker URL (bunker://<pubkey>?relay=<url>&secret=<secret>&cat=<token>).
*/
export function parseBunkerUrl(url: string): {
pubkey: string
relays: string[]
secret?: string
catToken?: string
} {
if (!url.startsWith('bunker://')) {
throw new Error('Invalid bunker URL: must start with bunker://')
@@ -80,6 +81,7 @@ export function parseBunkerUrl(url: string): {
const params = new URLSearchParams(queryPart || '')
const relays = params.getAll('relay')
const secret = params.get('secret') || undefined
const catToken = params.get('cat') || undefined
if (relays.length === 0) {
throw new Error('Invalid bunker URL: no relay specified')
@@ -88,7 +90,8 @@ export function parseBunkerUrl(url: string): {
return {
pubkey: pubkeyPart,
relays,
secret
secret,
catToken
}
}
@@ -175,19 +178,28 @@ export class BunkerSigner implements ISigner {
// Whether we're waiting for signer to connect (reverse flow)
private awaitingConnection = false
private connectionResolve: ((pubkey: string) => void) | null = null
private connectionReject: ((error: Error) => void) | null = null
/**
* Create a BunkerSigner.
* @param bunkerPubkey - The bunker's public key (hex)
* @param relayUrls - Relay URLs to connect to
* @param connectionSecret - Optional connection secret for initial handshake
* @param catToken - Optional CAT token (encoded string) for authorization
*/
constructor(bunkerPubkey: string, relayUrls: string[], connectionSecret?: string) {
constructor(bunkerPubkey: string, relayUrls: string[], connectionSecret?: string, catToken?: string) {
this.bunkerPubkey = bunkerPubkey
this.relayUrls = relayUrls
this.connectionSecret = connectionSecret
// Decode CAT token if provided
if (catToken) {
try {
this.token = cashuTokenService.decodeToken(catToken)
} catch (err) {
console.warn('Failed to decode CAT token from URL:', err)
}
}
// Generate local ephemeral keypair for NIP-46 communication
this.localPrivkey = secp256k1.utils.randomPrivateKey()
this.localPubkey = nGetPublicKey(this.localPrivkey)
@@ -230,8 +242,6 @@ export class BunkerSigner implements ISigner {
signer.awaitingConnection = false
resolve(signer)
}
signer.connectionReject = reject
// Set timeout
setTimeout(() => {
if (signer.awaitingConnection) {
@@ -407,7 +417,7 @@ export class BunkerSigner implements ISigner {
return
}
const mintInfo = await infoResponse.json()
await infoResponse.json() // Validate JSON response
console.log(`Relay ${relayUrl} requires Cashu token, acquiring...`)
// Configure the mint