NIP-05 Login
This commit is contained in:
parent
0d01e02f42
commit
98262463c4
@ -23,8 +23,8 @@ export function NoteCreator(props) {
|
||||
return (
|
||||
<>
|
||||
{replyTo ? <small>{`Reply to: ${replyTo.Id.substring(0, 8)}`}</small> : null}
|
||||
<div className="send-note">
|
||||
<input type="text" placeholder="Sup?" value={note} onChange={(e) => setNote(e.target.value)}></input>
|
||||
<div className="flex">
|
||||
<input type="text" placeholder="Sup?" value={note} onChange={(e) => setNote(e.target.value)} className="f-grow mr10"></input>
|
||||
<div className="btn" onClick={() => sendNote()}>Send</div>
|
||||
</div>
|
||||
</>
|
||||
|
@ -22,7 +22,6 @@ export default function useEventPublisher() {
|
||||
if (nip07 === true && hasNip07) {
|
||||
ev.Id = await ev.CreateId();
|
||||
let tmpEv = await window.nostr.signEvent(ev.ToObject());
|
||||
console.log(tmpEv);
|
||||
return Event.FromObject(tmpEv);
|
||||
} else {
|
||||
await ev.Sign(privKey);
|
||||
|
@ -172,4 +172,8 @@ body.scroll-lock {
|
||||
|
||||
.tabs > div {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
@ -4,13 +4,16 @@ import { useNavigate } from "react-router-dom";
|
||||
import * as secp from '@noble/secp256k1';
|
||||
import { bech32 } from "bech32";
|
||||
|
||||
import { setPrivateKey, setNip07PubKey } from "../state/Login";
|
||||
import { setPrivateKey, setPublicKey } from "../state/Login";
|
||||
|
||||
const EmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
|
||||
export default function LoginPage() {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const publicKey = useSelector(s => s.login.publicKey);
|
||||
const [key, setKey] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (publicKey) {
|
||||
@ -18,22 +21,51 @@ export default function LoginPage() {
|
||||
}
|
||||
}, [publicKey]);
|
||||
|
||||
function doLogin() {
|
||||
if (key.startsWith("nsec")) {
|
||||
let nKey = bech32.decode(key);
|
||||
let buff = bech32.fromWords(nKey.words);
|
||||
let hexKey = secp.utils.bytesToHex(Uint8Array.from(buff));
|
||||
if (secp.utils.isValidPrivateKey(hexKey)) {
|
||||
dispatch(setPrivateKey(hexKey));
|
||||
} else {
|
||||
throw "INVALID PRIVATE KEY";
|
||||
function bech32ToHex(str) {
|
||||
let nKey = bech32.decode(str);
|
||||
let buff = bech32.fromWords(nKey.words);
|
||||
return secp.utils.bytesToHex(Uint8Array.from(buff));
|
||||
}
|
||||
|
||||
async function getNip05PubKey(addr) {
|
||||
let [username, domain] = addr.split("@");
|
||||
let rsp = await fetch(`https://${domain}/.well-known/nostr.json?name=${encodeURIComponent(username)}`);
|
||||
if (rsp.ok) {
|
||||
let data = await rsp.json();
|
||||
let pKey = data.names[username];
|
||||
if (pKey) {
|
||||
return pKey;
|
||||
}
|
||||
} else {
|
||||
if (secp.utils.isValidPrivateKey(key)) {
|
||||
dispatch(setPrivateKey(key));
|
||||
}
|
||||
throw "User key not found"
|
||||
}
|
||||
|
||||
async function doLogin() {
|
||||
|
||||
try {
|
||||
if (key.startsWith("nsec")) {
|
||||
let hexKey = bech32ToHex(key);
|
||||
if (secp.utils.isValidPrivateKey(hexKey)) {
|
||||
dispatch(setPrivateKey(hexKey));
|
||||
} else {
|
||||
throw "INVALID PRIVATE KEY";
|
||||
}
|
||||
} else if (key.startsWith("npub")) {
|
||||
let hexKey = bech32ToHex(key);
|
||||
dispatch(setPublicKey(hexKey));
|
||||
} else if (key.match(EmailRegex)) {
|
||||
let hexKey = await getNip05PubKey(key);
|
||||
dispatch(setPublicKey(hexKey));
|
||||
} else {
|
||||
throw "INVALID PRIVATE KEY";
|
||||
if (secp.utils.isValidPrivateKey(key)) {
|
||||
dispatch(setPrivateKey(key));
|
||||
} else {
|
||||
throw "INVALID PRIVATE KEY";
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
setError(`Failed to load NIP-05 pub key (${e})`);
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +77,7 @@ export default function LoginPage() {
|
||||
|
||||
async function doNip07Login() {
|
||||
let pubKey = await window.nostr.getPublicKey();
|
||||
dispatch(setNip07PubKey(pubKey));
|
||||
dispatch(setPublicKey(pubKey));
|
||||
}
|
||||
|
||||
function altLogins() {
|
||||
@ -67,10 +99,10 @@ export default function LoginPage() {
|
||||
return (
|
||||
<>
|
||||
<h1>Login</h1>
|
||||
<p>Enter your private key:</p>
|
||||
<div className="flex">
|
||||
<input type="text" placeholder="Private key" className="f-grow" onChange={e => setKey(e.target.value)} />
|
||||
<input type="text" placeholder="nsec / npub / nip-05 / hex private key..." className="f-grow" onChange={e => setKey(e.target.value)} />
|
||||
</div>
|
||||
{error.length > 0 ? <b className="error">{error}</b> : null}
|
||||
<div className="tabs">
|
||||
<div className="btn" onClick={(e) => doLogin()}>Login</div>
|
||||
<div className="btn" onClick={() => makeRandomKey()}>Generate Key</div>
|
||||
|
@ -1,16 +0,0 @@
|
||||
.send-note {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.send-note > input[type="text"] {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.send-note > .btn {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.login {
|
||||
margin-bottom: 10px;
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
import "./Root.css";
|
||||
import { useSelector } from "react-redux";
|
||||
import Note from "../element/Note";
|
||||
import useTimelineFeed from "../feed/TimelineFeed";
|
||||
@ -19,9 +18,7 @@ export default function RootPage() {
|
||||
<>
|
||||
{pubKey ? <NoteCreator /> : null}
|
||||
{followHints()}
|
||||
<div className="timeline">
|
||||
{notes?.sort((a, b) => b.created_at - a.created_at).map(e => <Note key={e.id} data={e} />)}
|
||||
</div>
|
||||
{notes?.sort((a, b) => b.created_at - a.created_at).map(e => <Note key={e.id} data={e} />)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -2,7 +2,7 @@ import { createSlice } from '@reduxjs/toolkit'
|
||||
import * as secp from '@noble/secp256k1';
|
||||
|
||||
const PrivateKeyItem = "secret";
|
||||
const Nip07PublicKeyItem = "nip07:pubkey";
|
||||
const PublicKeyItem = "pubkey";
|
||||
const NotificationsReadItem = "notifications-read";
|
||||
|
||||
const LoginSlice = createSlice({
|
||||
@ -33,11 +33,6 @@ const LoginSlice = createSlice({
|
||||
*/
|
||||
follows: [],
|
||||
|
||||
/**
|
||||
* Login keys are managed by extension
|
||||
*/
|
||||
nip07: false,
|
||||
|
||||
/**
|
||||
* Notifications for this login session
|
||||
*/
|
||||
@ -52,7 +47,7 @@ const LoginSlice = createSlice({
|
||||
init: (state) => {
|
||||
state.privateKey = window.localStorage.getItem(PrivateKeyItem);
|
||||
if (state.privateKey) {
|
||||
window.localStorage.removeItem(Nip07PublicKeyItem); // reset nip07 if using private key
|
||||
window.localStorage.removeItem(PublicKeyItem); // reset nip07 if using private key
|
||||
state.publicKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(state.privateKey, true));
|
||||
state.loggedOut = false;
|
||||
} else {
|
||||
@ -65,11 +60,10 @@ const LoginSlice = createSlice({
|
||||
"wss://nostr-pub.wellorder.net": { read: true, write: true }
|
||||
};
|
||||
|
||||
// check nip07 pub key
|
||||
let nip07PubKey = window.localStorage.getItem(Nip07PublicKeyItem);
|
||||
if (nip07PubKey && !state.privateKey) {
|
||||
state.publicKey = nip07PubKey;
|
||||
state.nip07 = true;
|
||||
// check pub key only
|
||||
let pubKey = window.localStorage.getItem(PublicKeyItem);
|
||||
if (pubKey && !state.privateKey) {
|
||||
state.publicKey = pubKey;
|
||||
state.loggedOut = false;
|
||||
}
|
||||
|
||||
@ -80,18 +74,16 @@ const LoginSlice = createSlice({
|
||||
}
|
||||
},
|
||||
setPrivateKey: (state, action) => {
|
||||
state.loggedOut = false;
|
||||
state.privateKey = action.payload;
|
||||
window.localStorage.setItem(PrivateKeyItem, action.payload);
|
||||
state.publicKey = secp.utils.bytesToHex(secp.schnorr.getPublicKey(action.payload, true));
|
||||
},
|
||||
setPublicKey: (state, action) => {
|
||||
window.localStorage.setItem(PublicKeyItem, action.payload);
|
||||
state.loggedOut = false;
|
||||
state.publicKey = action.payload;
|
||||
},
|
||||
setNip07PubKey: (state, action) => {
|
||||
window.localStorage.setItem(Nip07PublicKeyItem, action.payload);
|
||||
state.publicKey = action.payload;
|
||||
state.nip07 = true;
|
||||
},
|
||||
setRelays: (state, action) => {
|
||||
// filter out non-websocket urls
|
||||
let filtered = Object.entries(action.payload)
|
||||
@ -119,12 +111,14 @@ const LoginSlice = createSlice({
|
||||
},
|
||||
logout: (state) => {
|
||||
window.localStorage.removeItem(PrivateKeyItem);
|
||||
window.localStorage.removeItem(Nip07PublicKeyItem);
|
||||
window.localStorage.removeItem(PublicKeyItem);
|
||||
window.localStorage.removeItem(NotificationsReadItem);
|
||||
state.privateKey = null;
|
||||
state.publicKey = null;
|
||||
state.follows = [];
|
||||
state.notifications = [];
|
||||
state.loggedOut = true;
|
||||
state.readNotifications = 0;
|
||||
},
|
||||
markNotificationsRead: (state) => {
|
||||
state.readNotifications = new Date().getTime();
|
||||
@ -133,5 +127,5 @@ const LoginSlice = createSlice({
|
||||
}
|
||||
});
|
||||
|
||||
export const { init, setPrivateKey, setPublicKey, setNip07PubKey, setRelays, setFollows, addNotifications, logout, markNotificationsRead } = LoginSlice.actions;
|
||||
export const { init, setPrivateKey, setPublicKey, setRelays, setFollows, addNotifications, logout, markNotificationsRead } = LoginSlice.actions;
|
||||
export const reducer = LoginSlice.reducer;
|
Loading…
x
Reference in New Issue
Block a user