// JWT (JSON Web Tokens) implementation with HS256 signing
// RFC 7519: https://tools.ietf.org/html/rfc7519
let jwt_header : String = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}"
fn fixed_to_array(fixed: FixedArray[UInt]) -> Array[UInt] {
let arr : Array[UInt] = []
for i in 0.. Bool {
if a.length() != b.length() {
return false
}
let mut diff : UInt = 0U
for i in 0.. Array[Int] {
let raw = @utf8.encode(s)
let result : Array[Int] = []
for i in 0.. Result[Array[String], String] {
let chars = string_to_bytes_filtered_jwt(token)
let parts : Array[String] = []
let mut current = ""
for c in chars {
if c == 46 { // '.'
parts.push(current)
current = ""
} else {
current = current + c.unsafe_to_char().to_string()
}
}
parts.push(current)
if parts.length() != 3 {
return Err("Invalid JWT format: expected 3 parts separated by dots")
}
Ok(parts)
}
/// Signs a JSON payload and returns a JWT token string.
/// The payload should be a valid JSON string.
///
/// Example:
/// ```
/// let token = sign("{\"sub\":\"1234567890\",\"name\":\"John\"}", "secret")
/// ```
pub fn sign(payload: String, secret: String) -> String {
let header_encoded = base64url_encode_string(jwt_header)
let payload_encoded = base64url_encode_string(payload)
let signing_input = header_encoded + "." + payload_encoded
let signature = @hmac.hmac_sha256_string(signing_input, secret)
let signature_encoded = base64url_encode(fixed_to_array(signature))
signing_input + "." + signature_encoded
}
/// Verifies a JWT token and returns the decoded payload if valid.
/// Returns an error if the signature is invalid or the token is malformed.
pub fn verify(token: String, secret: String) -> Result[String, String] {
match split_token(token) {
Err(e) => Err(e)
Ok(parts) => {
let header_encoded = parts[0]
let payload_encoded = parts[1]
let signature_encoded = parts[2]
let signing_input = header_encoded + "." + payload_encoded
let expected_signature = @hmac.hmac_sha256_string(signing_input, secret)
let actual_signature = base64url_decode(signature_encoded)
if !(constant_time_eq(fixed_to_array(expected_signature), actual_signature)) {
return Err("Invalid signature")
}
Ok(base64url_decode_to_string(payload_encoded))
}
}
}
/// Decodes a JWT token without verifying the signature.
/// Use this only when you need to inspect the payload without verification.
/// WARNING: Never trust the contents of an unverified token.
pub fn decode(token: String) -> Result[String, String] {
match split_token(token) {
Err(e) => Err(e)
Ok(parts) => {
let payload_encoded = parts[1]
Ok(base64url_decode_to_string(payload_encoded))
}
}
}
/// Decodes the header of a JWT token without verification.
pub fn decode_header(token: String) -> Result[String, String] {
match split_token(token) {
Err(e) => Err(e)
Ok(parts) => {
let header_encoded = parts[0]
Ok(base64url_decode_to_string(header_encoded))
}
}
}