// StarKingdoms.IO, a browser game about drifting through space // Copyright (C) 2023 ghostly_zsh, TerraMaster85, core // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . use crate::models::User; use crate::response::JsonAPIResponse; use crate::schema::users; use crate::tokens::UserToken; use crate::AppState; use actix_web::{ http::StatusCode, post, web::{Data, Json}, }; use argon2::Argon2; use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper}; use diesel_async::RunQueryDsl; use jwt::SignWithKey; use password_hash::{Encoding, PasswordHash}; use serde::{Deserialize, Serialize}; use std::time::{Duration, SystemTime}; #[derive(Deserialize)] pub struct LoginRequest { pub username: String, pub password: String, } #[derive(Serialize, Debug)] pub struct LoginResponse { pub token: String, pub user: UserToken, } #[post("/login")] pub async fn user_login_req( req: Json, state: Data, ) -> JsonAPIResponse { let mut conn = handle_error!(state.pool.get().await); let user: User = match handle_error!(users::dsl::users .filter(users::username.eq(&req.username)) .select(User::as_select()) .first(&mut conn) .await .optional()) { Some(t) => t, None => { err!( StatusCode::UNAUTHORIZED, make_err!("ERR_UNAUTHORIZED", "user does not exist", "username") ); } }; let hash = handle_error!(PasswordHash::parse(&user.password_hash, Encoding::B64)); let pwd_ok = hash .verify_password(&[&Argon2::default()], req.password.as_bytes()) .is_ok(); if !pwd_ok { err!( StatusCode::UNAUTHORIZED, make_err!("ERR_UNAUTHORIZED", "unauthorized") ) } let token = UserToken { id: user.id, username: user.username.clone(), permission_level: user.permission_level, expires: SystemTime::now() + Duration::from_secs(86400 * 365), // 1 year }; let jwt_str = handle_error!(token.clone().sign_with_key(&state.key)); ok!(LoginResponse { token: jwt_str, user: token }) }