1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[derive(Debug, Default)]
9pub struct AuthorizationParameters {
10 pub acr_values: Option<Vec<String>>,
12 pub audience: Option<Vec<String>>,
14 pub claims: Option<ClaimParam>,
16 pub claims_locales: Option<Vec<String>>,
18 pub client_id: Option<String>,
20 pub code_challenge_method: Option<String>,
22 pub code_challenge: Option<String>,
24 pub display: Option<String>,
26 pub id_token_hint: Option<String>,
29 pub login_hint: Option<String>,
32 pub max_age: Option<String>,
34 pub nonce: Option<String>,
36 pub prompt: Option<Vec<String>>,
38 pub redirect_uri: Option<String>,
41 pub registration: Option<String>,
44 pub request_uri: Option<String>,
46 pub request: Option<String>,
48 pub resource: Option<Vec<String>>,
50 pub response_mode: Option<String>,
52 pub response_type: Option<Vec<String>>,
54 pub scope: Option<Vec<String>>,
56 pub state: Option<String>,
58 pub ui_locales: Option<Vec<String>>,
60 pub other: Option<HashMap<String, String>>,
62}
63
64impl From<AuthorizationParameters> for HashMap<String, String> {
65 fn from(val: AuthorizationParameters) -> Self {
66 let mut query = HashMap::new();
67
68 if let Some(other) = val.other {
69 for (k, v) in other {
70 query.entry(k).or_insert(v);
71 }
72 }
73
74 insert_query(&mut query, "client_id", val.client_id);
75
76 if let Some(acr_arr) = val.acr_values {
77 let mut acr_str = String::new();
78 for acr in acr_arr {
79 acr_str += &format!("{acr} ");
80 }
81
82 insert_query(
83 &mut query,
84 "acr_values",
85 Some(acr_str.trim_end().to_owned()),
86 );
87 }
88
89 if let Some(aud_arr) = val.audience {
90 let mut aud_str = String::new();
91 for aud in aud_arr {
92 aud_str += &format!("{aud} ");
93 }
94
95 insert_query(&mut query, "audience", Some(aud_str.trim_end().to_owned()));
96 }
97
98 if let Some(locale_arr) = val.claims_locales {
99 let mut locale_str = String::new();
100 for locale in locale_arr {
101 locale_str += &format!("{locale} ");
102 }
103
104 insert_query(
105 &mut query,
106 "claims_locales",
107 Some(locale_str.trim_end().to_owned()),
108 );
109 }
110 insert_query(
111 &mut query,
112 "code_challenge_method",
113 val.code_challenge_method,
114 );
115 insert_query(&mut query, "code_challenge", val.code_challenge);
116 insert_query(&mut query, "display", val.display);
117 insert_query(&mut query, "id_token_hint", val.id_token_hint);
118 insert_query(&mut query, "login_hint", val.login_hint);
119 insert_query(&mut query, "max_age", val.max_age);
120 insert_query(&mut query, "nonce", val.nonce);
121
122 if let Some(prompt_arr) = val.prompt {
123 let mut prompt_str = String::new();
124 for prompt in prompt_arr {
125 prompt_str += &format!("{prompt} ");
126 }
127
128 insert_query(&mut query, "prompt", Some(prompt_str.trim_end().to_owned()));
129 }
130
131 insert_query(&mut query, "redirect_uri", val.redirect_uri);
132 insert_query(&mut query, "registration", val.registration);
133 insert_query(&mut query, "request_uri", val.request_uri);
134 insert_query(&mut query, "request", val.request);
135 insert_query(&mut query, "response_mode", val.response_mode);
136
137 if let Some(res_arr) = val.response_type {
138 let mut res_str = String::new();
139 for res in res_arr {
140 res_str += &format!("{res} ");
141 }
142
143 insert_query(
144 &mut query,
145 "response_type",
146 Some(res_str.trim_end().to_owned()),
147 );
148 }
149
150 if let Some(scope_arr) = val.scope {
151 let mut scope_str = String::new();
152 for scope in scope_arr {
153 scope_str += &format!("{scope} ");
154 }
155
156 insert_query(&mut query, "scope", Some(scope_str.trim_end().to_owned()));
157 }
158
159 insert_query(&mut query, "state", val.state);
160
161 if let Some(ui_locales_arr) = val.ui_locales {
162 let mut ui_locales_str = String::new();
163 for ui_locale in ui_locales_arr {
164 ui_locales_str += &format!("{ui_locale} ");
165 }
166
167 insert_query(
168 &mut query,
169 "ui_locales",
170 Some(ui_locales_str.trim_end().to_owned()),
171 );
172 }
173
174 if let Some(c) = &val.claims {
175 if let Ok(s) = serde_json::to_string(c) {
176 query.insert("claims".to_owned(), s);
177 }
178 }
179
180 if let Some(resource) = &val.resource {
181 let mut resource_str = String::new();
182 for r in resource {
183 resource_str += &format!("{r} ");
184 }
185
186 insert_query(
187 &mut query,
188 "resource",
189 Some(resource_str.trim_end().to_owned()),
190 );
191 }
192
193 query
194 }
195}
196
197fn insert_query(qp: &mut HashMap<String, String>, key: &str, value: Option<String>) {
198 if let Some(v) = value {
199 qp.insert(key.to_owned(), v);
200 }
201}
202
203#[derive(Serialize, Deserialize, Debug)]
206#[serde(untagged)]
207pub enum ClaimParamValue {
208 Null,
210 ClaimParamMember(ClaimsParameterMember),
212}
213
214#[derive(Serialize, Deserialize, Debug, Default)]
217pub struct ClaimParam {
218 #[serde(skip_serializing_if = "Option::is_none")]
219 pub id_token: Option<HashMap<String, ClaimParamValue>>,
221 #[serde(skip_serializing_if = "Option::is_none", default)]
222 pub userinfo: Option<HashMap<String, ClaimParamValue>>,
224}
225
226#[derive(Serialize, Deserialize, Debug, Default)]
229pub struct ClaimsParameterMember {
230 #[serde(skip_serializing_if = "Option::is_none")]
232 pub essential: Option<bool>,
233 #[serde(skip_serializing_if = "Option::is_none")]
234 pub value: Option<String>,
236 #[serde(skip_serializing_if = "Option::is_none")]
237 pub values: Option<Vec<String>>,
239 #[serde(flatten)]
240 pub other: Option<HashMap<String, Value>>,
242}
243
244#[cfg(test)]
245mod claim_param_tests {
246 use std::collections::HashMap;
247
248 use assert_json_diff::assert_json_eq;
249 use serde_json::{json, Value};
250
251 use super::{ClaimParam, ClaimParamValue, ClaimsParameterMember};
252
253 #[test]
254 fn serialize_test_1() {
255 let mut userinfo: HashMap<String, ClaimParamValue> = HashMap::new();
256 userinfo.insert("null".to_string(), ClaimParamValue::Null);
257
258 let cpm = ClaimsParameterMember {
259 essential: Some(false),
260 value: None,
261 values: None,
262 other: None,
263 };
264
265 let cv = ClaimParamValue::ClaimParamMember(cpm);
266
267 userinfo.insert("cpm".to_string(), cv);
268
269 let claim_param = ClaimParam {
270 id_token: None,
271 userinfo: Some(userinfo),
272 };
273
274 let serialized_result = serde_json::to_string(&claim_param);
275
276 assert!(serialized_result.is_ok());
277
278 let string = serialized_result.unwrap();
279
280 assert_json_eq!(
281 json!({"userinfo": {"null": null,"cpm":{"essential": false}}}),
282 serde_json::from_str::<Value>(&string).unwrap()
283 );
284 }
285
286 #[test]
287 fn serialize_test_2() {
288 let mut userinfo: HashMap<String, ClaimParamValue> = HashMap::new();
289
290 let mut other: HashMap<String, Value> = HashMap::new();
291
292 other.insert(
293 "extra".to_string(),
294 json!({"this_is_obj": {"str":"hi", "bool": true}}),
295 );
296
297 let cpm = ClaimsParameterMember {
298 essential: None,
299 value: None,
300 values: Some(vec!["hello".to_string()]),
301 other: Some(other),
302 };
303
304 let cv = ClaimParamValue::ClaimParamMember(cpm);
305
306 userinfo.insert("cpm".to_string(), cv);
307
308 let claim_param = ClaimParam {
309 id_token: None,
310 userinfo: Some(userinfo),
311 };
312
313 let serialized_result = serde_json::to_string(&claim_param);
314
315 assert!(serialized_result.is_ok());
316
317 let string = serialized_result.unwrap();
318
319 assert_json_eq!(
320 json!({"userinfo": {"cpm":{"values": ["hello"], "extra" : {"this_is_obj": {"str": "hi", "bool": true}}}}}),
321 serde_json::from_str::<Value>(&string).unwrap()
322 );
323 }
324}