use crate::Jwt;
#[derive(Debug, thiserror::Error)]
pub enum CredentialsProviderError {
#[error("session expired; please login using `rerun auth login`")]
SessionExpired,
#[error("{0}")]
Custom(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
}
impl CredentialsProviderError {
pub fn custom(inner: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
Self::Custom(inner.into())
}
}
#[async_trait::async_trait]
pub trait CredentialsProvider: std::fmt::Debug {
async fn get_token(&self) -> Result<Option<Jwt>, CredentialsProviderError>;
}
#[derive(Debug)]
pub struct StaticCredentialsProvider {
token: Jwt,
}
impl StaticCredentialsProvider {
pub fn new(token: Jwt) -> Self {
Self { token }
}
}
#[async_trait::async_trait]
impl CredentialsProvider for StaticCredentialsProvider {
async fn get_token(&self) -> Result<Option<Jwt>, CredentialsProviderError> {
Ok(Some(self.token.clone()))
}
}
#[cfg(feature = "oauth")]
pub use oauth::{CliCredentialsProvider, subscribe_auth_changes};
#[cfg(feature = "oauth")]
pub(crate) mod oauth {
use super::{CredentialsProvider, CredentialsProviderError, Jwt};
use crate::oauth;
use crate::oauth::{Credentials, load_and_refresh_credentials};
use tokio::sync::RwLock;
static CACHE: RwLock<Option<Credentials>> = RwLock::const_new(None);
type AuthCallback = Box<dyn Fn(Option<oauth::User>) + Send>;
static AUTH_SUBSCRIBERS: parking_lot::Mutex<Vec<AuthCallback>> =
parking_lot::Mutex::new(Vec::new());
pub(crate) fn auth_update(user: Option<&oauth::User>) {
let subscribers = AUTH_SUBSCRIBERS.lock();
for sub in &*subscribers {
sub(user.cloned());
}
}
pub fn subscribe_auth_changes(callback: impl Fn(Option<oauth::User>) + Send + 'static) {
let mut subscribers = AUTH_SUBSCRIBERS.lock();
subscribers.push(Box::new(callback));
}
pub(crate) fn clear_cache() {
*CACHE.blocking_write() = None;
}
#[derive(Debug, Default)]
pub struct CliCredentialsProvider {
_private: (),
}
impl CliCredentialsProvider {
pub fn new() -> Self {
Self::default()
}
}
#[async_trait::async_trait]
impl CredentialsProvider for CliCredentialsProvider {
async fn get_token(&self) -> Result<Option<Jwt>, CredentialsProviderError> {
{
if let Some(credentials) = &*CACHE.read().await
&& !credentials.access_token().is_expired()
{
return Ok(Some(credentials.access_token().jwt()));
}
}
let mut cache = CACHE.write().await;
if let Some(credentials) = &*cache
&& !credentials.access_token().is_expired()
{
return Ok(Some(credentials.access_token().jwt()));
}
re_log::debug!("loading and refreshing credentials");
match load_and_refresh_credentials().await {
Ok(Some(credentials)) => {
let token = credentials.access_token().jwt();
auth_update(Some(credentials.user()));
*cache = Some(credentials);
Ok(Some(token))
}
Ok(None) => {
re_log::debug!("no credentials available");
auth_update(None);
Ok(None)
}
Err(err) => {
auth_update(None);
Err(CredentialsProviderError::Custom(err.into()))
}
}
}
}
}