google_api_rust_client_unoffical/auth/
service_account.rs1use std::fs;
2use std::path::PathBuf;
3
4use anyhow::{bail, Ok, Result};
5use chrono::{Local, Duration};
6use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
7use reqwest::header::{HeaderValue, CONTENT_TYPE, HeaderMap};
8use reqwest::Client;
9use serde::{Serialize, Deserialize};
10use serde_json::Value;
11
12use super::auth_error::AuthErrorResponse;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ServiceAccountCredentials {
16 r#type: String,
17 project_id: String,
18 private_key_id: String,
19 private_key: String,
20 client_email: String,
21 client_id: String,
22 auth_uri: String,
23 token_uri: String,
24 auth_provider_x509_cert_url: String,
25 client_x509_cert_url: String,
26 universe_domain: String,
27
28 #[serde(skip_serializing_if = "Option::is_none")]
29 token: Option<Token>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 scopes: Option<Vec<String>>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 sub: Option<String>
34}
35
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct Token {
39 expiration_time: i64,
40 access_token: String,
41}
42
43impl ServiceAccountCredentials {
44 pub fn from_service_account_file(filepath: PathBuf) -> Result<Self> {
48 let credentials_json = fs::read_to_string(filepath)?;
49 Ok(serde_json::from_str::<ServiceAccountCredentials>(&credentials_json)?)
50 }
51
52 pub fn from_service_account_info(credentials_json: String) -> Result<Self> {
56 Ok(serde_json::from_str::<ServiceAccountCredentials>(&credentials_json)?)
57 }
58
59 pub fn with_scopes(&self, scopes: Vec<&str>) -> Self {
63 let mut scoped_credentials = self.clone();
64 scoped_credentials.scopes = Some(scopes.into_iter().map(|s| s.to_owned()).collect());
65 scoped_credentials.token = None;
66 return scoped_credentials
67 }
68
69 pub fn with_subject(&self, subject: &str) -> Self {
73 let mut subjected_credential = self.clone();
74 subjected_credential.sub = Some(subject.to_owned());
75 subjected_credential.token = None;
76 return subjected_credential
77 }
78
79 pub async fn get_access_token(&mut self) -> Result<String> {
81 let now = Local::now();
82 let iat = now.timestamp();
83
84 match self.token.clone() {
85 Some(token) => {
86 if iat > token.expiration_time {
87 let jwt = self.make_assertion()?;
88 let access_token = self.request_token(&jwt).await?;
89 self.token = Some(Token{
90 expiration_time: (now + Duration::minutes(58)).timestamp(),
91 access_token: access_token.clone(),
92 });
93 return Ok(access_token);
94 } else {
95 return Ok(token.access_token.clone());
96 }
97 },
98 None => {
99 let jwt = self.make_assertion()?;
100 let access_token = self.request_token(&jwt).await?;
101 self.token = Some(Token{
102 expiration_time: (now + Duration::minutes(58)).timestamp(),
103 access_token: access_token.clone(),
104 });
105 return Ok(access_token);
106 }
107 };
108 }
109
110 fn make_assertion(&self) -> Result<String> {
111 let scope: String = match self.scopes.clone() {
112 Some(scopes) => {
113 scopes.join(",")
114 },
115 None => {
116 "".to_owned()
117 },
118 };
119
120 let mut header = Header::new(Algorithm::RS256);
121 header.typ = Some("JWT".to_owned());
122 header.kid = Some("".to_owned());
123
124 let now = Local::now();
125 let iat = now.timestamp();
126 let exp = (now + Duration::hours(1)).timestamp();
127 let claims = Claims {
128 iss: self.client_id.clone(),
129 sub: self.sub.clone(),
130 aud: self.token_uri.clone(),
131 scope,
132 iat,
133 exp,
134 };
135
136 let jwt = encode(
137 &header,
138 &claims,
139 &EncodingKey::from_rsa_pem(self.private_key.as_bytes())?,
140 )?;
141
142 return Ok(jwt);
143 }
144
145
146 async fn request_token(&self, assertion: &str) -> Result<String> {
147 let client = Client::new();
148 let mut headers = HeaderMap::new();
149 headers.insert(
150 CONTENT_TYPE,
151 HeaderValue::from_static("application/x-www-form-urlencoded"),
152 );
153
154 let grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer".to_owned();
155
156 let body_encoded = url_encoded_data::stringify(&[
157 ("assertion", assertion),
158 ("grant_type", &grant_type)
159 ]);
160
161 let response = client
162 .post(self.token_uri.clone())
163 .headers(headers.clone())
164 .body(body_encoded)
165 .send()
166 .await?;
167
168 let status_code = response.status();
169 let body: String = response.text().await?;
170
171 if !status_code.is_success() {
172 let error_response: AuthErrorResponse = serde_json::from_str(&body).unwrap_or_default();
173 bail!(format!("Response Error: {}! Message: {}", error_response.error, error_response.error_description));
174 }
175
176 let v: Value = serde_json::from_str(&body)?;
177 if let Some(access_token) = v["access_token"].as_str() {
178 return Ok(access_token.to_owned());
179 } else {
180 bail!("Error parsing for access token!")
181 }
182 }
183}
184
185
186#[derive(Debug, Serialize, Deserialize)]
187struct Claims {
188 iss: String,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 sub: Option<String>,
191 aud: String,
192 scope: String,
193 iat: i64,
194 exp: i64,
195}