Skip to content

Commit 1980f0d

Browse files
authored
impl(auth): verifier should accept multiple audiences (#3799)
Towards #3449 #3516
1 parent 0573ff5 commit 1980f0d

File tree

3 files changed

+62
-34
lines changed

3 files changed

+62
-34
lines changed

src/auth/integration-tests/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ pub mod unstable {
563563
.await
564564
.expect("failed to get id token");
565565

566-
let verifier = VerifierBuilder::new(audience)
566+
let verifier = VerifierBuilder::new([audience])
567567
.with_email(expected_email)
568568
.build();
569569

@@ -594,7 +594,7 @@ pub mod unstable {
594594
.await
595595
.expect("failed to get id token");
596596

597-
let verifier = VerifierBuilder::new(target_audience)
597+
let verifier = VerifierBuilder::new([target_audience])
598598
.with_email(expected_email)
599599
.build();
600600

@@ -620,7 +620,7 @@ pub mod unstable {
620620
.await
621621
.expect("failed to get id token");
622622

623-
let verifier = VerifierBuilder::new(target_audience)
623+
let verifier = VerifierBuilder::new([target_audience])
624624
.with_email(expected_email)
625625
.build();
626626

@@ -652,7 +652,7 @@ pub mod unstable {
652652
.await
653653
.expect("failed to generate id token");
654654

655-
let verifier = VerifierBuilder::new(target_audience)
655+
let verifier = VerifierBuilder::new([target_audience])
656656
.with_email(target_principal_email)
657657
.build();
658658

src/auth/src/credentials/idtoken.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
//! # use google_cloud_auth::credentials::idtoken;
6161
//! # use std::time::Duration;
6262
//! let audience = "https://my-service.a.run.app";
63-
//! let verifier = idtoken::verifier::Builder::new(audience).build();
63+
//! let verifier = idtoken::verifier::Builder::new([audience]).build();
6464
//!
6565
//! async fn verify_id_token(token: &str) -> anyhow::Result<()> {
6666
//! let claims = verifier.verify(token).await?;

src/auth/src/credentials/idtoken/verifier.rs

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
//! # use google_cloud_auth::credentials::idtoken;
2525
//! # use std::time::Duration;
2626
//! let audience = "https://my-service.a.run.app";
27-
//! let verifier = idtoken::verifier::Builder::new(audience).build();
27+
//! let verifier = idtoken::verifier::Builder::new([audience]).build();
2828
//!
2929
//! async fn verify_my_token(token: &str) -> anyhow::Result<()> {
3030
//! let claims = verifier.verify(token).await?;
@@ -43,21 +43,29 @@ use std::time::Duration;
4343

4444
/// Builder is used construct a [Verifier] of id tokens.
4545
pub struct Builder {
46-
audience: String,
46+
audiences: Vec<String>,
4747
email: Option<String>,
4848
jwks_url: Option<String>,
4949
clock_skew: Option<Duration>,
5050
}
5151

5252
impl Builder {
53-
/// Create a [Verifier] for ID Tokens with a target audience
54-
/// for the token verification.
55-
pub fn new<S: Into<String>>(audience: S) -> Self {
53+
/// Create a [Verifier] for ID Tokens with a list of target
54+
/// audiences for the token verification.
55+
pub fn new<I, S>(audiences: I) -> Self
56+
where
57+
I: IntoIterator<Item = S>,
58+
S: Into<String>,
59+
{
60+
let audiences = audiences
61+
.into_iter()
62+
.map(|s| s.into())
63+
.collect::<Vec<String>>();
5664
Self {
57-
audience: audience.into(),
58-
clock_skew: None,
65+
audiences,
5966
email: None,
6067
jwks_url: None,
68+
clock_skew: None,
6169
}
6270
}
6371

@@ -70,8 +78,7 @@ impl Builder {
7078
///
7179
/// ```
7280
/// # use google_cloud_auth::credentials::idtoken::verifier::Builder;
73-
/// let audience = "https://example.com";
74-
/// let verifier = Builder::new(audience)
81+
/// let verifier = Builder::new(["https://my-service.a.run.app"])
7582
/// .with_email("[email protected]")
7683
/// .build();
7784
/// ```
@@ -89,8 +96,7 @@ impl Builder {
8996
///
9097
/// ```
9198
/// # use google_cloud_auth::credentials::idtoken::verifier::Builder;
92-
/// let audience = "https://example.com";
93-
/// let verifier = Builder::new(audience)
99+
/// let verifier = Builder::new(["https://my-service.a.run.app"])
94100
/// .with_jwks_url("https://www.googleapis.com/oauth2/v3/certs")
95101
/// .build();
96102
/// ```
@@ -109,8 +115,7 @@ impl Builder {
109115
/// ```
110116
/// # use google_cloud_auth::credentials::idtoken::Builder;
111117
/// # use std::time::Duration;
112-
/// let audience = "https://example.com";
113-
/// let verifier = Builder::new(audience)
118+
/// let verifier = Builder::new(["https://my-service.a.run.app"])
114119
/// .with_clock_skew(Duration::from_secs(60))
115120
/// .build();
116121
/// ```
@@ -123,7 +128,7 @@ impl Builder {
123128
pub fn build(self) -> Verifier {
124129
Verifier {
125130
jwk_client: JwkClient::new(),
126-
audience: self.audience.clone(),
131+
audiences: self.audiences.clone(),
127132
email: self.email.clone(),
128133
jwks_url: self.jwks_url.clone(),
129134
clock_skew: self.clock_skew.unwrap_or_else(|| Duration::from_secs(10)),
@@ -140,8 +145,7 @@ impl Builder {
140145
/// # use std::time::Duration;
141146
///
142147
/// async fn verify_id_token(token: &str) {
143-
/// let audience = "https://example.com";
144-
/// let verifier = Builder::new(audience).build();
148+
/// let verifier = Builder::new(["https://my-service.a.run.app"]).build();
145149
///
146150
/// let claims = verifier.verify(token).await.expect("Failed to verify ID token");
147151
/// println!("Verified claims: {:?}", claims);
@@ -150,7 +154,7 @@ impl Builder {
150154
#[derive(Debug)]
151155
pub struct Verifier {
152156
jwk_client: JwkClient,
153-
audience: String,
157+
audiences: Vec<String>,
154158
email: Option<String>,
155159
jwks_url: Option<String>,
156160
clock_skew: Duration,
@@ -169,7 +173,7 @@ impl Verifier {
169173
validation.leeway = self.clock_skew.as_secs();
170174
// TODO(#3591): Support TPC/REP that can have different issuers
171175
validation.set_issuer(&["https://accounts.google.com", "accounts.google.com"]);
172-
validation.set_audience(std::slice::from_ref(&self.audience));
176+
validation.set_audience(&self.audiences);
173177

174178
let expected_email = self.email.clone();
175179
let jwks_url = self.jwks_url.clone();
@@ -338,7 +342,7 @@ pub(crate) mod tests {
338342
let token = generate_test_id_token(audience);
339343
let token = token.as_str();
340344

341-
let verifier = Builder::new(audience)
345+
let verifier = Builder::new([audience])
342346
.with_jwks_url(format!("http://{}/certs", server.addr()))
343347
.build();
344348

@@ -364,7 +368,7 @@ pub(crate) mod tests {
364368
let token = generate_test_id_token(audience);
365369
let token = token.as_str();
366370

367-
let verifier = Builder::new("https://wrong-audience.com")
371+
let verifier = Builder::new(["https://wrong-audience.com"])
368372
.with_jwks_url(format!("http://{}/certs", server.addr()))
369373
.build();
370374

@@ -375,6 +379,30 @@ pub(crate) mod tests {
375379
Ok(())
376380
}
377381

382+
#[tokio::test]
383+
async fn test_verify_multiple_audience_success() -> TestResult {
384+
let server = Server::run();
385+
server.expect(
386+
Expectation::matching(all_of![request::path("/certs"),])
387+
.times(1)
388+
.respond_with(json_encoded(create_jwk_set_response())),
389+
);
390+
391+
let audiences = ["https://example.com", "https://another_example.com"];
392+
let verifier = Builder::new(audiences)
393+
.with_jwks_url(format!("http://{}/certs", server.addr()))
394+
.build();
395+
396+
for audience in audiences {
397+
let token = generate_test_id_token(audience);
398+
let token = token.as_str();
399+
let claims = verifier.verify(token).await?;
400+
assert!(!claims.is_empty());
401+
}
402+
403+
Ok(())
404+
}
405+
378406
#[tokio::test]
379407
async fn test_verify_invalid_issuer() -> TestResult {
380408
let server = Server::run();
@@ -390,7 +418,7 @@ pub(crate) mod tests {
390418
let token = generate_test_id_token_with_claims(audience, claims);
391419
let token = token.as_str();
392420

393-
let verifier = Builder::new(audience)
421+
let verifier = Builder::new([audience])
394422
.with_jwks_url(format!("http://{}/certs", server.addr()))
395423
.build();
396424

@@ -418,7 +446,7 @@ pub(crate) mod tests {
418446
let token = generate_test_id_token_with_claims(audience, claims);
419447
let token = token.as_str();
420448

421-
let verifier = Builder::new(audience)
449+
let verifier = Builder::new([audience])
422450
.with_jwks_url(format!("http://{}/certs", server.addr()))
423451
.with_email(email)
424452
.build();
@@ -448,7 +476,7 @@ pub(crate) mod tests {
448476
let token = generate_test_id_token_with_claims(audience, claims);
449477
let token = token.as_str();
450478

451-
let verifier = Builder::new(audience)
479+
let verifier = Builder::new([audience])
452480
.with_jwks_url(format!("http://{}/certs", server.addr()))
453481
.with_email("[email protected]")
454482
.build();
@@ -475,7 +503,7 @@ pub(crate) mod tests {
475503
let token = generate_test_id_token_with_claims(audience, claims);
476504
let token = token.as_str();
477505

478-
let verifier = Builder::new(audience)
506+
let verifier = Builder::new([audience])
479507
.with_jwks_url(format!("http://{}/certs", server.addr()))
480508
.build();
481509

@@ -503,7 +531,7 @@ pub(crate) mod tests {
503531
let token = generate_test_id_token_with_claims(audience, claims);
504532
let token = token.as_str();
505533

506-
let verifier = Builder::new(audience)
534+
let verifier = Builder::new([audience])
507535
.with_jwks_url(format!("http://{}/certs", server.addr()))
508536
.with_email(email)
509537
.build();
@@ -530,7 +558,7 @@ pub(crate) mod tests {
530558
let token = generate_test_id_token_with_claims(audience, claims);
531559
let token = token.as_str();
532560

533-
let verifier = Builder::new(audience)
561+
let verifier = Builder::new([audience])
534562
.with_jwks_url(format!("http://{}/certs", server.addr()))
535563
.with_clock_skew(Duration::from_secs(10))
536564
.build();
@@ -544,7 +572,7 @@ pub(crate) mod tests {
544572
#[tokio::test]
545573
async fn test_verify_decode_error() -> TestResult {
546574
let audience = "https://example.com";
547-
let verifier = Builder::new(audience).build();
575+
let verifier = Builder::new([audience]).build();
548576
let invalid_token = "invalid.token.format";
549577

550578
let result = verifier.verify(invalid_token).await;
@@ -568,7 +596,7 @@ pub(crate) mod tests {
568596
let token =
569597
jsonwebtoken::encode(&header, &claims, &private_key).expect("failed to encode jwt");
570598

571-
let verifier = Builder::new("https://example.com").build();
599+
let verifier = Builder::new(["https://example.com"]).build();
572600

573601
let result = verifier.verify(&token).await;
574602
assert!(result.is_err());
@@ -590,7 +618,7 @@ pub(crate) mod tests {
590618
let token = generate_test_id_token(audience);
591619
let token = token.as_str();
592620

593-
let verifier = Builder::new(audience)
621+
let verifier = Builder::new([audience])
594622
.with_jwks_url(format!("http://{}/certs", server.addr()))
595623
.build();
596624

0 commit comments

Comments
 (0)