pkce/
lib.rs

1//! This is a minimal library with functions to generate random code verifiers and challenges to be used for OAuth [Proof Key for Code Exchange](https://tools.ietf.org/html/rfc7636).
2//!
3//! ```
4//! extern crate pkce;
5//!
6//! fn main() {
7//!     let code_verify = pkce::code_verifier(128);
8//!     let code_challenge = pkce::code_challenge(&code_verify);
9//!
10//!     println!("Code challenge generated: {}", code_challenge);
11//! }
12//! ```
13
14extern crate base64;
15extern crate rand;
16extern crate sha2;
17
18use base64::Engine;
19use rand::{thread_rng, Rng};
20use sha2::{Digest, Sha256};
21
22const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
23    abcdefghijklmnopqrstuvwxyz\
24    0123456789-.~_";
25
26/// Generate a random code verifier.
27///
28/// # Arguments
29///
30/// * `length` - The desired length in bytes of the code verifier. This value should be between 43 and 128 or else the function will panic.
31pub fn code_verifier(length: usize) -> Vec<u8> {
32    assert!(
33        (43..=128).contains(&length),
34        "Code verifier length must be between 43 and 128 bytes"
35    );
36
37    let mut rng = thread_rng();
38
39    (0..length)
40        .map(|_| {
41            let i = rng.gen_range(0..CHARS.len());
42            CHARS[i]
43        })
44        .collect()
45}
46
47fn base64_url_encode(input: &[u8]) -> String {
48    let b64 = base64::engine::general_purpose::STANDARD.encode(input);
49    b64.chars()
50        .filter_map(|c| match c {
51            '=' => None,
52            '+' => Some('-'),
53            '/' => Some('_'),
54            x => Some(x),
55        })
56        .collect()
57}
58
59/// Generate a code challenge from a given code verifier with SHA256 and base64.
60///
61/// # Arguments
62///
63/// * `code_verifier` - The code verifier, such as the one generated by the [`code_verifier`] function.
64pub fn code_challenge(code_verifier: &[u8]) -> String {
65    let mut sha = Sha256::new();
66    sha.update(code_verifier);
67    let result = sha.finalize();
68    base64_url_encode(&result[..])
69}