Skip to content

Commit 488627c

Browse files
committed
Add TokenProvider::email() method
1 parent 4a10e2f commit 488627c

File tree

6 files changed

+55
-4
lines changed

6 files changed

+55
-4
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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ 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(
85+
metadata_request(DEFAULT_SERVICE_ACCOUNT_EMAIL_URI),
86+
)
87+
.await?;
88+
89+
String::from_utf8(email.to_vec()).map_err(|_| Error::Str("invalid UTF-8 email"))
90+
}
91+
8192
async fn project_id(&self) -> Result<Arc<str>, Error> {
8293
Ok(self.project_id.clone())
8394
}
@@ -97,3 +108,5 @@ const DEFAULT_PROJECT_ID_GCP_URI: &str =
97108
"http://metadata.google.internal/computeMetadata/v1/project/project-id";
98109
const DEFAULT_TOKEN_GCP_URI: &str =
99110
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token";
111+
const DEFAULT_SERVICE_ACCOUNT_EMAIL_URI: &str =
112+
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email";

src/types.rs

Lines changed: 26 additions & 4 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,10 +70,26 @@ impl HttpClient {
6970
.map_err(|err| Error::Json("failed to deserialize token from response", err))
7071
}
7172

72-
pub(crate) async fn request(
73-
&self,
74-
req: Request<Full<Bytes>>,
75-
) -> Result<Bytes, Error> {
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+
92+
pub(crate) async fn request(&self, req: Request<Full<Bytes>>) -> Result<Bytes, Error> {
7693
debug!(url = ?req.uri(), "requesting token");
7794
let (parts, body) = self
7895
.inner
@@ -299,6 +316,11 @@ impl fmt::Debug for AuthorizedUserRefreshToken {
299316
}
300317
}
301318

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

0 commit comments

Comments
 (0)