mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-26 17:52:18 +01:00
finish Auth0 signup flow
This commit is contained in:
parent
e8e3dc0fac
commit
006971409c
5
.changeset/popular-tools-breathe.md
Normal file
5
.changeset/popular-tools-breathe.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Add support for OAuth signup flow
|
@ -1,6 +1,7 @@
|
||||
import stringify from "json-stringify-deterministic";
|
||||
import { NostrRequestFilter, RelayQueryMap } from "../../types/nostr-query";
|
||||
import { Filter } from "nostr-tools";
|
||||
import { safeRelayUrls } from "../relay";
|
||||
|
||||
export function addQueryToFilter(filter: NostrRequestFilter, query: Filter) {
|
||||
if (Array.isArray(filter)) {
|
||||
@ -28,6 +29,6 @@ export function mapQueryMap(queryMap: RelayQueryMap, fn: (filter: NostrRequestFi
|
||||
|
||||
export function createSimpleQueryMap(relays: Iterable<string>, filter: NostrRequestFilter) {
|
||||
const map: RelayQueryMap = {};
|
||||
for (const relay of relays) map[relay] = filter;
|
||||
for (const relay of safeRelayUrls(relays)) map[relay] = filter;
|
||||
return map;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ export const getMatchLink = () =>
|
||||
/https?:\/\/([a-zA-Z0-9\.\-]+\.[a-zA-Z]+)([\p{L}\p{N}\p{M}&\.-\/\?=#\-@%\+_,:!~*]*)/gu;
|
||||
export const getMatchEmoji = () => /:([a-zA-Z0-9_-]+):/gi;
|
||||
export const getMatchCashu = () => /(cashuA[A-Za-z0-9_-]{0,10000}={0,3})/gi;
|
||||
export const getMatchSimpleEmail = () => /^[^\s]{1,64}@[^\s]+\.[^\s]{2,}$/;
|
||||
|
||||
// read more https://www.regular-expressions.info/unicode.html#category
|
||||
export function stripInvisibleChar(str?: string) {
|
||||
|
@ -53,8 +53,8 @@ export function safeRelayUrl(relayUrl: string | URL) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
export function safeRelayUrls(urls: string[]): string[] {
|
||||
return urls.map(safeRelayUrl).filter(Boolean) as string[];
|
||||
export function safeRelayUrls(urls: Iterable<string>): string[] {
|
||||
return Array.from(urls).map(safeRelayUrl).filter(Boolean) as string[];
|
||||
}
|
||||
|
||||
export function splitNostrFilterByPubkeys(
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { PersistentSubject } from "../classes/subject";
|
||||
import db from "./db";
|
||||
import { NostrConnectClient } from "./nostr-connect";
|
||||
import { AppSettings } from "./settings/migrations";
|
||||
|
||||
type CommonAccount = {
|
||||
@ -102,6 +103,15 @@ class AccountService {
|
||||
|
||||
db.put("accounts", account);
|
||||
}
|
||||
addFromNostrConnect(client: NostrConnectClient) {
|
||||
this.addAccount({
|
||||
type: "nostr-connect",
|
||||
signerRelays: client.relays,
|
||||
clientSecretKey: client.secretKey,
|
||||
pubkey: client.pubkey,
|
||||
readonly: false,
|
||||
});
|
||||
}
|
||||
removeAccount(pubkey: string) {
|
||||
this.accounts.next(this.accounts.value.filter((acc) => acc.pubkey !== pubkey));
|
||||
|
||||
|
@ -69,8 +69,6 @@ export class NostrConnectClient {
|
||||
secretKey: string;
|
||||
publicKey: string;
|
||||
|
||||
onAuthURL = new Subject<string>(undefined, false);
|
||||
|
||||
supportedMethods: NostrConnectMethod[] | undefined;
|
||||
|
||||
constructor(pubkey: string, relays: string[], secretKey?: string, provider?: string) {
|
||||
@ -103,6 +101,14 @@ export class NostrConnectClient {
|
||||
this.sub.close();
|
||||
}
|
||||
|
||||
handleAuthURL(url: string) {
|
||||
const popup = window.open(
|
||||
url,
|
||||
"auth",
|
||||
"width=400,height=600,resizable=no,status=no,location=no,toolbar=no,menubar=no",
|
||||
);
|
||||
}
|
||||
|
||||
private requests = new Map<string, Deferred<any>>();
|
||||
async handleEvent(event: NostrEvent) {
|
||||
if (this.provider && event.pubkey !== this.provider) return;
|
||||
@ -118,8 +124,13 @@ export class NostrConnectClient {
|
||||
const p = this.requests.get(response.id);
|
||||
if (!p) return;
|
||||
if (response.error) {
|
||||
if (response.result === "auth_url") this.onAuthURL.next(response.error);
|
||||
else p.reject(response);
|
||||
if (response.result === "auth_url") {
|
||||
try {
|
||||
await this.handleAuthURL(response.error);
|
||||
} catch (e) {
|
||||
p.reject(e);
|
||||
}
|
||||
} else p.reject(response);
|
||||
} else if (response.result) {
|
||||
this.log(response.id, response.result);
|
||||
p.resolve(response.result);
|
||||
|
@ -27,6 +27,7 @@ import dnsIdentityService from "../../../services/dns-identity";
|
||||
import { useUserMetadata } from "../../../hooks/use-user-metadata";
|
||||
import nostrConnectService from "../../../services/nostr-connect";
|
||||
import accountService from "../../../services/account";
|
||||
import { safeRelayUrls } from "../../../helpers/relay";
|
||||
|
||||
function ProviderCard({ onClick, provider }: { onClick: () => void; provider: NostrEvent }) {
|
||||
const metadata = JSON.parse(provider.content) as Kind0ParsedContent;
|
||||
@ -66,7 +67,7 @@ export default function LoginNostrAddressCreate() {
|
||||
const [name, setName] = useState("");
|
||||
const providers = useNip05Providers();
|
||||
const [selected, setSelected] = useState<NostrEvent>();
|
||||
const metadata = useUserMetadata(selected?.pubkey);
|
||||
const userMetadata = useUserMetadata(selected?.pubkey);
|
||||
|
||||
const createAccount: React.FormEventHandler<HTMLDivElement> = async (e) => {
|
||||
e.preventDefault();
|
||||
@ -74,31 +75,25 @@ export default function LoginNostrAddressCreate() {
|
||||
|
||||
try {
|
||||
setLoading("Creating...");
|
||||
if (!metadata) throw new Error("Cant verify provider");
|
||||
const providerMetadata = JSON.parse(selected.content) as Kind0ParsedContent;
|
||||
const metadata: Kind0ParsedContent = { ...userMetadata, ...providerMetadata };
|
||||
if (!metadata.nip05) throw new Error("Provider missing nip05 address");
|
||||
const nip05 = await dnsIdentityService.fetchIdentity(metadata.nip05);
|
||||
if (!nip05 || nip05.pubkey !== selected.pubkey) throw new Error("Invalid provider");
|
||||
if (nip05.name !== "_") throw new Error("Provider dose not own the domain");
|
||||
if (!nip05.hasNip46) throw new Error("Provider dose not support NIP-46");
|
||||
const relays = safeRelayUrls(nip05.nip46Relays || nip05.relays);
|
||||
if (relays.length === 0) throw new Error("Cant find providers relays");
|
||||
|
||||
const client = nostrConnectService.createClient("", nip05.nip46Relays || nip05.relays, undefined, nip05.pubkey);
|
||||
client.onAuthURL.subscribe((url) => {
|
||||
window.open(url, "auth", "width=400,height=600,resizable=no,status=no,location=no,toolbar=no,menubar=no");
|
||||
});
|
||||
|
||||
const newPubkey = await client.createAccount(name, nip05.domain);
|
||||
const client = nostrConnectService.createClient("", relays, undefined, nip05.pubkey);
|
||||
|
||||
const createPromise = client.createAccount(name, nip05.domain);
|
||||
await createPromise;
|
||||
await client.connect();
|
||||
|
||||
nostrConnectService.saveClient(client);
|
||||
accountService.addAccount({
|
||||
type: "nostr-connect",
|
||||
signerRelays: client.relays,
|
||||
clientSecretKey: client.secretKey,
|
||||
pubkey: client.pubkey,
|
||||
readonly: false,
|
||||
});
|
||||
accountService.switchAccount(client.pubkey!);
|
||||
accountService.addFromNostrConnect(client);
|
||||
accountService.switchAccount(client.pubkey);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) toast({ description: e.message, status: "error" });
|
||||
}
|
||||
|
@ -9,29 +9,27 @@ import nostrConnectService from "../../../services/nostr-connect";
|
||||
import accountService from "../../../services/account";
|
||||
import { COMMON_CONTACT_RELAY } from "../../../const";
|
||||
import { safeRelayUrls } from "../../../helpers/relay";
|
||||
import { getMatchSimpleEmail } from "../../../helpers/regexp";
|
||||
|
||||
export default function LoginNostrAddressView() {
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
|
||||
const [provider, setProvider] = useState("");
|
||||
const [address, setAddress] = useState("");
|
||||
const userSpecifiedDomain = address.includes("@");
|
||||
|
||||
const fullAddress = userSpecifiedDomain ? address || undefined : provider ? address + "@" + provider : undefined;
|
||||
|
||||
const [rootNip05, setRootNip05] = useState<DnsIdentity>();
|
||||
const [nip05, setNip05] = useState<DnsIdentity>();
|
||||
const [nip05, setNip05] = useState<DnsIdentity | null>();
|
||||
const [rootNip05, setRootNip05] = useState<DnsIdentity | null>();
|
||||
useDebounce(
|
||||
async () => {
|
||||
if (!fullAddress) return setNip05(undefined);
|
||||
let [name, domain] = fullAddress.split("@");
|
||||
if (!name || !domain || !domain.includes(".")) return setNip05(undefined);
|
||||
setNip05(await dnsIdentityService.fetchIdentity(fullAddress));
|
||||
setRootNip05(await dnsIdentityService.fetchIdentity(`_@${domain}`));
|
||||
if (!address) return setNip05(undefined);
|
||||
if (!getMatchSimpleEmail().test(address)) return setNip05(undefined);
|
||||
let [name, domain] = address.split("@");
|
||||
if (!name || !domain) return setNip05(undefined);
|
||||
setNip05((await dnsIdentityService.fetchIdentity(address)) ?? null);
|
||||
setRootNip05((await dnsIdentityService.fetchIdentity(`_@${domain}`)) ?? null);
|
||||
},
|
||||
300,
|
||||
[fullAddress],
|
||||
[address],
|
||||
);
|
||||
|
||||
const [loading, setLoading] = useState<string | undefined>();
|
||||
@ -44,20 +42,11 @@ export default function LoginNostrAddressView() {
|
||||
setLoading("Connecting...");
|
||||
const relays = safeRelayUrls(nip05.nip46Relays || rootNip05?.nip46Relays || rootNip05?.relays || nip05.relays);
|
||||
const client = nostrConnectService.fromHostedBunker(nip05.pubkey, relays, rootNip05?.pubkey);
|
||||
client.onAuthURL.subscribe((url) => {
|
||||
window.open(url, "auth", "width=400,height=600,resizable=no,status=no,location=no,toolbar=no,menubar=no");
|
||||
});
|
||||
await client.connect();
|
||||
|
||||
nostrConnectService.saveClient(client);
|
||||
accountService.addAccount({
|
||||
type: "nostr-connect",
|
||||
signerRelays: client.relays,
|
||||
clientSecretKey: client.secretKey,
|
||||
pubkey: client.pubkey!,
|
||||
readonly: false,
|
||||
});
|
||||
accountService.switchAccount(client.pubkey!);
|
||||
accountService.addFromNostrConnect(client);
|
||||
accountService.switchAccount(client.pubkey);
|
||||
} else {
|
||||
accountService.addAccount({
|
||||
type: "pubkey",
|
||||
@ -90,7 +79,7 @@ export default function LoginNostrAddressView() {
|
||||
return (
|
||||
<Card {...cardProps}>
|
||||
<Image w="7" h="7" src={`//${nip05.domain}/favicon.ico`} />
|
||||
<Text fontWeight="bold">{fullAddress}</Text>
|
||||
<Text fontWeight="bold">{address}</Text>
|
||||
<Text color="green.500" ml="auto">
|
||||
Found provider <CheckIcon boxSize={5} />
|
||||
</Text>
|
||||
@ -99,22 +88,24 @@ export default function LoginNostrAddressView() {
|
||||
} else
|
||||
return (
|
||||
<Card {...cardProps}>
|
||||
<Text fontWeight="bold">{fullAddress}</Text>
|
||||
<Text fontWeight="bold">{address}</Text>
|
||||
<Text color="yellow.500" ml="auto">
|
||||
Read-only
|
||||
</Text>
|
||||
</Card>
|
||||
);
|
||||
} else {
|
||||
} else if (nip05 === null) {
|
||||
return (
|
||||
<Card {...cardProps}>
|
||||
<Text fontWeight="bold">{fullAddress}</Text>
|
||||
<Text fontWeight="bold">{address}</Text>
|
||||
<Text color="red.500" ml="auto">
|
||||
Cant find identity
|
||||
</Text>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
@ -138,19 +129,18 @@ export default function LoginNostrAddressView() {
|
||||
{renderStatus()}
|
||||
</>
|
||||
)}
|
||||
<Flex justifyContent="space-between" gap="2" mt="2">
|
||||
<Button variant="link" onClick={() => navigate("../")}>
|
||||
<Flex gap="2" mt="2">
|
||||
<Button variant="link" onClick={() => navigate("../")} mr="auto">
|
||||
Back
|
||||
</Button>
|
||||
{nip05 ? (
|
||||
<Button colorScheme="primary" ml="auto" type="submit" isLoading={!!loading} isDisabled={!nip05}>
|
||||
Connect
|
||||
</Button>
|
||||
) : (
|
||||
<Button colorScheme="primary" ml="auto" as={RouterLink} to="/signin/address/create">
|
||||
{!loading && (
|
||||
<Button colorScheme="primary" as={RouterLink} to="/signin/address/create" variant="link" p="2">
|
||||
Find Provider
|
||||
</Button>
|
||||
)}
|
||||
<Button colorScheme="primary" type="submit" isLoading={!!loading} isDisabled={!nip05}>
|
||||
Connect
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
|
@ -29,13 +29,7 @@ export default function LoginNostrConnectView() {
|
||||
} else throw new Error("Unknown format");
|
||||
|
||||
nostrConnectService.saveClient(client);
|
||||
accountService.addAccount({
|
||||
type: "nostr-connect",
|
||||
signerRelays: client.relays,
|
||||
clientSecretKey: client.secretKey,
|
||||
pubkey: client.pubkey,
|
||||
readonly: false,
|
||||
});
|
||||
accountService.addFromNostrConnect(client);
|
||||
accountService.switchAccount(client.pubkey);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) toast({ status: "error", description: e.message });
|
||||
|
Loading…
x
Reference in New Issue
Block a user