feat: nip55
This commit is contained in:
parent
892b00810d
commit
5338f3acab
@ -38,6 +38,7 @@ Snort supports the following NIP's:
|
|||||||
- [x] NIP-50: Search
|
- [x] NIP-50: Search
|
||||||
- [x] NIP-51: Lists
|
- [x] NIP-51: Lists
|
||||||
- [x] NIP-53: Live Events
|
- [x] NIP-53: Live Events
|
||||||
|
- [x] NIP-55: Android signer application
|
||||||
- [x] NIP-57: Zaps
|
- [x] NIP-57: Zaps
|
||||||
- [x] NIP-58: Badges
|
- [x] NIP-58: Badges
|
||||||
- [x] NIP-59: Gift Wrap
|
- [x] NIP-59: Gift Wrap
|
||||||
|
@ -105,6 +105,7 @@
|
|||||||
"@types/webtorrent": "^0.109.3",
|
"@types/webtorrent": "^0.109.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
||||||
"@typescript-eslint/parser": "^6.1.0",
|
"@typescript-eslint/parser": "^6.1.0",
|
||||||
|
"@vitejs/plugin-basic-ssl": "^1.2.0",
|
||||||
"@vitejs/plugin-react": "^4.2.0",
|
"@vitejs/plugin-react": "^4.2.0",
|
||||||
"@webbtc/webln-types": "^3.0.0",
|
"@webbtc/webln-types": "^3.0.0",
|
||||||
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
|
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { unwrap } from "@snort/shared";
|
import { Nip7Signer, Nip55Signer, NotEncrypted } from "@snort/system";
|
||||||
import { NotEncrypted } from "@snort/system";
|
|
||||||
import { SnortContext } from "@snort/system-react";
|
import { SnortContext } from "@snort/system-react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { FormEvent, useContext, useState } from "react";
|
import { FormEvent, useContext, useState } from "react";
|
||||||
@ -16,6 +15,8 @@ import { NewUserState } from ".";
|
|||||||
|
|
||||||
const NSEC_NPUB_REGEX = /(nsec1|npub1)[a-zA-Z0-9]{20,65}/gi;
|
const NSEC_NPUB_REGEX = /(nsec1|npub1)[a-zA-Z0-9]{20,65}/gi;
|
||||||
|
|
||||||
|
const signer = new Nip55Signer();
|
||||||
|
|
||||||
export function SignIn() {
|
export function SignIn() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
@ -25,15 +26,23 @@ export function SignIn() {
|
|||||||
const loginHandler = useLoginHandler();
|
const loginHandler = useLoginHandler();
|
||||||
|
|
||||||
const hasNip7 = "nostr" in window;
|
const hasNip7 = "nostr" in window;
|
||||||
|
const hasNip55 = true;
|
||||||
|
|
||||||
async function doNip07Login() {
|
async function doNip07Login() {
|
||||||
/*const relays =
|
const signer = new Nip7Signer();
|
||||||
"getRelays" in unwrap(window.nostr) ? await unwrap(window.nostr?.getRelays).call(window.nostr) : undefined;*/
|
const pubKey = await signer.getPubKey();
|
||||||
const pubKey = await unwrap(window.nostr).getPublicKey();
|
|
||||||
LoginStore.loginWithPubkey(pubKey, LoginSessionType.Nip7);
|
LoginStore.loginWithPubkey(pubKey, LoginSessionType.Nip7);
|
||||||
trackEvent("Login", { type: "NIP7" });
|
trackEvent("Login", { type: "NIP7" });
|
||||||
navigate("/");
|
navigate("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function doNip55Login() {
|
||||||
|
const pubKey = await signer.getPubKey();
|
||||||
|
LoginStore.loginWithPubkey(pubKey, LoginSessionType.Nip55);
|
||||||
|
trackEvent("Login", { type: "NIP55" });
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
|
||||||
async function onSubmit(e) {
|
async function onSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
doLogin(key);
|
doLogin(key);
|
||||||
@ -69,7 +78,7 @@ export function SignIn() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const nip7Login = hasNip7 && !useKey;
|
const signerExtLogin = (hasNip7 || hasNip55) && !useKey;
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col g24">
|
<div className="flex flex-col g24">
|
||||||
<img src={CONFIG.icon} width={48} height={48} className="br mr-auto ml-auto" />
|
<img src={CONFIG.icon} width={48} height={48} className="br mr-auto ml-auto" />
|
||||||
@ -77,10 +86,10 @@ export function SignIn() {
|
|||||||
<h1>
|
<h1>
|
||||||
<FormattedMessage defaultMessage="Sign In" />
|
<FormattedMessage defaultMessage="Sign In" />
|
||||||
</h1>
|
</h1>
|
||||||
{nip7Login && <FormattedMessage defaultMessage="Use a nostr signer extension to sign in" />}
|
{signerExtLogin && <FormattedMessage defaultMessage="Use a nostr signer extension to sign in" />}
|
||||||
</div>
|
</div>
|
||||||
<div className={classNames("flex flex-col g16", { "items-center": nip7Login })}>
|
<div className={classNames("flex flex-col g16", { "items-center": signerExtLogin })}>
|
||||||
{hasNip7 && !useKey && (
|
{signerExtLogin && (
|
||||||
<>
|
<>
|
||||||
<AsyncButton onClick={doNip07Login}>
|
<AsyncButton onClick={doNip07Login}>
|
||||||
<div className="circle bg-warning p12 text-white">
|
<div className="circle bg-warning p12 text-white">
|
||||||
@ -88,6 +97,12 @@ export function SignIn() {
|
|||||||
</div>
|
</div>
|
||||||
<FormattedMessage defaultMessage="Sign in with Nostr Extension" />
|
<FormattedMessage defaultMessage="Sign in with Nostr Extension" />
|
||||||
</AsyncButton>
|
</AsyncButton>
|
||||||
|
<AsyncButton onClick={doNip55Login}>
|
||||||
|
<div className="circle bg-warning p12 text-white">
|
||||||
|
<Icon name="key" />
|
||||||
|
</div>
|
||||||
|
<FormattedMessage defaultMessage="Sign in with Android signer" />
|
||||||
|
</AsyncButton>
|
||||||
<Link to="" className="highlight">
|
<Link to="" className="highlight">
|
||||||
<FormattedMessage defaultMessage="Supported Extensions" />
|
<FormattedMessage defaultMessage="Supported Extensions" />
|
||||||
</Link>
|
</Link>
|
||||||
@ -96,13 +111,12 @@ export function SignIn() {
|
|||||||
</AsyncButton>
|
</AsyncButton>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{(!hasNip7 || useKey) && (
|
{(!signerExtLogin || useKey) && (
|
||||||
<form onSubmit={onSubmit} className="flex flex-col gap-4">
|
<form onSubmit={onSubmit} className="flex flex-col gap-4">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={formatMessage({
|
placeholder={formatMessage({
|
||||||
defaultMessage: "nsec, npub, nip-05, hex, mnemonic",
|
defaultMessage: "nsec, npub, nip-05, hex, mnemonic",
|
||||||
id: "X7xU8J",
|
|
||||||
})}
|
})}
|
||||||
value={key}
|
value={key}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
KeyStorage,
|
KeyStorage,
|
||||||
Nip7Signer,
|
Nip7Signer,
|
||||||
Nip46Signer,
|
Nip46Signer,
|
||||||
|
Nip55Signer,
|
||||||
PrivateKeySigner,
|
PrivateKeySigner,
|
||||||
RelaySettings,
|
RelaySettings,
|
||||||
SystemInterface,
|
SystemInterface,
|
||||||
@ -157,5 +158,8 @@ export function createPublisher(l: LoginSession) {
|
|||||||
case LoginSessionType.Nip7: {
|
case LoginSessionType.Nip7: {
|
||||||
return new EventPublisher(new Nip7Signer(), unwrap(l.publicKey));
|
return new EventPublisher(new Nip7Signer(), unwrap(l.publicKey));
|
||||||
}
|
}
|
||||||
|
case LoginSessionType.Nip55: {
|
||||||
|
return new EventPublisher(new Nip55Signer(), unwrap(l.publicKey));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ export const enum LoginSessionType {
|
|||||||
Nip7 = "nip7",
|
Nip7 = "nip7",
|
||||||
Nip46 = "nip46",
|
Nip46 = "nip46",
|
||||||
Nip7os = "nip7_os",
|
Nip7os = "nip7_os",
|
||||||
|
Nip55 = "nip55",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SnortAppData {
|
export interface SnortAppData {
|
||||||
|
@ -876,6 +876,9 @@
|
|||||||
"J2Q92B": {
|
"J2Q92B": {
|
||||||
"defaultMessage": "Emoji sets"
|
"defaultMessage": "Emoji sets"
|
||||||
},
|
},
|
||||||
|
"J6N9xl": {
|
||||||
|
"defaultMessage": "Sign in with Android signer"
|
||||||
|
},
|
||||||
"JCIgkj": {
|
"JCIgkj": {
|
||||||
"defaultMessage": "Username"
|
"defaultMessage": "Username"
|
||||||
},
|
},
|
||||||
|
@ -290,6 +290,7 @@
|
|||||||
"J1iLmb": "Notifications Not Allowed",
|
"J1iLmb": "Notifications Not Allowed",
|
||||||
"J2HeQ+": "Use commas to separate words e.g. word1, word2, word3",
|
"J2HeQ+": "Use commas to separate words e.g. word1, word2, word3",
|
||||||
"J2Q92B": "Emoji sets",
|
"J2Q92B": "Emoji sets",
|
||||||
|
"J6N9xl": "Sign in with Android signer",
|
||||||
"JCIgkj": "Username",
|
"JCIgkj": "Username",
|
||||||
"JGrt9q": "Send sats to {name}",
|
"JGrt9q": "Send sats to {name}",
|
||||||
"JHEHCk": "Zaps ({n})",
|
"JHEHCk": "Zaps ({n})",
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import basicSsl from "@vitejs/plugin-basic-ssl";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import appConfig from "config";
|
import appConfig from "config";
|
||||||
import { visualizer } from "rollup-plugin-visualizer";
|
import { visualizer } from "rollup-plugin-visualizer";
|
||||||
@ -7,6 +8,7 @@ import { vitePluginVersionMark } from "vite-plugin-version-mark";
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
|
basicSsl(),
|
||||||
react({
|
react({
|
||||||
jsxImportSource: "@welldone-software/why-did-you-render",
|
jsxImportSource: "@welldone-software/why-did-you-render",
|
||||||
babel: {
|
babel: {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@snort/system-react",
|
"name": "@snort/system-react",
|
||||||
"version": "1.5.7",
|
"version": "1.6.0",
|
||||||
"description": "React hooks for @snort/system",
|
"description": "React hooks for @snort/system",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"module": "src/index.ts",
|
"module": "src/index.ts",
|
||||||
@ -17,7 +17,7 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@snort/shared": "^1.0.17",
|
"@snort/shared": "^1.0.17",
|
||||||
"@snort/system": "^1.5.7",
|
"@snort/system": "^1.6.0",
|
||||||
"react": "^18.2.0"
|
"react": "^18.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@snort/system",
|
"name": "@snort/system",
|
||||||
"version": "1.5.7",
|
"version": "1.6.0",
|
||||||
"description": "Snort nostr system package",
|
"description": "Snort nostr system package",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
86
packages/system/src/impl/nip55.ts
Normal file
86
packages/system/src/impl/nip55.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import debug from "debug";
|
||||||
|
import { NostrEvent, NotSignedNostrEvent } from "../nostr";
|
||||||
|
import { EventSigner } from "../signer";
|
||||||
|
import { v4 as uuid } from "uuid";
|
||||||
|
import { bech32ToHex } from "@snort/shared";
|
||||||
|
|
||||||
|
export class Nip55Signer implements EventSigner {
|
||||||
|
#log = debug("NIP-55");
|
||||||
|
#queue: Array<{ id: string; resolve: (o: any) => void; reject: () => void }> = [];
|
||||||
|
|
||||||
|
init(): Promise<void> {
|
||||||
|
// nothing here
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPubKey() {
|
||||||
|
let pk = await this.#call("get_public_key", "signature");
|
||||||
|
if (pk.startsWith("npub")) {
|
||||||
|
pk = bech32ToHex(pk);
|
||||||
|
}
|
||||||
|
return pk;
|
||||||
|
}
|
||||||
|
|
||||||
|
async nip4Encrypt(content: string, key: string) {
|
||||||
|
return await this.#call("nip04_encrypt", "signature", content, new Map([["pubkey", key]]));
|
||||||
|
}
|
||||||
|
|
||||||
|
async nip4Decrypt(content: string, otherKey: string) {
|
||||||
|
return await this.#call("nip04_decrypt", "signature", content, new Map([["pubkey", otherKey]]));
|
||||||
|
}
|
||||||
|
|
||||||
|
async nip44Encrypt(content: string, key: string) {
|
||||||
|
return await this.#call("nip44_encrypt", "signature", content, new Map([["pubkey", key]]));
|
||||||
|
}
|
||||||
|
|
||||||
|
async nip44Decrypt(content: string, otherKey: string) {
|
||||||
|
return await this.#call("nip44_decrypt", "signature", content, new Map([["pubkey", otherKey]]));
|
||||||
|
}
|
||||||
|
|
||||||
|
async sign(ev: NostrEvent | NotSignedNostrEvent) {
|
||||||
|
const evRet = await this.#call("sign_event", "event", ev);
|
||||||
|
return JSON.parse(evRet);
|
||||||
|
}
|
||||||
|
|
||||||
|
get supports(): string[] {
|
||||||
|
return ["nip04", "nip44"];
|
||||||
|
}
|
||||||
|
|
||||||
|
#call(
|
||||||
|
method: string,
|
||||||
|
returnType: string,
|
||||||
|
obj?: NostrEvent | NotSignedNostrEvent | string,
|
||||||
|
otherParams?: Map<string, string>,
|
||||||
|
) {
|
||||||
|
const id = uuid();
|
||||||
|
const objString = typeof obj === "string" ? obj : obj != undefined ? JSON.stringify(obj) : undefined;
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append("compressionType", "none");
|
||||||
|
params.append("returnType", returnType);
|
||||||
|
params.append("type", method);
|
||||||
|
if (otherParams) {
|
||||||
|
for (const [k, v] of otherParams) {
|
||||||
|
params.append(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
const t = setInterval(async () => {
|
||||||
|
if (document.hasFocus()) {
|
||||||
|
const text = await navigator.clipboard.readText();
|
||||||
|
if (text) {
|
||||||
|
this.#log("Response: %s", text);
|
||||||
|
await navigator.clipboard.writeText("");
|
||||||
|
resolve(text);
|
||||||
|
clearInterval(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
const dst = `nostrsigner:${objString ?? ""}?${params.toString()}`;
|
||||||
|
this.#log("Sending command %s, %s", id, dst);
|
||||||
|
globalThis.location.href = dst;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,7 @@ export * from "./impl/nip10";
|
|||||||
export * from "./impl/nip44";
|
export * from "./impl/nip44";
|
||||||
export * from "./impl/nip46";
|
export * from "./impl/nip46";
|
||||||
export * from "./impl/nip57";
|
export * from "./impl/nip57";
|
||||||
|
export * from "./impl/nip55";
|
||||||
|
|
||||||
export * from "./cache/index";
|
export * from "./cache/index";
|
||||||
export * from "./cache/user-relays";
|
export * from "./cache/user-relays";
|
||||||
|
16
yarn.lock
16
yarn.lock
@ -4724,6 +4724,7 @@ __metadata:
|
|||||||
"@typescript-eslint/eslint-plugin": "npm:^6.1.0"
|
"@typescript-eslint/eslint-plugin": "npm:^6.1.0"
|
||||||
"@typescript-eslint/parser": "npm:^6.1.0"
|
"@typescript-eslint/parser": "npm:^6.1.0"
|
||||||
"@uidotdev/usehooks": "npm:^2.4.1"
|
"@uidotdev/usehooks": "npm:^2.4.1"
|
||||||
|
"@vitejs/plugin-basic-ssl": "npm:^1.2.0"
|
||||||
"@vitejs/plugin-react": "npm:^4.2.0"
|
"@vitejs/plugin-react": "npm:^4.2.0"
|
||||||
"@void-cat/api": "npm:^1.0.12"
|
"@void-cat/api": "npm:^1.0.12"
|
||||||
"@webbtc/webln-types": "npm:^3.0.0"
|
"@webbtc/webln-types": "npm:^3.0.0"
|
||||||
@ -4823,7 +4824,7 @@ __metadata:
|
|||||||
resolution: "@snort/system-react@workspace:packages/system-react"
|
resolution: "@snort/system-react@workspace:packages/system-react"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@snort/shared": "npm:^1.0.17"
|
"@snort/shared": "npm:^1.0.17"
|
||||||
"@snort/system": "npm:^1.5.6"
|
"@snort/system": "npm:^1.5.7"
|
||||||
"@types/react": "npm:^18.2.14"
|
"@types/react": "npm:^18.2.14"
|
||||||
react: "npm:^18.2.0"
|
react: "npm:^18.2.0"
|
||||||
typescript: "npm:^5.2.2"
|
typescript: "npm:^5.2.2"
|
||||||
@ -4858,7 +4859,7 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@snort/system@npm:^1.0.21, @snort/system@npm:^1.2.11, @snort/system@npm:^1.5.2, @snort/system@npm:^1.5.6, @snort/system@workspace:*, @snort/system@workspace:packages/system":
|
"@snort/system@npm:^1.0.21, @snort/system@npm:^1.2.11, @snort/system@npm:^1.5.2, @snort/system@npm:^1.5.7, @snort/system@workspace:*, @snort/system@workspace:packages/system":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@snort/system@workspace:packages/system"
|
resolution: "@snort/system@workspace:packages/system"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -4898,7 +4899,7 @@ __metadata:
|
|||||||
"@lightninglabs/lnc-web": "npm:^0.3.1-alpha"
|
"@lightninglabs/lnc-web": "npm:^0.3.1-alpha"
|
||||||
"@scure/base": "npm:^1.1.6"
|
"@scure/base": "npm:^1.1.6"
|
||||||
"@snort/shared": "npm:^1.0.17"
|
"@snort/shared": "npm:^1.0.17"
|
||||||
"@snort/system": "npm:^1.5.6"
|
"@snort/system": "npm:^1.5.7"
|
||||||
"@types/debug": "npm:^4.1.12"
|
"@types/debug": "npm:^4.1.12"
|
||||||
"@webbtc/webln-types": "npm:^3.0.0"
|
"@webbtc/webln-types": "npm:^3.0.0"
|
||||||
debug: "npm:^4.3.4"
|
debug: "npm:^4.3.4"
|
||||||
@ -6011,6 +6012,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@vitejs/plugin-basic-ssl@npm:^1.2.0":
|
||||||
|
version: 1.2.0
|
||||||
|
resolution: "@vitejs/plugin-basic-ssl@npm:1.2.0"
|
||||||
|
peerDependencies:
|
||||||
|
vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0
|
||||||
|
checksum: 10/34def4ab7838901f1f51bbff21fefac5f56346331f4ff1a8a3059d68e4cd43b62e1581a5ad39971d5d908343ee3217e782a0b506d97e0653c3373e27757ecedf
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@vitejs/plugin-react@npm:^4.2.0":
|
"@vitejs/plugin-react@npm:^4.2.0":
|
||||||
version: 4.2.0
|
version: 4.2.0
|
||||||
resolution: "@vitejs/plugin-react@npm:4.2.0"
|
resolution: "@vitejs/plugin-react@npm:4.2.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user