Skip to main content

danube_core/
jwt.rs

1//! Shared JWT types and utilities for Danube authentication.
2//!
3//! This module provides the canonical `Claims` structure and helper functions
4//! for creating and validating JWTs. Used by both `danube-broker` (validation)
5//! and `danube-admin` (offline token generation).
6
7use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
8use serde::{Deserialize, Serialize};
9
10/// JWT claims used by Danube for authentication.
11///
12/// The `sub` field is the principal name (e.g. "my-app"), and `principal_type`
13/// determines how the broker maps the identity (e.g. "service_account", "user").
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct Claims {
16    /// Token issuer (e.g. "danube-auth")
17    pub iss: String,
18    /// Expiration time (seconds since UNIX epoch)
19    pub exp: u64,
20    /// Subject — the principal name
21    pub sub: String,
22    /// Principal type: "service_account", "user", or "broker_internal"
23    pub principal_type: String,
24    /// Principal name (same as `sub` by convention)
25    pub principal_name: String,
26}
27
28/// Create a signed JWT from the given claims.
29pub fn create_token(
30    claims: &Claims,
31    jwt_secret: &str,
32) -> Result<String, jsonwebtoken::errors::Error> {
33    encode(
34        &Header::default(),
35        claims,
36        &EncodingKey::from_secret(jwt_secret.as_ref()),
37    )
38}
39
40/// Validate a JWT and return the decoded claims.
41pub fn validate_token(
42    token: &str,
43    jwt_secret: &str,
44) -> Result<Claims, jsonwebtoken::errors::Error> {
45    let validation = Validation::default();
46    let token_data = decode::<Claims>(
47        token,
48        &DecodingKey::from_secret(jwt_secret.as_ref()),
49        &validation,
50    )?;
51    Ok(token_data.claims)
52}
53
54/// Extract the bearer token from an "Authorization: Bearer <token>" header value.
55pub fn parse_bearer_token(header_value: &str) -> Option<&str> {
56    header_value
57        .strip_prefix("Bearer ")
58        .or_else(|| header_value.strip_prefix("bearer "))
59}