surrealdb_core/iam/
token.rs1use crate::sql::json;
2use crate::sql::Object;
3use crate::sql::Value;
4use jsonwebtoken::{Algorithm, Header};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::sync::LazyLock;
8
9pub static HEADER: LazyLock<Header> = LazyLock::new(|| Header::new(Algorithm::HS512));
10
11#[derive(Debug, Serialize, Deserialize, Clone)]
12#[serde(untagged)]
13pub enum Audience {
14 Single(String),
15 Multiple(Vec<String>),
16}
17
18#[derive(Debug, Default, Serialize, Deserialize, Clone)]
19#[non_exhaustive]
20pub struct Claims {
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub iat: Option<i64>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub nbf: Option<i64>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub exp: Option<i64>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub iss: Option<String>,
29 #[serde(skip_serializing_if = "Option::is_none")]
30 pub sub: Option<String>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 pub aud: Option<Audience>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub jti: Option<String>,
35 #[serde(alias = "ns")]
36 #[serde(alias = "NS")]
37 #[serde(rename = "NS")]
38 #[serde(alias = "https://surrealdb.com/ns")]
39 #[serde(alias = "https://surrealdb.com/namespace")]
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub ns: Option<String>,
42 #[serde(alias = "db")]
43 #[serde(alias = "DB")]
44 #[serde(rename = "DB")]
45 #[serde(alias = "https://surrealdb.com/db")]
46 #[serde(alias = "https://surrealdb.com/database")]
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub db: Option<String>,
49 #[serde(alias = "ac")]
50 #[serde(alias = "AC")]
51 #[serde(rename = "AC")]
52 #[serde(alias = "https://surrealdb.com/ac")]
53 #[serde(alias = "https://surrealdb.com/access")]
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub ac: Option<String>,
56 #[serde(alias = "id")]
57 #[serde(alias = "ID")]
58 #[serde(rename = "ID")]
59 #[serde(alias = "https://surrealdb.com/id")]
60 #[serde(alias = "https://surrealdb.com/record")]
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub id: Option<String>,
63 #[serde(alias = "rl")]
64 #[serde(alias = "RL")]
65 #[serde(rename = "RL")]
66 #[serde(alias = "https://surrealdb.com/rl")]
67 #[serde(alias = "https://surrealdb.com/roles")]
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub roles: Option<Vec<String>>,
70
71 #[serde(flatten)]
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub custom_claims: Option<HashMap<String, serde_json::Value>>,
74}
75
76impl From<Claims> for Value {
77 fn from(v: Claims) -> Value {
78 let mut out = Object::default();
80 if let Some(iss) = v.iss {
82 out.insert("iss".to_string(), iss.into());
83 }
84 if let Some(sub) = v.sub {
86 out.insert("sub".to_string(), sub.into());
87 }
88 if let Some(aud) = v.aud {
90 match aud {
91 Audience::Single(v) => out.insert("aud".to_string(), v.into()),
92 Audience::Multiple(v) => out.insert("aud".to_string(), v.into()),
93 };
94 }
95 if let Some(iat) = v.iat {
97 out.insert("iat".to_string(), iat.into());
98 }
99 if let Some(nbf) = v.nbf {
101 out.insert("nbf".to_string(), nbf.into());
102 }
103 if let Some(exp) = v.exp {
105 out.insert("exp".to_string(), exp.into());
106 }
107 if let Some(jti) = v.jti {
109 out.insert("jti".to_string(), jti.into());
110 }
111 if let Some(ns) = v.ns {
113 out.insert("NS".to_string(), ns.into());
114 }
115 if let Some(db) = v.db {
117 out.insert("DB".to_string(), db.into());
118 }
119 if let Some(ac) = v.ac {
121 out.insert("AC".to_string(), ac.into());
122 }
123 if let Some(id) = v.id {
125 out.insert("ID".to_string(), id.into());
126 }
127 if let Some(role) = v.roles {
129 out.insert("RL".to_string(), role.into());
130 }
131 if let Some(custom_claims) = v.custom_claims {
133 for (claim, value) in custom_claims {
134 let claim_json = match serde_json::to_string(&value) {
136 Ok(claim_json) => claim_json,
137 Err(err) => {
138 debug!("Failed to serialize token claim '{}': {}", claim, err);
139 continue;
140 }
141 };
142 let claim_value = match json(&claim_json) {
144 Ok(claim_value) => claim_value,
145 Err(err) => {
146 debug!("Failed to parse token claim '{}': {}", claim, err);
147 continue;
148 }
149 };
150 out.insert(claim.to_owned(), claim_value);
151 }
152 }
153 out.into()
155 }
156}
157
158impl From<&Claims> for Value {
159 fn from(v: &Claims) -> Value {
160 let mut out = Object::default();
162 if let Some(iss) = &v.iss {
164 out.insert("iss".to_string(), iss.clone().into());
165 }
166 if let Some(sub) = &v.sub {
168 out.insert("sub".to_string(), sub.clone().into());
169 }
170 if let Some(aud) = &v.aud {
172 match aud {
173 Audience::Single(v) => out.insert("aud".to_string(), v.clone().into()),
174 Audience::Multiple(v) => out.insert("aud".to_string(), v.clone().into()),
175 };
176 }
177 if let Some(iat) = v.iat {
179 out.insert("iat".to_string(), iat.into());
180 }
181 if let Some(nbf) = v.nbf {
183 out.insert("nbf".to_string(), nbf.into());
184 }
185 if let Some(exp) = v.exp {
187 out.insert("exp".to_string(), exp.into());
188 }
189 if let Some(jti) = &v.jti {
191 out.insert("jti".to_string(), jti.clone().into());
192 }
193 if let Some(ns) = &v.ns {
195 out.insert("NS".to_string(), ns.clone().into());
196 }
197 if let Some(db) = &v.db {
199 out.insert("DB".to_string(), db.clone().into());
200 }
201 if let Some(ac) = &v.ac {
203 out.insert("AC".to_string(), ac.clone().into());
204 }
205 if let Some(id) = &v.id {
207 out.insert("ID".to_string(), id.clone().into());
208 }
209 if let Some(role) = &v.roles {
211 out.insert("RL".to_string(), role.clone().into());
212 }
213 if let Some(custom_claims) = &v.custom_claims {
215 for (claim, value) in custom_claims {
216 let claim_json = match serde_json::to_string(&value) {
218 Ok(claim_json) => claim_json,
219 Err(err) => {
220 debug!("Failed to serialize token claim '{}': {}", claim, err);
221 continue;
222 }
223 };
224 let claim_value = match json(&claim_json) {
226 Ok(claim_value) => claim_value,
227 Err(err) => {
228 debug!("Failed to parse token claim '{}': {}", claim, err);
229 continue;
230 }
231 };
232 out.insert(claim.to_owned(), claim_value);
233 }
234 }
235 out.into()
237 }
238}