mirror of
https://github.com/OMGeeky/yup-oauth2.git
synced 2026-02-23 15:50:00 +01:00
Revert mutable authenticator interface change
Instead, suggest using interior mutability (and RwLock in the example) to manage storage of token states. This makes it easier to share authenticators between threads.
This commit is contained in:
@@ -44,7 +44,7 @@ async fn main() {
|
||||
let sec = yup_oauth2::read_application_secret("client_secret.json")
|
||||
.await
|
||||
.expect("client secret couldn't be read.");
|
||||
let mut auth = yup_oauth2::InstalledFlowAuthenticator::builder(
|
||||
let auth = yup_oauth2::InstalledFlowAuthenticator::builder(
|
||||
sec,
|
||||
yup_oauth2::InstalledFlowReturnMethod::HTTPRedirect,
|
||||
)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
//! Demonstrating how to create a custom token store
|
||||
use anyhow::anyhow;
|
||||
use async_trait::async_trait;
|
||||
use std::sync::RwLock;
|
||||
use yup_oauth2::storage::{ScopeSet, TokenInfo, TokenStorage};
|
||||
|
||||
struct ExampleTokenStore {
|
||||
store: Vec<StoredToken>,
|
||||
store: RwLock<Vec<StoredToken>>,
|
||||
}
|
||||
|
||||
struct StoredToken {
|
||||
@@ -15,12 +17,17 @@ struct StoredToken {
|
||||
/// to disk, an OS keychain, a database or whatever suits your use-case
|
||||
#[async_trait]
|
||||
impl TokenStorage for ExampleTokenStore {
|
||||
async fn set(&mut self, scopes: ScopeSet<'_, &str>, token: TokenInfo) -> anyhow::Result<()> {
|
||||
async fn set(&self, scopes: ScopeSet<'_, &str>, token: TokenInfo) -> anyhow::Result<()> {
|
||||
let data = serde_json::to_string(&token).unwrap();
|
||||
|
||||
println!("Storing token for scopes {:?}", scopes);
|
||||
|
||||
self.store.push(StoredToken {
|
||||
let mut store = self
|
||||
.store
|
||||
.write()
|
||||
.map_err(|_| anyhow!("Unable to lock store for writing"))?;
|
||||
|
||||
store.push(StoredToken {
|
||||
scopes: scopes.scopes(),
|
||||
serialized_token: data,
|
||||
});
|
||||
@@ -30,13 +37,15 @@ impl TokenStorage for ExampleTokenStore {
|
||||
|
||||
async fn get(&self, target_scopes: ScopeSet<'_, &str>) -> Option<TokenInfo> {
|
||||
// Retrieve the token data
|
||||
for stored_token in self.store.iter() {
|
||||
if target_scopes.is_covered_by(&stored_token.scopes) {
|
||||
return serde_json::from_str(&stored_token.serialized_token).ok();
|
||||
self.store.read().ok().and_then(|store| {
|
||||
for stored_token in store.iter() {
|
||||
if target_scopes.is_covered_by(&stored_token.scopes) {
|
||||
return serde_json::from_str(&stored_token.serialized_token).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,12 +55,14 @@ async fn main() {
|
||||
let sec = yup_oauth2::read_application_secret("client_secret.json")
|
||||
.await
|
||||
.expect("client secret couldn't be read.");
|
||||
let mut auth = yup_oauth2::InstalledFlowAuthenticator::builder(
|
||||
let auth = yup_oauth2::InstalledFlowAuthenticator::builder(
|
||||
sec,
|
||||
yup_oauth2::InstalledFlowReturnMethod::HTTPRedirect,
|
||||
)
|
||||
.with_storage(yup_oauth2::authenticator::StorageType::Custom(Box::new(
|
||||
ExampleTokenStore { store: vec![] },
|
||||
ExampleTokenStore {
|
||||
store: RwLock::new(vec![]),
|
||||
},
|
||||
)))
|
||||
.build()
|
||||
.await
|
||||
|
||||
@@ -47,7 +47,7 @@ where
|
||||
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
|
||||
{
|
||||
/// Return the current token for the provided scopes.
|
||||
pub async fn token<'a, T>(&'a mut self, scopes: &'a [T]) -> Result<AccessToken, Error>
|
||||
pub async fn token<'a, T>(&'a self, scopes: &'a [T]) -> Result<AccessToken, Error>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
@@ -57,7 +57,7 @@ where
|
||||
/// Return a token for the provided scopes, but don't reuse cached tokens. Instead,
|
||||
/// always fetch a new token from the OAuth server.
|
||||
pub async fn force_refreshed_token<'a, T>(
|
||||
&'a mut self,
|
||||
&'a self,
|
||||
scopes: &'a [T],
|
||||
) -> Result<AccessToken, Error>
|
||||
where
|
||||
@@ -68,7 +68,7 @@ where
|
||||
|
||||
/// Return a cached token or fetch a new one from the server.
|
||||
async fn find_token<'a, T>(
|
||||
&'a mut self,
|
||||
&'a self,
|
||||
scopes: &'a [T],
|
||||
force_refresh: bool,
|
||||
) -> Result<AccessToken, Error>
|
||||
|
||||
@@ -137,7 +137,7 @@ pub trait TokenStorage: Send + Sync {
|
||||
/// Store a token for the given set of scopes so that it can be retrieved later by get()
|
||||
/// ScopeSet implements Hash so that you can easily serialize and store it.
|
||||
/// TokenInfo can be serialized with serde.
|
||||
async fn set(&mut self, scopes: ScopeSet<'_, &str>, token: TokenInfo) -> anyhow::Result<()>;
|
||||
async fn set(&self, scopes: ScopeSet<'_, &str>, token: TokenInfo) -> anyhow::Result<()>;
|
||||
|
||||
/// Retrieve a token stored by set for the given set of scopes
|
||||
async fn get(&self, scopes: ScopeSet<'_, &str>) -> Option<TokenInfo>;
|
||||
@@ -151,7 +151,7 @@ pub(crate) enum Storage {
|
||||
|
||||
impl Storage {
|
||||
pub(crate) async fn set<T>(
|
||||
&mut self,
|
||||
&self,
|
||||
scopes: ScopeSet<'_, T>,
|
||||
token: TokenInfo,
|
||||
) -> anyhow::Result<()>
|
||||
|
||||
@@ -92,7 +92,7 @@ async fn test_device_success() {
|
||||
}))),
|
||||
);
|
||||
|
||||
let mut auth = create_device_flow_auth(&server).await;
|
||||
let auth = create_device_flow_auth(&server).await;
|
||||
let token = auth
|
||||
.token(&["https://www.googleapis.com/scope/1"])
|
||||
.await
|
||||
@@ -117,7 +117,7 @@ async fn test_device_no_code() {
|
||||
"error_description": "description"
|
||||
}))),
|
||||
);
|
||||
let mut auth = create_device_flow_auth(&server).await;
|
||||
let auth = create_device_flow_auth(&server).await;
|
||||
let res = auth.token(&["https://www.googleapis.com/scope/1"]).await;
|
||||
assert!(res.is_err());
|
||||
assert!(format!("{}", res.unwrap_err()).contains("invalid_client_id"));
|
||||
@@ -155,7 +155,7 @@ async fn test_device_no_token() {
|
||||
"error": "access_denied"
|
||||
}))),
|
||||
);
|
||||
let mut auth = create_device_flow_auth(&server).await;
|
||||
let auth = create_device_flow_auth(&server).await;
|
||||
let res = auth.token(&["https://www.googleapis.com/scope/1"]).await;
|
||||
assert!(res.is_err());
|
||||
assert!(format!("{}", res.unwrap_err()).contains("access_denied"));
|
||||
@@ -239,7 +239,7 @@ async fn create_installed_flow_auth(
|
||||
async fn test_installed_interactive_success() {
|
||||
let _ = env_logger::try_init();
|
||||
let server = Server::run();
|
||||
let mut auth =
|
||||
let auth =
|
||||
create_installed_flow_auth(&server, InstalledFlowReturnMethod::Interactive, None).await;
|
||||
server.expect(
|
||||
Expectation::matching(all_of![
|
||||
@@ -268,7 +268,7 @@ async fn test_installed_interactive_success() {
|
||||
async fn test_installed_redirect_success() {
|
||||
let _ = env_logger::try_init();
|
||||
let server = Server::run();
|
||||
let mut auth =
|
||||
let auth =
|
||||
create_installed_flow_auth(&server, InstalledFlowReturnMethod::HTTPRedirect, None).await;
|
||||
server.expect(
|
||||
Expectation::matching(all_of![
|
||||
@@ -297,7 +297,7 @@ async fn test_installed_redirect_success() {
|
||||
async fn test_installed_error() {
|
||||
let _ = env_logger::try_init();
|
||||
let server = Server::run();
|
||||
let mut auth =
|
||||
let auth =
|
||||
create_installed_flow_auth(&server, InstalledFlowReturnMethod::Interactive, None).await;
|
||||
server.expect(
|
||||
Expectation::matching(all_of![
|
||||
@@ -347,7 +347,7 @@ async fn test_service_account_success() {
|
||||
use chrono::Utc;
|
||||
let _ = env_logger::try_init();
|
||||
let server = Server::run();
|
||||
let mut auth = create_service_account_auth(&server).await;
|
||||
let auth = create_service_account_auth(&server).await;
|
||||
|
||||
server.expect(
|
||||
Expectation::matching(request::method_path("POST", "/token"))
|
||||
@@ -369,7 +369,7 @@ async fn test_service_account_success() {
|
||||
async fn test_service_account_error() {
|
||||
let _ = env_logger::try_init();
|
||||
let server = Server::run();
|
||||
let mut auth = create_service_account_auth(&server).await;
|
||||
let auth = create_service_account_auth(&server).await;
|
||||
server.expect(
|
||||
Expectation::matching(request::method_path("POST", "/token")).respond_with(json_encoded(
|
||||
serde_json::json!({
|
||||
@@ -388,7 +388,7 @@ async fn test_service_account_error() {
|
||||
async fn test_refresh() {
|
||||
let _ = env_logger::try_init();
|
||||
let server = Server::run();
|
||||
let mut auth =
|
||||
let auth =
|
||||
create_installed_flow_auth(&server, InstalledFlowReturnMethod::Interactive, None).await;
|
||||
// We refresh a token whenever it's within 1 minute of expiring. So
|
||||
// acquiring a token that expires in 59 seconds will force a refresh on
|
||||
@@ -486,7 +486,7 @@ async fn test_refresh() {
|
||||
async fn test_memory_storage() {
|
||||
let _ = env_logger::try_init();
|
||||
let server = Server::run();
|
||||
let mut auth =
|
||||
let auth =
|
||||
create_installed_flow_auth(&server, InstalledFlowReturnMethod::Interactive, None).await;
|
||||
server.expect(
|
||||
Expectation::matching(all_of![
|
||||
@@ -519,7 +519,7 @@ async fn test_memory_storage() {
|
||||
|
||||
// Create a new authenticator. This authenticator does not share a cache
|
||||
// with the previous one. Validate that it receives a different token.
|
||||
let mut auth2 =
|
||||
let auth2 =
|
||||
create_installed_flow_auth(&server, InstalledFlowReturnMethod::Interactive, None).await;
|
||||
server.expect(
|
||||
Expectation::matching(all_of![
|
||||
@@ -565,7 +565,7 @@ async fn test_disk_storage() {
|
||||
}))),
|
||||
);
|
||||
{
|
||||
let mut auth = create_installed_flow_auth(
|
||||
let auth = create_installed_flow_auth(
|
||||
&server,
|
||||
InstalledFlowReturnMethod::Interactive,
|
||||
Some(storage_path.clone()),
|
||||
@@ -589,7 +589,7 @@ async fn test_disk_storage() {
|
||||
// Create a new authenticator. This authenticator uses the same token
|
||||
// storage file as the previous one so should receive a token without
|
||||
// making any http requests.
|
||||
let mut auth = create_installed_flow_auth(
|
||||
let auth = create_installed_flow_auth(
|
||||
&server,
|
||||
InstalledFlowReturnMethod::Interactive,
|
||||
Some(storage_path.clone()),
|
||||
|
||||
Reference in New Issue
Block a user