mod in_memory;
use std::sync::{Arc, PoisonError, RwLock};
pub use in_memory::{InMemoryTokenCache, InMemoryTokenCacheBuilder};
use snafu::Snafu;
use crate::{
core::{
Error,
dpop::ResourceServerDPoP,
platform::{MaybeSendBoxFuture, MaybeSendSync},
},
grant::core::TokenResponse,
token::RefreshToken,
};
pub trait TokenCache: MaybeSendSync {
fn get_token_response(&self) -> MaybeSendBoxFuture<'_, Result<Arc<TokenResponse>, Error>>;
fn resource_server_dpop(&self) -> &dyn ResourceServerDPoP;
fn prime(&self, response: Arc<TokenResponse>) -> MaybeSendBoxFuture<'_, Result<(), Error>>;
fn invalidate(&self);
}
impl<T: TokenCache + ?Sized> TokenCache for &T {
fn get_token_response(&self) -> MaybeSendBoxFuture<'_, Result<Arc<TokenResponse>, Error>> {
(**self).get_token_response()
}
fn resource_server_dpop(&self) -> &dyn ResourceServerDPoP {
(**self).resource_server_dpop()
}
fn prime(&self, response: Arc<TokenResponse>) -> MaybeSendBoxFuture<'_, Result<(), Error>> {
(**self).prime(response)
}
fn invalidate(&self) {
(**self).invalidate();
}
}
impl<T: TokenCache + ?Sized> TokenCache for Box<T> {
fn get_token_response(&self) -> MaybeSendBoxFuture<'_, Result<Arc<TokenResponse>, Error>> {
(**self).get_token_response()
}
fn resource_server_dpop(&self) -> &dyn ResourceServerDPoP {
(**self).resource_server_dpop()
}
fn prime(&self, response: Arc<TokenResponse>) -> MaybeSendBoxFuture<'_, Result<(), Error>> {
(**self).prime(response)
}
fn invalidate(&self) {
(**self).invalidate();
}
}
impl<T: TokenCache + ?Sized> TokenCache for Arc<T> {
fn get_token_response(&self) -> MaybeSendBoxFuture<'_, Result<Arc<TokenResponse>, Error>> {
(**self).get_token_response()
}
fn resource_server_dpop(&self) -> &dyn ResourceServerDPoP {
(**self).resource_server_dpop()
}
fn prime(&self, response: Arc<TokenResponse>) -> MaybeSendBoxFuture<'_, Result<(), Error>> {
(**self).prime(response)
}
fn invalidate(&self) {
(**self).invalidate();
}
}
#[derive(Debug, Snafu)]
pub enum GetTokenError {
#[snafu(display("token refresh failed and no grant parameters were available: {source}"))]
RefreshFailed {
source: Error,
},
#[snafu(display(
"token refresh failed and exchange also failed: refresh={refresh_source}, exchange={exchange_source}"
))]
BothFailed {
refresh_source: Error,
exchange_source: Error,
},
#[snafu(display("no refresh token is stored and no grant parameters were provided"))]
NoTokenSource,
}
#[cfg(test)]
mod tests {
use crate::{
cache::{InMemoryRefreshTokenStore, InMemoryTokenCache},
core::{client_auth::NoAuth, http::HttpClient},
grant::client_credentials::{ClientCredentialsGrant, ClientCredentialsGrantParameters},
};
struct NoHttp;
impl HttpClient for NoHttp {
fn execute(
&self,
_request: http::Request<bytes::Bytes>,
_idempotency: crate::core::http::Idempotency,
) -> crate::core::platform::MaybeSendBoxFuture<
'_,
Result<crate::core::http::HttpResponse, crate::core::Error>,
> {
unreachable!("test only builds the cache, no HTTP expected")
}
}
#[test]
fn test_setup() {
let _cache = InMemoryTokenCache::builder()
.grant(
ClientCredentialsGrant::builder()
.client_id("client_id")
.client_auth(NoAuth)
.token_endpoint("https://blah".parse().unwrap())
.http_client(NoHttp)
.build(),
)
.grant_parameters(
ClientCredentialsGrantParameters::builder()
.scopes(["read", "write"])
.build(),
)
.refresh_store(InMemoryRefreshTokenStore::default())
.build();
}
}
pub trait RefreshTokenStore: MaybeSendSync {
fn get(&self) -> MaybeSendBoxFuture<'_, Result<Option<RefreshToken>, Error>>;
fn set<'a>(&'a self, token: &'a RefreshToken) -> MaybeSendBoxFuture<'a, Result<(), Error>>;
fn clear(&self) -> MaybeSendBoxFuture<'_, Result<(), Error>>;
}
macro_rules! forward_refresh_token_store {
($wrapper:ty) => {
impl<T: RefreshTokenStore + ?Sized> RefreshTokenStore for $wrapper {
fn get(&self) -> MaybeSendBoxFuture<'_, Result<Option<RefreshToken>, Error>> {
(**self).get()
}
fn set<'a>(
&'a self,
token: &'a RefreshToken,
) -> MaybeSendBoxFuture<'a, Result<(), Error>> {
(**self).set(token)
}
fn clear(&self) -> MaybeSendBoxFuture<'_, Result<(), Error>> {
(**self).clear()
}
}
};
}
forward_refresh_token_store!(&T);
forward_refresh_token_store!(Box<T>);
forward_refresh_token_store!(std::sync::Arc<T>);
#[derive(Debug, Default)]
pub struct InMemoryRefreshTokenStore {
refresh_token: RwLock<Option<RefreshToken>>,
}
impl RefreshTokenStore for InMemoryRefreshTokenStore {
fn get(&self) -> MaybeSendBoxFuture<'_, Result<Option<RefreshToken>, Error>> {
Box::pin(async {
Ok(self
.refresh_token
.read()
.unwrap_or_else(PoisonError::into_inner)
.clone())
})
}
fn set<'a>(&'a self, token: &'a RefreshToken) -> MaybeSendBoxFuture<'a, Result<(), Error>> {
Box::pin(async {
*self
.refresh_token
.write()
.unwrap_or_else(PoisonError::into_inner) = Some(token.clone());
Ok(())
})
}
fn clear(&self) -> MaybeSendBoxFuture<'_, Result<(), Error>> {
Box::pin(async {
*self
.refresh_token
.write()
.unwrap_or_else(PoisonError::into_inner) = None;
Ok(())
})
}
}