Skip to main content

horfimbor_jwt/
lib.rs

1// #![deny(missing_docs)]
2// #![doc = include_str!("../README.md")]
3
4#[cfg(feature = "server")]
5pub mod builder;
6
7#[cfg(feature = "server")]
8use horfimbor_eventsource::model_key::ModelKey;
9
10#[cfg(feature = "server")]
11use jsonwebtoken::{DecodingKey, Validation, decode};
12
13use serde::{Deserialize, Serialize};
14use thiserror::Error;
15
16#[cfg(feature = "client")]
17use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
18#[cfg(feature = "client")]
19use jsonwebtoken::decode_header;
20#[cfg(not(feature = "server"))]
21use uuid::Uuid;
22
23// TODO we could do better :thinking:
24#[cfg(not(feature = "server"))]
25#[derive(Deserialize, Serialize, Debug, Clone, Eq, PartialEq, Default, Hash)]
26pub struct ModelKey {
27    stream_name: String,
28    stream_id: Uuid,
29}
30
31#[derive(Debug, Serialize, Deserialize)]
32pub struct Claims {
33    #[serde(rename = "aud")]
34    audience: String,
35    #[serde(rename = "exp")]
36    expiration_at: u64,
37    #[serde(rename = "iat")]
38    issued_at: u64,
39    #[serde(rename = "iss")]
40    issuer: String,
41    #[serde(rename = "usr")]
42    user: ModelKey,
43    #[serde(rename = "acc")]
44    account: ModelKey,
45    #[serde(rename = "an")]
46    account_name: String,
47    #[serde(rename = "r")]
48    roles: Role,
49}
50
51#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
52pub enum Role {
53    #[serde(rename = "a")]
54    Admin,
55    #[serde(rename = "u")]
56    User,
57    #[serde(rename = "an")]
58    Anonymous,
59}
60
61#[derive(Error, Debug)]
62pub enum ClaimError {
63    #[error("jsonwebtoken")]
64    JWT(#[from] jsonwebtoken::errors::Error),
65
66    #[error("cannot get data `{0}`")]
67    Other(String),
68
69    #[error("no account when building claims")]
70    EmptyAccount,
71}
72
73impl Claims {
74    #[cfg(feature = "server")]
75    /// parse the token, validate the secrets, audience and issuer
76    ///
77    /// # Errors
78    ///
79    /// Will return `ClaimError` if the decoding failed
80    pub fn from_jwt(
81        token: &str,
82        secret: &str,
83        audience: &str,
84        issuer: &str,
85    ) -> Result<Self, ClaimError> {
86        let mut val = Validation::default();
87        val.set_audience(&[&audience]);
88        val.set_issuer(&[&issuer]);
89        val.set_required_spec_claims(&["exp", "iss", "aud"]);
90
91        let value = decode::<Self>(token, &DecodingKey::from_secret(secret.as_ref()), &val)
92            .map_err(ClaimError::JWT)?;
93
94        Ok(value.claims)
95    }
96
97    #[cfg(feature = "client")]
98    /// parse the token, but do not validate it
99    ///
100    /// # Errors
101    ///
102    /// Will return `ClaimError` if the decoding failed
103    pub fn from_jwt_insecure(token: &str) -> Result<Self, ClaimError> {
104        match decode_header(token) {
105            Ok(_) => {
106                let mut parts = token.split('.');
107                parts.next();
108                let Some(content) = parts.next() else {
109                    return Err(ClaimError::EmptyAccount);
110                };
111                let data = URL_SAFE_NO_PAD
112                    .decode(content)
113                    .map_err(|e| ClaimError::Other(e.to_string()))?;
114
115                Ok(serde_json::from_slice(&data).map_err(|e| ClaimError::Other(e.to_string()))?)
116            }
117            Err(e) => Err(ClaimError::JWT(e)),
118        }
119
120        // let mut val = Validation::default();
121        // val.set_audience(&[&audience]);
122        // val.set_issuer(&[&issuer]);
123        // val.set_required_spec_claims(&["exp", "iss", "aud"]);
124        //
125        // let value = decode::<Self>(token, &DecodingKey::from_secret(secret.as_ref()), &val)
126        //     .map_err(ClaimError::JWT)?;
127        //
128        // Ok(value.claims)
129    }
130
131    #[must_use]
132    pub fn audience(&self) -> &str {
133        &self.audience
134    }
135
136    #[must_use]
137    pub const fn expiration_at(&self) -> u64 {
138        self.expiration_at
139    }
140
141    #[must_use]
142    pub const fn issued_at(&self) -> u64 {
143        self.issued_at
144    }
145
146    #[must_use]
147    pub fn issuer(&self) -> &str {
148        &self.issuer
149    }
150
151    #[must_use]
152    pub const fn user(&self) -> &ModelKey {
153        &self.user
154    }
155
156    #[must_use]
157    pub const fn roles(&self) -> &Role {
158        &self.roles
159    }
160
161    #[must_use]
162    pub const fn account(&self) -> &ModelKey {
163        &self.account
164    }
165    #[must_use]
166    pub fn account_name(&self) -> &str {
167        &self.account_name
168    }
169}