use url::Url;
use pubky_common::crypto::random_bytes;
use crate::{
AuthToken, Capabilities, PubkyHttpClient, PubkySession, PublicKey,
actors::{
DEFAULT_HTTP_RELAY_INBOX,
auth::{
auth_subscription::AuthSubscription,
deep_links::{DeepLink, SigninDeepLink, SignupDeepLink},
},
},
errors::Result,
};
#[derive(Debug)]
pub struct PubkyAuthFlow {
subscription: AuthSubscription,
auth_url: DeepLink,
}
impl PubkyAuthFlow {
pub fn start(caps: &Capabilities, auth_kind: AuthFlowKind) -> Result<Self> {
PubkyAuthFlowBuilder::new(caps.clone(), auth_kind).start()
}
#[must_use]
pub fn builder(caps: &Capabilities, auth_kind: AuthFlowKind) -> PubkyAuthFlowBuilder {
PubkyAuthFlowBuilder::new(caps.clone(), auth_kind)
}
#[must_use]
pub fn authorization_url(&self) -> Url {
self.auth_url.clone().into()
}
pub async fn await_approval(self) -> Result<PubkySession> {
self.subscription.await_approval().await
}
pub async fn await_token(self) -> Result<AuthToken> {
self.subscription.await_token().await
}
pub async fn try_poll_once(&self) -> Result<Option<PubkySession>> {
self.subscription.try_poll_once().await
}
#[must_use]
pub fn try_token(&self) -> Option<Result<AuthToken>> {
self.subscription.try_token()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AuthFlowKind {
SignIn,
SignUp {
homeserver_public_key: Box<PublicKey>,
signup_token: Option<String>,
},
}
impl AuthFlowKind {
#[must_use]
pub fn signin() -> Self {
Self::SignIn
}
#[must_use]
pub fn signup(homeserver_public_key: PublicKey, signup_token: Option<String>) -> Self {
Self::SignUp {
homeserver_public_key: Box::new(homeserver_public_key),
signup_token,
}
}
}
#[derive(Debug, Clone)]
pub struct PubkyAuthFlowBuilder {
caps: Capabilities,
base_relay: Url,
client: Option<PubkyHttpClient>,
auth_kind: AuthFlowKind,
client_secret: [u8; 32],
}
impl PubkyAuthFlowBuilder {
pub(crate) fn new(caps: Capabilities, auth_kind: AuthFlowKind) -> Self {
Self {
caps,
base_relay: Url::parse(DEFAULT_HTTP_RELAY_INBOX)
.expect("Should be able to parse the default HTTP relay"),
client: None,
auth_kind,
client_secret: random_bytes::<32>(),
}
}
pub fn relay(mut self, url: Url) -> Self {
self.base_relay = url;
self
}
pub fn client(mut self, client: PubkyHttpClient) -> Self {
self.client = Some(client);
self
}
pub fn client_secret(mut self, client_secret: [u8; 32]) -> Self {
self.client_secret = client_secret;
self
}
pub fn start(self) -> Result<PubkyAuthFlow> {
let client = match &self.client {
Some(c) => c.clone(),
None => PubkyHttpClient::new()?,
};
let auth_url = self.create_url();
let subscription = AuthSubscription::builder(self.client_secret)
.relay_base_url(self.base_relay)
.client(client)
.start()?;
Ok(PubkyAuthFlow {
subscription,
auth_url,
})
}
fn create_url(&self) -> DeepLink {
match &self.auth_kind {
AuthFlowKind::SignIn => DeepLink::Signin(SigninDeepLink::new(
self.caps.clone(),
self.base_relay.clone(),
self.client_secret,
)),
AuthFlowKind::SignUp {
homeserver_public_key,
signup_token,
} => DeepLink::Signup(SignupDeepLink::new(
self.caps.clone(),
self.base_relay.clone(),
self.client_secret,
*homeserver_public_key.clone(),
signup_token.clone(),
)),
}
}
}