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