Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nostr permissions + nip04 #1793

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a108956
feat(nostr): support permission vor signMessage #1727
escapedcat Nov 22, 2022
335738e
feat(nostr): apply enable to support unlock #1727
escapedcat Nov 23, 2022
2d39f40
chore: some linting #1727
escapedcat Nov 23, 2022
0e4661e
revert(nostr): enable from handling #1727
escapedcat Nov 23, 2022
b46306c
chore: some linting #1727
escapedcat Nov 23, 2022
4cbf9cc
feat: implemented nip04
reneaaron Nov 25, 2022
ed24a95
refactor: call enable from provider #1727
escapedcat Nov 25, 2022
8b06d2f
refactor(nostr): remove enable from provide because we always need to…
escapedcat Nov 25, 2022
d2dde89
refactor(nostr): use await #1727
escapedcat Nov 25, 2022
56d0cb6
refactor(nostr): use dexie api to find permission #1727
escapedcat Nov 25, 2022
2acb7aa
refactor(nostr): utilize addPermission action #1727
escapedcat Nov 25, 2022
cc4f5bb
revert: enable action sender #1727
escapedcat Nov 25, 2022
dd542f1
chore(nostr): remove outdated type #1727
escapedcat Nov 25, 2022
0ae37e1
refactor(nostr): use diexie api to get boolean #1727
escapedcat Nov 25, 2022
a7c341e
fix: added test
reneaaron Nov 27, 2022
da31845
fix: refactored decrypt to crypto-js
reneaaron Nov 27, 2022
71dd5fc
fix: refactored encrypto to crypto-js
reneaaron Nov 27, 2022
b222477
fix: ts fixes
reneaaron Nov 27, 2022
78faf81
fix: ts fixes
reneaaron Nov 27, 2022
c4f800b
fix: ts fixes
reneaaron Nov 27, 2022
757b296
refactor(permissions): use permission enum #1727
escapedcat Nov 28, 2022
d74ac62
Update src/extension/background-script/actions/nostr/decryptOrPrompt.ts
reneaaron Nov 28, 2022
ea240b0
Update src/extension/background-script/actions/nostr/encryptOrPrompt.ts
reneaaron Nov 28, 2022
b007c2e
feat(nostr): i18n #1727
escapedcat Nov 28, 2022
29d91dc
refactor(nostr): use request approach, remove unused code #1727
escapedcat Nov 28, 2022
ad90f92
chore(nostr): remove unused code #1727
escapedcat Nov 28, 2022
49d7221
refactor(nostr): use dexie get #1727
escapedcat Nov 28, 2022
2150c92
feat(nostr): add permissions to the rest of the nostr call
bumi Nov 29, 2022
cc92079
Merge branch 'feature/nostr-nip04' into feat/1727_nostr-permission-si…
bumi Nov 29, 2022
45145ab
feat(nostr): add permissions to encrypt/decrypt calls
bumi Nov 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/app/router/Prompt/Prompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import LNURLChannel from "@screens/LNURLChannel";
import LNURLPay from "@screens/LNURLPay";
import LNURLWithdraw from "@screens/LNURLWithdraw";
import MakeInvoice from "@screens/MakeInvoice";
import NostrConfirm from "@screens/Nostr/Confirm";
import NostrConfirmGetPublicKey from "@screens/Nostr/ConfirmGetPublicKey";
import NostrConfirmSignMessage from "@screens/Nostr/ConfirmSignMessage";
import Unlock from "@screens/Unlock";
import { HashRouter, Outlet, Route, Routes, Navigate } from "react-router-dom";
import { ToastContainer } from "react-toastify";
import Providers from "~/app/context/Providers";
import RequireAuth from "~/app/router/RequireAuth";
import NostrConfirmGetPublicKey from "~/app/screens/Nostr/ConfirmGetPublicKey";
import NostrConfirmSignMessage from "~/app/screens/Nostr/ConfirmSignMessage";
import type { NavigationState, OriginData } from "~/types";

// Parse out the parameters from the querystring.
Expand Down Expand Up @@ -68,6 +69,11 @@ function Prompt() {
path="public/webln/enable"
element={<Enable origin={navigationState.origin as OriginData} />} // prompt will always have an `origin` set, just the type is optional to support usage via PopUp
/>
<Route
path="public/nostr/enable"
element={<Enable origin={navigationState.origin as OriginData} />} // prompt will always have an `origin` set, just the type is optional to support usage via PopUp
/>
<Route path="public/nostr/confirm" element={<NostrConfirm />} />
<Route
path="public/nostr/confirmGetPublicKey"
element={<NostrConfirmGetPublicKey />}
Expand Down
113 changes: 113 additions & 0 deletions src/app/screens/Nostr/Confirm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { CheckIcon } from "@bitcoin-design/bitcoin-icons-react/filled";
import ConfirmOrCancel from "@components/ConfirmOrCancel";
import Container from "@components/Container";
import PublisherCard from "@components/PublisherCard";
import Checkbox from "@components/form/Checkbox";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import ScreenHeader from "~/app/components/ScreenHeader";
import { useNavigationState } from "~/app/hooks/useNavigationState";
import { USER_REJECTED_ERROR } from "~/common/constants";
import msg from "~/common/lib/msg";
import utils from "~/common/lib/utils";
import { OriginData } from "~/types";

function NostrConfirm() {
const { t } = useTranslation("translation", {
keyPrefix: "nostr",
});
const { t: tCommon } = useTranslation("common");
const navState = useNavigationState();
const origin = navState.origin as OriginData;
const description = navState.args?.description;
const details = navState.args?.details;
const [loading, setLoading] = useState(false);
const [rememberPermission, setRememberPermission] = useState(false);

function confirm() {
setLoading(true);
msg.reply({
confirm: true,
rememberPermission,
});
setLoading(false);
}

function reject(event: React.MouseEvent<HTMLAnchorElement>) {
event.preventDefault();
msg.error(USER_REJECTED_ERROR);
}

async function block(event: React.MouseEvent<HTMLAnchorElement>) {
event.preventDefault();
await utils.call("addBlocklist", {
domain: origin.domain,
host: origin.host,
});
msg.error(USER_REJECTED_ERROR);
}

return (
<div className="h-full flex flex-col overflow-y-auto no-scrollbar">
<ScreenHeader title={t("title")} />
<Container justifyBetween maxWidth="sm">
<div>
<PublisherCard
title={origin.name}
image={origin.icon}
url={origin.host}
isSmall={false}
/>
<div className="dark:text-white pt-6 mb-4">
<p className="mb-2">{t("allow", { host: origin.host })}</p>
<p className="dark:text-white">
<CheckIcon className="w-5 h-5 mr-2 inline" />
{description}
{details && (
<>
<br />
<i className="ml-7">{details}</i>
</>
)}
</p>
</div>

<div className="flex items-center">
<Checkbox
id="remember_permission"
name="remember_permission"
checked={rememberPermission}
onChange={(event) => {
setRememberPermission(event.target.checked);
}}
/>
<label
htmlFor="remember_permission"
className="cursor-pointer ml-2 block text-sm text-gray-900 font-medium dark:text-white"
>
{t("confirm_sign_message.remember.label")}
</label>
</div>
</div>
<div className="mb-4 text-center flex flex-col">
<ConfirmOrCancel
disabled={loading}
loading={loading}
label={tCommon("actions.confirm")}
onConfirm={confirm}
onCancel={reject}
/>
<a
className="underline text-sm text-gray-400 mx-4 overflow-hidden text-ellipsis whitespace-nowrap"
href="#"
onClick={block}
>
{t("block_and_ignore", { host: origin.host })}
</a>
</div>
</Container>
</div>
);
}

export default NostrConfirm;
33 changes: 29 additions & 4 deletions src/app/screens/Nostr/ConfirmGetPublicKey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { CheckIcon } from "@bitcoin-design/bitcoin-icons-react/filled";
import ConfirmOrCancel from "@components/ConfirmOrCancel";
import Container from "@components/Container";
import PublisherCard from "@components/PublisherCard";
import Checkbox from "@components/form/Checkbox";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import ScreenHeader from "~/app/components/ScreenHeader";
import { useNavigationState } from "~/app/hooks/useNavigationState";
Expand All @@ -17,11 +19,16 @@ function NostrConfirmGetPublicKey() {
const { t: tCommon } = useTranslation("common");
const navState = useNavigationState();
const origin = navState.origin as OriginData;
const [loading, setLoading] = useState(false);
const [rememberPermission, setRememberPermission] = useState(false);

function enable() {
function confirm() {
setLoading(true);
msg.reply({
confirm: true,
rememberPermission,
});
setLoading(false);
}

function reject(event: React.MouseEvent<HTMLAnchorElement>) {
Expand Down Expand Up @@ -49,19 +56,37 @@ function NostrConfirmGetPublicKey() {
url={origin.host}
isSmall={false}
/>

<div className="dark:text-white pt-6 mb-4">
<p className="mb-2">{t("allow", { host: origin.host })}</p>
<div className="mb-2 flex items-center">
<CheckIcon className="w-5 h-5 mr-2" />
<p className="dark:text-white">{t("read_public_key")}</p>
</div>
</div>

<div className="flex items-center">
<Checkbox
id="remember_permission"
name="remember_permission"
checked={rememberPermission}
onChange={(event) => {
setRememberPermission(event.target.checked);
}}
/>
<label
htmlFor="remember_permission"
className="cursor-pointer ml-2 block text-sm text-gray-900 font-medium dark:text-white"
>
{t("confirm_sign_message.remember.label")}
</label>
</div>
</div>
<div className="mb-4 text-center flex flex-col">
<ConfirmOrCancel
label={tCommon("actions.connect")}
onConfirm={enable}
disabled={loading}
loading={loading}
label={tCommon("actions.confirm")}
onConfirm={confirm}
onCancel={reject}
/>
<a
Expand Down
22 changes: 20 additions & 2 deletions src/app/screens/Nostr/ConfirmSignMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//import Checkbox from "../../components/Form/Checkbox";
import ConfirmOrCancel from "@components/ConfirmOrCancel";
import Container from "@components/Container";
import ContentMessage from "@components/ContentMessage";
import PublisherCard from "@components/PublisherCard";
import SuccessMessage from "@components/SuccessMessage";
import Checkbox from "@components/form/Checkbox";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
Expand All @@ -19,21 +19,23 @@ function ConfirmSignMessage() {
const navState = useNavigationState();
const { t: tCommon } = useTranslation("common");
const { t } = useTranslation("translation", {
keyPrefix: "confirm_sign_message",
keyPrefix: "nostr",
});
const navigate = useNavigate();

const event = navState.args?.event as Event;
const origin = navState.origin as OriginData;
const [loading, setLoading] = useState(false);
const [successMessage, setSuccessMessage] = useState("");
const [rememberPermission, setRememberPermission] = useState(false);

// TODO: refactor: the success message and loading will not be displayed because after the reply the prompt is closed.
async function confirm() {
try {
setLoading(true);
msg.reply({
confirm: true,
rememberPermission,
});
setSuccessMessage(tCommon("success"));
} catch (e) {
Expand Down Expand Up @@ -73,6 +75,22 @@ function ConfirmSignMessage() {
heading={t("content", { host: origin.host })}
content={event.content}
/>
<div className="flex items-center">
<Checkbox
id="remember_permission"
name="remember_permission"
checked={rememberPermission}
onChange={(event) => {
setRememberPermission(event.target.checked);
}}
/>
<label
htmlFor="remember_permission"
className="cursor-pointer ml-2 block text-sm text-gray-900 font-medium dark:text-white"
>
{t("confirm_sign_message.remember.label")}
</label>
</div>
</div>
<ConfirmOrCancel
disabled={loading}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import db from "~/extension/background-script/db";
import type { DbAllowance, MessageAllowanceDelete } from "~/types";
import { PermissionMethod } from "~/types";

import deleteAllowance from "../delete";

Expand Down Expand Up @@ -38,7 +39,7 @@ const mockPermissions = [
allowanceId: 1,
createdAt: "1667291216372",
host: "pro.kollider.xyz",
method: "listChannels",
method: PermissionMethod["NOSTR_SIGNMESSAGE"],
blocked: false,
enabled: true,
},
Expand All @@ -47,7 +48,7 @@ const mockPermissions = [
allowanceId: 2,
createdAt: "1667291216372",
host: "lnmarkets.com",
method: "getinfo",
method: PermissionMethod["NOSTR_SIGNMESSAGE"],
blocked: false,
enabled: true,
},
Expand All @@ -56,7 +57,7 @@ const mockPermissions = [
allowanceId: 2,
createdAt: "1667291216372",
host: "lnmarkets.com",
method: "some-method",
method: PermissionMethod["NOSTR_SIGNMESSAGE"],
blocked: false,
enabled: true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe("enable allowance", () => {
const message: MessageAllowanceEnable = {
application: "LBE",
prompt: true,
action: "enableAllowance",
action: "public/webln/enable",
origin: {
location: "test",
domain: "",
Expand Down Expand Up @@ -95,7 +95,7 @@ describe("enable allowance", () => {
const message: MessageAllowanceEnable = {
application: "LBE",
prompt: true,
action: "enableAllowance",
action: "public/webln/enable",
origin: {
location: "test",
domain: "",
Expand Down
2 changes: 2 additions & 0 deletions src/extension/background-script/actions/allowances/enable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ const enable = async (
enabled: boolean;
remember: boolean;
}>(message);

if (response.data.enabled && sender.tab) {
await setIcon(ExtensionIcon.Active, sender.tab.id as number); // highlight the icon when enabled
}

// if the response should be saved/remembered we update the allowance for the domain
// as this returns a promise we must wait until it resolves
if (response.data.enabled && response.data.remember) {
Expand Down
4 changes: 2 additions & 2 deletions src/extension/background-script/actions/ln/request.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import utils from "~/common/lib/utils";
import { MessageGenericRequest } from "~/types";
import { MessageGenericRequest, PermissionMethod } from "~/types";

import db from "../../db";
import state from "../../state";
Expand Down Expand Up @@ -36,7 +36,7 @@ const request = async (
}

// prefix method with webln to prevent potential naming conflicts (e.g. with nostr calls that also use the permissions)
const weblnMethod = `${WEBLN_PREFIX}${method}`;
const weblnMethod = `${WEBLN_PREFIX}${method}` as PermissionMethod;

const permission = await db.permissions
.where("host")
Expand Down
Loading