Skip to main content

alibi_core/store/stateless/
device_codes.rs

1//! Native device grants share the no-database identity lifetime.
2use super::*;
3
4#[async_trait]
5impl DeviceCodeStore for StatelessStore {
6    async fn create_device_code(&self, input: CreateDeviceCode) -> AuthResult<DeviceCode> {
7        let device_code = DeviceCode {
8            id: uuid::Uuid::new_v4().to_string(),
9            device_code: input.device_code,
10            user_code: input.user_code,
11            user_id: input.user_id,
12            expires_at: input.expires_at,
13            status: input.status,
14            last_polled_at: input.last_polled_at,
15            polling_interval: input.polling_interval,
16            client_id: input.client_id,
17            scope: input.scope,
18        };
19        drop(
20            self.lock()?
21                .device_codes
22                .insert(device_code.id.clone(), device_code.clone()),
23        );
24        Ok(device_code)
25    }
26
27    async fn get_device_code_by_device_code(
28        &self,
29        device_code: &str,
30    ) -> AuthResult<Option<DeviceCode>> {
31        Ok(self
32            .lock()?
33            .device_codes
34            .values()
35            .find(|value| value.device_code == device_code)
36            .cloned())
37    }
38
39    async fn get_device_code_by_user_code(
40        &self,
41        user_code: &str,
42    ) -> AuthResult<Option<DeviceCode>> {
43        Ok(self
44            .lock()?
45            .device_codes
46            .values()
47            .find(|value| value.user_code == user_code)
48            .cloned())
49    }
50
51    async fn update_device_code(
52        &self,
53        id: &str,
54        update: UpdateDeviceCode,
55    ) -> AuthResult<DeviceCode> {
56        let mut state = self.lock()?;
57        let device_code = state
58            .device_codes
59            .get_mut(id)
60            .ok_or_else(|| AuthError::not_found("Device code not found"))?;
61
62        if let Some(status) = update.status {
63            device_code.status = status;
64        }
65        if let Some(user_id) = update.user_id {
66            device_code.user_id = user_id;
67        }
68        if let Some(last_polled_at) = update.last_polled_at {
69            device_code.last_polled_at = last_polled_at;
70        }
71
72        let locked_result = Ok(device_code.clone());
73        drop(state);
74        locked_result
75    }
76
77    async fn update_device_code_if_status(
78        &self,
79        id: &str,
80        current_status: &str,
81        update: UpdateDeviceCode,
82    ) -> AuthResult<bool> {
83        let mut state = self.lock()?;
84        let Some(device_code) = state.device_codes.get_mut(id) else {
85            return Ok(false);
86        };
87
88        if device_code.status != current_status {
89            return Ok(false);
90        }
91
92        if let Some(status) = update.status {
93            device_code.status = status;
94        }
95        if let Some(user_id) = update.user_id {
96            device_code.user_id = user_id;
97        }
98        if let Some(last_polled_at) = update.last_polled_at {
99            device_code.last_polled_at = last_polled_at;
100        }
101        drop(state);
102
103        Ok(true)
104    }
105
106    async fn claim_device_code(&self, id: &str, user_id: &str) -> AuthResult<bool> {
107        let mut state = self.lock()?;
108        let Some(device_code) = state.device_codes.get_mut(id) else {
109            return Ok(false);
110        };
111
112        if device_code.status != "pending" || device_code.user_id.is_some() {
113            return Ok(false);
114        }
115
116        device_code.user_id = Some(user_id.to_owned());
117        drop(state);
118
119        Ok(true)
120    }
121
122    async fn delete_device_code(&self, id: &str) -> AuthResult<()> {
123        drop(self.lock()?.device_codes.shift_remove(id));
124        Ok(())
125    }
126
127    async fn delete_device_code_if_status(&self, id: &str, status: &str) -> AuthResult<bool> {
128        let mut state = self.lock()?;
129        let should_delete = state
130            .device_codes
131            .get(id)
132            .is_some_and(|device_code| device_code.status == status);
133
134        if should_delete {
135            drop(state.device_codes.shift_remove(id));
136        }
137
138        let locked_result = Ok(should_delete);
139        drop(state);
140        locked_result
141    }
142}