///|
/// Sign a JWT token with the specified HMAC algorithm.
///
/// Automatically selects the correct HMAC function based on the algorithm.
pub fn sign_with_alg(
alg : Algorithm,
header_json : String,
claims_json : String,
secret : Bytes,
) -> String {
let header_b64 = base64url_encode_str(header_json)
let claims_b64 = base64url_encode_str(claims_json)
let signing_input = header_b64 + "." + claims_b64
let signing_bytes = @utf8.encode(signing_input)
let mac = match alg {
HS256 => hmac_sha256(secret, signing_bytes)
HS384 => hmac_sha384(secret, signing_bytes)
HS512 => hmac_sha512(secret, signing_bytes)
}
let sig_b64 = base64url_encode(mac)
signing_input + "." + sig_b64
}
///|
/// Sign HS384 with a JwtHeader.
pub fn sign_hs384(
header : JwtHeader,
claims_json : String,
secret : Bytes,
) -> String {
let h = header
h.alg = HS384
sign_with_alg(HS384, h.to_json().stringify(), claims_json, secret)
}
///|
/// Sign HS512 with a JwtHeader.
pub fn sign_hs512(
header : JwtHeader,
claims_json : String,
secret : Bytes,
) -> String {
let h = header
h.alg = HS512
sign_with_alg(HS512, h.to_json().stringify(), claims_json, secret)
}
///|
/// Verify a token and return the algorithm used.
///
/// This decodes the header to determine which HMAC variant was used,
/// then verifies with the corresponding function.
pub fn verify_with_alg(
token : String,
secret : Bytes,
) -> Result[Algorithm, JwtError] {
let parts = token.split(".").to_array()
if parts.length() != 3 {
return Err(InvalidToken("token must have 3 parts"))
}
let header_b64 = parts[0].to_owned()
let claims_b64 = parts[1].to_owned()
let sig_b64 = parts[2].to_owned()
// Parse header to determine algorithm
let header_json = match parse_header_json(header_b64) {
Ok(h) => h
Err(e) => return Err(e)
}
let alg = match header_json {
Object(o) =>
match o["alg"] {
String(s) =>
match s {
"HS256" => HS256
"HS384" => HS384
"HS512" => HS512
other => return Err(InvalidAlgorithm(other))
}
_ => return Err(InvalidHeader("alg field missing or not string"))
}
_ => return Err(InvalidHeader("header is not an object"))
}
let signing_input = header_b64 + "." + claims_b64
let signing_bytes = @utf8.encode(signing_input)
let expected_mac = match alg {
HS256 => hmac_sha256(secret, signing_bytes)
HS384 => hmac_sha384(secret, signing_bytes)
HS512 => hmac_sha512(secret, signing_bytes)
}
let provided_mac = match base64url_decode(sig_b64) {
None => return Err(InvalidSignature)
Some(b) => b
}
if expected_mac.length() != provided_mac.length() {
return Err(InvalidSignature)
}
let mut diff = 0
for i in 0.. JwtHeader {
{ alg: HS384, typ: "JWT", kid: None }
}
///|
/// Create a JwtHeader for HS512.
pub fn JwtHeader::hs512() -> JwtHeader {
{ alg: HS512, typ: "JWT", kid: None }
}