Skip to content

Commit 4f66ca8

Browse files
committed
pool: add AuthenticationMiddleware
- Add `AuthenticationMiddleware` trait - Move signer from `SharedState` to `Client` Signed-off-by: Yuki Kishimoto <yukikishimoto@protonmail.com>
1 parent c4c7beb commit 4f66ca8

File tree

10 files changed

+192
-77
lines changed

10 files changed

+192
-77
lines changed

crates/nostr-relay-pool/src/policy.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use std::fmt;
88

99
use nostr::util::BoxedFuture;
10-
use nostr::{Event, RelayUrl, SubscriptionId};
10+
use nostr::{Event, EventBuilder, RelayUrl, SubscriptionId};
1111

1212
/// Policy Error
1313
#[derive(Debug)]
@@ -96,3 +96,24 @@ pub trait AdmitPolicy: fmt::Debug + Send + Sync {
9696
Box::pin(async move { Ok(AdmitStatus::Success) })
9797
}
9898
}
99+
100+
/// Authentication middleware
101+
///
102+
/// <https://github.com/nostr-protocol/nips/blob/master/42.md>
103+
pub trait AuthenticationMiddleware: fmt::Debug + Send + Sync {
104+
/// Check if the middleware is ready for authentication
105+
///
106+
/// If the middleware doesn't have a signer yet, this will return `false`.
107+
fn is_ready(&self) -> BoxedFuture<'_, bool>;
108+
109+
/// Build authentication [`Event`].
110+
///
111+
/// Takes an [`EventBuilder`] and returns the signed [`Event`] for authenticating to the relay.
112+
///
113+
/// <https://github.com/nostr-protocol/nips/blob/master/42.md>
114+
fn authenticate<'a>(
115+
&'a self,
116+
relay_url: &'a RelayUrl,
117+
builder: EventBuilder,
118+
) -> BoxedFuture<'a, Result<Event, PolicyError>>;
119+
}

crates/nostr-relay-pool/src/pool/builder.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66
77
use std::sync::Arc;
88

9-
use nostr::NostrSigner;
109
use nostr_database::{MemoryDatabase, NostrDatabase};
1110

1211
use super::options::RelayPoolOptions;
1312
use super::RelayPool;
1413
use crate::monitor::Monitor;
15-
use crate::policy::AdmitPolicy;
14+
use crate::policy::{AdmitPolicy, AuthenticationMiddleware};
1615
use crate::transport::websocket::{DefaultWebsocketTransport, WebSocketTransport};
1716

1817
/// Relay Pool builder
@@ -22,26 +21,28 @@ pub struct RelayPoolBuilder {
2221
pub websocket_transport: Arc<dyn WebSocketTransport>,
2322
/// Admission policy
2423
pub admit_policy: Option<Arc<dyn AdmitPolicy>>,
24+
/// Authentication middleware
25+
///
26+
/// <https://github.com/nostr-protocol/nips/blob/master/42.md>
27+
pub auth_middleware: Option<Arc<dyn AuthenticationMiddleware>>,
2528
/// Relay monitor
2629
pub monitor: Option<Monitor>,
2730
/// Relay pool options
2831
pub opts: RelayPoolOptions,
2932
// Private stuff
3033
#[doc(hidden)]
3134
pub __database: Arc<dyn NostrDatabase>,
32-
#[doc(hidden)]
33-
pub __signer: Option<Arc<dyn NostrSigner>>,
3435
}
3536

3637
impl Default for RelayPoolBuilder {
3738
fn default() -> Self {
3839
Self {
3940
websocket_transport: Arc::new(DefaultWebsocketTransport),
4041
admit_policy: None,
42+
auth_middleware: None,
4143
monitor: None,
4244
opts: RelayPoolOptions::default(),
4345
__database: Arc::new(MemoryDatabase::default()),
44-
__signer: None,
4546
}
4647
}
4748
}

crates/nostr-relay-pool/src/pool/inner.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ impl InnerRelayPool {
5252
state: SharedState::new(
5353
builder.__database,
5454
builder.websocket_transport,
55-
builder.__signer,
5655
builder.admit_policy,
56+
builder.auth_middleware,
5757
builder.opts.nip42_auto_authentication,
5858
builder.monitor,
5959
),

crates/nostr-relay-pool/src/relay/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ pub enum Error {
114114
},
115115
/// Auth failed
116116
AuthenticationFailed,
117+
/// Authentication middleware not set
118+
AuthMiddlewareNotSet,
117119
/// Premature exit
118120
PrematureExit,
119121
}
@@ -178,6 +180,7 @@ impl fmt::Display for Error {
178180
current.as_millis()
179181
),
180182
Self::AuthenticationFailed => write!(f, "authentication failed"),
183+
Self::AuthMiddlewareNotSet => write!(f, "authentication middleware not set"),
181184
Self::PrematureExit => write!(f, "premature exit"),
182185
}
183186
}

crates/nostr-relay-pool/src/relay/inner.rs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use super::{
3434
Error, Reconciliation, RelayNotification, RelayStatus, SubscriptionActivity,
3535
SubscriptionAutoClosedReason,
3636
};
37-
use crate::policy::AdmitStatus;
37+
use crate::policy::{AdmitStatus, AuthenticationMiddleware};
3838
use crate::pool::RelayPoolNotification;
3939
use crate::relay::status::AtomicRelayStatus;
4040
use crate::shared::SharedState;
@@ -988,6 +988,7 @@ impl InnerRelay {
988988
);
989989

990990
// Check if NIP42 auto authentication is enabled
991+
// TODO: check also if middleware is set?
991992
if self.state.is_auto_authentication_enabled() {
992993
// Forward action to ingester
993994
let _ = ingester_tx.send(IngesterCommand::Authenticate {
@@ -1217,13 +1218,18 @@ impl InnerRelay {
12171218
}
12181219

12191220
async fn auth(&self, challenge: String) -> Result<(), Error> {
1220-
// Get signer
1221-
let signer = self.state.signer().await?;
1222-
1223-
// Construct event
1224-
let event: Event = EventBuilder::auth(challenge, self.url.clone())
1225-
.sign(&signer)
1226-
.await?;
1221+
// Get middleware
1222+
let middleware: &Arc<dyn AuthenticationMiddleware> = self
1223+
.state
1224+
.auth_middleware
1225+
.as_ref()
1226+
.ok_or(Error::AuthMiddlewareNotSet)?;
1227+
1228+
// Construct event builder
1229+
let builder: EventBuilder = EventBuilder::auth(challenge, self.url.clone());
1230+
1231+
// Create the authentication event
1232+
let event: Event = middleware.authenticate(&self.url, builder).await?;
12271233

12281234
// Subscribe to notifications
12291235
let mut notifications = self.internal_notification_sender.subscribe();

crates/nostr-relay-pool/src/relay/mod.rs

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -407,22 +407,29 @@ impl Relay {
407407

408408
// If auth required, wait for authentication adn resend it
409409
if let Some(MachineReadablePrefix::AuthRequired) = MachineReadablePrefix::parse(&message) {
410-
// Check if NIP42 auth is enabled and signer is set
411-
let has_signer: bool = self.inner.state.has_signer().await;
412-
if self.inner.state.is_auto_authentication_enabled() && has_signer {
413-
// Wait that relay authenticate
414-
self.wait_for_authentication(&mut notifications, WAIT_FOR_AUTHENTICATION_TIMEOUT)
410+
// Check if NIP42 auth is enabled and middleware is set
411+
if let Some(middleware) = &self.inner.state.auth_middleware {
412+
let is_enabled: bool = self.inner.state.is_auto_authentication_enabled();
413+
let is_ready: bool = middleware.is_ready().await;
414+
415+
if is_enabled && is_ready {
416+
// Wait that relay authenticate
417+
self.wait_for_authentication(
418+
&mut notifications,
419+
WAIT_FOR_AUTHENTICATION_TIMEOUT,
420+
)
415421
.await?;
416422

417-
// Try to resend event
418-
let (status, message) = self._send_event(&mut notifications, event).await?;
423+
// Try to resend event
424+
let (status, message) = self._send_event(&mut notifications, event).await?;
419425

420-
// Check status
421-
return if status {
422-
Ok(event.id)
423-
} else {
424-
Err(Error::RelayMessage(message))
425-
};
426+
// Check status
427+
return if status {
428+
Ok(event.id)
429+
} else {
430+
Err(Error::RelayMessage(message))
431+
};
432+
}
426433
}
427434
}
428435

@@ -746,9 +753,10 @@ mod tests {
746753

747754
use async_utility::time;
748755
use nostr_relay_builder::prelude::*;
756+
use tokio::sync::RwLock;
749757

750758
use super::{Error, *};
751-
use crate::policy::{AdmitPolicy, PolicyError};
759+
use crate::policy::{AdmitPolicy, AuthenticationMiddleware, PolicyError};
752760

753761
#[derive(Debug)]
754762
struct CustomTestPolicy {
@@ -770,10 +778,55 @@ mod tests {
770778
}
771779
}
772780

781+
#[derive(Debug, Default)]
782+
struct AuthenticationPolicy {
783+
signer: RwLock<Option<Arc<dyn NostrSigner>>>,
784+
}
785+
786+
impl AuthenticationPolicy {
787+
async fn set_signer(&self, signer: Option<Arc<dyn NostrSigner>>) {
788+
let mut s = self.signer.write().await;
789+
*s = signer;
790+
}
791+
}
792+
793+
impl AuthenticationMiddleware for AuthenticationPolicy {
794+
fn is_ready(&self) -> BoxedFuture<'_, bool> {
795+
Box::pin(async move { self.signer.read().await.is_some() })
796+
}
797+
798+
fn authenticate<'a>(
799+
&'a self,
800+
_relay_url: &'a RelayUrl,
801+
builder: EventBuilder,
802+
) -> BoxedFuture<'a, Result<Event, PolicyError>> {
803+
Box::pin(async move {
804+
let signer = self.signer.read().await;
805+
806+
match signer.as_ref() {
807+
Some(signer) => builder.sign(signer).await.map_err(PolicyError::backend),
808+
None => {
809+
return Err(PolicyError::backend(Error::AuthenticationFailed));
810+
}
811+
}
812+
})
813+
}
814+
}
815+
773816
fn new_relay(url: RelayUrl, opts: RelayOptions) -> Relay {
774817
Relay::new(url, SharedState::default(), opts)
775818
}
776819

820+
fn new_relay_with_auth_middleware(
821+
url: RelayUrl,
822+
middleware: Arc<dyn AuthenticationMiddleware>,
823+
opts: RelayOptions,
824+
) -> Relay {
825+
let mut state: SharedState = SharedState::default();
826+
state.auth_middleware = Some(middleware);
827+
Relay::new(url, state, opts)
828+
}
829+
777830
/// Setup public (without NIP42 auth) relay with N events to test event fetching
778831
///
779832
/// **Adds ONLY text notes**
@@ -1161,7 +1214,10 @@ mod tests {
11611214
let mock = LocalRelay::run(builder).await.unwrap();
11621215
let url = RelayUrl::parse(&mock.url()).unwrap();
11631216

1164-
let relay: Relay = new_relay(url, RelayOptions::default());
1217+
let middleware = Arc::new(AuthenticationPolicy::default());
1218+
1219+
let relay: Relay =
1220+
new_relay_with_auth_middleware(url, middleware.clone(), RelayOptions::default());
11651221

11661222
relay.inner.state.automatic_authentication(true);
11671223

@@ -1185,7 +1241,7 @@ mod tests {
11851241
}
11861242

11871243
// Set a signer
1188-
relay.inner.state.set_signer(keys.clone()).await;
1244+
middleware.set_signer(Some(Arc::new(keys.clone()))).await;
11891245

11901246
// Send as authenticated
11911247
let event = EventBuilder::text_note("Test")
@@ -1204,7 +1260,10 @@ mod tests {
12041260
let mock = LocalRelay::run(builder).await.unwrap();
12051261
let url = RelayUrl::parse(&mock.url()).unwrap();
12061262

1207-
let relay: Relay = new_relay(url, RelayOptions::default());
1263+
let middleware = Arc::new(AuthenticationPolicy::default());
1264+
1265+
let relay: Relay =
1266+
new_relay_with_auth_middleware(url, middleware.clone(), RelayOptions::default());
12081267

12091268
relay.connect();
12101269

@@ -1256,7 +1315,7 @@ mod tests {
12561315
assert!(matches!(err, Error::AuthenticationFailed));
12571316

12581317
// Set a signer
1259-
relay.inner.state.set_signer(keys).await;
1318+
middleware.set_signer(Some(Arc::new(keys))).await;
12601319

12611320
// Authenticated fetch
12621321
let res = relay

0 commit comments

Comments
 (0)