Skip to content

Commit

Permalink
Merge pull request #157 from getAlby/feat/remove-browserify
Browse files Browse the repository at this point in the history
feat: remove browserify, crypto-js, sourcemaps
  • Loading branch information
rolznz authored Dec 7, 2023
2 parents 1294131 + 548130c commit 890edc8
Show file tree
Hide file tree
Showing 18 changed files with 108 additions and 57 deletions.
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,31 @@ This JavaScript SDK for the Alby OAuth2 Wallet API and the Nostr Wallet Connect
npm install @getalby/sdk
```

or

```
yarn add @getalby/sdk
```

or for use without any build tools:

```html
<script type="module">
import { webln } from "https://esm.sh/@getalby/js-sdk@3.0.0"; // jsdelivr.net, skypack.dev also work
// use webln normally...
(async () => {
const nwc = new webln.NostrWebLNProvider({
nostrWalletConnectUrl: YOUR_NWC_URL,
});
await nwc.enable();
const balanceResponse = await nwc.getBalance();
console.log("Wallet balance", balanceResponse.balance);
nwc.close();
})();
</script>
```

**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.)

## Content
Expand Down Expand Up @@ -127,6 +152,7 @@ catch (e) {
```
#### React Native (Expo)
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.
#### For Node.js
Expand Down Expand Up @@ -249,7 +275,7 @@ const authClient = new auth.OAuth2User({
}, // initialize with existing token
});

const authUrl = authClient.generateAuthURL({
const authUrl = await authClient.generateAuthURL({
code_challenge_method: "S256",
// authorizeUrl: "https://getalby.com/oauth" endpoint for authorization (replace with the appropriate URL based on the environment)
});
Expand Down
2 changes: 1 addition & 1 deletion examples/boostagram.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const authClient = new auth.OAuth2User({
});

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

const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");
Expand Down
2 changes: 1 addition & 1 deletion examples/decode-invoice.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const authClient = new auth.OAuth2User({
});

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

const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");
Expand Down
2 changes: 1 addition & 1 deletion examples/invoices.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const authClient = new auth.OAuth2User({
});

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

const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");
Expand Down
2 changes: 1 addition & 1 deletion examples/keysends.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const authClient = new auth.OAuth2User({
});

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

const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");
Expand Down
6 changes: 3 additions & 3 deletions examples/nwc/get-balance.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { webln as providers } from "../../dist/index.module.js";

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

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

const webln = new providers.NostrWebLNProvider({
Expand Down
31 changes: 23 additions & 8 deletions examples/oauth2-public-callback_pkce_s256.mjs
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import { auth, Client } from "../dist/index.module.js";
import express from "express";

if (!process.env.CLIENT_ID || !process.env.CLIENT_SECRET) {
throw new Error("Please set CLIENT_ID and CLIENT_SECRET");
}

const app = express();

const authClient = new auth.OAuth2User({
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
callback: "http://localhost:8080/callback",
scopes: ["invoices:read", "account:read", "balance:read", "invoices:create", "invoices:read", "payments:send"],
token: {access_token: undefined, refresh_token: undefined, expires_at: undefined} // initialize with existing token
scopes: [
"invoices:read",
"account:read",
"balance:read",
"invoices:create",
"invoices:read",
"payments:send",
],
token: {
access_token: undefined,
refresh_token: undefined,
expires_at: undefined,
}, // initialize with existing token
});

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

app.get("/login", async function (req, res) {
const authUrl = authClient.generateAuthURL({
const authUrl = await authClient.generateAuthURL({
state: STATE,
code_challenge_method: "S256",
});
Expand All @@ -52,20 +67,20 @@ app.get("/value4value", async function (req, res) {
});

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

app.get("/bolt11/:invoice", async function(req, res) {
const result = await client.sendPayment({invoice: req.params.invoice});
app.get("/bolt11/:invoice", async function (req, res) {
const result = await client.sendPayment({ invoice: req.params.invoice });
res.send(result);
});

app.get('/keysend/:destination', async function(req, res) {
app.get("/keysend/:destination", async function (req, res) {
const result = await client.keysend({
destination: req.params.destination,
amount: 10,
memo: req.query.memo
memo: req.query.memo,
});
res.send(result);
});
Expand Down
2 changes: 1 addition & 1 deletion examples/send-to-ln-address.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const authClient = new auth.OAuth2User({
});

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

const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");
Expand Down
2 changes: 1 addition & 1 deletion examples/webhooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const authClient = new auth.OAuth2User({
});

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

const code = await rl.question("Code: (localhost:8080?code=[THIS CODE]: ");
Expand Down
7 changes: 2 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getalby/sdk",
"version": "2.7.0",
"version": "3.0.0",
"description": "The SDK to integrate with Nostr Wallet Connect and the Alby API",
"repository": "https://github.com/getAlby/js-sdk.git",
"bugs": "https://github.com/getAlby/js-sdk/issues",
Expand Down Expand Up @@ -33,20 +33,17 @@
"prepack": "yarn run build",
"test": "jest",
"clean": "rm -rf dist",
"build:browser": "cp src/window.js dist && browserify dist/window.js > dist/index.browser.js",
"build": "microbundle && yarn build:browser",
"build": "microbundle --no-sourcemap",
"dev": "microbundle watch",
"prepare": "husky install"
},
"dependencies": {
"crypto-js": "^4.1.1",
"nostr-tools": "^1.17.0",
"events": "^3.3.0"
},
"devDependencies": {
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@types/crypto-js": "^4.1.1",
"@types/jest": "^29.5.5",
"@types/node": "^20.8.6",
"@typescript-eslint/eslint-plugin": "^6.3.0",
Expand Down
41 changes: 28 additions & 13 deletions src/OAuth2User.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import CryptoJS from "crypto-js";
import { buildQueryString, basicAuthHeader } from "./utils";
import { buildQueryString, basicAuthHeader, toHexString } from "./utils";
import {
OAuthClient,
AuthHeader,
Expand Down Expand Up @@ -75,6 +74,7 @@ export class OAuth2User implements OAuthClient {
if (this._refreshAccessTokenPromise) {
return this._refreshAccessTokenPromise;
}
// eslint-disable-next-line no-async-promise-executor
this._refreshAccessTokenPromise = new Promise(async (resolve, reject) => {
try {
const refresh_token = this.token?.refresh_token;
Expand Down Expand Up @@ -109,7 +109,7 @@ export class OAuth2User implements OAuthClient {
resolve({ token });
this._tokenEvents.emit("tokenRefreshed", this.token);
} catch (error) {
console.log(error);
console.error(error);
reject(error);
this._tokenEvents.emit("tokenRefreshFailed", error);
} finally {
Expand Down Expand Up @@ -140,7 +140,9 @@ export class OAuth2User implements OAuthClient {
throw new Error("client_id is required");
}
if (!client_secret && !code_verifier) {
throw new Error("either client_secret is required, or code should be generated using a challenge");
throw new Error(
"either client_secret is required, or code should be generated using a challenge",
);
}
if (!callback) {
throw new Error("callback is required");
Expand Down Expand Up @@ -171,23 +173,16 @@ export class OAuth2User implements OAuthClient {
return { token };
}

generateAuthURL(options?: GenerateAuthUrlOptions): string {
async generateAuthURL(options?: GenerateAuthUrlOptions): Promise<string> {
if (!options) {
options = {};
}
console.log(options);
const { client_id, callback, scopes } = this.options;
if (!callback) throw new Error("callback required");
if (!scopes) throw new Error("scopes required");
let code_challenge_method;
if (options.code_challenge_method === "S256") {
const code_verifier = CryptoJS.lib.WordArray.random(64);
this.code_verifier = code_verifier.toString();
this.code_challenge = CryptoJS.SHA256(this.code_verifier)
.toString(CryptoJS.enc.Base64)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/\=+$/, "");
await this._generateS256Challenge();
code_challenge_method = "S256";
} else if (
options.code_challenge_method === "plain" &&
Expand Down Expand Up @@ -220,4 +215,24 @@ export class OAuth2User implements OAuthClient {
Authorization: `Bearer ${this.token.access_token}`,
};
}

private async _generateS256Challenge() {
const codeVerifierBytes = crypto.getRandomValues(new Uint8Array(64));
this.code_verifier = toHexString(codeVerifierBytes);

// from https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest
const hashBuffer = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(this.code_verifier),
);
const hashArray = new Uint8Array(hashBuffer);

// from https://stackoverflow.com/a/45313868
// TODO: consider using Buffer.from(hashBuffer).toString("base64") in NodeJS
this.code_challenge = btoa(String.fromCharCode(...hashArray))
// from https://gist.github.com/jhurliman/1250118?permalink_comment_id=3194799
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
}
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export type GenerateAuthUrlOptions = { authorizeUrl?: string } & (

export abstract class OAuthClient implements AuthClient {
abstract token?: Token;
abstract generateAuthURL(options: GenerateAuthUrlOptions): string;
abstract generateAuthURL(options: GenerateAuthUrlOptions): Promise<string>;
abstract requestAccessToken(code?: string): Promise<{ token: Token }>;
abstract getAuthHeader(
url?: string,
Expand Down
4 changes: 4 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ export function basicAuthHeader(
) {
return `Basic ${btoa(`${client_id}:${client_secret}`)}`;
}

// from https://stackoverflow.com/a/50868276
export const toHexString = (bytes: Uint8Array) =>
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
12 changes: 7 additions & 5 deletions src/webln/OauthWeblnProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class OauthWeblnProvider {
client: Client;
auth: OAuthClient;
oauth: boolean;
subscribers: Record<string, (payload: any) => void>;
subscribers: Record<string, (payload: unknown) => void>;
isExecuting: boolean;

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

notify(name: string, payload?: any) {
notify(name: string, payload?: unknown) {
const callback = this.subscribers[name];
if (callback) {
callback(payload);
Expand All @@ -45,7 +45,7 @@ export class OauthWeblnProvider {
if (isBrowser()) {
try {
this.isExecuting = true;
const result = await this.openAuthorization();
await this.openAuthorization();
} finally {
this.isExecuting = false;
}
Expand Down Expand Up @@ -123,12 +123,14 @@ export class OauthWeblnProvider {
}
}

openAuthorization() {
async openAuthorization() {
const height = 700;
const width = 600;
const top = window.outerHeight / 2 + window.screenY - height / 2;
const left = window.outerWidth / 2 + window.screenX - width / 2;
const url = this.auth.generateAuthURL({ code_challenge_method: "S256" });
const url = await this.auth.generateAuthURL({
code_challenge_method: "S256",
});

return new Promise((resolve, reject) => {
const popup = window.open(
Expand Down
3 changes: 0 additions & 3 deletions src/window.js

This file was deleted.

5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
"skipLibCheck": true,
"checkJs": true,
"allowJs": true,
"declarationMap": true,
"declarationMap": false,
"declaration": true,
"allowSyntheticDefaultImports": true,
"target": "es2020",
"module": "ESNext",
"rootDir": "./src",
"sourceMap": true,
"sourceMap": false,
"moduleResolution": "node"
},
"include": ["src/**/*"],
Expand Down
Loading

0 comments on commit 890edc8

Please sign in to comment.