Skip to content

Commit 92c0793

Browse files
authored
fix: default to allowing missing issuer (#1051)
1 parent 14298b7 commit 92c0793

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
@@ -1000,6 +1000,7 @@ pub struct AuthorizationManager {
10001000
resource_scopes: RwLock<Vec<String>>,
10011001
/// OIDC Dynamic Client Registration `application_type` (SEP-837)
10021002
application_type: Option<String>,
1003+
strict_issuer_validation: bool,
10031004
}
10041005

10051006
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -1245,6 +1246,7 @@ impl AuthorizationManager {
12451246
www_auth_scopes: RwLock::new(Vec::new()),
12461247
resource_scopes: RwLock::new(Vec::new()),
12471248
application_type: Some(DEFAULT_APPLICATION_TYPE.to_string()),
1249+
strict_issuer_validation: false,
12481250
};
12491251

12501252
Ok(manager)
@@ -1255,6 +1257,17 @@ impl AuthorizationManager {
12551257
self.scope_upgrade_config = config;
12561258
}
12571259

1260+
/// Configure whether authorization server metadata discovery requires the
1261+
/// metadata `issuer` field.
1262+
///
1263+
/// The default is `false` to preserve compatibility with legacy
1264+
/// authorization servers that omit `issuer`. Set this to `true` to enforce
1265+
/// the RFC 8414/OIDC requirement that discovered metadata include `issuer`
1266+
/// whenever the expected issuer can be derived from the discovery URL.
1267+
pub fn set_strict_issuer_validation(&mut self, strict: bool) {
1268+
self.strict_issuer_validation = strict;
1269+
}
1270+
12581271
/// Set a custom credential store
12591272
///
12601273
/// This allows you to provide your own implementation of credential storage,
@@ -2226,7 +2239,7 @@ impl AuthorizationManager {
22262239

22272240
match serde_json::from_slice::<AuthorizationMetadata>(response.body()) {
22282241
Ok(metadata) => {
2229-
Self::validate_authorization_metadata_issuer(discovery_url, &metadata)?;
2242+
self.validate_authorization_metadata_issuer(discovery_url, &metadata)?;
22302243
Ok(Some(metadata))
22312244
}
22322245
Err(err) => {
@@ -2288,6 +2301,7 @@ impl AuthorizationManager {
22882301
}
22892302

22902303
fn validate_authorization_metadata_issuer(
2304+
&self,
22912305
discovery_url: &Url,
22922306
metadata: &AuthorizationMetadata,
22932307
) -> Result<(), AuthError> {
@@ -2297,7 +2311,10 @@ impl AuthorizationManager {
22972311
return Ok(());
22982312
};
22992313
let Some(received_issuer) = metadata.issuer.as_deref() else {
2300-
return Err(AuthError::AuthorizationServerMissingIssuer { expected_issuer });
2314+
if self.strict_issuer_validation {
2315+
return Err(AuthError::AuthorizationServerMissingIssuer { expected_issuer });
2316+
}
2317+
return Ok(());
23012318
};
23022319
if !Self::issuer_identifiers_match(received_issuer, &expected_issuer) {
23032320
return Err(AuthError::AuthorizationServerMismatch {
@@ -4006,8 +4023,8 @@ mod tests {
40064023
);
40074024
}
40084025

4009-
#[test]
4010-
fn authorization_metadata_accepts_oidc_path_appended_issuer() {
4026+
#[tokio::test]
4027+
async fn authorization_metadata_accepts_oidc_path_appended_issuer() {
40114028
let discovery_url =
40124029
Url::parse("https://auth.example.com/tenant1/.well-known/openid-configuration")
40134030
.unwrap();
@@ -4017,8 +4034,12 @@ mod tests {
40174034
token_endpoint: "https://auth.example.com/tenant1/token".to_string(),
40184035
..Default::default()
40194036
};
4037+
let manager = AuthorizationManager::new("https://mcp.example.com/")
4038+
.await
4039+
.unwrap();
40204040

4021-
AuthorizationManager::validate_authorization_metadata_issuer(&discovery_url, &metadata)
4041+
manager
4042+
.validate_authorization_metadata_issuer(&discovery_url, &metadata)
40224043
.unwrap();
40234044
}
40244045

@@ -4034,8 +4055,8 @@ mod tests {
40344055
));
40354056
}
40364057

4037-
#[test]
4038-
fn authorization_metadata_accepts_oidc_path_inserted_issuer() {
4058+
#[tokio::test]
4059+
async fn authorization_metadata_accepts_oidc_path_inserted_issuer() {
40394060
let discovery_url =
40404061
Url::parse("https://auth.example.com/.well-known/openid-configuration/tenant1")
40414062
.unwrap();
@@ -4045,13 +4066,17 @@ mod tests {
40454066
token_endpoint: "https://auth.example.com/tenant1/token".to_string(),
40464067
..Default::default()
40474068
};
4069+
let manager = AuthorizationManager::new("https://mcp.example.com/")
4070+
.await
4071+
.unwrap();
40484072

4049-
AuthorizationManager::validate_authorization_metadata_issuer(&discovery_url, &metadata)
4073+
manager
4074+
.validate_authorization_metadata_issuer(&discovery_url, &metadata)
40504075
.unwrap();
40514076
}
40524077

4053-
#[test]
4054-
fn authorization_metadata_rejects_missing_issuer_for_standard_discovery_url() {
4078+
#[tokio::test]
4079+
async fn authorization_metadata_allows_missing_issuer_by_default() {
40554080
let discovery_url =
40564081
Url::parse("https://auth.example.com/.well-known/openid-configuration/tenant1")
40574082
.unwrap();
@@ -4062,9 +4087,34 @@ mod tests {
40624087
..Default::default()
40634088
};
40644089

4065-
let error =
4066-
AuthorizationManager::validate_authorization_metadata_issuer(&discovery_url, &metadata)
4067-
.unwrap_err();
4090+
let manager = AuthorizationManager::new("https://mcp.example.com/")
4091+
.await
4092+
.unwrap();
4093+
4094+
manager
4095+
.validate_authorization_metadata_issuer(&discovery_url, &metadata)
4096+
.unwrap();
4097+
}
4098+
4099+
#[tokio::test]
4100+
async fn authorization_metadata_rejects_missing_issuer_when_required() {
4101+
let discovery_url =
4102+
Url::parse("https://auth.example.com/.well-known/openid-configuration/tenant1")
4103+
.unwrap();
4104+
let metadata = AuthorizationMetadata {
4105+
issuer: None,
4106+
authorization_endpoint: "https://auth.example.com/tenant1/authorize".to_string(),
4107+
token_endpoint: "https://auth.example.com/tenant1/token".to_string(),
4108+
..Default::default()
4109+
};
4110+
let mut manager = AuthorizationManager::new("https://mcp.example.com/")
4111+
.await
4112+
.unwrap();
4113+
manager.set_strict_issuer_validation(true);
4114+
4115+
let error = manager
4116+
.validate_authorization_metadata_issuer(&discovery_url, &metadata)
4117+
.unwrap_err();
40684118

40694119
assert!(
40704120
matches!(

0 commit comments

Comments
 (0)