Git Nostr signer from currently-logged-in account
This commit is contained in:
parent
44411fdf29
commit
a83916537a
|
@ -1,8 +1,5 @@
|
||||||
import { nip19 } from 'nostr-tools';
|
|
||||||
|
|
||||||
import { importEntities } from 'soapbox/entity-store/actions';
|
import { importEntities } from 'soapbox/entity-store/actions';
|
||||||
import { Entities } from 'soapbox/entity-store/entities';
|
import { Entities } from 'soapbox/entity-store/entities';
|
||||||
import { signer } from 'soapbox/features/nostr/sign';
|
|
||||||
import { selectAccount } from 'soapbox/selectors';
|
import { selectAccount } from 'soapbox/selectors';
|
||||||
import { isLoggedIn } from 'soapbox/utils/auth';
|
import { isLoggedIn } from 'soapbox/utils/auth';
|
||||||
import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features';
|
import { getFeatures, parseVersion, PLEROMA } from 'soapbox/utils/features';
|
||||||
|
@ -132,14 +129,8 @@ const noOp = () => new Promise(f => f(undefined));
|
||||||
|
|
||||||
const createAccount = (params: Record<string, any>) =>
|
const createAccount = (params: Record<string, any>) =>
|
||||||
async (dispatch: AppDispatch, getState: () => RootState) => {
|
async (dispatch: AppDispatch, getState: () => RootState) => {
|
||||||
const { instance } = getState();
|
|
||||||
const { nostrSignup } = getFeatures(instance);
|
|
||||||
const pubkey = (signer && nostrSignup) ? await signer.getPublicKey() : undefined;
|
|
||||||
|
|
||||||
dispatch({ type: ACCOUNT_CREATE_REQUEST, params });
|
dispatch({ type: ACCOUNT_CREATE_REQUEST, params });
|
||||||
return api(getState, 'app').post('/api/v1/accounts', params, {
|
return api(getState, 'app').post('/api/v1/accounts', params).then(({ data: token }) => {
|
||||||
headers: pubkey ? { authorization: `Bearer ${nip19.npubEncode(pubkey)}` } : undefined,
|
|
||||||
}).then(({ data: token }) => {
|
|
||||||
return dispatch({ type: ACCOUNT_CREATE_SUCCESS, params, token });
|
return dispatch({ type: ACCOUNT_CREATE_SUCCESS, params, token });
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch({ type: ACCOUNT_CREATE_FAIL, error, params });
|
dispatch({ type: ACCOUNT_CREATE_FAIL, error, params });
|
||||||
|
|
|
@ -5,6 +5,14 @@ import { type AppDispatch } from 'soapbox/store';
|
||||||
import { verifyCredentials } from './auth';
|
import { verifyCredentials } from './auth';
|
||||||
import { closeModal } from './modals';
|
import { closeModal } from './modals';
|
||||||
|
|
||||||
|
/** Log in with a Nostr pubkey. */
|
||||||
|
function logInNostr(pubkey: string) {
|
||||||
|
return (dispatch: AppDispatch) => {
|
||||||
|
const npub = nip19.npubEncode(pubkey);
|
||||||
|
return dispatch(verifyCredentials(npub));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** Log in with a Nostr extension. */
|
/** Log in with a Nostr extension. */
|
||||||
function nostrExtensionLogIn() {
|
function nostrExtensionLogIn() {
|
||||||
return async (dispatch: AppDispatch) => {
|
return async (dispatch: AppDispatch) => {
|
||||||
|
@ -13,10 +21,9 @@ function nostrExtensionLogIn() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const pubkey = await window.nostr.getPublicKey();
|
const pubkey = await window.nostr.getPublicKey();
|
||||||
const npub = nip19.npubEncode(pubkey);
|
|
||||||
|
|
||||||
dispatch(closeModal('NOSTR_SIGNIN'));
|
dispatch(closeModal('NOSTR_SIGNIN'));
|
||||||
return dispatch(verifyCredentials(npub));
|
return dispatch(logInNostr(pubkey));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { NRelay, NRelay1, NostrSigner } from '@soapbox/nspec';
|
import { NRelay, NRelay1, NostrSigner } from '@soapbox/nspec';
|
||||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
|
|
||||||
import { signer } from 'soapbox/features/nostr/sign';
|
import { NKeys } from 'soapbox/features/nostr/keys';
|
||||||
|
import { useOwnAccount } from 'soapbox/hooks';
|
||||||
import { useInstance } from 'soapbox/hooks/useInstance';
|
import { useInstance } from 'soapbox/hooks/useInstance';
|
||||||
|
|
||||||
interface NostrContextType {
|
interface NostrContextType {
|
||||||
|
@ -20,8 +21,13 @@ export const NostrProvider: React.FC<NostrProviderProps> = ({ children }) => {
|
||||||
const instance = useInstance();
|
const instance = useInstance();
|
||||||
const [relay, setRelay] = useState<NRelay1>();
|
const [relay, setRelay] = useState<NRelay1>();
|
||||||
|
|
||||||
|
const { account } = useOwnAccount();
|
||||||
|
|
||||||
const url = instance.nostr?.relay;
|
const url = instance.nostr?.relay;
|
||||||
const pubkey = instance.nostr?.pubkey;
|
const pubkey = instance.nostr?.pubkey;
|
||||||
|
const accountPubkey = account?.nostr.pubkey;
|
||||||
|
|
||||||
|
const signer = (accountPubkey ? NKeys.get(accountPubkey) : undefined) ?? window.nostr;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (url) {
|
if (url) {
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { hexToBytes } from '@noble/hashes/utils';
|
|
||||||
import { type NostrSigner, type NostrEvent, NSecSigner } from '@soapbox/nspec';
|
|
||||||
|
|
||||||
/** Use key from `localStorage` if available, falling back to NIP-07. */
|
|
||||||
export class SoapboxSigner implements NostrSigner {
|
|
||||||
|
|
||||||
#signer: NostrSigner;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
const privateKey = localStorage.getItem('soapbox:nostr:privateKey');
|
|
||||||
const signer = privateKey ? new NSecSigner(hexToBytes(privateKey)) : window.nostr;
|
|
||||||
|
|
||||||
if (!signer) {
|
|
||||||
throw new Error('No Nostr signer available');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#signer = signer;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getPublicKey(): Promise<string> {
|
|
||||||
return this.#signer.getPublicKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
async signEvent(event: Omit<NostrEvent, 'id' | 'pubkey' | 'sig'>): Promise<NostrEvent> {
|
|
||||||
return this.#signer.signEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
nip04 = {
|
|
||||||
encrypt: (pubkey: string, plaintext: string): Promise<string> => {
|
|
||||||
if (!this.#signer.nip04) {
|
|
||||||
throw new Error('NIP-04 not supported by signer');
|
|
||||||
}
|
|
||||||
return this.#signer.nip04.encrypt(pubkey, plaintext);
|
|
||||||
},
|
|
||||||
|
|
||||||
decrypt: (pubkey: string, ciphertext: string): Promise<string> => {
|
|
||||||
if (!this.#signer.nip04) {
|
|
||||||
throw new Error('NIP-04 not supported by signer');
|
|
||||||
}
|
|
||||||
return this.#signer.nip04.decrypt(pubkey, ciphertext);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { type NostrSigner } from '@soapbox/nspec';
|
|
||||||
|
|
||||||
import { SoapboxSigner } from './SoapboxSigner';
|
|
||||||
|
|
||||||
let signer: NostrSigner | undefined;
|
|
||||||
|
|
||||||
try {
|
|
||||||
signer = new SoapboxSigner();
|
|
||||||
} catch (_) {
|
|
||||||
// No signer available
|
|
||||||
}
|
|
||||||
|
|
||||||
export { signer };
|
|
Loading…
Reference in New Issue