821537186e
- Reorganized import statements in adapter tests for better readability - Replaced or_insert_with(Vec::new) with or_default() in test closures - Updated Cargo.lock with new dependency versions and checksums - Added TLS features to tonic dependency configuration - Included sqlx, chrono, and uuid dependencies with specific features - Added jsonwebtoken and arc-swap as project dependencies - Reformatted assertion statements to comply with line length limits - Adjusted base64 import order in engine codec module - Updated protobuf include statement formatting
88 lines
2.5 KiB
Rust
88 lines
2.5 KiB
Rust
//! JWT claims structure — mirrors proto `TokenClaims` for local verification.
|
|
//!
|
|
//! Used as the deserialization target for `jsonwebtoken::decode`.
|
|
//! Field names match standard JWT claim names (`sub`, `iss`, `exp`, etc.).
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
/// Parsed JWT payload, matching the proto `TokenClaims` shape.
|
|
///
|
|
/// Deserialized by `jsonwebtoken` during HS256 verification.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TokenClaims {
|
|
/// Subject — the user UUID.
|
|
pub sub: String,
|
|
/// Issuer — expected to be `"appks"`.
|
|
pub iss: String,
|
|
/// Issued-at (unix seconds).
|
|
pub iat: i64,
|
|
/// Expiration (unix seconds).
|
|
pub exp: i64,
|
|
/// Unique token ID (used for revocation tracking via `jti`).
|
|
pub jti: String,
|
|
/// Space-separated scopes, e.g. `"im:read im:write"`.
|
|
pub scope: String,
|
|
/// Extensible metadata (workspace_id, role, etc.).
|
|
#[serde(default)]
|
|
pub extra: HashMap<String, String>,
|
|
}
|
|
|
|
impl TokenClaims {
|
|
/// Check whether this token carries a specific scope.
|
|
pub fn has_scope(&self, scope: &str) -> bool {
|
|
self.scope.split_whitespace().any(|s| s == scope)
|
|
}
|
|
|
|
/// Convert from the proto-generated `TokenClaims` (RPC verify response).
|
|
pub fn from_proto(proto: crate::pb::core::TokenClaims) -> Self {
|
|
Self {
|
|
sub: proto.sub,
|
|
iss: proto.iss,
|
|
iat: proto.iat,
|
|
exp: proto.exp,
|
|
jti: proto.jti,
|
|
scope: proto.scope,
|
|
extra: proto.extra,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_has_scope() {
|
|
let claims = TokenClaims {
|
|
sub: "user-1".into(),
|
|
iss: "appks".into(),
|
|
iat: 0,
|
|
exp: 9999999999,
|
|
jti: "tok-1".into(),
|
|
scope: "im:read im:write admin".into(),
|
|
extra: HashMap::new(),
|
|
};
|
|
assert!(claims.has_scope("im:read"));
|
|
assert!(claims.has_scope("admin"));
|
|
assert!(!claims.has_scope("im:delete"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_deserialize_from_json() {
|
|
let json = r#"{
|
|
"sub": "user-1",
|
|
"iss": "appks",
|
|
"iat": 1000,
|
|
"exp": 2000,
|
|
"jti": "tok-1",
|
|
"scope": "im:read",
|
|
"extra": {"workspace_id": "ws-1"}
|
|
}"#;
|
|
let claims: TokenClaims = serde_json::from_str(json).unwrap();
|
|
assert_eq!(claims.sub, "user-1");
|
|
assert_eq!(claims.extra.get("workspace_id").unwrap(), "ws-1");
|
|
}
|
|
}
|