Skip to main content

arcly_stream/
auth.rs

1//! Stream-level authentication and authorization.
2//!
3//! Like [`Observer`](crate::Observer), authorization is an **injected trait with
4//! a permit-all default** — the engine never bakes in an identity scheme. A host
5//! supplies a [`StreamAuthenticator`] (validating stream keys, signed tokens,
6//! IP allow-lists, an external auth service, …) and the engine enforces it on
7//! the publish and play paths.
8//!
9//! ```no_run
10//! use arcly_stream::auth::{Credentials, StreamAuthenticator};
11//! use arcly_stream::prelude::*;
12//! use std::sync::Arc;
13//!
14//! struct KeyAuth { secret: String }
15//!
16//! #[async_trait]
17//! impl StreamAuthenticator for KeyAuth {
18//!     async fn authorize_publish(&self, _key: &StreamKey, creds: &Credentials) -> Result<()> {
19//!         match creds.token.as_deref() {
20//!             Some(t) if t == self.secret => Ok(()),
21//!             _ => Err(StreamError::Unauthorized("bad publish key".into())),
22//!         }
23//!     }
24//! }
25//!
26//! let engine = Engine::builder()
27//!     .application(AppSpec::new("live"))
28//!     .authenticator(KeyAuth { secret: "s3cr3t".into() })
29//!     .build();
30//! # let _ = engine;
31//! ```
32
33use crate::{Result, StreamKey};
34use async_trait::async_trait;
35use std::net::SocketAddr;
36
37/// Credentials presented by a connecting publisher or player.
38///
39/// Protocol handlers populate whichever fields their transport carries (an RTMP
40/// stream key in `token`, a WHIP bearer in `token`, the peer address in `addr`,
41/// query parameters in `params`).
42#[derive(Debug, Default, Clone)]
43pub struct Credentials {
44    /// Opaque secret: a stream key, signed URL token, or bearer credential.
45    pub token: Option<String>,
46    /// Remote peer address, when the transport exposes one.
47    pub addr: Option<SocketAddr>,
48    /// Arbitrary protocol-supplied key/value parameters (e.g. query string).
49    pub params: Vec<(String, String)>,
50}
51
52impl Credentials {
53    /// Credentials carrying only a token (the common stream-key case).
54    pub fn token(token: impl Into<String>) -> Self {
55        Self {
56            token: Some(token.into()),
57            ..Self::default()
58        }
59    }
60
61    /// Look up a protocol parameter by key.
62    pub fn param(&self, key: &str) -> Option<&str> {
63        self.params
64            .iter()
65            .find(|(k, _)| k == key)
66            .map(|(_, v)| v.as_str())
67    }
68}
69
70/// Authorizes publish and play attempts. Both methods **default to permit**, so
71/// an implementor overrides only the side it gates.
72#[async_trait]
73pub trait StreamAuthenticator: Send + Sync + 'static {
74    /// Decide whether `creds` may publish to `key`. Return
75    /// [`StreamError::Unauthorized`](crate::StreamError::Unauthorized) to reject.
76    async fn authorize_publish(&self, _key: &StreamKey, _creds: &Credentials) -> Result<()> {
77        Ok(())
78    }
79
80    /// Decide whether `creds` may subscribe to `key`.
81    async fn authorize_play(&self, _key: &StreamKey, _creds: &Credentials) -> Result<()> {
82        Ok(())
83    }
84}
85
86/// The default authenticator: permits everything. Selected when the builder is
87/// given none, preserving the engine's zero-policy default.
88#[derive(Debug, Default, Clone, Copy)]
89pub struct AllowAll;
90
91impl StreamAuthenticator for AllowAll {}