Add access_token module
This commit is contained in:
parent
1617a025ce
commit
59d266a111
|
@ -9,6 +9,9 @@ homepage = "https://solana.com/"
|
|||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.8"
|
||||
goauth = "0.7.1"
|
||||
smpl_jwt = "0.5.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib"]
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/// A module for managing a Google API access token
|
||||
use goauth::{
|
||||
auth::{JwtClaims, Token},
|
||||
credentials::Credentials,
|
||||
get_token,
|
||||
};
|
||||
use log::*;
|
||||
use smpl_jwt::Jwt;
|
||||
use std::time::Instant;
|
||||
|
||||
pub use goauth::scopes::Scope;
|
||||
|
||||
fn load_credentials() -> Result<Credentials, String> {
|
||||
// Use standard GOOGLE_APPLICATION_CREDENTIALS environment variable
|
||||
let credentials_file = std::env::var("GOOGLE_APPLICATION_CREDENTIALS")
|
||||
.map_err(|_| "GOOGLE_APPLICATION_CREDENTIALS environment variable not found".to_string())?;
|
||||
|
||||
Credentials::from_file(&credentials_file).map_err(|err| {
|
||||
format!(
|
||||
"Failed to read GCP credentials from {}: {}",
|
||||
credentials_file, err
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub struct AccessToken {
|
||||
credentials: Credentials,
|
||||
jwt: Jwt<JwtClaims>,
|
||||
token: Option<(Token, Instant)>,
|
||||
}
|
||||
|
||||
impl AccessToken {
|
||||
pub fn new(scope: &Scope) -> Result<Self, String> {
|
||||
let credentials = load_credentials()?;
|
||||
|
||||
let claims = JwtClaims::new(
|
||||
credentials.iss(),
|
||||
&scope,
|
||||
credentials.token_uri(),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let jwt = Jwt::new(
|
||||
claims,
|
||||
credentials
|
||||
.rsa_key()
|
||||
.map_err(|err| format!("Invalid rsa key: {}", err))?,
|
||||
None,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
credentials,
|
||||
jwt,
|
||||
token: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// The project that this token grants access to
|
||||
pub fn project(&self) -> String {
|
||||
self.credentials.project()
|
||||
}
|
||||
|
||||
/// Call this function regularly, and before calling `access_token()`
|
||||
pub async fn refresh(&mut self) {
|
||||
if let Some((token, last_refresh)) = self.token.as_ref() {
|
||||
if last_refresh.elapsed().as_secs() < token.expires_in() as u64 / 2 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
info!("Refreshing token");
|
||||
match get_token(&self.jwt, &self.credentials).await {
|
||||
Ok(new_token) => {
|
||||
info!("Token expires in {} seconds", new_token.expires_in());
|
||||
self.token = Some((new_token, Instant::now()));
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Failed to get new token: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an access token suitable for use in an HTTP authorization header
|
||||
pub fn get(&self) -> Result<String, String> {
|
||||
if let Some((token, _)) = self.token.as_ref() {
|
||||
Ok(format!("{} {}", token.token_type(), token.access_token()))
|
||||
} else {
|
||||
Err("Access token not available".into())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
mod access_token;
|
Loading…
Reference in New Issue