Skip to content

Commit d5ded0c

Browse files
committed
Add TokenProvider::email() method
1 parent 7ed524e commit d5ded0c

File tree

6 files changed

+52
-0
lines changed

6 files changed

+52
-0
lines changed

src/config_default_credentials.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ impl TokenProvider for ConfigDefaultCredentials {
8989
Ok(token)
9090
}
9191

92+
async fn email(&self) -> Result<String, Error> {
93+
let token = self.token(&[]).await?;
94+
let info = self.client.token_info(&token).await?;
95+
Ok(info.email)
96+
}
97+
9298
async fn project_id(&self) -> Result<Arc<str>, Error> {
9399
self.credentials
94100
.quota_project_id

src/custom_service_account.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ impl TokenProvider for CustomServiceAccount {
132132
return Ok(token);
133133
}
134134

135+
async fn email(&self) -> Result<String, Error> {
136+
Ok(self.credentials.client_email.clone())
137+
}
138+
135139
async fn project_id(&self) -> Result<Arc<str>, Error> {
136140
match &self.credentials.project_id {
137141
Some(pid) => Ok(pid.clone()),

src/gcloud_authorized_user.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ impl TokenProvider for GCloudAuthorizedUser {
5151
Ok(token)
5252
}
5353

54+
async fn email(&self) -> Result<String, Error> {
55+
run(&["auth", "print-identity-token"])
56+
}
57+
5458
async fn project_id(&self) -> Result<Arc<str>, Error> {
5559
self.project_id
5660
.clone()

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ pub trait TokenProvider: Send + Sync {
167167
/// the current token (for the given scopes) has expired.
168168
async fn token(&self, scopes: &[&str]) -> Result<Arc<Token>, Error>;
169169

170+
async fn email(&self) -> Result<String, Error>;
171+
170172
/// Get the project ID for the authentication context
171173
async fn project_id(&self) -> Result<Arc<str>, Error>;
172174
}

src/metadata_service_account.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ impl TokenProvider for MetadataServiceAccount {
7878
Ok(token)
7979
}
8080

81+
async fn email(&self) -> Result<String, Error> {
82+
let email = self
83+
.client
84+
.request(metadata_request(DEFAULT_SERVICE_ACCOUNT_EMAIL_URI))
85+
.await?;
86+
87+
String::from_utf8(email.to_vec()).map_err(|_| Error::Str("invalid UTF-8 email"))
88+
}
89+
8190
async fn project_id(&self) -> Result<Arc<str>, Error> {
8291
Ok(self.project_id.clone())
8392
}
@@ -97,3 +106,5 @@ const DEFAULT_PROJECT_ID_GCP_URI: &str =
97106
"http://metadata.google.internal/computeMetadata/v1/project/project-id";
98107
const DEFAULT_TOKEN_GCP_URI: &str =
99108
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
109+
const DEFAULT_SERVICE_ACCOUNT_EMAIL_URI: &str =
110+
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email";

src/types.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::{env, fmt};
77

88
use bytes::Buf;
99
use chrono::{DateTime, Utc};
10+
use http::Method;
1011
use http_body_util::{BodyExt, Full};
1112
use hyper::body::Bytes;
1213
use hyper::Request;
@@ -69,6 +70,25 @@ impl HttpClient {
6970
.map_err(|err| Error::Json("failed to deserialize token from response", err))
7071
}
7172

73+
pub(crate) async fn token_info(&self, token: &Token) -> Result<TokenInfo, Error> {
74+
let req = Request::builder()
75+
.method(Method::GET)
76+
.uri(format!(
77+
"https://oauth2.googleapis.com/tokeninfo?access_token={}",
78+
token.as_str()
79+
))
80+
.body(Full::from(Bytes::new()))
81+
.map_err(|err| Error::Other("failed to build HTTP request", Box::new(err)))?;
82+
83+
let body = self
84+
.request(req)
85+
.await
86+
.map_err(|err| Error::Other("failed to fetch token info", Box::new(err)))?;
87+
88+
serde_json::from_slice(&body)
89+
.map_err(|err| Error::Json("failed to deserialize token info from response", err))
90+
}
91+
7292
pub(crate) async fn request(&self, req: Request<Full<Bytes>>) -> Result<Bytes, Error> {
7393
debug!(url = ?req.uri(), "requesting token");
7494
let (parts, body) = self
@@ -296,6 +316,11 @@ impl fmt::Debug for AuthorizedUserRefreshToken {
296316
}
297317
}
298318

319+
#[derive(Deserialize)]
320+
pub(crate) struct TokenInfo {
321+
pub(crate) email: String,
322+
}
323+
299324
/// How many times to attempt to fetch a token from the set credentials token endpoint.
300325
const RETRY_COUNT: u8 = 5;
301326

0 commit comments

Comments
 (0)