use hex;
use sha256;
use nanoid::nanoid;
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
use crate::Error;
const RANDOM_STATE_LENGTH: usize = 1024;
const CODE_VERIFIER_LENGTH: usize = 128; const CODE_VERIFIER_ALPHABET: [char; 66] = [
'-', '.', '_', '~',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
];
pub fn get_code_verifier() -> Result<(String, String), Error> {
let code_verifier = nanoid!(CODE_VERIFIER_LENGTH, &CODE_VERIFIER_ALPHABET);
let hashed_code_verifier = sha256::digest(&code_verifier);
let hash_as_bytes = hex::decode(hashed_code_verifier)?;
let base_64_url_encoded_hash = URL_SAFE_NO_PAD.encode(hash_as_bytes);
Ok(( code_verifier, base_64_url_encoded_hash ))
}
pub fn get_random_state() -> String {
let random_string = nanoid!(RANDOM_STATE_LENGTH);
sha256::digest(random_string)
}
#[cfg(test)]
mod tests {
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
use super::{get_random_state, get_code_verifier, CODE_VERIFIER_ALPHABET};
#[test]
fn get_code_verifier_test() {
let (code_verifier, challenge) = get_code_verifier().unwrap();
assert!( code_verifier.chars().all( |c| CODE_VERIFIER_ALPHABET.contains(&c) ) );
assert!( code_verifier.len() >= 43 );
assert!( code_verifier.len() <= 128 );
let hashed_code_verifier = sha256::digest(&code_verifier);
let hash_as_bytes = hex::decode(hashed_code_verifier).unwrap();
let decoded_challenge = URL_SAFE_NO_PAD.decode( &challenge.as_bytes() ).unwrap();
assert_eq!(hash_as_bytes, decoded_challenge)
}
#[test]
fn get_random_state_test() {
let random_state = get_random_state();
assert_eq!( random_state.len(), 64 );
}
}