feat: reject auth servers lacking S256 PKCE support (#955)
This commit is contained in:
parent
bdf0c32e8c
commit
95490facd6
1 changed files with 46 additions and 16 deletions
|
|
@ -477,6 +477,9 @@ pub enum AuthError {
|
||||||
#[error("Metadata error: {0}")]
|
#[error("Metadata error: {0}")]
|
||||||
MetadataError(String),
|
MetadataError(String),
|
||||||
|
|
||||||
|
#[error("Authorization server does not support the required PKCE code challenge method (S256)")]
|
||||||
|
PkceUnsupported,
|
||||||
|
|
||||||
#[error("URL parse error: {0}")]
|
#[error("URL parse error: {0}")]
|
||||||
UrlError(#[from] url::ParseError),
|
UrlError(#[from] url::ParseError),
|
||||||
|
|
||||||
|
|
@ -1159,18 +1162,21 @@ impl AuthorizationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for PKCE, we always send s256 since oauth 2.1 requires servers to support it,
|
// The client always sends an S256 challenge. A server that advertises
|
||||||
// but warn if the server metadata suggests otherwise
|
// methods without S256 can't do the flow we require, so refuse it. A
|
||||||
|
// server that omits the field is tolerated: it usually means the server
|
||||||
|
// didn't advertise PKCE, not that it lacks S256.
|
||||||
match &metadata.code_challenge_methods_supported {
|
match &metadata.code_challenge_methods_supported {
|
||||||
Some(methods) if !methods.iter().any(|m| m == "S256") => {
|
Some(methods) if !methods.iter().any(|m| m == "S256") => {
|
||||||
|
return Err(AuthError::PkceUnsupported);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
warn!(
|
warn!(
|
||||||
?methods,
|
"authorization server metadata omits code_challenge_methods_supported; \
|
||||||
"server does not advertise S256 in code_challenge_methods_supported, \
|
proceeding with an S256 challenge anyway"
|
||||||
proceeding with S256 anyway as oauth 2.1 requires it. \
|
|
||||||
The server is not compliant with the specification!"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {}
|
Some(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -4307,19 +4313,43 @@ mod tests {
|
||||||
assert!(manager.validate_server_metadata("code").is_err());
|
assert!(manager.validate_server_metadata("code").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
fn as_metadata_with_pkce(methods: Option<Vec<String>>) -> AuthorizationMetadata {
|
||||||
async fn test_validate_as_metadata_passes_without_pkce_s256() {
|
AuthorizationMetadata {
|
||||||
let mut manager = AuthorizationManager::new("https://example.com")
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let metadata = AuthorizationMetadata {
|
|
||||||
authorization_endpoint: "https://auth.example.com/authorize".to_string(),
|
authorization_endpoint: "https://auth.example.com/authorize".to_string(),
|
||||||
token_endpoint: "https://auth.example.com/token".to_string(),
|
token_endpoint: "https://auth.example.com/token".to_string(),
|
||||||
response_types_supported: Some(vec!["code".to_string()]),
|
response_types_supported: Some(vec!["code".to_string()]),
|
||||||
code_challenge_methods_supported: Some(vec!["plain".to_string()]),
|
code_challenge_methods_supported: methods,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
}
|
||||||
manager.set_metadata(metadata);
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_validate_as_metadata_rejects_without_pkce_s256() {
|
||||||
|
let mut manager = AuthorizationManager::new("https://example.com")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
manager.set_metadata(as_metadata_with_pkce(Some(vec!["plain".to_string()])));
|
||||||
|
assert!(matches!(
|
||||||
|
manager.validate_server_metadata("code"),
|
||||||
|
Err(AuthError::PkceUnsupported)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_validate_as_metadata_allows_absent_pkce_methods_by_default() {
|
||||||
|
let mut manager = AuthorizationManager::new("https://example.com")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
manager.set_metadata(as_metadata_with_pkce(None));
|
||||||
|
assert!(manager.validate_server_metadata("code").is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_validate_as_metadata_passes_with_pkce_s256() {
|
||||||
|
let mut manager = AuthorizationManager::new("https://example.com")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
manager.set_metadata(as_metadata_with_pkce(Some(vec!["S256".to_string()])));
|
||||||
assert!(manager.validate_server_metadata("code").is_ok());
|
assert!(manager.validate_server_metadata("code").is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue