1use std::collections::HashSet;
2use std::sync::Arc;
3
4use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
5use serde::{Deserialize, Serialize};
6
7use crate::error::Error;
8
9#[derive(Debug, Serialize, Deserialize)]
10pub struct ResourceAccess {
11 pub account: RealmAccess,
12}
13
14#[derive(Debug, Serialize, Deserialize)]
15pub struct RealmAccess {
16 pub roles: Vec<Arc<str>>,
17}
18
19#[derive(Serialize, Deserialize, Default)]
20pub struct PartialClaims {
21 pub iss: String,
22}
23
24#[derive(Debug, Serialize, Deserialize)]
25pub struct Claims {
26 pub exp: i64,
27 pub iat: i64,
28 pub auth_time: Option<i64>,
29 pub jti: String,
30 pub iss: String,
31 pub aud: serde_json::Value,
32 pub sub: Arc<str>,
33 pub typ: String,
34 pub azp: String,
35 pub session_state: String,
36 pub acr: String,
37 #[serde(rename = "allowed-origins")]
38 pub allowed_origins: Vec<Arc<str>>,
39 pub realm_access: RealmAccess,
40 pub resource_access: ResourceAccess,
41 #[serde(default)]
42 pub scope: String,
43 #[serde(default)]
44 pub sid: String,
45 pub email_verified: bool,
46 #[serde(default)]
47 pub name: String,
48 #[serde(default)]
49 pub preferred_username: String,
50 pub given_name: String,
51 #[serde(default)]
52 pub family_name: String,
53 #[serde(default)]
54 pub email: String,
55 #[serde(skip)]
56 pub is_api_test: bool,
57}
58
59impl Default for Claims {
60 fn default() -> Self {
61 Self {
62 exp: 0,
63 iat: 0,
64 auth_time: None,
65 jti: "".to_string(),
66 iss: "".to_string(),
67 is_api_test: true,
68 sub: Arc::from("user-id"),
69 typ: "".to_string(),
70 azp: "".to_string(),
71 session_state: "".to_string(),
72 acr: "".to_string(),
73 allowed_origins: vec![],
74 realm_access: RealmAccess { roles: vec![] },
75 resource_access: ResourceAccess {
76 account: RealmAccess { roles: vec![] },
77 },
78 scope: "".to_string(),
79 sid: "".to_string(),
80 email_verified: false,
81 name: "".to_string(),
82 preferred_username: "".to_string(),
83 given_name: "".to_string(),
84 family_name: "".to_string(),
85 aud: Default::default(),
86 email: "".to_string(),
87 }
88 }
89}
90
91#[derive(Debug, Serialize, Deserialize)]
92pub struct LogoutClaims {
93 pub iat: i64,
94 pub jti: String,
95 pub iss: String,
96 pub aud: serde_json::Value,
97 pub sub: String,
98 pub typ: String,
99 pub sid: String,
100}
101
102#[derive(Clone)]
103pub struct JwtDecoder {
104 pub kid: String,
105 validation: Validation,
106 logout_validation: Validation,
107 decoding_key: DecodingKey,
108}
109
110impl JwtDecoder {
111 pub fn new(alg: Algorithm, kid: String, public_key: &str) -> Result<Self, Error> {
112 let mut validation = Validation::new(alg);
113 validation.set_audience(&["account"]);
114 let mut logout_validation = Validation::new(alg);
115 logout_validation.validate_exp = false;
116 logout_validation.required_spec_claims = HashSet::new();
117 logout_validation
118 .required_spec_claims
119 .insert("sub".to_string());
120 logout_validation
121 .required_spec_claims
122 .insert("iss".to_string());
123 logout_validation
124 .required_spec_claims
125 .insert("aud".to_string());
126 Ok(Self {
127 kid,
128 validation,
129 logout_validation,
130 decoding_key: DecodingKey::from_rsa_pem(
131 format!("-----BEGIN PUBLIC KEY-----\n{public_key}\n-----END PUBLIC KEY-----")
132 .as_bytes(),
133 )?,
134 })
135 }
136 pub fn decode(&self, token: &str) -> Result<Claims, Error> {
137 let result =
138 decode::<Claims>(token, &self.decoding_key, &self.validation).map_err(|e| {
139 log::error!("{e:#?}");
140 e
141 })?;
142 Ok(result.claims)
143 }
144 pub fn decode_logout_token(&self, token: &str) -> Result<LogoutClaims, Error> {
145 let result = decode::<LogoutClaims>(token, &self.decoding_key, &self.logout_validation)
146 .map_err(|e| {
147 log::error!("{e:#?}");
148 e
149 })?;
150 Ok(result.claims)
151 }
152}