wallet_adapter/wallet_ser_der/
signin_standard.rs1use std::time::{Duration, SystemTime, UNIX_EPOCH};
2
3use crate::{Reflection, WalletError, WalletResult};
4
5use wallet_adapter_common::{clusters::Cluster, signin_standard::SigninInput as SigninInputLib};
6use web_sys::{
7 js_sys::{self, Array},
8 wasm_bindgen::JsValue,
9 Window,
10};
11
12#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct SigninInput(pub(crate) SigninInputLib);
18
19impl SigninInput {
20 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn set_domain(&mut self, window: &Window) -> WalletResult<&mut Self> {
29 let host = window.location().host()?;
30
31 self.0.set_domain(&host);
32
33 Ok(self)
34 }
35
36 pub fn set_custom_domain(&mut self, domain: &str) -> &mut Self {
41 self.0.set_domain(domain);
42
43 self
44 }
45
46 pub fn set_address(&mut self, address: &str) -> WalletResult<&mut Self> {
51 self.0.set_address(address)?;
52
53 Ok(self)
54 }
55
56 pub fn set_statement(&mut self, statement: &str) -> &mut Self {
59 self.0.set_statement(statement);
60
61 self
62 }
63
64 pub fn set_uri(&mut self, window: &Window) -> WalletResult<&mut Self> {
69 self.0.set_uri(&window.location().href()?);
70
71 Ok(self)
72 }
73
74 pub fn set_version(&mut self, version: &str) -> &mut Self {
77 self.0.set_version(version);
78
79 self
80 }
81
82 pub fn set_chain_id(&mut self, cluster: Cluster) -> &mut Self {
86 self.0.set_chain_id(cluster);
87
88 self
89 }
90
91 pub fn set_nonce(&mut self) -> &mut Self {
95 self.0.set_nonce();
96 self
97 }
98
99 pub fn custom_nonce(&mut self, nonce: &str) -> WalletResult<&mut Self> {
103 self.0.set_custom_nonce(nonce)?;
104
105 Ok(self)
106 }
107
108 pub fn time_now() -> WalletResult<SystemTime> {
111 let date_now = js_sys::Date::now() as u64;
112
113 UNIX_EPOCH
114 .checked_add(Duration::from_millis(date_now))
115 .ok_or(WalletError::JsError {
116 name: "UNIX_EPOCH.checked_add(js_sys::Date::now()".to_string(),
117 message: "Unable to get the current time".to_string(),
118 stack: "INTERNAL ERROR".to_string(),
119 })
120 }
121
122 pub fn set_issued_at(&mut self) -> WalletResult<&mut Self> {
128 self.0.set_issued_at(Self::time_now()?);
129
130 Ok(self)
131 }
132
133 pub fn set_expiration_time_millis(
136 &mut self,
137 expiration_time_milliseconds: u64,
138 ) -> WalletResult<&mut Self> {
139 self.0
140 .set_expiration_time_millis(Self::time_now()?, expiration_time_milliseconds)?;
141
142 Ok(self)
143 }
144
145 pub fn set_expiration_time_seconds(
148 &mut self,
149 expiration_time_seconds: u64,
150 ) -> WalletResult<&mut Self> {
151 self.0
152 .set_expiration_time_seconds(Self::time_now()?, expiration_time_seconds)?;
153
154 Ok(self)
155 }
156
157 pub fn set_expiration_time(&mut self, expiration_time: SystemTime) -> WalletResult<&mut Self> {
161 if let Some(issued_at) = self.0.issued_at() {
162 if issued_at > &expiration_time {
163 return Err(WalletError::ExpiryTimeEarlierThanIssuedTime);
164 }
165 }
166
167 let now = Self::time_now()?;
168
169 if now > expiration_time {
170 return Err(WalletError::ExpirationTimeIsInThePast);
171 }
172
173 self.0.set_expiration_time(now, expiration_time)?;
174
175 Ok(self)
176 }
177
178 pub fn set_not_before_time_millis(
181 &mut self,
182 expiration_time_milliseconds: u64,
183 ) -> WalletResult<&mut Self> {
184 self.0
185 .set_not_before_time_millis(Self::time_now()?, expiration_time_milliseconds)?;
186
187 Ok(self)
188 }
189
190 pub fn set_not_before_time_seconds(
193 &mut self,
194 expiration_time_seconds: u64,
195 ) -> WalletResult<&mut Self> {
196 self.0
197 .set_not_before_time_seconds(Self::time_now()?, expiration_time_seconds)?;
198
199 Ok(self)
200 }
201
202 pub fn set_not_before_time(&mut self, not_before: SystemTime) -> WalletResult<&mut Self> {
207 self.0.set_not_before_time(Self::time_now()?, not_before)?;
208
209 Ok(self)
210 }
211
212 pub fn get_object(&self) -> WalletResult<JsValue> {
215 let mut signin_input_object = Reflection::new_object();
216
217 signin_input_object.set_object_string_optional("domain", self.0.domain())?;
218 signin_input_object.set_object_string_optional("address", self.0.address())?;
219 signin_input_object.set_object_string_optional("statement", self.0.statement())?;
220 signin_input_object.set_object_string_optional("uri", self.0.uri())?;
221 signin_input_object.set_object_string_optional("version", self.0.version())?;
222 signin_input_object.set_object_string_optional("address", self.0.address())?;
223 signin_input_object.set_object_string_optional(
224 "chainId",
225 self.0
226 .chain_id()
227 .map(|cluster| cluster.chain().to_string())
228 .as_ref(),
229 )?;
230 signin_input_object.set_object_string_optional("nonce", self.0.nonce())?;
231 signin_input_object
232 .set_object_string_optional("issuedAt", self.issued_at_iso8601().as_ref())?;
233 signin_input_object.set_object_string_optional(
234 "expirationTime",
235 self.expiration_time_iso8601().as_ref(),
236 )?;
237 signin_input_object
238 .set_object_string_optional("notBefore", self.not_before_iso8601().as_ref())?;
239 signin_input_object.set_object_string_optional("requestId", self.0.request_id())?;
240
241 if !self.0.resources().is_empty() {
242 let stringify_resources = Array::new();
243 self.0.resources().iter().for_each(|resource| {
244 stringify_resources.push(&resource.into());
245 });
246 signin_input_object.set_object(&"resources".into(), &stringify_resources)?;
247 }
248
249 Ok(signin_input_object.take())
250 }
251
252 pub fn set_request_id(&mut self, id: &str) -> &mut Self {
259 self.0.set_request_id(id);
260
261 self
262 }
263
264 pub fn add_resource(&mut self, resource: &str) -> &mut Self {
269 self.0.add_resource(resource);
270
271 self
272 }
273
274 pub fn add_resources(&mut self, resources: &[&str]) -> &mut Self {
276 self.0.add_resources(resources);
277
278 self
279 }
280
281 pub fn domain(&self) -> Option<&String> {
283 self.0.domain()
284 }
285
286 pub fn address(&self) -> Option<&String> {
288 self.0.address()
289 }
290
291 pub fn statement(&self) -> Option<&String> {
293 self.0.statement()
294 }
295
296 pub fn uri(&self) -> Option<&String> {
298 self.0.uri()
299 }
300
301 pub fn version(&self) -> Option<&String> {
303 self.0.version()
304 }
305
306 pub fn chain_id(&self) -> Option<&Cluster> {
308 self.0.chain_id()
309 }
310
311 pub fn nonce(&self) -> Option<&String> {
313 self.0.nonce()
314 }
315
316 pub fn issued_at(&self) -> Option<&SystemTime> {
318 self.0.issued_at()
319 }
320
321 pub fn expiration_time(&self) -> Option<&SystemTime> {
323 self.0.expiration_time()
324 }
325
326 pub fn not_before(&self) -> Option<&SystemTime> {
328 self.0.not_before()
329 }
330
331 pub fn issued_at_iso8601(&self) -> Option<String> {
333 self.0.issued_at_iso8601()
334 }
335
336 pub fn expiration_time_iso8601(&self) -> Option<String> {
338 self.0.expiration_time_iso8601()
339 }
340
341 pub fn not_before_iso8601(&self) -> Option<String> {
343 self.0.not_before_iso8601()
344 }
345
346 pub fn request_id(&self) -> Option<&String> {
348 self.0.request_id()
349 }
350
351 pub fn resources(&self) -> &[String] {
353 self.0.resources()
354 }
355}
356
357#[cfg(test)]
358#[cfg(target_arch = "wasm32")]
359mod signin_input_sanity_checks {
360 use super::*;
361
362 #[test]
363 fn set_issued_at() {
364 let mut signin_input = SigninInput::default();
365
366 assert!(signin_input.issued_at().is_none());
367
368 signin_input.set_issued_at().unwrap();
369
370 assert!(signin_input.issued_at.unwrap() > SystemTime::UNIX_EPOCH)
371 }
372
373 #[test]
374 fn set_expiration_time() {
375 let mut signin_input = SigninInput::default();
376
377 let now = SigninInput::time_now().unwrap();
378
379 let past_time = now.checked_sub(Duration::from_secs(300)).unwrap();
380 assert_eq!(
381 Some(WalletError::ExpirationTimeIsInThePast),
382 signin_input.set_expiration_time(past_time).err()
383 );
384
385 signin_input.set_issued_at().unwrap();
386 assert_eq!(
387 Some(WalletError::ExpiryTimeEarlierThanIssuedTime),
388 signin_input.set_expiration_time(past_time).err()
389 );
390
391 let valid_expiry = now.checked_add(Duration::from_secs(300)).unwrap();
392 assert!(signin_input.set_expiration_time(valid_expiry).is_ok());
393
394 assert!(signin_input.issued_at.unwrap() > SystemTime::UNIX_EPOCH);
395
396 assert!(signin_input.set_expiration_time_millis(4000).is_ok());
397 assert!(signin_input.set_expiration_time_seconds(4).is_ok());
398 }
399
400 #[test]
401 fn set_not_before_time() {
402 let mut signin_input = SigninInput::default();
403
404 let now = SigninInput::time_now().unwrap();
405
406 let past_time = now.checked_sub(Duration::from_secs(300)).unwrap();
407 assert_eq!(
408 Some(WalletError::NotBeforeTimeIsInThePast),
409 signin_input.set_not_before_time(past_time).err()
410 );
411
412 signin_input.set_issued_at().unwrap();
413 let future_time = now.checked_sub(Duration::from_secs(3000000)).unwrap();
414 assert_eq!(
415 Some(WalletError::NotBeforeTimeEarlierThanIssuedTime),
416 signin_input.set_not_before_time(future_time).err()
417 );
418
419 signin_input.set_issued_at().unwrap();
420 let future_time = SigninInput::time_now()
421 .unwrap()
422 .checked_add(Duration::from_secs(30000))
423 .unwrap();
424 signin_input.set_expiration_time(future_time).unwrap();
425 let future_time = now.checked_add(Duration::from_secs(3000000)).unwrap();
426 assert_eq!(
427 Some(WalletError::NotBeforeTimeLaterThanExpirationTime),
428 signin_input.set_not_before_time(future_time).err()
429 );
430
431 let valid_expiry = now.checked_add(Duration::from_secs(300)).unwrap();
432 assert!(signin_input.set_not_before_time(valid_expiry).is_ok());
433
434 assert!(signin_input.issued_at.unwrap() > SystemTime::UNIX_EPOCH);
435
436 assert!(signin_input.set_not_before_time_millis(4000).is_ok());
437 assert!(signin_input.set_not_before_time_seconds(4).is_ok());
438 }
439}