surrealdb_core/iam/
token.rs

1use 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		// Set default value
79		let mut out = Object::default();
80		// Add iss field if set
81		if let Some(iss) = v.iss {
82			out.insert("iss".to_string(), iss.into());
83		}
84		// Add sub field if set
85		if let Some(sub) = v.sub {
86			out.insert("sub".to_string(), sub.into());
87		}
88		// Add aud field if set
89		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		// Add iat field if set
96		if let Some(iat) = v.iat {
97			out.insert("iat".to_string(), iat.into());
98		}
99		// Add nbf field if set
100		if let Some(nbf) = v.nbf {
101			out.insert("nbf".to_string(), nbf.into());
102		}
103		// Add exp field if set
104		if let Some(exp) = v.exp {
105			out.insert("exp".to_string(), exp.into());
106		}
107		// Add jti field if set
108		if let Some(jti) = v.jti {
109			out.insert("jti".to_string(), jti.into());
110		}
111		// Add NS field if set
112		if let Some(ns) = v.ns {
113			out.insert("NS".to_string(), ns.into());
114		}
115		// Add DB field if set
116		if let Some(db) = v.db {
117			out.insert("DB".to_string(), db.into());
118		}
119		// Add AC field if set
120		if let Some(ac) = v.ac {
121			out.insert("AC".to_string(), ac.into());
122		}
123		// Add ID field if set
124		if let Some(id) = v.id {
125			out.insert("ID".to_string(), id.into());
126		}
127		// Add RL field if set
128		if let Some(role) = v.roles {
129			out.insert("RL".to_string(), role.into());
130		}
131		// Add custom claims if set
132		if let Some(custom_claims) = v.custom_claims {
133			for (claim, value) in custom_claims {
134				// Serialize the raw JSON string representing the claim value
135				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				// Parse that JSON string into the corresponding SurrealQL value
143				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		// Return value
154		out.into()
155	}
156}
157
158impl From<&Claims> for Value {
159	fn from(v: &Claims) -> Value {
160		// Set default value
161		let mut out = Object::default();
162		// Add iss field if set
163		if let Some(iss) = &v.iss {
164			out.insert("iss".to_string(), iss.clone().into());
165		}
166		// Add sub field if set
167		if let Some(sub) = &v.sub {
168			out.insert("sub".to_string(), sub.clone().into());
169		}
170		// Add aud field if set
171		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		// Add iat field if set
178		if let Some(iat) = v.iat {
179			out.insert("iat".to_string(), iat.into());
180		}
181		// Add nbf field if set
182		if let Some(nbf) = v.nbf {
183			out.insert("nbf".to_string(), nbf.into());
184		}
185		// Add exp field if set
186		if let Some(exp) = v.exp {
187			out.insert("exp".to_string(), exp.into());
188		}
189		// Add jti field if set
190		if let Some(jti) = &v.jti {
191			out.insert("jti".to_string(), jti.clone().into());
192		}
193		// Add NS field if set
194		if let Some(ns) = &v.ns {
195			out.insert("NS".to_string(), ns.clone().into());
196		}
197		// Add DB field if set
198		if let Some(db) = &v.db {
199			out.insert("DB".to_string(), db.clone().into());
200		}
201		// Add AC field if set
202		if let Some(ac) = &v.ac {
203			out.insert("AC".to_string(), ac.clone().into());
204		}
205		// Add ID field if set
206		if let Some(id) = &v.id {
207			out.insert("ID".to_string(), id.clone().into());
208		}
209		// Add RL field if set
210		if let Some(role) = &v.roles {
211			out.insert("RL".to_string(), role.clone().into());
212		}
213		// Add custom claims if set
214		if let Some(custom_claims) = &v.custom_claims {
215			for (claim, value) in custom_claims {
216				// Serialize the raw JSON string representing the claim value
217				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				// Parse that JSON string into the corresponding SurrealQL value
225				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		// Return value
236		out.into()
237	}
238}