Skip to main content

headless_lms_models/library/oauth/
tokens.rs

1use hmac::{Hmac, KeyInit, Mac};
2use rand::distr::SampleString;
3use rand::rng;
4use secrecy::{ExposeSecret, SecretString};
5use sha2::Sha256;
6
7use crate::library::oauth::Digest;
8
9const ACCESS_TOKEN_LENGTH: usize = 64;
10
11/// Generate a cryptographically strong opaque token suitable for access/refresh/auth codes.
12pub fn generate_access_token() -> String {
13    rand::distr::Alphanumeric.sample_string(&mut rng(), ACCESS_TOKEN_LENGTH)
14}
15
16/// Produce a `Digest` (HMAC-SHA-256) from an access/refresh token plaintext using a secret key.
17///
18/// This function uses HMAC-SHA-256 instead of plain SHA-256 to provide better security
19/// by requiring knowledge of the secret key to compute valid digests.
20///
21/// # Arguments
22/// * `token_plaintext` - The token string to hash
23/// * `key` - The secret key (pepper) to use for HMAC
24pub fn token_digest_sha256(token_plaintext: &str, key: &SecretString) -> Digest {
25    // The HMAC key is exposed only here, where it is fed into the MAC.
26    let mut mac = Hmac::<Sha256>::new_from_slice(key.expose_secret().as_bytes())
27        .expect("HMAC can take key of any size");
28    mac.update(token_plaintext.as_bytes());
29    let result = mac.finalize();
30    let code_bytes = result.into_bytes();
31    let mut arr = [0u8; Digest::LEN];
32    arr.copy_from_slice(&code_bytes);
33    Digest::new(arr)
34}