service_authenticator/
authenticator.rs1use crate::error::Error;
3use crate::service_account::{ServiceAccountFlow, ServiceAccountFlowOpts, ServiceAccountKey};
4use crate::storage::{self, Storage};
5use crate::types::AccessToken;
6use actix_web::client as awc;
7use futures::lock::Mutex;
8use std::fmt;
9use std::io;
10use std::path::PathBuf;
11pub struct Authenticator {
14 pub client: awc::Client,
17 storage: Storage,
18 auth_flow: ServiceAccountFlow,
19}
20
21impl Authenticator {
22 pub async fn token<'a, T>(
24 &'a self,
25 scopes: &'a [T],
26 ) -> Result<AccessToken, Error>
27 where
28 T: AsRef<str>,
29 {
30 self.find_token(scopes, false).await
31 }
32 pub async fn header<'a, T>(
34 self: &Self,
35 scopes: &'a [T],
36 ) -> Result<String, Error>
37 where
38 T: AsRef<str>,
39 {
40 let tok = self.token(scopes).await?;
41 Ok(format!("Bearer {}", tok.as_str()))
42 }
43 pub async fn force_refreshed_token<'a, T>(
46 &'a self,
47 scopes: &'a [T],
48 ) -> Result<AccessToken, Error>
49 where
50 T: AsRef<str>,
51 {
52 self.find_token(scopes, true).await
53 }
54 async fn find_token<'a, T>(
56 &'a self,
57 scopes: &'a [T],
58 force_refresh: bool,
59 ) -> Result<AccessToken, Error>
60 where
61 T: AsRef<str>,
62 {
63 log::debug!(
64 "access token requested for scopes: {}",
65 DisplayScopes(scopes)
66 );
67 let hashed_scopes = storage::ScopeSet::from(scopes);
68 match self.storage.get(hashed_scopes).await {
69 Some(t) if !t.is_expired() && !force_refresh => {
70 log::debug!("found valid token in cache: {:?}", t);
72 Ok(t.into())
73 }
74 _ => {
75 let token_info = self.auth_flow.token(&self.client, scopes).await?;
77 self.storage.set(hashed_scopes, token_info.clone()).await?;
78 Ok(token_info.into())
79 }
80 }
81 }
82}
83struct DisplayScopes<'a, T>(&'a [T]);
84impl<'a, T> fmt::Display for DisplayScopes<'a, T>
85where
86 T: AsRef<str>,
87{
88 fn fmt(
89 &self,
90 f: &mut fmt::Formatter,
91 ) -> fmt::Result {
92 f.write_str("[")?;
93 let mut iter = self.0.iter();
94 if let Some(first) = iter.next() {
95 f.write_str(first.as_ref())?;
96 for scope in iter {
97 f.write_str(", ")?;
98 f.write_str(scope.as_ref())?;
99 }
100 }
101 f.write_str("]")
102 }
103}
104pub struct AuthenticatorBuilder {
106 client_builder: awc::ClientBuilder,
107 storage_type: StorageType,
108 auth_flow_opts: ServiceAccountFlowOpts,
109}
110
111impl AuthenticatorBuilder {
112 async fn common_build(
113 client_builder: awc::ClientBuilder,
114 storage_type: StorageType,
115 auth_flow_opts: ServiceAccountFlowOpts,
116 ) -> io::Result<Authenticator> {
117 let client = client_builder.finish();
118 let storage = match storage_type {
119 StorageType::Memory => Storage::Memory {
120 tokens: Mutex::new(storage::JSONTokens::new()),
121 },
122 StorageType::Disk(path) => Storage::Disk(storage::DiskStorage::new(path).await?),
123 };
124 let auth_flow = ServiceAccountFlow::new(auth_flow_opts)?;
125 Ok(Authenticator {
126 client,
127 storage,
128 auth_flow,
129 })
130 }
131 pub fn client(
133 self,
134 client_builder: awc::ClientBuilder,
135 ) -> AuthenticatorBuilder {
136 AuthenticatorBuilder {
137 client_builder: client_builder,
138 storage_type: self.storage_type,
139 auth_flow_opts: self.auth_flow_opts,
140 }
141 }
142 pub fn persist_tokens_to_disk<P: Into<PathBuf>>(
144 self,
145 path: P,
146 ) -> AuthenticatorBuilder {
147 AuthenticatorBuilder {
148 storage_type: StorageType::Disk(path.into()),
149 ..self
150 }
151 }
152 pub fn with_service_key(
154 key: ServiceAccountKey,
155 subject: &str,
156 ) -> AuthenticatorBuilder {
157 let auth_flow_opts = ServiceAccountFlowOpts {
158 key,
159 subject: Some(subject.to_string()),
160 };
161 AuthenticatorBuilder {
162 client_builder: awc::ClientBuilder::new(),
163 storage_type: StorageType::Memory,
164 auth_flow_opts,
165 }
166 }
167}
168impl AuthenticatorBuilder {
182 pub fn subject(
184 self,
185 subject: impl Into<String>,
186 ) -> Self {
187 AuthenticatorBuilder {
188 auth_flow_opts: ServiceAccountFlowOpts {
189 subject: Some(subject.into()),
190 ..self.auth_flow_opts
191 },
192 ..self
193 }
194 }
195
196 pub async fn build(self) -> io::Result<Authenticator> {
198 Self::common_build(self.client_builder, self.storage_type, self.auth_flow_opts).await
199 }
200}
201enum StorageType {
202 Memory,
203 Disk(PathBuf),
204}