this repo has no description
1use crate::state::AppState;
2use axum::{
3 Json,
4 extract::State,
5 http::StatusCode,
6 response::{IntoResponse, Response},
7};
8use bcrypt::verify;
9use serde::{Deserialize, Serialize};
10use serde_json::json;
11use sqlx::Row;
12use tracing::{error, info, warn};
13
14#[derive(Deserialize)]
15pub struct CreateSessionInput {
16 pub identifier: String,
17 pub password: String,
18}
19
20#[derive(Serialize)]
21#[serde(rename_all = "camelCase")]
22pub struct CreateSessionOutput {
23 pub access_jwt: String,
24 pub refresh_jwt: String,
25 pub handle: String,
26 pub did: String,
27}
28
29pub async fn create_session(
30 State(state): State<AppState>,
31 Json(input): Json<CreateSessionInput>,
32) -> Response {
33 info!("create_session: identifier='{}'", input.identifier);
34
35 let user_row = sqlx::query("SELECT u.did, u.handle, u.password_hash, k.key_bytes FROM users u JOIN user_keys k ON u.id = k.user_id WHERE u.handle = $1 OR u.email = $1")
36 .bind(&input.identifier)
37 .fetch_optional(&state.db)
38 .await;
39
40 match user_row {
41 Ok(Some(row)) => {
42 let stored_hash: String = row.get("password_hash");
43
44 if verify(&input.password, &stored_hash).unwrap_or(false) {
45 let did: String = row.get("did");
46 let handle: String = row.get("handle");
47 let key_bytes: Vec<u8> = row.get("key_bytes");
48
49 let access_jwt = match crate::auth::create_access_token(&did, &key_bytes) {
50 Ok(t) => t,
51 Err(e) => {
52 error!("Failed to create access token: {:?}", e);
53 return (
54 StatusCode::INTERNAL_SERVER_ERROR,
55 Json(json!({"error": "InternalError"})),
56 )
57 .into_response();
58 }
59 };
60
61 let refresh_jwt = match crate::auth::create_refresh_token(&did, &key_bytes) {
62 Ok(t) => t,
63 Err(e) => {
64 error!("Failed to create refresh token: {:?}", e);
65 return (
66 StatusCode::INTERNAL_SERVER_ERROR,
67 Json(json!({"error": "InternalError"})),
68 )
69 .into_response();
70 }
71 };
72
73 let session_insert = sqlx::query(
74 "INSERT INTO sessions (access_jwt, refresh_jwt, did) VALUES ($1, $2, $3)",
75 )
76 .bind(&access_jwt)
77 .bind(&refresh_jwt)
78 .bind(&did)
79 .execute(&state.db)
80 .await;
81
82 match session_insert {
83 Ok(_) => {
84 return (
85 StatusCode::OK,
86 Json(CreateSessionOutput {
87 access_jwt,
88 refresh_jwt,
89 handle,
90 did,
91 }),
92 )
93 .into_response();
94 }
95 Err(e) => {
96 error!("Failed to insert session: {:?}", e);
97 return (
98 StatusCode::INTERNAL_SERVER_ERROR,
99 Json(json!({"error": "InternalError"})),
100 )
101 .into_response();
102 }
103 }
104 } else {
105 warn!(
106 "Password verification failed for identifier: {}",
107 input.identifier
108 );
109 }
110 }
111 Ok(None) => {
112 warn!("User not found for identifier: {}", input.identifier);
113 }
114 Err(e) => {
115 error!("Database error fetching user: {:?}", e);
116 return (
117 StatusCode::INTERNAL_SERVER_ERROR,
118 Json(json!({"error": "InternalError"})),
119 )
120 .into_response();
121 }
122 }
123
124 (
125 StatusCode::UNAUTHORIZED,
126 Json(json!({"error": "AuthenticationFailed", "message": "Invalid identifier or password"})),
127 )
128 .into_response()
129}
130
131pub async fn get_session(
132 State(state): State<AppState>,
133 headers: axum::http::HeaderMap,
134) -> Response {
135 let auth_header = headers.get("Authorization");
136 if auth_header.is_none() {
137 return (
138 StatusCode::UNAUTHORIZED,
139 Json(json!({"error": "AuthenticationRequired"})),
140 )
141 .into_response();
142 }
143
144 let token = auth_header
145 .unwrap()
146 .to_str()
147 .unwrap_or("")
148 .replace("Bearer ", "");
149
150 let result = sqlx::query(
151 r#"
152 SELECT u.handle, u.did, u.email, k.key_bytes
153 FROM sessions s
154 JOIN users u ON s.did = u.did
155 JOIN user_keys k ON u.id = k.user_id
156 WHERE s.access_jwt = $1
157 "#,
158 )
159 .bind(&token)
160 .fetch_optional(&state.db)
161 .await;
162
163 match result {
164 Ok(Some(row)) => {
165 let handle: String = row.get("handle");
166 let did: String = row.get("did");
167 let email: String = row.get("email");
168 let key_bytes: Vec<u8> = row.get("key_bytes");
169
170 if let Err(_) = crate::auth::verify_token(&token, &key_bytes) {
171 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid token signature"}))).into_response();
172 }
173
174 return (
175 StatusCode::OK,
176 Json(json!({
177 "handle": handle,
178 "did": did,
179 "email": email,
180 "didDoc": {}
181 })),
182 )
183 .into_response();
184 }
185 Ok(None) => {
186 return (
187 StatusCode::UNAUTHORIZED,
188 Json(json!({"error": "AuthenticationFailed"})),
189 )
190 .into_response();
191 }
192 Err(e) => {
193 error!("Database error in get_session: {:?}", e);
194 return (
195 StatusCode::INTERNAL_SERVER_ERROR,
196 Json(json!({"error": "InternalError"})),
197 )
198 .into_response();
199 }
200 }
201}
202
203pub async fn delete_session(
204 State(state): State<AppState>,
205 headers: axum::http::HeaderMap,
206) -> Response {
207 let auth_header = headers.get("Authorization");
208 if auth_header.is_none() {
209 return (
210 StatusCode::UNAUTHORIZED,
211 Json(json!({"error": "AuthenticationRequired"})),
212 )
213 .into_response();
214 }
215
216 let token = auth_header
217 .unwrap()
218 .to_str()
219 .unwrap_or("")
220 .replace("Bearer ", "");
221
222 let result = sqlx::query("DELETE FROM sessions WHERE access_jwt = $1")
223 .bind(token)
224 .execute(&state.db)
225 .await;
226
227 match result {
228 Ok(res) => {
229 if res.rows_affected() > 0 {
230 return (StatusCode::OK, Json(json!({}))).into_response();
231 }
232 }
233 Err(e) => {
234 error!("Database error in delete_session: {:?}", e);
235 }
236 }
237
238 (
239 StatusCode::UNAUTHORIZED,
240 Json(json!({"error": "AuthenticationFailed"})),
241 )
242 .into_response()
243}
244
245pub async fn refresh_session(
246 State(state): State<AppState>,
247 headers: axum::http::HeaderMap,
248) -> Response {
249 let auth_header = headers.get("Authorization");
250 if auth_header.is_none() {
251 return (
252 StatusCode::UNAUTHORIZED,
253 Json(json!({"error": "AuthenticationRequired"})),
254 )
255 .into_response();
256 }
257
258 let refresh_token = auth_header
259 .unwrap()
260 .to_str()
261 .unwrap_or("")
262 .replace("Bearer ", "");
263
264 let session = sqlx::query(
265 "SELECT s.did, k.key_bytes FROM sessions s JOIN users u ON s.did = u.did JOIN user_keys k ON u.id = k.user_id WHERE s.refresh_jwt = $1"
266 )
267 .bind(&refresh_token)
268 .fetch_optional(&state.db)
269 .await;
270
271 match session {
272 Ok(Some(session_row)) => {
273 let did: String = session_row.get("did");
274 let key_bytes: Vec<u8> = session_row.get("key_bytes");
275
276 if let Err(_) = crate::auth::verify_token(&refresh_token, &key_bytes) {
277 return (StatusCode::UNAUTHORIZED, Json(json!({"error": "AuthenticationFailed", "message": "Invalid refresh token signature"}))).into_response();
278 }
279
280 let new_access_jwt = match crate::auth::create_access_token(&did, &key_bytes) {
281 Ok(t) => t,
282 Err(e) => {
283 error!("Failed to create access token: {:?}", e);
284 return (
285 StatusCode::INTERNAL_SERVER_ERROR,
286 Json(json!({"error": "InternalError"})),
287 )
288 .into_response();
289 }
290 };
291 let new_refresh_jwt = match crate::auth::create_refresh_token(&did, &key_bytes) {
292 Ok(t) => t,
293 Err(e) => {
294 error!("Failed to create refresh token: {:?}", e);
295 return (
296 StatusCode::INTERNAL_SERVER_ERROR,
297 Json(json!({"error": "InternalError"})),
298 )
299 .into_response();
300 }
301 };
302
303 let update = sqlx::query(
304 "UPDATE sessions SET access_jwt = $1, refresh_jwt = $2 WHERE refresh_jwt = $3",
305 )
306 .bind(&new_access_jwt)
307 .bind(&new_refresh_jwt)
308 .bind(&refresh_token)
309 .execute(&state.db)
310 .await;
311
312 match update {
313 Ok(_) => {
314 let user = sqlx::query("SELECT handle FROM users WHERE did = $1")
315 .bind(&did)
316 .fetch_optional(&state.db)
317 .await;
318
319 match user {
320 Ok(Some(u)) => {
321 let handle: String = u.get("handle");
322 return (
323 StatusCode::OK,
324 Json(json!({
325 "accessJwt": new_access_jwt,
326 "refreshJwt": new_refresh_jwt,
327 "handle": handle,
328 "did": did
329 })),
330 )
331 .into_response();
332 }
333 Ok(None) => {
334 error!("User not found for existing session: {}", did);
335 return (
336 StatusCode::INTERNAL_SERVER_ERROR,
337 Json(json!({"error": "InternalError"})),
338 )
339 .into_response();
340 }
341 Err(e) => {
342 error!("Database error fetching user: {:?}", e);
343 return (
344 StatusCode::INTERNAL_SERVER_ERROR,
345 Json(json!({"error": "InternalError"})),
346 )
347 .into_response();
348 }
349 }
350 }
351 Err(e) => {
352 error!("Database error updating session: {:?}", e);
353 return (
354 StatusCode::INTERNAL_SERVER_ERROR,
355 Json(json!({"error": "InternalError"})),
356 )
357 .into_response();
358 }
359 }
360 }
361 Ok(None) => {
362 return (
363 StatusCode::UNAUTHORIZED,
364 Json(json!({"error": "AuthenticationFailed", "message": "Invalid refresh token"})),
365 )
366 .into_response();
367 }
368 Err(e) => {
369 error!("Database error fetching session: {:?}", e);
370 return (
371 StatusCode::INTERNAL_SERVER_ERROR,
372 Json(json!({"error": "InternalError"})),
373 )
374 .into_response();
375 }
376 }
377}