Implement login and genkey routes
This commit is contained in:
parent
fe62b28a03
commit
78f450fbef
2 changed files with 89 additions and 7 deletions
|
@ -1,3 +1,5 @@
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use crate::env;
|
use crate::env;
|
||||||
use jsonwebtoken::{TokenData, Header, Validation};
|
use jsonwebtoken::{TokenData, Header, Validation};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
@ -17,3 +19,10 @@ pub fn encode_jwt(claims: &JwtUser) -> jsonwebtoken::errors::Result<String> {
|
||||||
pub fn decode_jwt(jwt: &str) -> jsonwebtoken::errors::Result<TokenData<JwtUser>> {
|
pub fn decode_jwt(jwt: &str) -> jsonwebtoken::errors::Result<TokenData<JwtUser>> {
|
||||||
jsonwebtoken::decode::<JwtUser>(jwt, &env::jwt_secret().1, &Validation::new(*env::jwt_alg()))
|
jsonwebtoken::decode::<JwtUser>(jwt, &env::jwt_secret().1, &Validation::new(*env::jwt_alg()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expiration_time() -> u64 {
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.expect("time went backwards")
|
||||||
|
.as_secs() + 180 * 24 * 3600
|
||||||
|
}
|
|
@ -1,17 +1,18 @@
|
||||||
use std::time::SystemTime;
|
use argon2::{password_hash::{rand_core::OsRng, SaltString}, Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
|
||||||
use argon2::{Argon2, PasswordHasher, password_hash::{rand_core::OsRng, SaltString}};
|
|
||||||
use axum::{extract::State, http::StatusCode, routing::{get, post}, Json, Router};
|
use axum::{extract::State, http::StatusCode, routing::{get, post}, Json, Router};
|
||||||
use chrono::{Utc, NaiveDateTime};
|
use chrono::{Utc, NaiveDateTime};
|
||||||
use diesel::{RunQueryDsl, ExpressionMethods};
|
use diesel::{RunQueryDsl, ExpressionMethods};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::{database::actions, http::extractors::auth::ExtractUser, auth::JwtUser, AppState};
|
use crate::{auth::{encode_jwt, expiration_time, JwtUser}, database::actions, http::extractors::auth::{ExtractJwtUser, ExtractUser}, AppState};
|
||||||
|
|
||||||
pub fn auth_router() -> Router<AppState> {
|
pub fn auth_router() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/auth/account", get(account))
|
.route("/auth/account", get(account))
|
||||||
.route("/auth/register", post(register))
|
.route("/auth/register", post(register))
|
||||||
|
.route("/auth/login", post(login))
|
||||||
|
.route("/auth/genkey", get(genkey))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
@ -33,6 +34,81 @@ async fn account(ExtractUser(user): ExtractUser) -> Result<Json<AccountResponse>
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct GenkeyResponse {
|
||||||
|
session_key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn genkey(State(state): State<AppState>, ExtractJwtUser(user): ExtractJwtUser) -> Result<Json<GenkeyResponse>, StatusCode> {
|
||||||
|
use crate::database::schema::session_keys::dsl::*;
|
||||||
|
|
||||||
|
if let Ok(mut conn) = state.pool.get() {
|
||||||
|
let session_key = Uuid::new_v4().to_string();
|
||||||
|
let result = diesel::insert_into(session_keys)
|
||||||
|
.values((
|
||||||
|
user_id.eq(user.uid),
|
||||||
|
key.eq(&session_key),
|
||||||
|
))
|
||||||
|
.execute(&mut conn);
|
||||||
|
|
||||||
|
if result.is_ok() {
|
||||||
|
Ok(Json(GenkeyResponse {
|
||||||
|
session_key,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
error!("failed to insert into session_keys");
|
||||||
|
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("failed to obtain pooled connection");
|
||||||
|
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct LoginRequest {
|
||||||
|
email: String,
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct LoginResponse {
|
||||||
|
token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn login(State(state): State<AppState>, Json(req): Json<LoginRequest>) -> Result<Json<LoginResponse>, StatusCode> {
|
||||||
|
if let Ok(mut conn) = state.pool.get() {
|
||||||
|
if let Ok(Some(user)) = actions::user_by_email(&req.email, &mut conn) {
|
||||||
|
let parsed_hash = PasswordHash::new(&user.password).expect("invalid argon2 password hash");
|
||||||
|
if Argon2::default().verify_password(req.password.as_bytes(), &parsed_hash).is_err() {
|
||||||
|
info!("failed login attempt, invalid password: {}", &req.email);
|
||||||
|
Err(StatusCode::UNAUTHORIZED)
|
||||||
|
} else {
|
||||||
|
info!("valid login attempt: {}", req.email);
|
||||||
|
match encode_jwt(&JwtUser {
|
||||||
|
uid: user.id,
|
||||||
|
email: user.email,
|
||||||
|
name: user.name,
|
||||||
|
exp: expiration_time(),
|
||||||
|
}) {
|
||||||
|
Ok(token) => Ok(Json(LoginResponse { token })),
|
||||||
|
Err(err) => {
|
||||||
|
error!("token couldn't be encoded: {:?}", err);
|
||||||
|
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!("failed login attempt, email does not exist: {}", &req.email);
|
||||||
|
Err(StatusCode::UNAUTHORIZED)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("failed to obtain pooled connection");
|
||||||
|
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct RegisterRequest {
|
struct RegisterRequest {
|
||||||
email: String,
|
email: String,
|
||||||
|
@ -105,10 +181,7 @@ async fn register(State(state): State<AppState>, Json(req): Json<RegisterRequest
|
||||||
uid: user_id,
|
uid: user_id,
|
||||||
email: req.email,
|
email: req.email,
|
||||||
name: req.name,
|
name: req.name,
|
||||||
exp: SystemTime::now()
|
exp: expiration_time(),
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
|
||||||
.expect("time went backwards")
|
|
||||||
.as_secs() + 180 * 24 * 3600,
|
|
||||||
}) {
|
}) {
|
||||||
Ok(token) => Ok(Json(RegisterResponse { token })),
|
Ok(token) => Ok(Json(RegisterResponse { token })),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
Loading…
Reference in a new issue