From 936d9320fec2c749858495fd9f10363e5e2ed6df Mon Sep 17 00:00:00 2001 From: Big-Iron-Cheems <52252627+Big-Iron-Cheems@users.noreply.github.com> Date: Mon, 12 Feb 2024 14:06:32 +0100 Subject: [PATCH] Implement account deletion When a user wants to delete their account from the website, we'll email the corresponding address with a confirmation link. --- pkg/db/accounts.go | 13 ++++++++++++ pkg/web/api/account.go | 48 ++++++++++++++++++++++++++++++++++++++++++ pkg/web/web.go | 2 ++ 3 files changed, 63 insertions(+) diff --git a/pkg/db/accounts.go b/pkg/db/accounts.go index 150e4fa..8e4b268 100644 --- a/pkg/db/accounts.go +++ b/pkg/db/accounts.go @@ -169,6 +169,19 @@ func GetAccountUsername(id ksuid.KSUID) (string, error) { return account.Username, nil } +func DeleteAccount(id ksuid.KSUID) error { + result, err := accounts.DeleteOne(nil, bson.M{"id": id}) + if err != nil { + return err + } + + if result.DeletedCount == 0 { + return errors.New("no account found with the provided ID") + } + + return nil +} + func (acc *Account) PasswordMatches(password string) bool { return bcrypt.CompareHashAndPassword(acc.Password, []byte(password)) == nil } diff --git a/pkg/web/api/account.go b/pkg/web/api/account.go index f791b91..8e39986 100644 --- a/pkg/web/api/account.go +++ b/pkg/web/api/account.go @@ -53,6 +53,7 @@ type accountTimeInfo struct { var changeEmailTokens = make(map[ksuid.KSUID]passwordChangeInfo) var discordLinkTokens = make(map[ksuid.KSUID]accountTimeInfo) var forgotPasswordTokens = make(map[ksuid.KSUID]accountTimeInfo) +var deleteAccountTokens = make(map[ksuid.KSUID]accountTimeInfo) var ts *turnstile.Turnstile @@ -622,3 +623,50 @@ func ForgotPasswordHandler(w http.ResponseWriter, r *http.Request) { core.SendEmail(email, "Forgot password", "To change the password to "+email+" go to https://meteorclient.com/changePassword?token="+token.String()+" . The link is valid for 15 minutes.") core.Json(w, core.J{}) } + +func DeleteAccountHandler(w http.ResponseWriter, r *http.Request) { + // Get account + account, err := db.GetAccount(r) + if err != nil { + core.JsonError(w, "Could not get account.") + return + } + + token := ksuid.New() + deleteAccountTokens[token] = accountTimeInfo{account.ID, time.Now()} + + core.SendEmail(account.Email, "Confirm account deletion", "To delete your account go to https://meteorclient.com/confirmDeleteAccount?token="+token.String()+" . The link is valid for 15 minutes.") + core.Json(w, core.J{}) +} + +func ConfirmDeleteAccountHandler(w http.ResponseWriter, r *http.Request) { + // Validate token + tokenStr := r.URL.Query().Get("token") + + token, err := ksuid.Parse(tokenStr) + if err != nil { + core.JsonError(w, "Invalid token.") + return + } + + info, exists := deleteAccountTokens[token] + if !exists { + core.JsonError(w, "Invalid token.") + return + } + + delete(deleteAccountTokens, token) + + if time.Now().Sub(info.time).Minutes() > 15 { + core.JsonError(w, "Invalid token.") + return + } + + err = db.DeleteAccount(info.accountId) + if err != nil { + core.JsonError(w, err.Error()) + return + } + + core.Json(w, core.J{}) +} diff --git a/pkg/web/web.go b/pkg/web/web.go index 81bbbd5..0fb6283 100644 --- a/pkg/web/web.go +++ b/pkg/web/web.go @@ -113,6 +113,8 @@ func Main() { r.Get("/confirmChangeEmail", api.ConfirmChangeEmailHandlerApi) r.Post("/changePassword", auth.Auth(api.ChangePasswordHandler)) r.Post("/changePasswordToken", api.ChangePasswordTokenHandler) + r.Post("/deleteAccount", auth.Auth(api.DeleteAccountHandler)) + r.Get("/confirmDeleteAccount", api.ConfirmDeleteAccountHandler) }) // /api/online