about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2022-09-20 00:26:04 +0300
committerVika <vika@fireburn.ru>2022-09-20 00:26:04 +0300
commitcfc12adcb8dc680e5fa105e38d79825fb91a3f3b (patch)
treea4e72ecf624c084b7ee3cd5fd9287fb057ddcaa4
parent53c868691a09b84d71f724d23a09d1fb89368792 (diff)
downloadkittybox-cfc12adcb8dc680e5fa105e38d79825fb91a3f3b.tar.zst
kittybox-indieauth: fix extraneous padding in PKCE challenges
-rw-r--r--kittybox-rs/indieauth/src/lib.rs2
-rw-r--r--kittybox-rs/indieauth/src/pkce.rs28
2 files changed, 25 insertions, 5 deletions
diff --git a/kittybox-rs/indieauth/src/lib.rs b/kittybox-rs/indieauth/src/lib.rs
index 2cce1b9..752d9e9 100644
--- a/kittybox-rs/indieauth/src/lib.rs
+++ b/kittybox-rs/indieauth/src/lib.rs
@@ -285,7 +285,7 @@ impl AsRef<str> for State {
 ///     client_id: "https://kittybox.fireburn.ru/companion/native".parse().unwrap(),
 ///     redirect_uri: "https://kittybox.fireburn.ru/companion/native/redirect".parse().unwrap(),
 ///     state: State::new(),
-///     code_challenge: PKCEChallenge::new(verifier, PKCEMethod::default()),
+///     code_challenge: PKCEChallenge::new(&verifier, PKCEMethod::default()),
 ///     scope: Some(Scopes::new(vec![Scope::Create, Scope::Update, Scope::Delete, Scope::Media])),
 ///     me: Some("https://fireburn.ru/".parse().unwrap())
 /// };
diff --git a/kittybox-rs/indieauth/src/pkce.rs b/kittybox-rs/indieauth/src/pkce.rs
index 511b9fc..bf8d1a0 100644
--- a/kittybox-rs/indieauth/src/pkce.rs
+++ b/kittybox-rs/indieauth/src/pkce.rs
@@ -68,13 +68,16 @@ pub struct PKCEChallenge {
 impl PKCEChallenge {
     /// Create a new challenge from a [PKCEVerifier] using a certain
     /// [PKCEMethod].
-    pub fn new(code_verifier: PKCEVerifier, method: PKCEMethod) -> Self {
+    pub fn new(code_verifier: &PKCEVerifier, method: PKCEMethod) -> Self {
         Self {
             code_challenge: match method {
                 PKCEMethod::S256 => {
                     let mut hasher = Sha256::new();
                     hasher.update(code_verifier.as_ref());
-                    BASE64URL.encode(&hasher.finalize())
+                    let mut challenge = BASE64URL.encode(&hasher.finalize());
+                    challenge.retain(|c| c != '=');
+
+                    challenge
                 },
                 PKCEMethod::Plain => code_verifier.to_string(),
             },
@@ -90,14 +93,14 @@ impl PKCEChallenge {
     /// use kittybox_indieauth::{PKCEVerifier, PKCEMethod, PKCEChallenge};
     ///
     /// let verifier = PKCEVerifier::new();
-    /// let challenge = PKCEChallenge::new(verifier.clone(), PKCEMethod::default());
+    /// let challenge = PKCEChallenge::new(&verifier, PKCEMethod::default());
     /// // Meanwhile, at the token endpoint, in the end of the ceremony...
     /// // ...the challenge gets retrieved from the stored data and verified
     /// assert!(challenge.verify(verifier))
     /// ```
     #[must_use]
     pub fn verify(&self, code_verifier: PKCEVerifier) -> bool {
-        Self::new(code_verifier, self.method) == *self
+        Self::new(&code_verifier, self.method) == *self
     }
 
     /// Return a reference to the code challenge string.
@@ -110,3 +113,20 @@ impl PKCEChallenge {
         self.method
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::{PKCEMethod, PKCEVerifier, PKCEChallenge};
+
+    #[test]
+    /// A snapshot test generated using [Aaron Parecki's PKCE
+    /// tools](https://example-app.com/pkce) that checks for a
+    /// conforming challenge.
+    fn test_pkce_challenge_verification() {
+        let verifier = PKCEVerifier("ec03310e4e90f7bc988af05384060c3c1afeae4bb4d0f648c5c06b63".to_owned());
+
+        let challenge = PKCEChallenge::new(&verifier, PKCEMethod::S256);
+
+        assert_eq!(challenge.as_str(), "aB8OG20Rh8UoQ9gFhI0YvPkx4dDW2MBspBKGXL6j6Wg");
+    }
+}