Skip to content

Commit

Permalink
Generate secret locally (#4)
Browse files Browse the repository at this point in the history
* Add option to init NostrWeblnProvider with newly, locally created secret

this allows apps to generate the secret and pass the public key to  NWC

* Correct pubkey attribute

* cleanup

* v1.4.3

* Add initNWC function to initiate the pairing.

cont nwc = NostrWebLNProvider.withNewSecret();

nwc.initNWC().then(() => {
  // nwc is ready
});

* Correct Alby NWC URL

* Remove message event listener once the success message is processed

* Add support for custom NWC URLs

* Reject the initNWC promise if the popup is closed

* chore: update nwc default relay

* doc: add example of generating new NWC connect url

* chore: fix ts warning

* doc: update nwc withNewSecret example

Co-authored-by: Michael Bumann <[email protected]>

* doc: update README NWC example documentation

* chore: make options parameter optional

---------

Co-authored-by: Roland Bewick <[email protected]>
Co-authored-by: Roland <[email protected]>
  • Loading branch information
3 people authored Apr 18, 2023
1 parent bc49e2f commit a77123d
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 10 deletions.
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ The `NostrWebLNProvider` exposes the [WebLN](webln.guide/) sendPayment interface

(note: in the future more WebLN functions will be added to Nostr Wallet Connect)


### NostrWebLNProvider Options

* `nostrWalletConnectUrl`: the full Nostr Wallet Connect URL as defined by the [spec](https://github.com/getAlby/nips/blob/master/47.md)
Expand Down Expand Up @@ -53,7 +52,7 @@ global.crypto = crypto;
```js
import { NostrWebLNProvider } from 'alby-js-sdk';

const webln = new NostrWebLNProvider(); // use defaults (will use window.nostr to sign the request)
const webln = new NostrWebLNProvider(); // use defaults (connects to Alby's relay, will use window.nostr to sign the request)
await webln.enable(); // connect to the relay
const response = await webln.sendPayment(invoice);
console.log(response.preimage);
Expand All @@ -73,6 +72,21 @@ console.log(response.preimage);
webln.close(); // close the websocket connection
```
#### Generate a new NWC connect url using a locally-generated secret
```js
// same options can be provided to .withNewSecret() as creating a new NostrWebLNProvider()
const webln = webln.NostrWebLNProvider.withNewSecret();
await webln.initNWC("alby", {
name: `My app name`,
});

// ... enable and send a payment

// if you want to get the connect url with the secret:
// const nostrWalletConnectUrl nwc.getNostrWalletConnectUrl(true)

```
## OAuth API Documentation
Please have a look a the Alby OAuth2 Wallet API:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "alby-js-sdk",
"version": "1.4.2",
"version": "1.4.3",
"description": "Alby OAuth2 Client",
"type": "module",
"source": "src/index.ts",
Expand Down
86 changes: 79 additions & 7 deletions src/webln/NostrWeblnProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,40 @@ import {
relayInit,
signEvent,
getEventHash,
getPublicKey,
nip19,
generatePrivateKey,
getPublicKey,
Relay,
Event,
UnsignedEvent
} from 'nostr-tools';

const DEFAULT_OPTIONS = {
relayUrl: 'wss://relay.damus.io',
relayUrl: "wss://relay.getalby.com/v1",
walletPubkey: '69effe7b49a6dd5cf525bd0905917a5005ffe480b58eeb8e861418cf3ae760d9' // Alby
};

const NWC_URLS = {
alby: "https://nwc.getalby.com/apps/new"
};

interface Nostr {
signEvent: (event: UnsignedEvent) => Promise<Event>;
nip04: {
decrypt: (pubkey: string, content: string) => Promise<string>;
encrypt: (pubkey: string, content: string) => Promise<string>;
};
}
};

declare global {
var nostr: Nostr | undefined;
}
};

interface NostrWebLNOptions {
relayUrl: string;
walletPubkey: string;
secret?: string;
}
};


export class NostrWebLNProvider {
Expand All @@ -55,13 +61,20 @@ export class NostrWebLNProvider {
}
return options;
}
constructor(options: { relayUrl?: string, secret?: string, walletPubkey?: string, nostrWalletConnectUrl?: string }) {

static withNewSecret(options?: ConstructorParameters<typeof NostrWebLNProvider>[0]) {
options = options || {};
options.secret = generatePrivateKey();
return new NostrWebLNProvider(options);
}

constructor(options?: { relayUrl?: string, secret?: string, walletPubkey?: string, nostrWalletConnectUrl?: string }) {
if (options && options.nostrWalletConnectUrl) {
options = {
...NostrWebLNProvider.parseWalletConnectUrl(options.nostrWalletConnectUrl), ...options
};
}
const _options = { ...DEFAULT_OPTIONS, ...options } as NostrWebLNOptions;
const _options = { ...DEFAULT_OPTIONS, ...(options || {}) } as NostrWebLNOptions;
this.relayUrl = _options.relayUrl;
this.relay = relayInit(this.relayUrl);
if (_options.secret) {
Expand All @@ -82,6 +95,18 @@ export class NostrWebLNProvider {
}
}

getNostrWalletConnectUrl(includeSecret = false) {
let url = `nostrwalletconnect://${this.walletPubkey}?relay=${this.relayUrl}&pubkey=${this.publicKey}`;
if (includeSecret) {
url = `${url}&secret=${this.secret}`;
}
return url;
}

get nostrWalletConnectUrl() {
return this.getNostrWalletConnectUrl();
}

get connected() {
return this.relay.status === 1;
}
Expand Down Expand Up @@ -213,6 +238,53 @@ export class NostrWebLNProvider {
});
});
}

initNWC(providerNameOrUrl: string, options: { name: string, returnTo?: string }) {
const height = 600;
const width = 400;
const top = window.outerHeight / 2 + window.screenY - height / 2;
const left = window.outerWidth / 2 + window.screenX - width / 2;

const urlStr = NWC_URLS[providerNameOrUrl as keyof typeof NWC_URLS] || providerNameOrUrl;
const url = new URL(urlStr);
url.searchParams.set('c', options.name);
url.searchParams.set('pubkey', this.publicKey);
url.searchParams.set('url', document.location.origin);
if (options.returnTo) {
url.searchParams.set('returnTo', options.returnTo);
}
return new Promise((resolve, reject) => {
const popup = window.open(
url.toString(),
`${document.title} - Wallet Connect`,
`height=${height},width=${width},top=${top},left=${left}`
);
if (!popup) { reject(); return; } // only for TS?

const checkForPopup = () => {
if (popup && popup.closed) {
reject();
clearInterval(popupChecker);
window.removeEventListener('message', onMessage);
}
};

const onMessage = (message: { data: any, origin: string }) => {
const data = message.data;
if (data && data.type === 'nwc:success' && message.origin === `${url.protocol}//${url.host}`) {
resolve(data);
clearInterval(popupChecker);
window.removeEventListener('message', onMessage);
if (popup) {
popup.close(); // close the popup
}
}
};
const popupChecker = setInterval(checkForPopup, 500);
window.addEventListener('message', onMessage);
});
}

private checkConnected() {
if (!this.connected) {
throw new Error("please call enable() and await the promise before calling this function")
Expand Down

0 comments on commit a77123d

Please sign in to comment.