alibi_core/store/stateless/
device_codes.rs1use crate::store::DeviceCodeStore;
3use crate::store::stateless::StatelessStore;
4use crate::{AuthError, AuthResult, CreateDeviceCode, DeviceCode, UpdateDeviceCode};
5use async_trait::async_trait;
6
7#[async_trait]
8impl DeviceCodeStore for StatelessStore {
9 async fn create_device_code(&self, input: CreateDeviceCode) -> AuthResult<DeviceCode> {
10 self.create_device_code_with_fields(input, serde_json::Map::new())
11 .await
12 }
13 async fn create_device_code_with_fields(
14 &self,
15 input: CreateDeviceCode,
16 fields: serde_json::Map<String, serde_json::Value>,
17 ) -> AuthResult<DeviceCode> {
18 let device_code = DeviceCode {
19 id: uuid::Uuid::new_v4().to_string(),
20 device_code: input.device_code,
21 user_code: input.user_code,
22 user_id: input.user_id,
23 expires_at: input.expires_at,
24 status: input.status,
25 last_polled_at: input.last_polled_at,
26 polling_interval: input.polling_interval,
27 client_id: input.client_id,
28 scope: input.scope,
29 };
30 let mut state = self.lock()?;
31 _ = state
32 .device_code_fields
33 .insert(device_code.id.clone(), fields);
34 _ = state
35 .device_codes
36 .insert(device_code.id.clone(), device_code.clone());
37 Ok(device_code)
38 }
39 async fn device_code_fields(
40 &self,
41 id: &str,
42 ) -> AuthResult<serde_json::Map<String, serde_json::Value>> {
43 Ok(self
44 .lock()?
45 .device_code_fields
46 .get(id)
47 .cloned()
48 .unwrap_or_default())
49 }
50 async fn consume_device_code(
51 &self,
52 id: &str,
53 status: &str,
54 ownership: &serde_json::Map<String, serde_json::Value>,
55 ) -> AuthResult<Option<DeviceCode>> {
56 let mut state = self.lock()?;
57 let matches = state
58 .device_codes
59 .get(id)
60 .is_some_and(|row| row.status == status)
61 && ownership.iter().all(|(key, value)| {
62 state
63 .device_code_fields
64 .get(id)
65 .and_then(|fields| fields.get(key))
66 == Some(value)
67 });
68 if !matches {
69 return Ok(None);
70 }
71 _ = state.device_code_fields.shift_remove(id);
72 Ok(state.device_codes.shift_remove(id))
73 }
74 async fn get_device_code_by_device_code(
75 &self,
76 device_code: &str,
77 ) -> AuthResult<Option<DeviceCode>> {
78 Ok(self
79 .lock()?
80 .device_codes
81 .values()
82 .find(|value| value.device_code == device_code)
83 .cloned())
84 }
85
86 async fn get_device_code_by_user_code(
87 &self,
88 user_code: &str,
89 ) -> AuthResult<Option<DeviceCode>> {
90 Ok(self
91 .lock()?
92 .device_codes
93 .values()
94 .find(|value| value.user_code == user_code)
95 .cloned())
96 }
97
98 async fn update_device_code(
99 &self,
100 id: &str,
101 update: UpdateDeviceCode,
102 ) -> AuthResult<DeviceCode> {
103 let mut state = self.lock()?;
104 let device_code = state
105 .device_codes
106 .get_mut(id)
107 .ok_or_else(|| AuthError::not_found("Device code not found"))?;
108
109 if let Some(status) = update.status {
110 device_code.status = status;
111 }
112 if let Some(user_id) = update.user_id {
113 device_code.user_id = user_id;
114 }
115 if let Some(last_polled_at) = update.last_polled_at {
116 device_code.last_polled_at = last_polled_at;
117 }
118
119 Ok(device_code.clone())
120 }
121
122 async fn update_device_code_if_status(
123 &self,
124 id: &str,
125 current_status: &str,
126 update: UpdateDeviceCode,
127 ) -> AuthResult<bool> {
128 let mut state = self.lock()?;
129 let Some(device_code) = state.device_codes.get_mut(id) else {
130 return Ok(false);
131 };
132
133 if device_code.status != current_status {
134 return Ok(false);
135 }
136
137 if let Some(status) = update.status {
138 device_code.status = status;
139 }
140 if let Some(user_id) = update.user_id {
141 device_code.user_id = user_id;
142 }
143 if let Some(last_polled_at) = update.last_polled_at {
144 device_code.last_polled_at = last_polled_at;
145 }
146 Ok(true)
147 }
148
149 async fn claim_device_code(&self, id: &str, user_id: &str) -> AuthResult<bool> {
150 let mut state = self.lock()?;
151 let Some(device_code) = state.device_codes.get_mut(id) else {
152 return Ok(false);
153 };
154
155 if device_code.status != "pending" || device_code.user_id.is_some() {
156 return Ok(false);
157 }
158
159 device_code.user_id = Some(user_id.to_owned());
160 Ok(true)
161 }
162
163 async fn delete_device_code(&self, id: &str) -> AuthResult<()> {
164 let mut state = self.lock()?;
165 _ = state.device_codes.shift_remove(id);
166 _ = state.device_code_fields.shift_remove(id);
167 Ok(())
168 }
169
170 async fn delete_device_code_if_status(&self, id: &str, status: &str) -> AuthResult<bool> {
171 let mut state = self.lock()?;
172 let should_delete = state
173 .device_codes
174 .get(id)
175 .is_some_and(|device_code| device_code.status == status);
176
177 if should_delete {
178 _ = state.device_codes.shift_remove(id);
179 _ = state.device_code_fields.shift_remove(id);
180 }
181
182 Ok(should_delete)
183 }
184}