Browse Source

FEATURE: Email verification code lifetime is functional now

master
parent
commit
100fb4b5b2
  1. 13
      pages/register.html
  2. 8
      src/db/verification.go
  3. 19
      src/server/endpoints.go
  4. 6
      src/server/validation.go

13
pages/register.html

@ -45,7 +45,6 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="verificationModalLabel">Email Verification</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Enter the verification code sent to your email address</p>
@ -56,7 +55,6 @@
<p class="text-danger"><span id="error-message-modal"></span></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="verifyButton" onclick="verify();">Verify</button>
</div>
</div>
@ -85,7 +83,15 @@ async function verify() {
}
}
let verificationSent = false;
async function register() {
if (verificationSent) {
// No need to resend registration data, reopen verification modal,
showVerificationModal();
return;
}
let emailInput = document.getElementById("input-email");
if (!emailInput.reportValidity()) {
return;
@ -106,9 +112,10 @@ async function register() {
};
let response = await postNewUser(postData);
if (response.ok) {
if (response.ok) {
let json = await response.json();
if (json.confirm_email) {
verificationSent = true;
// Open email confirmation modal
showVerificationModal();
} else {

8
src/db/verification.go

@ -1,6 +1,8 @@
package db
import "database/sql"
import (
"database/sql"
)
type Verification struct {
ID uint64 `json:"id"`
@ -55,10 +57,10 @@ func (db *DB) GetVerification(id uint64) (*Verification, error) {
return verification, nil
}
// Returns the first email verification by email
// Returns the last email verification by email
func (db *DB) GetVerificationByEmail(email string) (*Verification, error) {
rows, err := db.Query(
"SELECT * FROM verifications WHERE email=?",
"SELECT * FROM verifications WHERE (email=?) ORDER BY life_seconds DESC",
email,
)
if err != nil {

19
src/server/endpoints.go

@ -108,7 +108,7 @@ func (s *Server) EndpointUserCreate(w http.ResponseWriter, req *http.Request) {
}
// Send email verification message
verification, err := GenerateVerificationCode(s.db, user.Email)
verification, err := GenerateVerificationCode(s.db, user.Email, 5, uint64(time.Hour.Seconds()))
if err != nil {
logger.Error("[Server][EndpointUserCreate] Failed to generate verification code for %s: %s", user.Email, err)
http.Error(w, "Failed to generate confirmation code", http.StatusInternalServerError)
@ -120,7 +120,7 @@ func (s *Server) EndpointUserCreate(w http.ResponseWriter, req *http.Request) {
email.NewEmail(
s.config.Verification.Emailer.User,
"Dela: Email verification",
fmt.Sprintf("Your email verification code: <b>%s</b>\nPlease, verify your email in %f hours.\nThis email was specified during Dela account creation. Ignore this message if it wasn't you", verification.Code, float32(verification.LifeSeconds)/3600),
fmt.Sprintf("<p>Your email verification code is: <b>%s</b></p><p>Please, verify your email in %.1f hours. Your account will be deleted after some time without verified status.</p><p>This email was specified during Dela account creation. Ignore this message if it wasn't you.</p>", verification.Code, float32(verification.LifeSeconds)/3600),
[]string{user.Email},
),
)
@ -130,8 +130,8 @@ func (s *Server) EndpointUserCreate(w http.ResponseWriter, req *http.Request) {
return
}
// Autodelete user account if email was not verified in time
time.AfterFunc(time.Second*time.Duration(verification.LifeSeconds), func() {
// Autodelete user account after some more time if email was not verified in time
time.AfterFunc((time.Second*time.Duration(verification.LifeSeconds))*5, func() {
err = s.db.DeleteUnverifiedUserClean(user.Email)
if err != nil {
logger.Error("[Server][EndpointUserCreate] Failed to autodelete unverified user %s: %s", user.Email, err)
@ -173,8 +173,8 @@ func (s *Server) EndpointUserVerify(w http.ResponseWriter, req *http.Request) {
// Retrieve user
user, err := s.db.GetUser(answer.Email)
if err != nil {
logger.Error("[Server][EndpointUserVerify] Failed to retrieve information on \"%s\": %s", answer.Email, err)
http.Error(w, "Failed to get user information", http.StatusInternalServerError)
// Most likely already deleted this user's account
http.Error(w, "Account no longer exists, try registering again", http.StatusInternalServerError)
return
}
@ -193,6 +193,13 @@ func (s *Server) EndpointUserVerify(w http.ResponseWriter, req *http.Request) {
return
}
// Check for lifetime
if time.Now().Unix() > int64(dbCode.IssuedUnix+dbCode.LifeSeconds) {
// Expired!
http.Error(w, "This code is expired!", http.StatusForbidden)
return
}
// All's good!
err = s.db.UserSetEmailConfirmed(user.Email)
if err != nil {

6
src/server/validation.go

@ -127,12 +127,12 @@ func GetLoginFromReq(req *http.Request) string {
/*
Generates a new verification code for given email with 8-digit numeric code,
current issue time and one hour lifetime.
current issue time and provided life time.
Inserts newly created email verification into database.
*/
func GenerateVerificationCode(dbase *db.DB, email string) (*db.Verification, error) {
func GenerateVerificationCode(dbase *db.DB, email string, length uint, lifeTimeSeconds uint64) (*db.Verification, error) {
verification := db.NewVerification(
email, misc.GenerateNumericCode(8), uint64(time.Now().Unix()), uint64(time.Hour.Seconds()),
email, misc.GenerateNumericCode(length), uint64(time.Now().Unix()), lifeTimeSeconds,
)
err := dbase.CreateVerification(*verification)

Loading…
Cancel
Save