TODO: Wrap this in a database transaction create a user session for the user with [`user_id`](`i32`) # Errors - 400: 'device' cannot be longer than 256 characters. - 500: An internal server error occurred. - 500: Could not create session. # Panics - could not connect to database - could not get `SECRET_KEY` from environment TODO: don't panic if db connection fails, just return an error
(
db: &mut Connection,
device_type: Option<String>,
ttl: Option<i64>,
user_id: i32,
)
| 290 | /// |
| 291 | /// TODO: don't panic if db connection fails, just return an error |
| 292 | pub fn create_user_session( |
| 293 | db: &mut Connection, |
| 294 | device_type: Option<String>, |
| 295 | ttl: Option<i64>, |
| 296 | user_id: i32, |
| 297 | ) -> Result<(AccessToken, RefreshToken), (StatusCode, Message)> { |
| 298 | // verify device |
| 299 | let device = match device_type { |
| 300 | Some(device) if device.len() > 256 => { |
| 301 | return Err((400, "'device' cannot be longer than 256 characters.")); |
| 302 | } |
| 303 | Some(device) => Some(device), |
| 304 | None => None, |
| 305 | }; |
| 306 | |
| 307 | let Ok(permissions) = Permission::fetch_all(db, user_id) else { |
| 308 | return Err((500, "An internal server error occurred.")); |
| 309 | }; |
| 310 | |
| 311 | let Ok(roles) = Role::fetch_all(db, user_id) else { |
| 312 | return Err((500, "An internal server error occurred.")); |
| 313 | }; |
| 314 | |
| 315 | let access_token_duration = chrono::Duration::seconds( |
| 316 | ttl.map_or_else(|| /* 15 minutes */ 15 * 60, |tt| std::cmp::max(tt, 1)), |
| 317 | ); |
| 318 | |
| 319 | #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] |
| 320 | let access_token_claims = AccessTokenClaims { |
| 321 | exp: (chrono::Utc::now() + access_token_duration).timestamp() as usize, |
| 322 | sub: user_id, |
| 323 | token_type: "access_token".to_string(), |
| 324 | roles, |
| 325 | permissions, |
| 326 | }; |
| 327 | |
| 328 | #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] |
| 329 | let refresh_token_claims = RefreshTokenClaims { |
| 330 | exp: (chrono::Utc::now() + chrono::Duration::hours(24)).timestamp() as usize, |
| 331 | sub: user_id, |
| 332 | token_type: "refresh_token".to_string(), |
| 333 | }; |
| 334 | |
| 335 | let access_token = encode( |
| 336 | &Header::default(), |
| 337 | &access_token_claims, |
| 338 | &EncodingKey::from_secret(std::env::var("SECRET_KEY").unwrap().as_ref()), |
| 339 | ) |
| 340 | .unwrap(); |
| 341 | |
| 342 | let refresh_token = encode( |
| 343 | &Header::default(), |
| 344 | &refresh_token_claims, |
| 345 | &EncodingKey::from_secret(std::env::var("SECRET_KEY").unwrap().as_ref()), |
| 346 | ) |
| 347 | .unwrap(); |
| 348 | |
| 349 | UserSession::create( |
no test coverage detected