Skip to main content

cratestack_core/context/
identity.rs

1//! Caller identity captured by the auth provider.
2//!
3//! [`CoolAuthIdentity`] is the legacy flat-claims shape kept for the
4//! generated route handlers; modern callers reach for
5//! [`super::principal::PrincipalContext`] which surfaces the same
6//! claims plus structured actor / session / tenant facets.
7
8use std::collections::BTreeMap;
9
10use serde::{Deserialize, Serialize};
11
12use crate::error::CoolError;
13use crate::value::Value;
14
15#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
16pub struct CoolAuthIdentity {
17    pub fields: BTreeMap<String, Value>,
18}
19
20impl CoolAuthIdentity {
21    pub fn from_principal<P: Serialize>(principal: P) -> Result<Self, CoolError> {
22        let value = serde_json::to_value(principal).map_err(|error| {
23            CoolError::Internal(format!("failed to serialize auth principal: {error}"))
24        })?;
25        let serde_json::Value::Object(object) = value else {
26            return Err(CoolError::Internal(
27                "auth principal must serialize to a JSON object".to_owned(),
28            ));
29        };
30
31        let mut fields = BTreeMap::new();
32        for (key, value) in object {
33            fields.insert(key, json_value_to_cool_value(value)?);
34        }
35
36        Ok(Self { fields })
37    }
38}
39
40/// Convert a `serde_json::Value` claim into a framework-native
41/// [`Value`]. Shared by the principal/identity builders.
42pub(super) fn json_value_to_cool_value(value: serde_json::Value) -> Result<Value, CoolError> {
43    match value {
44        serde_json::Value::Null => Ok(Value::Null),
45        serde_json::Value::Bool(value) => Ok(Value::Bool(value)),
46        serde_json::Value::Number(number) => {
47            if let Some(value) = number.as_i64() {
48                Ok(Value::Int(value))
49            } else if let Some(value) = number.as_f64() {
50                Ok(Value::Float(value))
51            } else {
52                Err(CoolError::Internal(format!(
53                    "unsupported auth principal number '{number}'"
54                )))
55            }
56        }
57        serde_json::Value::String(value) => Ok(Value::String(value)),
58        serde_json::Value::Array(values) => values
59            .into_iter()
60            .map(json_value_to_cool_value)
61            .collect::<Result<Vec<_>, _>>()
62            .map(Value::List),
63        serde_json::Value::Object(object) => object
64            .into_iter()
65            .map(|(key, value)| json_value_to_cool_value(value).map(|value| (key, value)))
66            .collect::<Result<BTreeMap<_, _>, _>>()
67            .map(Value::Map),
68    }
69}