Functional Weave
Code in Python

auth.access-token@1.0.0

impl/rust/issue_access_token.rs

3,142 bytes · the Rust implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::auth_jwt_sign_jwt::sign_jwt;
use super::time_unix_to_iso::unix_to_iso;  ← from time.unix-to-iso ^1.0.0 · built alongside by fune

/// A signed access token for a user who has just logged in, with the claims
/// this API relies on: sub, name, email, ver (token version), jti, iat and
/// exp. Everything that varies (the time, the random jti) is passed in.
///
/// # Panics
/// Panics on an empty subject or jti, a negative token version, a ttl below
/// 1, or a secret under 32 bytes.
#[allow(clippy::too_many_arguments)]
pub fn issue_access_token(
    subject: &str,
    name: &str,
    email: &str,
    token_version: i64,
    jti: &str,
    now: i64,
    ttl_seconds: i64,
    secret: &[i64],
) -> AccessToken {
    if subject.is_empty() {
        panic!("subject must be a non-empty string");
    }
    if token_version < 0 {
        panic!("tokenVersion must be a whole number, 0 or more");
    }
    if jti.is_empty() {
        panic!("jti must be a non-empty string");
    }
    if ttl_seconds < 1 {
        panic!("ttlSeconds must be a whole number of at least 1");
    }
    let exp = now + ttl_seconds;
    let expires_at = unix_to_iso(exp);
    let claims = Value::obj(vec![
        ("sub", Value::str(subject)),
        ("name", Value::str(name)),
        ("email", Value::str(email)),
        ("ver", Value::Int(token_version)),
        ("jti", Value::str(jti)),
        ("iat", Value::Int(now)),
        ("exp", Value::Int(exp)),
    ]);
    AccessToken {
        access_token: sign_jwt(&claims, secret),
        token_type: "Bearer".to_string(),
        expires_at,
        expires_in: ttl_seconds,
    }
}

pub fn access_token_to_value(token: &AccessToken) -> Value {
    Value::obj(vec![
        ("accessToken", Value::str(&token.access_token)),
        ("tokenType", Value::str(&token.token_type)),
        ("expiresAt", Value::str(&token.expires_at)),
        ("expiresIn", Value::Int(token.expires_in)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let text = |i: usize, message: &str| -> String {
        match &args[i] {
            Value::Str(s) => s.clone(),
            _ => panic!("{}", message),
        }
    };
    let whole = |i: usize, message: &str| -> i64 {
        match &args[i] {
            Value::Int(n) => *n,
            _ => panic!("{}", message),
        }
    };
    let secret: Vec<i64> = match &args[7] {
        Value::Arr(items) => items
            .iter()
            .map(|item| match item {
                Value::Int(i) => *i,
                _ => panic!("secret must be a list of integers from 0 to 255"),
            })
            .collect(),
        _ => panic!("secret must be a list of integers from 0 to 255"),
    };
    access_token_to_value(&issue_access_token(
        &text(0, "subject must be a non-empty string"),
        &text(1, "name must be a string"),
        &text(2, "email must be a string"),
        whole(3, "tokenVersion must be a whole number, 0 or more"),
        &text(4, "jti must be a non-empty string"),
        whole(5, "now must be a whole number of Unix seconds"),
        whole(6, "ttlSeconds must be a whole number of at least 1"),
        &secret,
    ))
}