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