@@ -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