token.rs

 1use anyhow::{anyhow, Result};
 2use hmac::{Hmac, Mac};
 3use jwt::{SignWithKey, VerifyWithKey};
 4use serde::{Deserialize, Serialize};
 5use sha2::Sha256;
 6use std::{
 7    borrow::Cow,
 8    ops::Add,
 9    time::{Duration, SystemTime, UNIX_EPOCH},
10};
11
12static DEFAULT_TTL: Duration = Duration::from_secs(6 * 60 * 60); // 6 hours
13
14#[derive(Default, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct ClaimGrants<'a> {
17    pub iss: Cow<'a, str>,
18    pub sub: Option<Cow<'a, str>>,
19    pub iat: u64,
20    pub exp: u64,
21    pub nbf: u64,
22    pub jwtid: Option<Cow<'a, str>>,
23    pub video: VideoGrant<'a>,
24}
25
26#[derive(Default, Serialize, Deserialize)]
27#[serde(rename_all = "camelCase")]
28pub struct VideoGrant<'a> {
29    pub room_create: Option<bool>,
30    pub room_join: Option<bool>,
31    pub room_list: Option<bool>,
32    pub room_record: Option<bool>,
33    pub room_admin: Option<bool>,
34    pub room: Option<Cow<'a, str>>,
35    pub can_publish: Option<bool>,
36    pub can_subscribe: Option<bool>,
37    pub can_publish_data: Option<bool>,
38    pub hidden: Option<bool>,
39    pub recorder: Option<bool>,
40}
41
42impl<'a> VideoGrant<'a> {
43    pub fn to_admin(room: &'a str) -> Self {
44        Self {
45            room_admin: Some(true),
46            room: Some(Cow::Borrowed(room)),
47            ..Default::default()
48        }
49    }
50
51    pub fn to_join(room: &'a str) -> Self {
52        Self {
53            room: Some(Cow::Borrowed(room)),
54            room_join: Some(true),
55            can_publish: Some(true),
56            can_subscribe: Some(true),
57            ..Default::default()
58        }
59    }
60}
61
62pub fn create(
63    api_key: &str,
64    secret_key: &str,
65    identity: Option<&str>,
66    video_grant: VideoGrant,
67) -> Result<String> {
68    if video_grant.room_join.is_some() && identity.is_none() {
69        Err(anyhow!(
70            "identity is required for room_join grant, but it is none"
71        ))?;
72    }
73
74    let secret_key: Hmac<Sha256> = Hmac::new_from_slice(secret_key.as_bytes())?;
75
76    let now = SystemTime::now();
77
78    let claims = ClaimGrants {
79        iss: Cow::Borrowed(api_key),
80        sub: identity.map(Cow::Borrowed),
81        iat: now.duration_since(UNIX_EPOCH).unwrap().as_secs(),
82        exp: now
83            .add(DEFAULT_TTL)
84            .duration_since(UNIX_EPOCH)
85            .unwrap()
86            .as_secs(),
87        nbf: 0,
88        jwtid: identity.map(Cow::Borrowed),
89        video: video_grant,
90    };
91    Ok(claims.sign_with_key(&secret_key)?)
92}
93
94pub fn validate<'a>(token: &'a str, secret_key: &str) -> Result<ClaimGrants<'a>> {
95    let secret_key: Hmac<Sha256> = Hmac::new_from_slice(secret_key.as_bytes())?;
96    Ok(token.verify_with_key(&secret_key)?)
97}