Accept only S256 PKCE challenges #205

Merged
jcoffey-dev merged 1 commits from fix/pkce-s256-only into main 2026-10-18 22:21:28 +00:00
2 changed files with 33 additions and 8 deletions

No files matched your search

+10 -8
View File
@@ -2,6 +2,8 @@
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*
* Modified by Coffey Labs in 2026 for INBUXA.
*/
use super::{DeviceAuthResponse, FormData, MAX_POST_LEN, OAuthCode, PkceCodeChallenge};
@@ -260,16 +262,16 @@ impl OAuthApiHandler for Server {
}
// Parse and validate PKCE challenge (RFC 7636).
// inbuxa: S256 only, as the metadata says. With `plain` (also
// RFC 7636's default when no method is named) the challenge is
// the verifier, so it protects nothing.
let pkce_challenge = match code_challenge {
Some(challenge) => match code_challenge_method.as_deref().unwrap_or("plain") {
"S256" => PkceCodeChallenge::S256(challenge),
"plain" if stateless_client.is_none() => {
PkceCodeChallenge::Plain(challenge)
}
Some(challenge) => match code_challenge_method.as_deref() {
Some("S256") => PkceCodeChallenge::S256(challenge),
_ => {
return Err(trc::AuthEvent::Error
.into_err()
.details("Unsupported PKCE code_challenge_method."));
return Err(trc::AuthEvent::Error.into_err().details(
"Unsupported PKCE code_challenge_method: use S256.",
));
}
},
None => {
+23
View File
@@ -351,6 +351,29 @@ pub async fn test(test: &mut TestServer) {
"unknown resource indicator must not be authorized"
);
// inbuxa: only S256 is accepted. `plain`, named or as RFC 7636's default
// when no method is given, sends the verifier itself as the challenge.
for method in [Some("plain"), None] {
let (status, _) = post_login_raw(&LoginRequest::AuthCode {
account_name: "[email protected]".to_string(),
account_secret: "this is a very strong password".to_string(),
mfa_token: None,
client_id: client_id.to_string(),
redirect_uri: "com.example.app:/cb".to_string().into(),
nonce: None,
scope: Some(PROFILE_SCOPE.to_string()),
code_challenge: Some(PKCE_VERIFIER.to_string()),
code_challenge_method: method.map(str::to_string),
state: None,
resource: vec![],
})
.await;
assert_ne!(
status, 200,
"PKCE method {method:?} must not be authorized"
);
}
// Authenticate with the correct password, PKCE (S256), scope and a valid resource indicator
let response = http
.post::<LoginResponse>(