Skip to content

Commit

Permalink
feat(auth): implement resend of verification email
Browse files Browse the repository at this point in the history
  • Loading branch information
H1ghBre4k3r committed Sep 1, 2023
1 parent da67bb5 commit 200b479
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 15 deletions.
6 changes: 5 additions & 1 deletion src/contexts/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use cfg_if::cfg_if;
use leptos::*;

use crate::functions::{
Login, LoginResult, Logout, Register, RegistrationResult, VerificationResult, Verify,
Login, LoginResult, Logout, Register, RegistrationResult, ResendVerification,
VerificationResult, Verify,
};

cfg_if! {
Expand All @@ -18,6 +19,7 @@ pub struct AuthContext {
pub register: Action<Register, Result<RegistrationResult, ServerFnError>>,
pub logout: Action<Logout, Result<(), ServerFnError>>,
pub verify: Action<Verify, Result<VerificationResult, ServerFnError>>,
pub resend_verification_email: Action<ResendVerification, Result<(), ServerFnError>>,
pub user: Resource<(usize, usize, usize), Result<Option<String>, ServerFnError>>,
}

Expand All @@ -27,6 +29,7 @@ impl AuthContext {
let logout = create_server_action::<Logout>(cx);
let register = create_server_action::<Register>(cx);
let verify = create_server_action::<Verify>(cx);
let resend_verification_email = create_server_action::<ResendVerification>(cx);

let user = create_resource(
cx,
Expand All @@ -45,6 +48,7 @@ impl AuthContext {
logout,
register,
verify,
resend_verification_email,
user,
}
}
Expand Down
46 changes: 35 additions & 11 deletions src/functions/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,30 @@ use serde::{Deserialize, Serialize};

cfg_if! {
if #[cfg(feature = "ssr")] {
use std::error::Error;
use std::collections::BTreeMap;
use actix_identity::IdentityExt;
use crate::hooks::use_identity;
use crate::utils::password::hash_password;
use crate::model::{User, LoginError, Session};
use crate::services::{mail::Mail, jwt};

fn create_jwt(username: &str) -> Result<String, Box<dyn Error>> {
let mut claims = BTreeMap::new();
claims.insert("sub".into(), username.to_string());
jwt::sign(claims)
}

fn send_verification_mail(username: String, email: String, token: String) -> Result<(), Box<dyn Error>> {

let mail = Mail {
subject: Some("Registration Mail".into()),
recipient: email,
content: Some(format!("Hey {username}! \nThank you for registering! To complete your registration, please use the following link: https://aoc.inf-cau.de/verify?token={token}"))
};

mail.send()
}
}
}

Expand Down Expand Up @@ -50,9 +68,7 @@ pub async fn register(
}

// create JWT for verification mail
let mut claims = BTreeMap::new();
claims.insert("sub".into(), username.clone());
let token_str = match jwt::sign(claims) {
let token = match create_jwt(&username) {
Ok(token) => token,
Err(e) => {
tracing::error!("failed to create JWT: {e:#?}");
Expand All @@ -75,13 +91,7 @@ pub async fn register(
return Ok(e);
};

let mail = Mail {
subject: Some("Registration Mail".into()),
recipient: email,
content: Some(format!("Hey {username}! \nThank you for registering! To complete your registration, please use the following link: http://localhost:3000/verify?token={token_str}"))
};

if mail.send().is_err() {
if send_verification_mail(username, email, token).is_err() {
return Ok(RegistrationResult::InternalServerError);
}

Expand All @@ -105,7 +115,7 @@ impl Display for LoginResult {
Ok => f.write_str("Login Successful"),
InternalServerError => f.write_str("Internal Server Error"),
WrongCredentials => f.write_str("Wrong Credentials"),
VerifyEmail => f.write_str("Verify your Email bevore logging in"),
VerifyEmail => f.write_str("Verify your Email before logging in"),
AlreadyLoggedIn => f.write_str("You are already logged in"),
}
}
Expand Down Expand Up @@ -197,3 +207,17 @@ pub async fn verify_user(cx: Scope, token: String) -> Result<VerificationResult,

Ok(VerificationResult::Ok)
}

#[server(ResendVerification, "/api")]
pub async fn resend_verification_mail(cx: Scope, username: String) -> Result<(), ServerFnError> {
let Some(user) = User::get_by_username(&username).await else {
return Ok(());
};

let Ok(token) = create_jwt(&username) else {
return Ok(());
};

let _ = send_verification_mail(username, user.email, token);
Ok(())
}
42 changes: 39 additions & 3 deletions src/views/login.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use leptos::*;
use leptos_router::ActionForm;

use crate::{functions::LoginResult, hooks::use_auth};
use crate::{
functions::{LoginResult, ResendVerification},
hooks::use_auth,
};

#[component]
pub fn LoginView(cx: Scope) -> impl IntoView {
Expand Down Expand Up @@ -30,6 +33,17 @@ pub fn LoginView(cx: Scope) -> impl IntoView {

let is_ok = move || matches!(result(), Some(LoginResult::Ok));

let need_to_verify_email = move || matches!(result(), Some(LoginResult::VerifyEmail));

let (username, set_username) = create_signal(cx, "".to_string());
let (password, set_password) = create_signal(cx, "".to_string());

let resend_verification_email = move |_| {
auth.resend_verification_email.dispatch(ResendVerification {
username: username(),
});
};

view! { cx,
<Transition
fallback=move || ()>
Expand All @@ -55,15 +69,37 @@ pub fn LoginView(cx: Scope) -> impl IntoView {
>
{message()}
</div>
<Show
when=need_to_verify_email
fallback=|_| view! {cx, <></>}>
<a
href="#"
on:click=resend_verification_email
>"Resend Email"</a>
</Show>
</Show>
<h1>"Login"</h1>
<label>
<span>"Username"</span>
<input type="text" name="username" required/>
<input
type="text"
name="username"
prop:value=username
on:input=move |ev| {
set_username(event_target_value(&ev));
}
required/>
</label>
<label>
<span>"Password"</span>
<input type="password" name="password" required/>
<input
type="password"
name="password"
prop:value=password
on:input=move |ev| {
set_password(event_target_value(&ev));
}
required/>
</label>
<button type="submit" class="primary">"Login"</button>
</ActionForm>
Expand Down

0 comments on commit 200b479

Please sign in to comment.