-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature to send an OTP code via email.
- Loading branch information
Showing
10 changed files
with
266 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,13 @@ services: | |
- NODE_MAX_INSTANCES=4 | ||
- NODE_MAX_SESSION=4 | ||
|
||
# MailHog is an email testing tool for developers. | ||
smtp: | ||
image: mailhog/mailhog | ||
ports: | ||
- "1025:1025" | ||
- "8025:8025" | ||
|
||
# Devconatiner development | ||
development: | ||
build: | ||
|
@@ -52,14 +59,11 @@ services: | |
|
||
environment: | ||
DATABASE_URL: postgresql://vscode:testpassword@db:5432 | ||
# To capture emails use | ||
# sudo apt-get update | ||
# sudo apt-get install -y postfix | ||
# smtp-sink -c -d "tmp/%M." 2525 1000 | ||
SMTP_HOST: localhost | ||
SMTP_PORT: 2525 | ||
SMTP_HOST: smtp | ||
SMTP_PORT: 1025 | ||
SMTP_USERNAME: thisisnotused | ||
SMTP_PASSWORD: thisisnotused | ||
SMTP_TLS_OFF: 'true' | ||
RESET_DOMAIN: http://localhost:9095 | ||
RESET_FROM_EMAIL_ADDRESS: [email protected] | ||
PORT: 9095 | ||
|
@@ -68,6 +72,7 @@ services: | |
FORWARD_URL: whoami | ||
FORWARD_PORT: 80 | ||
REDIRECT_URL: / | ||
ENABLE_EMAIL_OTP: 'true' | ||
|
||
working_dir: /vscode | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
ALTER TABLE sessions DROP COLUMN otp_code; | ||
ALTER TABLE sessions DROP COLUMN otp_code_confirmed; | ||
ALTER TABLE sessions DROP COLUMN otp_code_sent; | ||
ALTER TABLE sessions DROP COLUMN otp_code_attempts; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
ALTER TABLE sessions ADD otp_code INTEGER NOT NULL DEFAULT (random() * 100000 + 1)::int; | ||
ALTER TABLE sessions ADD otp_code_attempts INTEGER NOT NULL DEFAULT 0; | ||
ALTER TABLE sessions ADD otp_code_confirmed BOOLEAN NOT NULL DEFAULT false; | ||
ALTER TABLE sessions ADD otp_code_sent BOOLEAN NOT NULL DEFAULT false; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
use crate::components::forms; | ||
use crate::config; | ||
use crate::custom_error::CustomError; | ||
use crate::layouts; | ||
use actix_web::{http, web, HttpResponse, Result}; | ||
use lettre::Message; | ||
use serde::{Deserialize, Serialize}; | ||
use sqlx::{types::Uuid, PgPool}; | ||
use std::default::Default; | ||
use validator::ValidationErrors; | ||
|
||
#[derive(Serialize, Deserialize, Default)] | ||
pub struct Otp { | ||
pub code: i32, | ||
#[serde(rename = "h-captcha-response")] | ||
pub h_captcha_response: Option<String>, | ||
} | ||
|
||
#[derive(sqlx::FromRow, Debug)] | ||
pub struct Session { | ||
otp_code: i32, | ||
otp_code_attempts: i32, | ||
otp_code_sent: bool, | ||
} | ||
|
||
pub async fn email_otp( | ||
config: web::Data<config::Config>, | ||
session: Option<crate::Session>, | ||
pool: web::Data<PgPool>, | ||
) -> Result<HttpResponse, CustomError> { | ||
if let Some(session) = session { | ||
if let Ok(uuid) = Uuid::parse_str(&session.session_uuid) { | ||
let db_session: Session = sqlx::query_as::<_, Session>( | ||
" | ||
SELECT otp_code, otp_code_attempts, otp_code_sent FROM sessions WHERE session_uuid = $1 | ||
", | ||
) | ||
.bind(uuid) | ||
.fetch_one(pool.get_ref()) // -> Vec<Person> | ||
.await?; | ||
|
||
if !db_session.otp_code_sent { | ||
sqlx::query( | ||
" | ||
UPDATE sessions SET otp_code_sent = true WHERE session_uuid = $1 | ||
", | ||
) | ||
.bind(uuid) | ||
.execute(pool.get_ref()) | ||
.await?; | ||
|
||
if let Some(smtp_config) = &config.smtp_config { | ||
let email = Message::builder() | ||
.from(smtp_config.from_email.clone()) | ||
.to("[email protected]".parse().unwrap()) | ||
.subject("Your confirmation code") | ||
.body( | ||
format!( | ||
" | ||
Your confirmation code is {} | ||
", | ||
db_session.otp_code | ||
) | ||
.trim() | ||
.to_string(), | ||
) | ||
.unwrap(); | ||
|
||
crate::email::send_email(&config, email) | ||
} | ||
} | ||
} | ||
} | ||
|
||
let body = OtpPage { | ||
form: &Otp::default(), | ||
errors: &ValidationErrors::default(), | ||
}; | ||
|
||
Ok(layouts::session_layout("Login", &body.to_string())) | ||
} | ||
|
||
pub async fn process_otp( | ||
config: web::Data<config::Config>, | ||
pool: web::Data<PgPool>, | ||
session: Option<crate::Session>, | ||
form: web::Form<Otp>, | ||
) -> Result<HttpResponse, CustomError> { | ||
if let Some(session) = session { | ||
if let Ok(uuid) = Uuid::parse_str(&session.session_uuid) { | ||
if super::verify_hcaptcha(&config.hcaptcha_config, &form.h_captcha_response).await { | ||
let db_session: Session = sqlx::query_as::<_, Session>( | ||
" | ||
SELECT otp_code, otp_code_attempts, otp_code_sent FROM sessions WHERE session_uuid = $1 | ||
", | ||
) | ||
.bind(uuid) | ||
.fetch_one(pool.get_ref()) // -> Vec<Person> | ||
.await?; | ||
|
||
if db_session.otp_code == form.code { | ||
sqlx::query( | ||
" | ||
UPDATE sessions SET otp_code_confirmed = true WHERE session_uuid = $1 | ||
", | ||
) | ||
.bind(uuid) | ||
.execute(pool.get_ref()) | ||
.await?; | ||
|
||
return Ok(HttpResponse::SeeOther() | ||
.append_header((http::header::LOCATION, config.redirect_url.clone())) | ||
.finish()); | ||
} else { | ||
sqlx::query( | ||
" | ||
UPDATE sessions SET otp_code_attempts = otp_code_attempts + 1 WHERE session_uuid = $1 | ||
" | ||
) | ||
.bind(uuid ) | ||
.execute(pool.get_ref()) | ||
.await?; | ||
|
||
return Ok(HttpResponse::SeeOther() | ||
.append_header((http::header::LOCATION, crate::EMAIL_OTP_URL)) | ||
.finish()); | ||
} | ||
} | ||
} | ||
} | ||
|
||
return Ok(HttpResponse::SeeOther() | ||
.append_header((http::header::LOCATION, crate::SIGN_IN_URL)) | ||
.finish()); | ||
} | ||
|
||
markup::define! { | ||
OtpPage<'a>(form: &'a Otp, | ||
errors: &'a ValidationErrors) { | ||
form.m_authentication[method = "post"] { | ||
|
||
h1 { "Password Reset Request" } | ||
|
||
@forms::NumberInput{ title: "Code", name: "code", value: &form.code.to_string(), help_text: "", errors } | ||
|
||
button.a_button.success[type = "submit"] { "Submit Code" } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.