use std::time::Duration;
use moka::future::Cache;
use serde::Deserialize;
use crate::{
OidcResult,
pending_store::{PendingOauth, PendingOauthStore, PendingOauthStoreConfig},
};
#[derive(Debug, Clone, Deserialize)]
pub struct MokaPendingOauthStoreConfig {
#[serde(default = "default_ttl", with = "humantime_serde")]
pub ttl: Duration,
#[serde(default = "default_max_capacity")]
pub max_capacity: u64,
}
impl Default for MokaPendingOauthStoreConfig {
fn default() -> Self {
Self {
ttl: default_ttl(),
max_capacity: default_max_capacity(),
}
}
}
impl PendingOauthStoreConfig for MokaPendingOauthStoreConfig {}
fn default_ttl() -> Duration {
Duration::from_secs(300) }
fn default_max_capacity() -> u64 {
1000
}
#[derive(Clone)]
pub struct MokaPendingOauthStore {
inner: Cache<String, PendingOauth>,
}
impl PendingOauthStore for MokaPendingOauthStore {
type Config = MokaPendingOauthStoreConfig;
fn from_config(config: &Self::Config) -> Self {
let inner = Cache::builder()
.time_to_live(config.ttl)
.max_capacity(config.max_capacity)
.build();
Self { inner }
}
async fn insert(
&self,
state: String,
nonce: String,
code_verifier: Option<String>,
extra_data: Option<serde_json::Value>,
) -> OidcResult<()> {
self.inner
.insert(
state,
PendingOauth {
nonce,
code_verifier,
extra_data,
},
)
.await;
Ok(())
}
async fn take(&self, state: &str) -> OidcResult<Option<PendingOauth>> {
Ok(self.inner.remove(state).await)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_insert_and_take() -> OidcResult<()> {
let store = MokaPendingOauthStore::from_config_opt(None);
store
.insert(
"state1".to_string(),
"nonce1".to_string(),
Some("verifier1".to_string()),
None,
)
.await?;
let result = store.take("state1").await?.unwrap();
assert_eq!(result.nonce, "nonce1");
assert_eq!(result.code_verifier, Some("verifier1".to_string()));
assert!(result.extra_data.is_none());
assert!(store.take("state1").await?.is_none());
Ok(())
}
#[tokio::test]
async fn test_unknown_state() -> OidcResult<()> {
let store = MokaPendingOauthStore::from_config_opt(None);
assert!(store.take("unknown").await?.is_none());
Ok(())
}
}