sign in with email
This commit is contained in:
parent
c581d8d0a3
commit
d5aceb60b4
7 changed files with 68 additions and 27 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -441,7 +441,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "argon2-creds"
|
||||
version = "0.2.1"
|
||||
source = "git+https://github.com/realaravinth/argon2-creds?branch=master#c899181a1bdb65134cc329f26c5dd3601d89fc45"
|
||||
source = "git+https://github.com/realaravinth/argon2-creds?branch=master#3330634832150ad2af1c31768ed3b84e69f4c8ad"
|
||||
dependencies = [
|
||||
"ammonia",
|
||||
"derive_builder 0.10.2",
|
||||
|
|
|
@ -40,6 +40,7 @@ futures = "0.3.14"
|
|||
|
||||
sqlx = { version = "0.4.0", features = [ "runtime-actix-rustls", "postgres", "time", "offline" ] }
|
||||
argon2-creds = { branch = "master", git = "https://github.com/realaravinth/argon2-creds"}
|
||||
#argon2-creds = { version="*", path = "../../argon2-creds/" }
|
||||
config = "0.11"
|
||||
validator = { version = "0.13", features = ["derive"]}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ use actix_identity::Identity;
|
|||
use actix_web::http::header;
|
||||
use actix_web::{web, HttpResponse, Responder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
//use futures::{future::TryFutureExt, join};
|
||||
|
||||
use super::mcaptcha::get_random;
|
||||
use crate::errors::*;
|
||||
|
@ -60,7 +61,9 @@ pub mod runners {
|
|||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct Login {
|
||||
pub username: String,
|
||||
// login accepts both username and email under "username field"
|
||||
// TODO update all instances where login is used
|
||||
pub login: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
|
@ -70,28 +73,59 @@ pub mod runners {
|
|||
}
|
||||
|
||||
/// returns Ok(()) when everything checks out and the user is authenticated. Erros otherwise
|
||||
pub async fn login_runner(payload: &Login, data: &AppData) -> ServiceResult<()> {
|
||||
pub async fn login_runner(payload: Login, data: &AppData) -> ServiceResult<String> {
|
||||
use argon2_creds::Config;
|
||||
use sqlx::Error::RowNotFound;
|
||||
|
||||
let rec = sqlx::query_as!(
|
||||
Password,
|
||||
r#"SELECT password FROM mcaptcha_users WHERE name = ($1)"#,
|
||||
&payload.username,
|
||||
)
|
||||
.fetch_one(&data.db)
|
||||
.await;
|
||||
|
||||
match rec {
|
||||
Ok(s) => {
|
||||
if Config::verify(&s.password, &payload.password)? {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ServiceError::WrongPassword)
|
||||
}
|
||||
let verify = |stored: &str, received: &str| {
|
||||
if Config::verify(&stored, &received)? {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ServiceError::WrongPassword)
|
||||
}
|
||||
};
|
||||
|
||||
if payload.login.contains("@") {
|
||||
#[derive(Clone, Debug)]
|
||||
struct EmailLogin {
|
||||
name: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
let email_fut = sqlx::query_as!(
|
||||
EmailLogin,
|
||||
r#"SELECT name, password FROM mcaptcha_users WHERE email = ($1)"#,
|
||||
&payload.login,
|
||||
)
|
||||
.fetch_one(&data.db)
|
||||
.await;
|
||||
|
||||
match email_fut {
|
||||
Ok(s) => {
|
||||
verify(&s.password, &payload.password)?;
|
||||
Ok(s.name)
|
||||
}
|
||||
|
||||
Err(RowNotFound) => Err(ServiceError::AccountNotFound),
|
||||
Err(_) => Err(ServiceError::InternalServerError),
|
||||
}
|
||||
} else {
|
||||
let username_fut = sqlx::query_as!(
|
||||
Password,
|
||||
r#"SELECT password FROM mcaptcha_users WHERE name = ($1)"#,
|
||||
&payload.login,
|
||||
)
|
||||
.fetch_one(&data.db)
|
||||
.await;
|
||||
|
||||
match username_fut {
|
||||
Ok(s) => {
|
||||
verify(&s.password, &payload.password)?;
|
||||
Ok(payload.login)
|
||||
}
|
||||
Err(RowNotFound) => Err(ServiceError::AccountNotFound),
|
||||
Err(_) => Err(ServiceError::InternalServerError),
|
||||
}
|
||||
Err(RowNotFound) => Err(ServiceError::UsernameNotFound),
|
||||
Err(_) => Err(ServiceError::InternalServerError),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,8 +215,8 @@ async fn login(
|
|||
payload: web::Json<runners::Login>,
|
||||
data: AppData,
|
||||
) -> ServiceResult<impl Responder> {
|
||||
runners::login_runner(&payload, &data).await?;
|
||||
id.remember(payload.into_inner().username);
|
||||
let username = runners::login_runner(payload.into_inner(), &data).await?;
|
||||
id.remember(username);
|
||||
Ok(HttpResponse::Ok())
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,9 @@ async fn auth_works() {
|
|||
let (_, _, signin_resp) = register_and_signin(NAME, EMAIL, PASSWORD).await;
|
||||
let cookies = get_cookie!(signin_resp);
|
||||
|
||||
// Sign in with email
|
||||
signin(EMAIL, PASSWORD).await;
|
||||
|
||||
// 2. check if duplicate username is allowed
|
||||
let msg = Register {
|
||||
username: NAME.into(),
|
||||
|
@ -76,7 +79,7 @@ async fn auth_works() {
|
|||
|
||||
// 3. sigining in with non-existent user
|
||||
let mut creds = Login {
|
||||
username: "nonexistantuser".into(),
|
||||
login: "nonexistantuser".into(),
|
||||
password: msg.password.clone(),
|
||||
};
|
||||
bad_post_req_test(
|
||||
|
@ -84,13 +87,13 @@ async fn auth_works() {
|
|||
PASSWORD,
|
||||
ROUTES.auth.login,
|
||||
&creds,
|
||||
ServiceError::UsernameNotFound,
|
||||
ServiceError::AccountNotFound,
|
||||
StatusCode::NOT_FOUND,
|
||||
)
|
||||
.await;
|
||||
|
||||
// 4. trying to signin with wrong password
|
||||
creds.username = NAME.into();
|
||||
creds.login = NAME.into();
|
||||
creds.password = NAME.into();
|
||||
|
||||
bad_post_req_test(
|
||||
|
|
|
@ -50,6 +50,8 @@ pub enum ServiceError {
|
|||
WrongPassword,
|
||||
#[display(fmt = "Username not found")]
|
||||
UsernameNotFound,
|
||||
#[display(fmt = "Account not found")]
|
||||
AccountNotFound,
|
||||
|
||||
/// when the value passed contains profainity
|
||||
#[display(fmt = "Can't allow profanity in usernames")]
|
||||
|
@ -114,6 +116,7 @@ impl ResponseError for ServiceError {
|
|||
ServiceError::NotAUrl => StatusCode::BAD_REQUEST,
|
||||
ServiceError::WrongPassword => StatusCode::UNAUTHORIZED,
|
||||
ServiceError::UsernameNotFound => StatusCode::NOT_FOUND,
|
||||
ServiceError::AccountNotFound => StatusCode::NOT_FOUND,
|
||||
|
||||
ServiceError::ProfainityError => StatusCode::BAD_REQUEST,
|
||||
ServiceError::BlacklistError => StatusCode::BAD_REQUEST,
|
||||
|
|
|
@ -131,7 +131,7 @@ pub async fn signin(name: &str, password: &str) -> (Arc<Data>, Login, ServiceRes
|
|||
|
||||
// 2. signin
|
||||
let creds = Login {
|
||||
username: name.into(),
|
||||
login: name.into(),
|
||||
password: password.into(),
|
||||
};
|
||||
let signin_resp = test::call_service(
|
||||
|
|
|
@ -45,7 +45,7 @@ const registerUser = async (e: Event) => {
|
|||
);
|
||||
const passwordCheck = passwordCheckElement.value;
|
||||
if (password != passwordCheck) {
|
||||
return alert("passwords don't match, check again!");
|
||||
return createError("passwords don't match, check again!");
|
||||
}
|
||||
|
||||
let exists = await userExists();
|
||||
|
|
Loading…
Reference in a new issue