Skip to content

Commit a97d2da

Browse files
committed
Make the issue optional on upstream OAuth 2.0 providers
1 parent 80903ed commit a97d2da

File tree

26 files changed

+85
-58
lines changed

26 files changed

+85
-58
lines changed

crates/cli/src/commands/manage.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -764,8 +764,10 @@ impl std::fmt::Display for HumanReadable<&UpstreamOAuthProvider> {
764764
let provider = self.0;
765765
if let Some(human_name) = &provider.human_name {
766766
write!(f, "{} ({})", human_name, provider.id)
767+
} else if let Some(issuer) = &provider.issuer {
768+
write!(f, "{} ({})", issuer, provider.id)
767769
} else {
768-
write!(f, "{} ({})", provider.issuer, provider.id)
770+
write!(f, "{}", provider.id)
769771
}
770772
}
771773
}

crates/config/src/sections/upstream_oauth2.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ impl ConfigurationSection for UpstreamOAuth2Config {
4747
Err(error)
4848
};
4949

50+
if !matches!(provider.discovery_mode, DiscoveryMode::Disabled)
51+
&& provider.issuer.is_none()
52+
{
53+
return annotate(figment::Error::custom(
54+
"The `issuer` field is required when discovery is enabled",
55+
));
56+
}
57+
5058
match provider.token_endpoint_auth_method {
5159
TokenAuthMethod::None
5260
| TokenAuthMethod::PrivateKeyJwt
@@ -438,7 +446,10 @@ pub struct Provider {
438446
pub id: Ulid,
439447

440448
/// The OIDC issuer URL
441-
pub issuer: String,
449+
///
450+
/// This is required if OIDC discovery is enabled (which is the default)
451+
#[serde(skip_serializing_if = "Option::is_none")]
452+
pub issuer: Option<String>,
442453

443454
/// A human-readable name for the provider, that will be shown to users
444455
#[serde(skip_serializing_if = "Option::is_none")]

crates/data-model/src/upstream_oauth2/provider.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ pub struct InvalidUpstreamOAuth2TokenAuthMethod(String);
219219
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
220220
pub struct UpstreamOAuthProvider {
221221
pub id: Ulid,
222-
pub issuer: String,
222+
pub issuer: Option<String>,
223223
pub human_name: Option<String>,
224224
pub brand_name: Option<String>,
225225
pub discovery_mode: DiscoveryMode,

crates/handlers/src/graphql/model/upstream_oauth.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ impl UpstreamOAuth2Provider {
3737
}
3838

3939
/// OpenID Connect issuer URL.
40-
pub async fn issuer(&self) -> &str {
41-
&self.provider.issuer
40+
pub async fn issuer(&self) -> Option<&str> {
41+
self.provider.issuer.as_deref()
4242
}
4343

4444
/// Client ID used for this provider.

crates/handlers/src/upstream_oauth2/cache.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@ impl<'a> LazyProviderInfos<'a> {
6161
}
6262
};
6363

64-
let metadata = self
65-
.cache
66-
.get(self.client, &self.provider.issuer, verify)
67-
.await?;
64+
let Some(issuer) = &self.provider.issuer else {
65+
return Err(DiscoveryError::MissingIssuer);
66+
};
67+
68+
let metadata = self.cache.get(self.client, issuer, verify).await?;
6869

6970
self.loaded_metadata = Some(metadata);
7071
}
@@ -179,8 +180,13 @@ impl MetadataCache {
179180
UpstreamOAuthProviderDiscoveryMode::Disabled => continue,
180181
};
181182

182-
if let Err(e) = self.fetch(client, &provider.issuer, verify).await {
183-
tracing::error!(issuer = %provider.issuer, error = &e as &dyn std::error::Error, "Failed to fetch provider metadata");
183+
let Some(issuer) = &provider.issuer else {
184+
tracing::error!(%provider.id, "Provider doesn't have an issuer set, but discovery is enabled!");
185+
continue;
186+
};
187+
188+
if let Err(e) = self.fetch(client, issuer, verify).await {
189+
tracing::error!(%issuer, error = &e as &dyn std::error::Error, "Failed to fetch provider metadata");
184190
}
185191
}
186192

@@ -395,7 +401,7 @@ mod tests {
395401
let clock = MockClock::default();
396402
let provider = UpstreamOAuthProvider {
397403
id: Ulid::nil(),
398-
issuer: mock_server.uri(),
404+
issuer: Some(mock_server.uri()),
399405
human_name: Some("Example Ltd.".to_owned()),
400406
brand_name: None,
401407
discovery_mode: UpstreamOAuthProviderDiscoveryMode::Insecure,

crates/handlers/src/upstream_oauth2/callback.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ pub(crate) async fn handler(
284284
);
285285

286286
let id_token_verification_data = JwtVerificationData {
287-
issuer: &provider.issuer,
287+
issuer: provider.issuer.as_deref(),
288288
jwks: jwks.as_ref().unwrap(),
289289
signing_algorithm: &provider.id_token_signed_response_alg,
290290
client_id: &provider.client_id,
@@ -350,7 +350,7 @@ pub(crate) async fn handler(
350350
lazy_metadata.userinfo_endpoint().await?,
351351
token_response.access_token.as_str(),
352352
Some(JwtVerificationData {
353-
issuer: &provider.issuer,
353+
issuer: provider.issuer.as_deref(),
354354
jwks: &jwks,
355355
signing_algorithm,
356356
client_id: &provider.client_id,

crates/handlers/src/upstream_oauth2/link.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@ mod tests {
916916
&mut rng,
917917
&state.clock,
918918
UpstreamOAuthProviderParams {
919-
issuer: "https://example.com/".to_owned(),
919+
issuer: Some("https://example.com/".to_owned()),
920920
human_name: Some("Example Ltd.".to_owned()),
921921
brand_name: None,
922922
scope: Scope::from_iter([OPENID]),

crates/handlers/src/upstream_oauth2/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ fn client_credentials_for_provider(
131131

132132
ClientCredentials::SignInWithApple {
133133
client_id,
134-
audience: provider.issuer.clone(),
135134
key,
136135
key_id: params.key_id,
137136
team_id: params.team_id,

crates/handlers/src/views/login.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ mod test {
398398
&mut rng,
399399
&state.clock,
400400
UpstreamOAuthProviderParams {
401-
issuer: "https://first.com/".to_owned(),
401+
issuer: Some("https://first.com/".to_owned()),
402402
human_name: Some("First Ltd.".to_owned()),
403403
brand_name: None,
404404
scope: [OPENID].into_iter().collect(),
@@ -438,7 +438,7 @@ mod test {
438438
&mut rng,
439439
&state.clock,
440440
UpstreamOAuthProviderParams {
441-
issuer: "https://second.com/".to_owned(),
441+
issuer: Some("https://second.com/".to_owned()),
442442
human_name: None,
443443
brand_name: None,
444444
scope: [OPENID].into_iter().collect(),

crates/oidc-client/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ pub enum DiscoveryError {
5555
/// An error occurred validating the metadata.
5656
Validation(#[from] ProviderMetadataVerificationError),
5757

58+
/// The provider doesn't have an issuer set, which is required if discovery
59+
/// is enabled.
60+
#[error("Provider doesn't have an issuer set")]
61+
MissingIssuer,
62+
5863
/// Discovery is disabled for this provider.
5964
#[error("Discovery is disabled for this provider")]
6065
Disabled,

0 commit comments

Comments
 (0)