Pass pubkey to nip 55 signer to avoid mixing up sessions
This commit is contained in:
+4
-79
@@ -18,7 +18,8 @@ import { Nip55Signer, getNip55 } from '@welshman/signer'
|
|||||||
// Check for available signing apps
|
// Check for available signing apps
|
||||||
const apps = await getNip55()
|
const apps = await getNip55()
|
||||||
if (apps.length > 0) {
|
if (apps.length > 0) {
|
||||||
const signer = new Nip55Signer(apps[0].packageName)
|
const optionalSavedPubkey = localStorage.getItem('my-saved-pubkey')
|
||||||
|
const signer = new Nip55Signer(apps[0].packageName, optionalSavedPubkey)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -40,28 +41,11 @@ interface AppInfo {
|
|||||||
### Constructor
|
### Constructor
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
constructor(packageName: string)
|
constructor(packageName: string, publicKey?: string)
|
||||||
```
|
```
|
||||||
Creates a new signer instance that will communicate with the specified native app.
|
Creates a new signer instance that will communicate with the specified native app.
|
||||||
- `packageName`: The package identifier of the native signing app
|
- `packageName`: The package identifier of the native signing app
|
||||||
|
- `publicKey`: optional user pubkey. Recommended for resuming existing signer sessions when the signer is managing multiple user accounts.
|
||||||
### ISigner implementation
|
|
||||||
|
|
||||||
The `Nip55Signer` class implements the [`ISigner`](/signer/) interface
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
class Nip55Signer implements ISigner {
|
|
||||||
// Constructor
|
|
||||||
constructor(private secret: string)
|
|
||||||
|
|
||||||
// ISigner implementation
|
|
||||||
sign: SignWithOptions
|
|
||||||
getPubkey: () => Promise<string>
|
|
||||||
nip04: { encrypt, decrypt }
|
|
||||||
nip44: { encrypt, decrypt }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Complete Example
|
## Complete Example
|
||||||
|
|
||||||
@@ -104,62 +88,3 @@ async function example() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Implementation Details
|
|
||||||
|
|
||||||
### Request Serialization
|
|
||||||
|
|
||||||
The signer implements a lock mechanism to prevent concurrent requests:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
class Nip55Signer implements ISigner {
|
|
||||||
#lock = Promise.resolve()
|
|
||||||
#plugin = NostrSignerPlugin
|
|
||||||
#packageName: string
|
|
||||||
#packageNameSet = false
|
|
||||||
|
|
||||||
#then = async <T>(f: (signer: typeof NostrSignerPlugin) => Promise<T>) => {
|
|
||||||
const promise = this.#lock.then(async () => {
|
|
||||||
if (!this.#packageNameSet) {
|
|
||||||
await this.#initialize()
|
|
||||||
}
|
|
||||||
return f(this.#plugin)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.#lock = promise.then(() => Promise.resolve())
|
|
||||||
|
|
||||||
return promise
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Public Key Caching
|
|
||||||
|
|
||||||
The signer caches the public key to minimize native app interactions:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
class Nip55Signer {
|
|
||||||
#npub?: string
|
|
||||||
#publicKey?: string
|
|
||||||
|
|
||||||
getPubkey = async (): Promise<string> => {
|
|
||||||
return this.#then(async signer => {
|
|
||||||
if (!this.#publicKey || !this.#npub) {
|
|
||||||
const {npub} = await signer.getPublicKey()
|
|
||||||
this.#npub = npub
|
|
||||||
const {data} = decode(npub)
|
|
||||||
this.#publicKey = data as string
|
|
||||||
}
|
|
||||||
return this.#publicKey
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Platform Support
|
|
||||||
|
|
||||||
- iOS: Requires compatible signing app
|
|
||||||
- Android: Requires compatible signing app
|
|
||||||
- Operations availability depends on native app implementation
|
|
||||||
- Some features might be platform-specific
|
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ export const getSigner = cached({
|
|||||||
getValue: ([session]: [Session | undefined]) => {
|
getValue: ([session]: [Session | undefined]) => {
|
||||||
if (isNip07Session(session)) return wrapSigner(new Nip07Signer())
|
if (isNip07Session(session)) return wrapSigner(new Nip07Signer())
|
||||||
if (isNip01Session(session)) return wrapSigner(new Nip01Signer(session.secret))
|
if (isNip01Session(session)) return wrapSigner(new Nip01Signer(session.secret))
|
||||||
if (isNip55Session(session)) return wrapSigner(new Nip55Signer(session.signer))
|
if (isNip55Session(session)) return wrapSigner(new Nip55Signer(session.signer, session.pubkey))
|
||||||
if (isNip46Session(session)) {
|
if (isNip46Session(session)) {
|
||||||
const {
|
const {
|
||||||
secret: clientSecret,
|
secret: clientSecret,
|
||||||
|
|||||||
@@ -622,7 +622,7 @@ export const sortBy = <T>(f: (x: T) => any, xs: T[]) =>
|
|||||||
* @param xs - Array to group
|
* @param xs - Array to group
|
||||||
* @returns Map of groups
|
* @returns Map of groups
|
||||||
*/
|
*/
|
||||||
export const groupBy = <T, K>(f: (x: T) => K, xs: T[]) => {
|
export const groupBy = <T, K>(f: (x: T) => K, xs: Iterable<T>) => {
|
||||||
const r = new Map<K, T[]>()
|
const r = new Map<K, T[]>()
|
||||||
|
|
||||||
for (const x of xs) {
|
for (const x of xs) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {NostrSignerPlugin, AppInfo} from "nostr-signer-capacitor-plugin"
|
import {NostrSignerPlugin, AppInfo} from "nostr-signer-capacitor-plugin"
|
||||||
import {decode} from "nostr-tools/nip19"
|
import * as nip19 from "nostr-tools/nip19"
|
||||||
import {SignedEvent, StampedEvent} from "@welshman/util"
|
import {SignedEvent, StampedEvent} from "@welshman/util"
|
||||||
import {hash, own, signWithOptions, SignOptions, ISigner} from "../util.js"
|
import {hash, own, signWithOptions, SignOptions, ISigner} from "../util.js"
|
||||||
|
|
||||||
@@ -16,8 +16,14 @@ export class Nip55Signer implements ISigner {
|
|||||||
#npub?: string
|
#npub?: string
|
||||||
#publicKey?: string
|
#publicKey?: string
|
||||||
|
|
||||||
constructor(packageName: string) {
|
constructor(packageName: string, publicKey?: string) {
|
||||||
this.#packageName = packageName
|
this.#packageName = packageName
|
||||||
|
|
||||||
|
if (publicKey) {
|
||||||
|
this.#publicKey = publicKey
|
||||||
|
this.#npub = nip19.npubEncode(publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
this.#initialize()
|
this.#initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,8 +59,9 @@ export class Nip55Signer implements ISigner {
|
|||||||
if (!this.#publicKey || !this.#npub) {
|
if (!this.#publicKey || !this.#npub) {
|
||||||
try {
|
try {
|
||||||
const {npub} = await signer.getPublicKey()
|
const {npub} = await signer.getPublicKey()
|
||||||
|
const {data} = nip19.decode(npub)
|
||||||
|
|
||||||
this.#npub = npub
|
this.#npub = npub
|
||||||
const {data} = decode(npub)
|
|
||||||
this.#publicKey = data as string
|
this.#publicKey = data as string
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error("Failed to get public key")
|
throw new Error("Failed to get public key")
|
||||||
|
|||||||
Reference in New Issue
Block a user