feat: install extension on insecure connections
Some checks failed
Docker build on tag / build (push) Has been cancelled

This commit is contained in:
2023-03-16 18:30:50 +00:00
parent 424c7c79d7
commit ff0f6efa29
4 changed files with 165 additions and 48 deletions

View File

@ -6,6 +6,10 @@
font-weight: 400;
font-size: 14px;
line-height: 24px;
}
.login p,
.login a {
color: #999999;
}
@ -21,7 +25,7 @@
.login .logo {
margin-top: 16px;
margin-bottom: 67px;
margin-bottom: 50px;
}
.login > div:nth-child(1) {
@ -73,14 +77,16 @@
}
@media (max-width: 1024px) {
.login {
width: unset;
height: unset;
}
.login > div:nth-child(2) {
display: none;
}
}
.login .login-note {
}
.login .login-actions {
margin-top: 32px;
}
@ -99,20 +105,14 @@
font-weight: 400;
font-size: 14px;
line-height: 24px;
margin-top: 56px;
margin-bottom: 64px;
}
@media (max-width: 520px) {
.login .login-or {
margin-top: 32px;
margin-bottom: 32px;
}
margin-top: 40px;
margin-bottom: 40px;
}
.light .login-or {
color: #a1a1aa;
}
.login-container input[type="text"] {
border: none;
background-color: var(--gray-secondary);

View File

@ -5,14 +5,14 @@ import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import * as secp from "@noble/secp256k1";
import { useIntl, FormattedMessage } from "react-intl";
import { HexKey } from "@snort/nostr";
import { RootState } from "State/Store";
import { setPrivateKey, setPublicKey, setRelays, setGeneratedPrivateKey } from "State/Login";
import { DefaultRelays, EmailRegex, MnemonicRegex } from "Const";
import { bech32ToHex, generateBip39Entropy, entropyToDerivedKey, unwrap } from "Util";
import { HexKey } from "@snort/nostr";
import ZapButton from "Element/ZapButton";
// import useImgProxy from "Feed/ImgProxy";
import useImgProxy from "Hooks/useImgProxy";
import messages from "./messages";
@ -67,7 +67,9 @@ export default function LoginPage() {
const [error, setError] = useState("");
const [art, setArt] = useState<ArtworkEntry>();
const { formatMessage } = useIntl();
//const { proxy } = useImgProxy();
const { proxy } = useImgProxy();
const hasNip7 = "nostr" in window;
const isSecure = window.location.protocol === "https:";
useEffect(() => {
if (publicKey) {
@ -77,14 +79,19 @@ export default function LoginPage() {
useEffect(() => {
const ret = unwrap(Artwork.at(Artwork.length * Math.random()));
// disable for now because imgproxy is ded
// proxy(ret.link).then(a => setArt({ ...ret, link: a }));
setArt(ret);
proxy(ret.link).then(a => setArt({ ...ret, link: a }));
}, []);
async function doLogin() {
const insecureMsg = formatMessage({
defaultMessage:
"Can't login with private key on an insecure connection, please use a Nostr key manager extension instead",
});
try {
if (key.startsWith("nsec")) {
if (!isSecure) {
throw new Error(insecureMsg);
}
const hexKey = bech32ToHex(key);
if (secp.utils.isValidPrivateKey(hexKey)) {
dispatch(setPrivateKey(hexKey));
@ -98,16 +105,30 @@ export default function LoginPage() {
const hexKey = await getNip05PubKey(key);
dispatch(setPublicKey(hexKey));
} else if (key.match(MnemonicRegex)) {
if (!isSecure) {
throw new Error(insecureMsg);
}
const ent = generateBip39Entropy(key);
const keyHex = entropyToDerivedKey(ent);
dispatch(setPrivateKey(keyHex));
} else if (secp.utils.isValidPrivateKey(key)) {
if (!isSecure) {
throw new Error(insecureMsg);
}
dispatch(setPrivateKey(key));
} else {
throw new Error("INVALID PRIVATE KEY");
}
} catch (e) {
setError(`Failed to load NIP-05 pub key (${e})`);
if (e instanceof Error) {
setError(e.message);
} else {
setError(
formatMessage({
defaultMessage: "Unknown login error",
})
);
}
console.error(e);
}
}
@ -139,9 +160,8 @@ export default function LoginPage() {
}
function altLogins() {
const nip07 = "nostr" in window;
if (!nip07) {
return null;
if (!hasNip7) {
return;
}
return (
@ -154,6 +174,92 @@ export default function LoginPage() {
);
}
function generateKey() {
if (!isSecure) return;
return (
<>
<div className="flex login-or">
<FormattedMessage defaultMessage="OR" description="Seperator text for Login / Generate Key" />
<div className="divider w-max"></div>
</div>
<h1 dir="auto">
<FormattedMessage defaultMessage="Create an Account" description="Heading for generate key flow" />
</h1>
<p>
<FormattedMessage
defaultMessage="Generate a public / private key pair. Do not share your private key with anyone, this acts as your password. Once lost, it cannot be “reset” or recovered. Keep safe!"
description="Note about key security before generating a new key"
/>
</p>
<div className="login-actions">
<button type="button" onClick={() => makeRandomKey()}>
<FormattedMessage defaultMessage="Generate Key" description="Button: Generate a new key" />
</button>
</div>
</>
);
}
function installExtension() {
if (isSecure) return;
return (
<>
<div className="flex login-or">
<FormattedMessage defaultMessage="OR" description="Seperator text for Login / Generate Key" />
<div className="divider w-max"></div>
</div>
<h1 dir="auto">
<FormattedMessage
defaultMessage="Install Extension"
description="Heading for install key manager extension"
/>
</h1>
<p>
<FormattedMessage defaultMessage="Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:" />
</p>
<ul>
<li>
<a href="https://getalby.com/" target="_blank" rel="noreferrer">
Alby
</a>
</li>
<li>
<a
href="https://chrome.google.com/webstore/detail/nos2x/kpgefcfmnafjgpblomihpgmejjdanjjp"
target="_blank"
rel="noreferrer">
nos2x
</a>
</li>
</ul>
<p>
<FormattedMessage
defaultMessage="If you want to try out some others, check out {link} for more!"
values={{
link: <a href="https://github.com/aljazceru/awesome-nostr#browser-extensions">awesome-nostr</a>,
}}
/>
</p>
<p>
<FormattedMessage defaultMessage="Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow." />
</p>
{hasNip7 ? (
<div className="login-actions">
<button type="button" onClick={() => doNip07Login().then(() => navigate("/new/username"))}>
<FormattedMessage defaultMessage="Setup Profile" />
</button>
</div>
) : (
<b className="error">
<FormattedMessage defaultMessage="Hmm, can't find a key manager extension.. try reloading the page." />
</b>
)}
</>
);
}
return (
<div className="login">
<div>
@ -183,36 +289,14 @@ export default function LoginPage() {
description="Explanation for public key only login is read-only"
/>
</p>
{/* <a href="">
<FormattedMessage
defaultMessage="Why is there no password field?"
description="Link to why your private key is your password"
/>
</a>*/}
<div dir="auto" className="login-actions">
<button type="button" onClick={doLogin}>
<FormattedMessage defaultMessage="Login" description="Login button" />
</button>
{altLogins()}
</div>
<div className="flex login-or">
<FormattedMessage defaultMessage="OR" description="Seperator text for Login / Generate Key" />
<div className="divider w-max"></div>
</div>
<h1 dir="auto">
<FormattedMessage defaultMessage="Create an Account" description="Heading for generate key flow" />
</h1>
<p>
<FormattedMessage
defaultMessage="Generate a public / private key pair. Do not share your private key with anyone, this acts as your password. Once lost, it cannot be “reset” or recovered. Keep safe!"
description="Note about key security before generating a new key"
/>
</p>
<div className="login-actions">
<button type="button" onClick={() => makeRandomKey()}>
<FormattedMessage defaultMessage="Generate Key" description="Button: Generate a new key" />
</button>
</div>
{generateKey()}
{installExtension()}
</div>
</div>
<div>