use async_trait::async_trait; use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl}; use std::{env, ops::Deref, sync::Arc}; #[async_trait] pub trait OAuthClient { async fn get_token(&self) -> anyhow::Result<()>; } pub struct OAuth(Arc); impl OAuth { pub fn new_zitadel(config: ZitadelConfig) -> Self { Self(Arc::new(ZitadelOAuthClient::from(config))) } pub fn new_noop() -> Self { Self(Arc::new(NoopOAuthClient {})) } } pub enum OAuthConfig { Zitadel(ZitadelConfig), Noop, } impl From for OAuth { fn from(value: OAuthConfig) -> Self { match value { OAuthConfig::Zitadel(c) => c.into(), OAuthConfig::Noop => Self::new_noop(), } } } impl Deref for OAuth { type Target = Arc; fn deref(&self) -> &Self::Target { &self.0 } } impl From for OAuth { fn from(value: ZitadelConfig) -> Self { Self::new_zitadel(value) } } // -- Noop pub struct NoopOAuthClient; #[async_trait] impl OAuthClient for NoopOAuthClient { async fn get_token(&self) -> anyhow::Result<()> { Ok(()) } } // -- Zitadel pub struct ZitadelConfig { client_id: String, client_secret: String, redirect_url: String, auth_url: String, token_url: String, } pub struct ZitadelOAuthClient { client: BasicClient, } impl ZitadelOAuthClient { pub fn new( client_id: impl Into, client_secret: impl Into, redirect_url: impl Into, auth_url: impl Into, token_url: impl Into, ) -> Self { Self { client: Self::oauth_client(ZitadelConfig { client_id: client_id.into(), client_secret: client_secret.into(), redirect_url: redirect_url.into(), auth_url: auth_url.into(), token_url: token_url.into(), }), } } fn oauth_client(config: ZitadelConfig) -> BasicClient { BasicClient::new( ClientId::new(config.client_id), Some(ClientSecret::new(config.client_secret)), AuthUrl::new(config.auth_url).unwrap(), Some(TokenUrl::new(config.token_url).unwrap()), ) .set_redirect_uri(RedirectUrl::new(config.redirect_url).unwrap()) } } impl From for ZitadelOAuthClient { fn from(value: ZitadelConfig) -> Self { Self::new( value.client_id, value.client_secret, value.redirect_url, value.auth_url, value.token_url, ) } } #[async_trait] impl OAuthClient for ZitadelOAuthClient { async fn get_token(&self) -> anyhow::Result<()> { Ok(()) } } #[cfg(test)] mod tests { use crate::oauth::{OAuth, OAuthConfig, ZitadelConfig}; #[tokio::test] async fn test_noop() { OAuth::from(OAuthConfig::Noop).get_token().await.unwrap(); } #[tokio::test] async fn test_zitadel() { OAuth::from(OAuthConfig::Zitadel(ZitadelConfig { client_id: "something".into(), client_secret: "something".into(), redirect_url: "https://something".into(), auth_url: "https://something".into(), token_url: "https://something".into(), })) .get_token() .await .unwrap(); } }