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-51: Lists
|
||||
- [x] NIP-53: Live Events
|
||||
- [x] NIP-55: Android signer application
|
||||
- [x] NIP-57: Zaps
|
||||
- [x] NIP-58: Badges
|
||||
- [x] NIP-59: Gift Wrap
|
||||
|
@ -105,6 +105,7 @@
|
||||
"@types/webtorrent": "^0.109.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
||||
"@typescript-eslint/parser": "^6.1.0",
|
||||
"@vitejs/plugin-basic-ssl": "^1.2.0",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"@webbtc/webln-types": "^3.0.0",
|
||||
"@webscopeio/react-textarea-autocomplete": "^4.9.2",
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { unwrap } from "@snort/shared";
|
||||
import { NotEncrypted } from "@snort/system";
|
||||
import { Nip7Signer, Nip55Signer, NotEncrypted } from "@snort/system";
|
||||
import { SnortContext } from "@snort/system-react";
|
||||
import classNames from "classnames";
|
||||
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 signer = new Nip55Signer();
|
||||
|
||||
export function SignIn() {
|
||||
const navigate = useNavigate();
|
||||
const { formatMessage } = useIntl();
|
||||
@ -25,15 +26,23 @@ export function SignIn() {
|
||||
const loginHandler = useLoginHandler();
|
||||
|
||||
const hasNip7 = "nostr" in window;
|
||||
const hasNip55 = true;
|
||||
|
||||
async function doNip07Login() {
|
||||
/*const relays =
|
||||
"getRelays" in unwrap(window.nostr) ? await unwrap(window.nostr?.getRelays).call(window.nostr) : undefined;*/
|
||||
const pubKey = await unwrap(window.nostr).getPublicKey();
|
||||
const signer = new Nip7Signer();
|
||||
const pubKey = await signer.getPubKey();
|
||||
LoginStore.loginWithPubkey(pubKey, LoginSessionType.Nip7);
|
||||
trackEvent("Login", { type: "NIP7" });
|
||||
navigate("/");
|
||||
}
|
||||
|
||||
async function doNip55Login() {
|
||||
const pubKey = await signer.getPubKey();
|
||||
LoginStore.loginWithPubkey(pubKey, LoginSessionType.Nip55);
|
||||
trackEvent("Login", { type: "NIP55" });
|
||||
navigate("/");
|
||||
}
|
||||
|
||||
async function onSubmit(e) {
|
||||
e.preventDefault();
|
||||
doLogin(key);
|
||||
@ -69,7 +78,7 @@ export function SignIn() {
|
||||
}
|
||||
};
|
||||
|
||||
const nip7Login = hasNip7 && !useKey;
|
||||
const signerExtLogin = (hasNip7 || hasNip55) && !useKey;
|
||||
return (
|
||||
<div className="flex flex-col g24">
|
||||
<img src={CONFIG.icon} width={48} height={48} className="br mr-auto ml-auto" />
|
||||
@ -77,10 +86,10 @@ export function SignIn() {
|
||||
<h1>
|
||||
<FormattedMessage defaultMessage="Sign In" />
|
||||
</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 className={classNames("flex flex-col g16", { "items-center": nip7Login })}>
|
||||
{hasNip7 && !useKey && (
|
||||
<div className={classNames("flex flex-col g16", { "items-center": signerExtLogin })}>
|
||||
{signerExtLogin && (
|
||||
<>
|
||||
<AsyncButton onClick={doNip07Login}>
|
||||
<div className="circle bg-warning p12 text-white">
|
||||
@ -88,6 +97,12 @@ export function SignIn() {
|
||||
</div>
|
||||
<FormattedMessage defaultMessage="Sign in with Nostr Extension" />
|
||||
</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">
|
||||
<FormattedMessage defaultMessage="Supported Extensions" />
|
||||
</Link>
|
||||
@ -96,13 +111,12 @@ export function SignIn() {
|
||||
</AsyncButton>
|
||||
</>
|
||||
)}
|
||||
{(!hasNip7 || useKey) && (
|
||||
{(!signerExtLogin || useKey) && (
|
||||
<form onSubmit={onSubmit} className="flex flex-col gap-4">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={formatMessage({
|
||||
defaultMessage: "nsec, npub, nip-05, hex, mnemonic",
|
||||
id: "X7xU8J",
|
||||
})}
|
||||
value={key}
|
||||
onChange={onChange}
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
KeyStorage,
|
||||
Nip7Signer,
|
||||
Nip46Signer,
|
||||
Nip55Signer,
|
||||
PrivateKeySigner,
|
||||
RelaySettings,
|
||||
SystemInterface,
|
||||
@ -157,5 +158,8 @@ export function createPublisher(l: LoginSession) {
|
||||
case LoginSessionType.Nip7: {
|
||||
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",
|
||||
Nip46 = "nip46",
|
||||
Nip7os = "nip7_os",
|
||||
Nip55 = "nip55",
|
||||
}
|
||||
|
||||
export interface SnortAppData {
|
||||
|
@ -876,6 +876,9 @@
|
||||
"J2Q92B": {
|
||||
"defaultMessage": "Emoji sets"
|
||||
},
|
||||
"J6N9xl": {
|
||||
"defaultMessage": "Sign in with Android signer"
|
||||
},
|
||||
"JCIgkj": {
|
||||
"defaultMessage": "Username"
|
||||
},
|
||||
|
@ -290,6 +290,7 @@
|
||||
"J1iLmb": "Notifications Not Allowed",
|
||||
"J2HeQ+": "Use commas to separate words e.g. word1, word2, word3",
|
||||
"J2Q92B": "Emoji sets",
|
||||
"J6N9xl": "Sign in with Android signer",
|
||||
"JCIgkj": "Username",
|
||||
"JGrt9q": "Send sats to {name}",
|
||||
"JHEHCk": "Zaps ({n})",
|
||||
|
@ -1,3 +1,4 @@
|
||||
import basicSsl from "@vitejs/plugin-basic-ssl";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import appConfig from "config";
|
||||
import { visualizer } from "rollup-plugin-visualizer";
|
||||
@ -7,6 +8,7 @@ import { vitePluginVersionMark } from "vite-plugin-version-mark";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
basicSsl(),
|
||||
react({
|
||||
jsxImportSource: "@welldone-software/why-did-you-render",
|
||||
babel: {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@snort/system-react",
|
||||
"version": "1.5.7",
|
||||
"version": "1.6.0",
|
||||
"description": "React hooks for @snort/system",
|
||||
"main": "dist/index.js",
|
||||
"module": "src/index.ts",
|
||||
@ -17,7 +17,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@snort/shared": "^1.0.17",
|
||||
"@snort/system": "^1.5.7",
|
||||
"@snort/system": "^1.6.0",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@snort/system",
|
||||
"version": "1.5.7",
|
||||
"version": "1.6.0",
|
||||
"description": "Snort nostr system package",
|
||||
"type": "module",
|
||||
"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/nip46";
|
||||
export * from "./impl/nip57";
|
||||
export * from "./impl/nip55";
|
||||
|
||||
export * from "./cache/index";
|
||||
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/parser": "npm:^6.1.0"
|
||||
"@uidotdev/usehooks": "npm:^2.4.1"
|
||||
"@vitejs/plugin-basic-ssl": "npm:^1.2.0"
|
||||
"@vitejs/plugin-react": "npm:^4.2.0"
|
||||
"@void-cat/api": "npm:^1.0.12"
|
||||
"@webbtc/webln-types": "npm:^3.0.0"
|
||||
@ -4823,7 +4824,7 @@ __metadata:
|
||||
resolution: "@snort/system-react@workspace:packages/system-react"
|
||||
dependencies:
|
||||
"@snort/shared": "npm:^1.0.17"
|
||||
"@snort/system": "npm:^1.5.6"
|
||||
"@snort/system": "npm:^1.5.7"
|
||||
"@types/react": "npm:^18.2.14"
|
||||
react: "npm:^18.2.0"
|
||||
typescript: "npm:^5.2.2"
|
||||
@ -4858,7 +4859,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
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
|
||||
resolution: "@snort/system@workspace:packages/system"
|
||||
dependencies:
|
||||
@ -4898,7 +4899,7 @@ __metadata:
|
||||
"@lightninglabs/lnc-web": "npm:^0.3.1-alpha"
|
||||
"@scure/base": "npm:^1.1.6"
|
||||
"@snort/shared": "npm:^1.0.17"
|
||||
"@snort/system": "npm:^1.5.6"
|
||||
"@snort/system": "npm:^1.5.7"
|
||||
"@types/debug": "npm:^4.1.12"
|
||||
"@webbtc/webln-types": "npm:^3.0.0"
|
||||
debug: "npm:^4.3.4"
|
||||
@ -6011,6 +6012,15 @@ __metadata:
|
||||
languageName: node
|
||||
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":
|
||||
version: 4.2.0
|
||||
resolution: "@vitejs/plugin-react@npm:4.2.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user