mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-09-28 04:27:35 +02:00
Add "Proactively authenticate to relays" option to privacy settings, defaults to off
Fix automatically disconnecting from authenticated relays
This commit is contained in:
5
.changeset/rotten-garlics-beg.md
Normal file
5
.changeset/rotten-garlics-beg.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Add "Proactively authenticate to relays" option to privacy settings, defaults to off
|
5
.changeset/swift-trainers-fold.md
Normal file
5
.changeset/swift-trainers-fold.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"nostrudel": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix automatically disconnecting from authenticated relays
|
@@ -10,6 +10,7 @@ import { offlineMode } from "../services/offline-mode";
|
|||||||
import processManager from "../services/process-manager";
|
import processManager from "../services/process-manager";
|
||||||
import signingService from "../services/signing";
|
import signingService from "../services/signing";
|
||||||
import accountService from "../services/account";
|
import accountService from "../services/account";
|
||||||
|
import localSettings from "../services/local-settings";
|
||||||
|
|
||||||
export type Notice = {
|
export type Notice = {
|
||||||
message: string;
|
message: string;
|
||||||
@@ -20,6 +21,8 @@ export type Notice = {
|
|||||||
export type RelayAuthMode = "always" | "ask" | "never";
|
export type RelayAuthMode = "always" | "ask" | "never";
|
||||||
|
|
||||||
export default class RelayPool {
|
export default class RelayPool {
|
||||||
|
log = logger.extend("RelayPool");
|
||||||
|
|
||||||
relays = new Map<string, AbstractRelay>();
|
relays = new Map<string, AbstractRelay>();
|
||||||
onRelayCreated = new Subject<AbstractRelay>();
|
onRelayCreated = new Subject<AbstractRelay>();
|
||||||
onRelayChallenge = new Subject<[AbstractRelay, string]>();
|
onRelayChallenge = new Subject<[AbstractRelay, string]>();
|
||||||
@@ -35,8 +38,6 @@ export default class RelayPool {
|
|||||||
|
|
||||||
authenticated = new SuperMap<AbstractRelay, Subject<boolean>>(() => new Subject());
|
authenticated = new SuperMap<AbstractRelay, Subject<boolean>>(() => new Subject());
|
||||||
|
|
||||||
log = logger.extend("RelayPool");
|
|
||||||
|
|
||||||
getRelay(relayOrUrl: string | URL | AbstractRelay) {
|
getRelay(relayOrUrl: string | URL | AbstractRelay) {
|
||||||
if (typeof relayOrUrl === "string") {
|
if (typeof relayOrUrl === "string") {
|
||||||
const safeURL = safeRelayUrl(relayOrUrl);
|
const safeURL = safeRelayUrl(relayOrUrl);
|
||||||
@@ -69,10 +70,7 @@ export default class RelayPool {
|
|||||||
const key = url.toString();
|
const key = url.toString();
|
||||||
if (!this.relays.has(key)) {
|
if (!this.relays.has(key)) {
|
||||||
const r = new AbstractRelay(key, { verifyEvent: verifyEventMethod });
|
const r = new AbstractRelay(key, { verifyEvent: verifyEventMethod });
|
||||||
r._onauth = (challenge) => {
|
r._onauth = (challenge) => this.handleRelayChallenge(r, challenge);
|
||||||
this.onRelayChallenge.next([r, challenge]);
|
|
||||||
this.challenges.get(r).next(challenge);
|
|
||||||
};
|
|
||||||
r.onnotice = (notice) => this.handleRelayNotice(r, notice);
|
r.onnotice = (notice) => this.handleRelayNotice(r, notice);
|
||||||
|
|
||||||
this.relays.set(key, r);
|
this.relays.set(key, r);
|
||||||
@@ -130,7 +128,7 @@ export default class RelayPool {
|
|||||||
let relay = this.getRelay(relayOrUrl);
|
let relay = this.getRelay(relayOrUrl);
|
||||||
if (!relay) return;
|
if (!relay) return;
|
||||||
|
|
||||||
const defaultMode = (localStorage.getItem(`default-auth-mode`) as RelayAuthMode) ?? undefined;
|
const defaultMode = localSettings.defaultAuthenticationMode.value;
|
||||||
const mode = (localStorage.getItem(this.getRelayAuthStorageKey(relay)) as RelayAuthMode) ?? undefined;
|
const mode = (localStorage.getItem(this.getRelayAuthStorageKey(relay)) as RelayAuthMode) ?? undefined;
|
||||||
|
|
||||||
return mode || defaultMode;
|
return mode || defaultMode;
|
||||||
@@ -189,6 +187,30 @@ export default class RelayPool {
|
|||||||
return this.authForSubscribe.get(relay).value !== false;
|
return this.authForSubscribe.get(relay).value !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private automaticallyAuthenticate(relay: AbstractRelay) {
|
||||||
|
const authMode = this.getRelayAuthMode(relay);
|
||||||
|
// only automatically authenticate if auth mode is set to "always"
|
||||||
|
if (authMode === "always") {
|
||||||
|
const account = accountService.current.value;
|
||||||
|
if (!account) return;
|
||||||
|
|
||||||
|
this.authenticate(relay, (draft) => {
|
||||||
|
return signingService.requestSignature(draft, account);
|
||||||
|
}).then(() => {
|
||||||
|
this.log(`Automatically authenticated to ${relay.url}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleRelayChallenge(relay: AbstractRelay, challenge: string) {
|
||||||
|
this.onRelayChallenge.next([relay, challenge]);
|
||||||
|
this.challenges.get(relay).next(challenge);
|
||||||
|
|
||||||
|
if (localSettings.proactivelyAuthenticate.value) {
|
||||||
|
this.automaticallyAuthenticate(relay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleRelayNotice(relay: AbstractRelay, message: string) {
|
handleRelayNotice(relay: AbstractRelay, message: string) {
|
||||||
const subject = this.notices.get(relay);
|
const subject = this.notices.get(relay);
|
||||||
subject.next([...subject.value, { message, date: dayjs().unix(), relay }]);
|
subject.next([...subject.value, { message, date: dayjs().unix(), relay }]);
|
||||||
@@ -197,18 +219,8 @@ export default class RelayPool {
|
|||||||
const authForSubscribe = this.authForSubscribe.get(relay);
|
const authForSubscribe = this.authForSubscribe.get(relay);
|
||||||
if (!authForSubscribe.value) authForSubscribe.next(true);
|
if (!authForSubscribe.value) authForSubscribe.next(true);
|
||||||
|
|
||||||
const account = accountService.current.value;
|
// try to authenticate
|
||||||
if (account) {
|
this.automaticallyAuthenticate(relay);
|
||||||
const authMode = this.getRelayAuthMode(relay);
|
|
||||||
|
|
||||||
if (authMode === "always") {
|
|
||||||
this.authenticate(relay, (draft) => {
|
|
||||||
return signingService.requestSignature(draft, account);
|
|
||||||
}).then(() => {
|
|
||||||
this.log(`Automatically authenticated to ${relay.url}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,6 +228,9 @@ export default class RelayPool {
|
|||||||
for (const [url, relay] of this.relays) {
|
for (const [url, relay] of this.relays) {
|
||||||
if (!relay.connected) continue;
|
if (!relay.connected) continue;
|
||||||
|
|
||||||
|
// don't disconnect from authenticated relays
|
||||||
|
if (this.authenticated.get(relay).value) continue;
|
||||||
|
|
||||||
let disconnect = true;
|
let disconnect = true;
|
||||||
for (const process of processManager.processes) {
|
for (const process of processManager.processes) {
|
||||||
if (process.active && process.relays.has(relay)) {
|
if (process.active && process.relays.has(relay)) {
|
||||||
|
@@ -51,6 +51,10 @@ const addClientTag = new BooleanLocalStorageEntry("add-client-tag", false);
|
|||||||
const verifyEventMethod = new LocalStorageEntry("verify-event-method", "wasm"); // wasm, internal, none
|
const verifyEventMethod = new LocalStorageEntry("verify-event-method", "wasm"); // wasm, internal, none
|
||||||
const enableKeyboardShortcuts = new BooleanLocalStorageEntry("enable-keyboard-shortcuts", true);
|
const enableKeyboardShortcuts = new BooleanLocalStorageEntry("enable-keyboard-shortcuts", true);
|
||||||
|
|
||||||
|
// privacy
|
||||||
|
const defaultAuthenticationMode = new LocalStorageEntry("default-relay-auth-mode", "ask"); // ask, always, never
|
||||||
|
const proactivelyAuthenticate = new BooleanLocalStorageEntry("proactively-authenticate", false);
|
||||||
|
|
||||||
// display settings
|
// display settings
|
||||||
const showBrandLogo = new BooleanLocalStorageEntry("show-brand-logo", true);
|
const showBrandLogo = new BooleanLocalStorageEntry("show-brand-logo", true);
|
||||||
|
|
||||||
@@ -66,6 +70,8 @@ const localSettings = {
|
|||||||
verifyEventMethod,
|
verifyEventMethod,
|
||||||
enableKeyboardShortcuts,
|
enableKeyboardShortcuts,
|
||||||
showBrandLogo,
|
showBrandLogo,
|
||||||
|
defaultAuthenticationMode,
|
||||||
|
proactivelyAuthenticate,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (import.meta.env.DEV) {
|
if (import.meta.env.DEV) {
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { useLocalStorage } from "react-use";
|
|
||||||
import {
|
import {
|
||||||
Flex,
|
Flex,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -18,6 +17,8 @@ import { createRequestProxyUrl } from "../../../helpers/request";
|
|||||||
import { RelayAuthMode } from "../../../classes/relay-pool";
|
import { RelayAuthMode } from "../../../classes/relay-pool";
|
||||||
import VerticalPageLayout from "../../../components/vertical-page-layout";
|
import VerticalPageLayout from "../../../components/vertical-page-layout";
|
||||||
import useSettingsForm from "../use-settings-form";
|
import useSettingsForm from "../use-settings-form";
|
||||||
|
import useSubject from "../../../hooks/use-subject";
|
||||||
|
import localSettings from "../../../services/local-settings";
|
||||||
|
|
||||||
async function validateInvidiousUrl(url?: string) {
|
async function validateInvidiousUrl(url?: string) {
|
||||||
if (!url) return true;
|
if (!url) return true;
|
||||||
@@ -44,9 +45,8 @@ async function validateRequestProxy(url?: string) {
|
|||||||
export default function PrivacySettings() {
|
export default function PrivacySettings() {
|
||||||
const { register, submit, formState } = useSettingsForm();
|
const { register, submit, formState } = useSettingsForm();
|
||||||
|
|
||||||
const [defaultAuthMode, setDefaultAuthMode] = useLocalStorage<RelayAuthMode>("default-relay-auth-mode", "ask", {
|
const defaultAuthenticationMode = useSubject(localSettings.defaultAuthenticationMode);
|
||||||
raw: true,
|
const proactivelyAuthenticate = useSubject(localSettings.proactivelyAuthenticate);
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VerticalPageLayout as="form" onSubmit={submit} flex={1}>
|
<VerticalPageLayout as="form" onSubmit={submit} flex={1}>
|
||||||
@@ -58,8 +58,8 @@ export default function PrivacySettings() {
|
|||||||
w="xs"
|
w="xs"
|
||||||
rounded="md"
|
rounded="md"
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
value={defaultAuthMode || "ask"}
|
value={defaultAuthenticationMode}
|
||||||
onChange={(e) => setDefaultAuthMode(e.target.value as RelayAuthMode)}
|
onChange={(e) => localSettings.defaultAuthenticationMode.next(e.target.value as RelayAuthMode)}
|
||||||
>
|
>
|
||||||
<option value="always">Always authenticate</option>
|
<option value="always">Always authenticate</option>
|
||||||
<option value="ask">Ask every time</option>
|
<option value="ask">Ask every time</option>
|
||||||
@@ -68,6 +68,22 @@ export default function PrivacySettings() {
|
|||||||
<FormHelperText>How should the app handle relays requesting identification</FormHelperText>
|
<FormHelperText>How should the app handle relays requesting identification</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
|
<FormControl>
|
||||||
|
<Flex alignItems="center">
|
||||||
|
<FormLabel htmlFor="proactivelyAuthenticate" mb="0">
|
||||||
|
Proactively authenticate to relays
|
||||||
|
</FormLabel>
|
||||||
|
<Switch
|
||||||
|
id="proactivelyAuthenticate"
|
||||||
|
isChecked={proactivelyAuthenticate}
|
||||||
|
onChange={(e) => localSettings.proactivelyAuthenticate.next(e.currentTarget.checked)}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
<FormHelperText>
|
||||||
|
<span>Authenticate to relays as soon as they send the authentication challenge</span>
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
<FormControl isInvalid={!!formState.errors.twitterRedirect}>
|
<FormControl isInvalid={!!formState.errors.twitterRedirect}>
|
||||||
<FormLabel>Nitter instance</FormLabel>
|
<FormLabel>Nitter instance</FormLabel>
|
||||||
<Input
|
<Input
|
||||||
|
@@ -32,6 +32,8 @@ import useRouteSearchValue from "../../../hooks/use-route-search-value";
|
|||||||
import processManager from "../../../services/process-manager";
|
import processManager from "../../../services/process-manager";
|
||||||
import { RelayAuthMode } from "../../../classes/relay-pool";
|
import { RelayAuthMode } from "../../../classes/relay-pool";
|
||||||
import Timestamp from "../../../components/timestamp";
|
import Timestamp from "../../../components/timestamp";
|
||||||
|
import localSettings from "../../../services/local-settings";
|
||||||
|
import useSubject from "../../../hooks/use-subject";
|
||||||
|
|
||||||
function RelayCard({ relay }: { relay: AbstractRelay }) {
|
function RelayCard({ relay }: { relay: AbstractRelay }) {
|
||||||
return (
|
return (
|
||||||
@@ -50,11 +52,15 @@ function RelayCard({ relay }: { relay: AbstractRelay }) {
|
|||||||
function RelayAuthCard({ relay }: { relay: AbstractRelay }) {
|
function RelayAuthCard({ relay }: { relay: AbstractRelay }) {
|
||||||
const { authenticated } = useRelayAuthMethod(relay);
|
const { authenticated } = useRelayAuthMethod(relay);
|
||||||
|
|
||||||
|
const defaultMode = useSubject(localSettings.defaultAuthenticationMode);
|
||||||
|
|
||||||
const processes = processManager.getRootProcessesForRelay(relay);
|
const processes = processManager.getRootProcessesForRelay(relay);
|
||||||
const [authMode, setAuthMode] = useLocalStorage<RelayAuthMode>(
|
const [authMode, setAuthMode] = useLocalStorage<RelayAuthMode | "">(
|
||||||
relayPoolService.getRelayAuthStorageKey(relay),
|
relayPoolService.getRelayAuthStorageKey(relay),
|
||||||
"ask",
|
"",
|
||||||
{ raw: true },
|
{
|
||||||
|
raw: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -74,9 +80,10 @@ function RelayAuthCard({ relay }: { relay: AbstractRelay }) {
|
|||||||
w="auto"
|
w="auto"
|
||||||
rounded="md"
|
rounded="md"
|
||||||
flexShrink={0}
|
flexShrink={0}
|
||||||
value={authMode || "ask"}
|
value={authMode}
|
||||||
onChange={(e) => setAuthMode(e.target.value as RelayAuthMode)}
|
onChange={(e) => setAuthMode(e.target.value as RelayAuthMode)}
|
||||||
>
|
>
|
||||||
|
<option value="">Default ({defaultMode})</option>
|
||||||
<option value="always">Always</option>
|
<option value="always">Always</option>
|
||||||
<option value="ask">Ask</option>
|
<option value="ask">Ask</option>
|
||||||
<option value="never">Never</option>
|
<option value="never">Never</option>
|
||||||
|
Reference in New Issue
Block a user