///|
/// Hono JWT Helper
/// https://hono.dev/docs/helpers/jwt
///|
/// JWT Algorithm types
pub(all) enum JwtAlgorithm {
HS256
HS384
HS512
RS256
RS384
RS512
PS256
PS384
PS512
ES256
ES384
ES512
EdDSA
}
///|
fn algorithm_to_string(alg : JwtAlgorithm) -> String {
match alg {
HS256 => "HS256"
HS384 => "HS384"
HS512 => "HS512"
RS256 => "RS256"
RS384 => "RS384"
RS512 => "RS512"
PS256 => "PS256"
PS384 => "PS384"
PS512 => "PS512"
ES256 => "ES256"
ES384 => "ES384"
ES512 => "ES512"
EdDSA => "EdDSA"
}
}
///|
/// JWT Payload type
pub(all) struct JwtPayload {
sub : String? // Subject
iss : String? // Issuer
aud : String? // Audience
exp : Int? // Expiration time (Unix timestamp)
nbf : Int? // Not before time
iat : Int? // Issued at time
jti : String? // JWT ID
}
///|
/// Default JWT payload
pub fn JwtPayload::default() -> JwtPayload {
{
sub: None,
iss: None,
aud: None,
exp: None,
nbf: None,
iat: None,
jti: None,
}
}
///|
fn payload_to_js(payload : JwtPayload) -> @core.Any {
let obj = @core.new_object()
if payload.sub is Some(v) {
obj["sub"] = @core.any(v)
}
if payload.iss is Some(v) {
obj["iss"] = @core.any(v)
}
if payload.aud is Some(v) {
obj["aud"] = @core.any(v)
}
if payload.exp is Some(v) {
obj["exp"] = @core.any(v)
}
if payload.nbf is Some(v) {
obj["nbf"] = @core.any(v)
}
if payload.iat is Some(v) {
obj["iat"] = @core.any(v)
}
if payload.jti is Some(v) {
obj["jti"] = @core.any(v)
}
obj
}
///|
/// Decoded JWT result
pub struct JwtDecoded {
header : @core.Any
payload : @core.Any
}
///| FFI with #module for ESM
///|
#module("hono/jwt")
extern "js" fn ffi_jwt_sign(
payload : @core.Any,
secret : String,
) -> @js.Promise[String] = "sign"
///|
#module("hono/jwt")
extern "js" fn ffi_jwt_sign_with_alg(
payload : @core.Any,
secret : String,
alg : String,
) -> @js.Promise[String] = "sign"
///|
#module("hono/jwt")
extern "js" fn ffi_jwt_verify(
token : String,
secret : String,
) -> @js.Promise[@core.Any] = "verify"
///|
#module("hono/jwt")
extern "js" fn ffi_jwt_verify_with_alg(
token : String,
secret : String,
alg : String,
) -> @js.Promise[@core.Any] = "verify"
///|
#module("hono/jwt")
extern "js" fn ffi_jwt_decode(token : String) -> @core.Any = "decode"
///| Public API
///|
/// Sign a JWT token
/// https://hono.dev/docs/helpers/jwt#sign
pub async fn jwt_sign(
payload : @core.Any,
secret : String,
alg? : JwtAlgorithm,
) -> String {
match alg {
Some(a) =>
ffi_jwt_sign_with_alg(payload, secret, algorithm_to_string(a)).wait()
None => ffi_jwt_sign(payload, secret).wait()
}
}
///|
/// Sign a JWT token with structured payload
pub async fn jwt_sign_payload(
payload : JwtPayload,
secret : String,
alg? : JwtAlgorithm,
) -> String {
jwt_sign(payload_to_js(payload), secret, alg?)
}
///|
/// Verify a JWT token
/// Returns the payload if valid, throws on invalid token
/// https://hono.dev/docs/helpers/jwt#verify
pub async fn jwt_verify(
token : String,
secret : String,
alg? : JwtAlgorithm,
) -> @core.Any {
match alg {
Some(a) =>
ffi_jwt_verify_with_alg(token, secret, algorithm_to_string(a)).wait()
None => ffi_jwt_verify(token, secret).wait()
}
}
///|
/// Decode a JWT token without verification
/// https://hono.dev/docs/helpers/jwt#decode
pub fn jwt_decode(token : String) -> JwtDecoded {
let result = ffi_jwt_decode(token)
{ header: result["header"], payload: result["payload"], }
}