Skip to content

Commit 890edc8

Browse files
authored
Merge pull request #157 from getAlby/feat/remove-browserify
feat: remove browserify, crypto-js, sourcemaps
2 parents 1294131 + 548130c commit 890edc8

18 files changed

+108
-57
lines changed

.vscode/settings.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"editor.formatOnSave": true,
3+
"editor.defaultFormatter": "esbenp.prettier-vscode"
4+
}

README.md

+27-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ This JavaScript SDK for the Alby OAuth2 Wallet API and the Nostr Wallet Connect
1010
npm install @getalby/sdk
1111
```
1212

13+
or
14+
15+
```
16+
yarn add @getalby/sdk
17+
```
18+
19+
or for use without any build tools:
20+
21+
```html
22+
<script type="module">
23+
import { webln } from "https://esm.sh/@getalby/[email protected]"; // jsdelivr.net, skypack.dev also work
24+
25+
// use webln normally...
26+
(async () => {
27+
const nwc = new webln.NostrWebLNProvider({
28+
nostrWalletConnectUrl: YOUR_NWC_URL,
29+
});
30+
await nwc.enable();
31+
const balanceResponse = await nwc.getBalance();
32+
console.log("Wallet balance", balanceResponse.balance);
33+
nwc.close();
34+
})();
35+
</script>
36+
```
37+
1338
**This library relies on a global fetch() function which will work in browsers and node v18.x or newer.** (In older versions you have to use a polyfill.)
1439

1540
## Content
@@ -127,6 +152,7 @@ catch (e) {
127152
```
128153
129154
#### React Native (Expo)
155+
130156
Look at our [NWC React Native Expo Demo app](https://github.com/getAlby/nwc-react-native-expo) for how to use NWC in a React Native expo project.
131157
132158
#### For Node.js
@@ -249,7 +275,7 @@ const authClient = new auth.OAuth2User({
249275
}, // initialize with existing token
250276
});
251277

252-
const authUrl = authClient.generateAuthURL({
278+
const authUrl = await authClient.generateAuthURL({
253279
code_challenge_method: "S256",
254280
// authorizeUrl: "https://getalby.com/oauth" endpoint for authorization (replace with the appropriate URL based on the environment)
255281
});

examples/boostagram.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const authClient = new auth.OAuth2User({
2525
});
2626

2727
console.log(`Open the following URL and authenticate the app:`);
28-
console.log(authClient.generateAuthURL());
28+
console.log(await authClient.generateAuthURL());
2929
console.log("----\n");
3030

3131
const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");

examples/decode-invoice.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const authClient = new auth.OAuth2User({
2020
});
2121

2222
console.log(`Open the following URL and authenticate the app:`);
23-
console.log(authClient.generateAuthURL());
23+
console.log(await authClient.generateAuthURL());
2424
console.log("----\n");
2525

2626
const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");

examples/invoices.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const authClient = new auth.OAuth2User({
1818
});
1919

2020
console.log(`Open the following URL and authenticate the app:`);
21-
console.log(authClient.generateAuthURL());
21+
console.log(await authClient.generateAuthURL());
2222
console.log("----\n");
2323

2424
const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");

examples/keysends.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const authClient = new auth.OAuth2User({
2525
});
2626

2727
console.log(`Open the following URL and authenticate the app:`);
28-
console.log(authClient.generateAuthURL());
28+
console.log(await authClient.generateAuthURL());
2929
console.log("----\n");
3030

3131
const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");

examples/nwc/get-balance.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import { webln as providers } from "../../dist/index.module.js";
99

1010
const rl = readline.createInterface({ input, output });
1111

12-
const nwcUrl = await rl.question(
13-
"Nostr Wallet Connect URL (nostr+walletconnect://...): ",
14-
);
12+
const nwcUrl =
13+
process.env.NWC_URL ||
14+
(await rl.question("Nostr Wallet Connect URL (nostr+walletconnect://...): "));
1515
rl.close();
1616

1717
const webln = new providers.NostrWebLNProvider({

examples/oauth2-public-callback_pkce_s256.mjs

+23-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
11
import { auth, Client } from "../dist/index.module.js";
22
import express from "express";
33

4+
if (!process.env.CLIENT_ID || !process.env.CLIENT_SECRET) {
5+
throw new Error("Please set CLIENT_ID and CLIENT_SECRET");
6+
}
7+
48
const app = express();
59

610
const authClient = new auth.OAuth2User({
711
client_id: process.env.CLIENT_ID,
812
client_secret: process.env.CLIENT_SECRET,
913
callback: "http://localhost:8080/callback",
10-
scopes: ["invoices:read", "account:read", "balance:read", "invoices:create", "invoices:read", "payments:send"],
11-
token: {access_token: undefined, refresh_token: undefined, expires_at: undefined} // initialize with existing token
14+
scopes: [
15+
"invoices:read",
16+
"account:read",
17+
"balance:read",
18+
"invoices:create",
19+
"invoices:read",
20+
"payments:send",
21+
],
22+
token: {
23+
access_token: undefined,
24+
refresh_token: undefined,
25+
expires_at: undefined,
26+
}, // initialize with existing token
1227
});
1328

1429
const client = new Client(authClient);
@@ -29,7 +44,7 @@ app.get("/callback", async function (req, res) {
2944
});
3045

3146
app.get("/login", async function (req, res) {
32-
const authUrl = authClient.generateAuthURL({
47+
const authUrl = await authClient.generateAuthURL({
3348
state: STATE,
3449
code_challenge_method: "S256",
3550
});
@@ -52,20 +67,20 @@ app.get("/value4value", async function (req, res) {
5267
});
5368

5469
app.get("/make-invoice", async function (req, res) {
55-
const result = await client.createInvoice({amount: 1000});
70+
const result = await client.createInvoice({ amount: 1000 });
5671
res.send(result);
5772
});
5873

59-
app.get("/bolt11/:invoice", async function(req, res) {
60-
const result = await client.sendPayment({invoice: req.params.invoice});
74+
app.get("/bolt11/:invoice", async function (req, res) {
75+
const result = await client.sendPayment({ invoice: req.params.invoice });
6176
res.send(result);
6277
});
6378

64-
app.get('/keysend/:destination', async function(req, res) {
79+
app.get("/keysend/:destination", async function (req, res) {
6580
const result = await client.keysend({
6681
destination: req.params.destination,
6782
amount: 10,
68-
memo: req.query.memo
83+
memo: req.query.memo,
6984
});
7085
res.send(result);
7186
});

examples/send-to-ln-address.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const authClient = new auth.OAuth2User({
1919
});
2020

2121
console.log(`Open the following URL and authenticate the app:`);
22-
console.log(authClient.generateAuthURL());
22+
console.log(await authClient.generateAuthURL());
2323
console.log("----\n");
2424

2525
const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");

examples/webhooks.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const authClient = new auth.OAuth2User({
2525
});
2626

2727
console.log(`Open the following URL and authenticate the app:`);
28-
console.log(authClient.generateAuthURL());
28+
console.log(await authClient.generateAuthURL());
2929
console.log("----\n");
3030

3131
const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");

package.json

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@getalby/sdk",
3-
"version": "2.7.0",
3+
"version": "3.0.0",
44
"description": "The SDK to integrate with Nostr Wallet Connect and the Alby API",
55
"repository": "https://github.com/getAlby/js-sdk.git",
66
"bugs": "https://github.com/getAlby/js-sdk/issues",
@@ -33,20 +33,17 @@
3333
"prepack": "yarn run build",
3434
"test": "jest",
3535
"clean": "rm -rf dist",
36-
"build:browser": "cp src/window.js dist && browserify dist/window.js > dist/index.browser.js",
37-
"build": "microbundle && yarn build:browser",
36+
"build": "microbundle --no-sourcemap",
3837
"dev": "microbundle watch",
3938
"prepare": "husky install"
4039
},
4140
"dependencies": {
42-
"crypto-js": "^4.1.1",
4341
"nostr-tools": "^1.17.0",
4442
"events": "^3.3.0"
4543
},
4644
"devDependencies": {
4745
"@commitlint/cli": "^17.7.1",
4846
"@commitlint/config-conventional": "^17.7.0",
49-
"@types/crypto-js": "^4.1.1",
5047
"@types/jest": "^29.5.5",
5148
"@types/node": "^20.8.6",
5249
"@typescript-eslint/eslint-plugin": "^6.3.0",

src/OAuth2User.ts

+28-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import CryptoJS from "crypto-js";
2-
import { buildQueryString, basicAuthHeader } from "./utils";
1+
import { buildQueryString, basicAuthHeader, toHexString } from "./utils";
32
import {
43
OAuthClient,
54
AuthHeader,
@@ -75,6 +74,7 @@ export class OAuth2User implements OAuthClient {
7574
if (this._refreshAccessTokenPromise) {
7675
return this._refreshAccessTokenPromise;
7776
}
77+
// eslint-disable-next-line no-async-promise-executor
7878
this._refreshAccessTokenPromise = new Promise(async (resolve, reject) => {
7979
try {
8080
const refresh_token = this.token?.refresh_token;
@@ -109,7 +109,7 @@ export class OAuth2User implements OAuthClient {
109109
resolve({ token });
110110
this._tokenEvents.emit("tokenRefreshed", this.token);
111111
} catch (error) {
112-
console.log(error);
112+
console.error(error);
113113
reject(error);
114114
this._tokenEvents.emit("tokenRefreshFailed", error);
115115
} finally {
@@ -140,7 +140,9 @@ export class OAuth2User implements OAuthClient {
140140
throw new Error("client_id is required");
141141
}
142142
if (!client_secret && !code_verifier) {
143-
throw new Error("either client_secret is required, or code should be generated using a challenge");
143+
throw new Error(
144+
"either client_secret is required, or code should be generated using a challenge",
145+
);
144146
}
145147
if (!callback) {
146148
throw new Error("callback is required");
@@ -171,23 +173,16 @@ export class OAuth2User implements OAuthClient {
171173
return { token };
172174
}
173175

174-
generateAuthURL(options?: GenerateAuthUrlOptions): string {
176+
async generateAuthURL(options?: GenerateAuthUrlOptions): Promise<string> {
175177
if (!options) {
176178
options = {};
177179
}
178-
console.log(options);
179180
const { client_id, callback, scopes } = this.options;
180181
if (!callback) throw new Error("callback required");
181182
if (!scopes) throw new Error("scopes required");
182183
let code_challenge_method;
183184
if (options.code_challenge_method === "S256") {
184-
const code_verifier = CryptoJS.lib.WordArray.random(64);
185-
this.code_verifier = code_verifier.toString();
186-
this.code_challenge = CryptoJS.SHA256(this.code_verifier)
187-
.toString(CryptoJS.enc.Base64)
188-
.replace(/\+/g, "-")
189-
.replace(/\//g, "_")
190-
.replace(/\=+$/, "");
185+
await this._generateS256Challenge();
191186
code_challenge_method = "S256";
192187
} else if (
193188
options.code_challenge_method === "plain" &&
@@ -220,4 +215,24 @@ export class OAuth2User implements OAuthClient {
220215
Authorization: `Bearer ${this.token.access_token}`,
221216
};
222217
}
218+
219+
private async _generateS256Challenge() {
220+
const codeVerifierBytes = crypto.getRandomValues(new Uint8Array(64));
221+
this.code_verifier = toHexString(codeVerifierBytes);
222+
223+
// from https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest
224+
const hashBuffer = await crypto.subtle.digest(
225+
"SHA-256",
226+
new TextEncoder().encode(this.code_verifier),
227+
);
228+
const hashArray = new Uint8Array(hashBuffer);
229+
230+
// from https://stackoverflow.com/a/45313868
231+
// TODO: consider using Buffer.from(hashBuffer).toString("base64") in NodeJS
232+
this.code_challenge = btoa(String.fromCharCode(...hashArray))
233+
// from https://gist.github.com/jhurliman/1250118?permalink_comment_id=3194799
234+
.replace(/\+/g, "-")
235+
.replace(/\//g, "_")
236+
.replace(/=+$/, "");
237+
}
223238
}

src/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export type GenerateAuthUrlOptions = { authorizeUrl?: string } & (
4747

4848
export abstract class OAuthClient implements AuthClient {
4949
abstract token?: Token;
50-
abstract generateAuthURL(options: GenerateAuthUrlOptions): string;
50+
abstract generateAuthURL(options: GenerateAuthUrlOptions): Promise<string>;
5151
abstract requestAccessToken(code?: string): Promise<{ token: Token }>;
5252
abstract getAuthHeader(
5353
url?: string,

src/utils.ts

+4
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,7 @@ export function basicAuthHeader(
1212
) {
1313
return `Basic ${btoa(`${client_id}:${client_secret}`)}`;
1414
}
15+
16+
// from https://stackoverflow.com/a/50868276
17+
export const toHexString = (bytes: Uint8Array) =>
18+
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");

src/webln/OauthWeblnProvider.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export class OauthWeblnProvider {
1313
client: Client;
1414
auth: OAuthClient;
1515
oauth: boolean;
16-
subscribers: Record<string, (payload: any) => void>;
16+
subscribers: Record<string, (payload: unknown) => void>;
1717
isExecuting: boolean;
1818

1919
constructor(options: { auth: OAuthClient }) {
@@ -28,7 +28,7 @@ export class OauthWeblnProvider {
2828
this.subscribers[name] = callback;
2929
}
3030

31-
notify(name: string, payload?: any) {
31+
notify(name: string, payload?: unknown) {
3232
const callback = this.subscribers[name];
3333
if (callback) {
3434
callback(payload);
@@ -45,7 +45,7 @@ export class OauthWeblnProvider {
4545
if (isBrowser()) {
4646
try {
4747
this.isExecuting = true;
48-
const result = await this.openAuthorization();
48+
await this.openAuthorization();
4949
} finally {
5050
this.isExecuting = false;
5151
}
@@ -123,12 +123,14 @@ export class OauthWeblnProvider {
123123
}
124124
}
125125

126-
openAuthorization() {
126+
async openAuthorization() {
127127
const height = 700;
128128
const width = 600;
129129
const top = window.outerHeight / 2 + window.screenY - height / 2;
130130
const left = window.outerWidth / 2 + window.screenX - width / 2;
131-
const url = this.auth.generateAuthURL({ code_challenge_method: "S256" });
131+
const url = await this.auth.generateAuthURL({
132+
code_challenge_method: "S256",
133+
});
132134

133135
return new Promise((resolve, reject) => {
134136
const popup = window.open(

src/window.js

-3
This file was deleted.

tsconfig.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
"skipLibCheck": true,
77
"checkJs": true,
88
"allowJs": true,
9-
"declarationMap": true,
9+
"declarationMap": false,
1010
"declaration": true,
1111
"allowSyntheticDefaultImports": true,
1212
"target": "es2020",
13+
"module": "ESNext",
1314
"rootDir": "./src",
14-
"sourceMap": true,
15+
"sourceMap": false,
1516
"moduleResolution": "node"
1617
},
1718
"include": ["src/**/*"],

0 commit comments

Comments
 (0)