use std::sync::Arc;
use std::time::Duration;
use crate::error::{ConfigError, OAuthError};
use crate::token_store::{InMemoryTokenStore, TokenStore};
#[derive(Clone)]
pub struct OAuthConfig {
client_id: String,
client_secret: String,
issuer: Option<String>,
access_token_ttl: Duration,
refresh_token_ttl: Option<Duration>,
refresh_tokens_enabled: bool,
token_store: Arc<dyn TokenStore>,
}
impl OAuthConfig {
pub fn builder() -> OAuthConfigBuilder {
OAuthConfigBuilder::default()
}
pub fn client_id(&self) -> &str {
&self.client_id
}
pub fn issuer(&self) -> Option<&str> {
self.issuer.as_deref()
}
pub fn access_token_ttl(&self) -> Duration {
self.access_token_ttl
}
pub fn refresh_token_ttl(&self) -> Option<Duration> {
self.refresh_token_ttl
}
pub fn refresh_tokens_enabled(&self) -> bool {
self.refresh_tokens_enabled
}
pub fn token_store(&self) -> Arc<dyn TokenStore> {
Arc::clone(&self.token_store)
}
pub(crate) fn verify_client(
&self,
client_id: &str,
client_secret: &str,
) -> Result<(), OAuthError> {
if client_id != self.client_id || client_secret != self.client_secret {
return Err(OAuthError::InvalidClient);
}
Ok(())
}
}
#[derive(Default)]
pub struct OAuthConfigBuilder {
client_id: Option<String>,
client_secret: Option<String>,
issuer: Option<String>,
access_token_ttl: Option<Duration>,
refresh_token_ttl: Option<Duration>,
refresh_tokens_enabled: bool,
token_store: Option<Arc<dyn TokenStore>>,
}
impl OAuthConfigBuilder {
pub fn client_id(mut self, client_id: impl Into<String>) -> Self {
self.client_id = Some(client_id.into());
self
}
pub fn client_secret(mut self, client_secret: impl Into<String>) -> Self {
self.client_secret = Some(client_secret.into());
self
}
pub fn issuer(mut self, issuer: impl Into<String>) -> Self {
self.issuer = Some(issuer.into());
self
}
pub fn access_token_ttl(mut self, ttl: Duration) -> Self {
self.access_token_ttl = Some(ttl);
self
}
pub fn refresh_token_ttl(mut self, ttl: Duration) -> Self {
self.refresh_token_ttl = Some(ttl);
self
}
pub fn enable_refresh_tokens(mut self, enabled: bool) -> Self {
self.refresh_tokens_enabled = enabled;
self
}
pub fn token_store<T>(mut self, store: T) -> Self
where
T: TokenStore + 'static,
{
self.token_store = Some(Arc::new(store));
self
}
pub fn build(self) -> Result<OAuthConfig, ConfigError> {
let client_id = self
.client_id
.ok_or(ConfigError::MissingField("client_id"))?;
let client_secret = self
.client_secret
.ok_or(ConfigError::MissingField("client_secret"))?;
let access_token_ttl = self
.access_token_ttl
.unwrap_or_else(|| Duration::from_secs(3600));
if self.refresh_tokens_enabled && self.refresh_token_ttl.is_none() {
return Err(ConfigError::InvalidValue {
field: "refresh_token_ttl",
message: "must be set when refresh tokens are enabled".into(),
});
}
let token_store = self
.token_store
.unwrap_or_else(|| Arc::new(InMemoryTokenStore::default()));
Ok(OAuthConfig {
client_id,
client_secret,
issuer: self.issuer,
access_token_ttl,
refresh_token_ttl: self.refresh_token_ttl,
refresh_tokens_enabled: self.refresh_tokens_enabled,
token_store,
})
}
}