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