Skip to content

Commit 68fc9f7

Browse files
committed
fix: default to allowing missing issuer
1 parent e660b80 commit 68fc9f7

1 file changed

Lines changed: 63 additions & 13 deletions

File tree

crates/rmcp/src/transport/auth.rs

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,7 @@ pub struct AuthorizationManager {
963963
resource_scopes: RwLock<Vec<String>>,
964964
/// OIDC Dynamic Client Registration `application_type` (SEP-837)
965965
application_type: Option<String>,
966+
strict_issuer_validation: bool,
966967
}
967968

968969
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -1208,6 +1209,7 @@ impl AuthorizationManager {
12081209
www_auth_scopes: RwLock::new(Vec::new()),
12091210
resource_scopes: RwLock::new(Vec::new()),
12101211
application_type: Some(DEFAULT_APPLICATION_TYPE.to_string()),
1212+
strict_issuer_validation: false,
12111213
};
12121214

12131215
Ok(manager)
@@ -1218,6 +1220,17 @@ impl AuthorizationManager {
12181220
self.scope_upgrade_config = config;
12191221
}
12201222

1223+
/// Configure whether authorization server metadata discovery requires the
1224+
/// metadata `issuer` field.
1225+
///
1226+
/// The default is `false` to preserve compatibility with legacy
1227+
/// authorization servers that omit `issuer`. Set this to `true` to enforce
1228+
/// the RFC 8414/OIDC requirement that discovered metadata include `issuer`
1229+
/// whenever the expected issuer can be derived from the discovery URL.
1230+
pub fn set_strict_issuer_validation(&mut self, strict: bool) {
1231+
self.strict_issuer_validation = strict;
1232+
}
1233+
12211234
/// Set a custom credential store
12221235
///
12231236
/// This allows you to provide your own implementation of credential storage,
@@ -2156,7 +2169,7 @@ impl AuthorizationManager {
21562169

21572170
match serde_json::from_slice::<AuthorizationMetadata>(response.body()) {
21582171
Ok(metadata) => {
2159-
Self::validate_authorization_metadata_issuer(discovery_url, &metadata)?;
2172+
self.validate_authorization_metadata_issuer(discovery_url, &metadata)?;
21602173
Ok(Some(metadata))
21612174
}
21622175
Err(err) => {
@@ -2218,6 +2231,7 @@ impl AuthorizationManager {
22182231
}
22192232

22202233
fn validate_authorization_metadata_issuer(
2234+
&self,
22212235
discovery_url: &Url,
22222236
metadata: &AuthorizationMetadata,
22232237
) -> Result<(), AuthError> {
@@ -2227,7 +2241,10 @@ impl AuthorizationManager {
22272241
return Ok(());
22282242
};
22292243
let Some(received_issuer) = metadata.issuer.as_deref() else {
2230-
return Err(AuthError::AuthorizationServerMissingIssuer { expected_issuer });
2244+
if self.strict_issuer_validation {
2245+
return Err(AuthError::AuthorizationServerMissingIssuer { expected_issuer });
2246+
}
2247+
return Ok(());
22312248
};
22322249
if !Self::issuer_identifiers_match(received_issuer, &expected_issuer) {
22332250
return Err(AuthError::AuthorizationServerMismatch {
@@ -3938,8 +3955,8 @@ mod tests {
39383955
);
39393956
}
39403957

3941-
#[test]
3942-
fn authorization_metadata_accepts_oidc_path_appended_issuer() {
3958+
#[tokio::test]
3959+
async fn authorization_metadata_accepts_oidc_path_appended_issuer() {
39433960
let discovery_url =
39443961
Url::parse("https://auth.example.com/tenant1/.well-known/openid-configuration")
39453962
.unwrap();
@@ -3949,8 +3966,12 @@ mod tests {
39493966
token_endpoint: "https://auth.example.com/tenant1/token".to_string(),
39503967
..Default::default()
39513968
};
3969+
let manager = AuthorizationManager::new("https://mcp.example.com/")
3970+
.await
3971+
.unwrap();
39523972

3953-
AuthorizationManager::validate_authorization_metadata_issuer(&discovery_url, &metadata)
3973+
manager
3974+
.validate_authorization_metadata_issuer(&discovery_url, &metadata)
39543975
.unwrap();
39553976
}
39563977

@@ -3966,8 +3987,8 @@ mod tests {
39663987
));
39673988
}
39683989

3969-
#[test]
3970-
fn authorization_metadata_accepts_oidc_path_inserted_issuer() {
3990+
#[tokio::test]
3991+
async fn authorization_metadata_accepts_oidc_path_inserted_issuer() {
39713992
let discovery_url =
39723993
Url::parse("https://auth.example.com/.well-known/openid-configuration/tenant1")
39733994
.unwrap();
@@ -3977,13 +3998,17 @@ mod tests {
39773998
token_endpoint: "https://auth.example.com/tenant1/token".to_string(),
39783999
..Default::default()
39794000
};
4001+
let manager = AuthorizationManager::new("https://mcp.example.com/")
4002+
.await
4003+
.unwrap();
39804004

3981-
AuthorizationManager::validate_authorization_metadata_issuer(&discovery_url, &metadata)
4005+
manager
4006+
.validate_authorization_metadata_issuer(&discovery_url, &metadata)
39824007
.unwrap();
39834008
}
39844009

3985-
#[test]
3986-
fn authorization_metadata_rejects_missing_issuer_for_standard_discovery_url() {
4010+
#[tokio::test]
4011+
async fn authorization_metadata_allows_missing_issuer_by_default() {
39874012
let discovery_url =
39884013
Url::parse("https://auth.example.com/.well-known/openid-configuration/tenant1")
39894014
.unwrap();
@@ -3994,9 +4019,34 @@ mod tests {
39944019
..Default::default()
39954020
};
39964021

3997-
let error =
3998-
AuthorizationManager::validate_authorization_metadata_issuer(&discovery_url, &metadata)
3999-
.unwrap_err();
4022+
let manager = AuthorizationManager::new("https://mcp.example.com/")
4023+
.await
4024+
.unwrap();
4025+
4026+
manager
4027+
.validate_authorization_metadata_issuer(&discovery_url, &metadata)
4028+
.unwrap();
4029+
}
4030+
4031+
#[tokio::test]
4032+
async fn authorization_metadata_rejects_missing_issuer_when_required() {
4033+
let discovery_url =
4034+
Url::parse("https://auth.example.com/.well-known/openid-configuration/tenant1")
4035+
.unwrap();
4036+
let metadata = AuthorizationMetadata {
4037+
issuer: None,
4038+
authorization_endpoint: "https://auth.example.com/tenant1/authorize".to_string(),
4039+
token_endpoint: "https://auth.example.com/tenant1/token".to_string(),
4040+
..Default::default()
4041+
};
4042+
let mut manager = AuthorizationManager::new("https://mcp.example.com/")
4043+
.await
4044+
.unwrap();
4045+
manager.set_strict_issuer_validation(true);
4046+
4047+
let error = manager
4048+
.validate_authorization_metadata_issuer(&discovery_url, &metadata)
4049+
.unwrap_err();
40004050

40014051
assert!(
40024052
matches!(

0 commit comments

Comments
 (0)