chore: remove recovery flow
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"mal/internal/templates"
|
"mal/internal/templates"
|
||||||
@@ -66,35 +65,3 @@ func (h *Handler) HandleLogout(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (h *Handler) HandleLoginPage(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) HandleLoginPage(w http.ResponseWriter, r *http.Request) {
|
||||||
templates.Login(rateLimitErrorFromQuery(r), "").Render(r.Context(), w)
|
templates.Login(rateLimitErrorFromQuery(r), "").Render(r.Context(), w)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) HandleRecoverPage(w http.ResponseWriter, r *http.Request) {
|
|
||||||
templates.Recover(rateLimitErrorFromQuery(r), "", "").Render(r.Context(), w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) HandleRecover(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if err := r.ParseForm(); err != nil {
|
|
||||||
templates.Recover("Something went wrong. Please try again.", "", "").Render(r.Context(), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
username := r.FormValue("username")
|
|
||||||
recoveryKey := r.FormValue("recovery_key")
|
|
||||||
newPassword := r.FormValue("new_password")
|
|
||||||
|
|
||||||
if username == "" || recoveryKey == "" || newPassword == "" {
|
|
||||||
templates.Recover("Unable to recover account with those details.", username, recoveryKey).Render(r.Context(), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
newRecoveryKey, err := h.authService.RecoverAccount(r.Context(), username, recoveryKey, newPassword)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, ErrInvalidRecoveryKey) || errors.Is(err, ErrInvalidPassword) {
|
|
||||||
templates.Recover("Unable to recover account with those details.", username, recoveryKey).Render(r.Context(), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
templates.Recover("Something went wrong. Please try again.", username, recoveryKey).Render(r.Context(), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
templates.RecoveryComplete(newRecoveryKey).Render(r.Context(), w)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -68,13 +68,6 @@ func NewRouter(cfg Config) http.Handler {
|
|||||||
middleware.RateLimitAuth(middleware.VerifyOrigin(http.HandlerFunc(authHandler.HandleLogin))).ServeHTTP(w, r)
|
middleware.RateLimitAuth(middleware.VerifyOrigin(http.HandlerFunc(authHandler.HandleLogin))).ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
mux.HandleFunc("/recover", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if r.Method == http.MethodGet {
|
|
||||||
authHandler.HandleRecoverPage(w, r)
|
|
||||||
} else {
|
|
||||||
middleware.RateLimitAuth(middleware.VerifyOrigin(http.HandlerFunc(authHandler.HandleRecover))).ServeHTTP(w, r)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
mux.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) {
|
||||||
middleware.VerifyOrigin(http.HandlerFunc(authHandler.HandleLogout)).ServeHTTP(w, r)
|
middleware.VerifyOrigin(http.HandlerFunc(authHandler.HandleLogout)).ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func RequireAuth(next http.Handler) http.Handler {
|
|||||||
func RequireGlobalAuth(next http.Handler) http.Handler {
|
func RequireGlobalAuth(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Allow unauthenticated access to auth pages, search, and static files
|
// Allow unauthenticated access to auth pages, search, and static files
|
||||||
if r.URL.Path == "/login" || r.URL.Path == "/recover" ||
|
if r.URL.Path == "/login" ||
|
||||||
strings.HasPrefix(r.URL.Path, "/static/") || strings.HasPrefix(r.URL.Path, "/dist/") ||
|
strings.HasPrefix(r.URL.Path, "/static/") || strings.HasPrefix(r.URL.Path, "/dist/") ||
|
||||||
r.URL.Path == "/search" || r.URL.Path == "/api/search" || r.URL.Path == "/api/search-quick" ||
|
r.URL.Path == "/search" || r.URL.Path == "/api/search" || r.URL.Path == "/api/search-quick" ||
|
||||||
r.URL.Path == "/" {
|
r.URL.Path == "/" {
|
||||||
|
|||||||
@@ -20,62 +20,6 @@ templ Login(formError string, username string) {
|
|||||||
<p class="mt-2 text-xs text-(--danger)" role="alert" aria-live="polite">{ formError }</p>
|
<p class="mt-2 text-xs text-(--danger)" role="alert" aria-live="polite">{ formError }</p>
|
||||||
}
|
}
|
||||||
</form>
|
</form>
|
||||||
<p class="mt-5 mb-0 text-center text-sm text-(--text-muted)">
|
|
||||||
Lost access? <a class="text-(--accent)" href="/recover">Recover account</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
templ Recover(formError string, username string, recoveryKey string) {
|
|
||||||
@Layout("Recover account", false) {
|
|
||||||
<div class="w-full max-w-xl">
|
|
||||||
<div class="mx-auto w-full bg-(--panel) p-6">
|
|
||||||
<h2 class="m-0 text-2xl">Recover account</h2>
|
|
||||||
<p class="my-3 mb-5 text-sm text-(--text-muted)">Enter your username, recovery key, and a new password.</p>
|
|
||||||
<form action="/recover" method="POST" class="grid gap-4">
|
|
||||||
<div class="grid gap-1">
|
|
||||||
<label for="username">Username / Email</label>
|
|
||||||
<input class="h-10 border border-transparent bg-(--surface-search) px-3 text-(--text) transition-colors duration-120 focus:border-(--surface-search-focus-border) focus:outline-none" type="text" id="username" name="username" required placeholder="you@example.com" value={ username }/>
|
|
||||||
</div>
|
|
||||||
<div class="grid gap-1">
|
|
||||||
<label for="recovery_key">Recovery key</label>
|
|
||||||
<input class="h-10 border border-transparent bg-(--surface-search) px-3 text-(--text) transition-colors duration-120 focus:border-(--surface-search-focus-border) focus:outline-none" type="text" id="recovery_key" name="recovery_key" required value={ recoveryKey }/>
|
|
||||||
</div>
|
|
||||||
<div class="grid gap-1">
|
|
||||||
<label for="new_password">New password</label>
|
|
||||||
<input class="h-10 border border-transparent bg-(--surface-search) px-3 text-(--text) transition-colors duration-120 focus:border-(--surface-search-focus-border) focus:outline-none" type="password" id="new_password" name="new_password" required placeholder="Minimum 12 chars"/>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="h-10 cursor-pointer border-0 bg-(--accent) text-sm font-semibold text-(--text-on-accent) hover:brightness-95">Reset password</button>
|
|
||||||
if formError != "" {
|
|
||||||
<p class="mt-2 text-xs text-(--danger)" role="alert" aria-live="polite">{ formError }</p>
|
|
||||||
}
|
|
||||||
</form>
|
|
||||||
<p class="mt-5 mb-0 text-center text-sm text-(--text-muted)">
|
|
||||||
Remembered your password? <a class="text-(--accent)" href="/login">Sign in</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
templ RecoveryComplete(newRecoveryKey string) {
|
|
||||||
@Layout("Recovery complete", false) {
|
|
||||||
<div class="w-full max-w-xl">
|
|
||||||
<div class="mx-auto w-full bg-(--panel) p-6">
|
|
||||||
<h2>Account recovered</h2>
|
|
||||||
<p class="my-3 mb-5 text-sm text-(--text-muted)">Your password was reset and your recovery key was rotated.</p>
|
|
||||||
<div class="grid gap-2">
|
|
||||||
<p class="m-0 break-all border border-(--surface-search-focus-border) bg-(--surface-search) p-3 text-sm text-(--text)" id="recovery-complete-key">{ newRecoveryKey }</p>
|
|
||||||
<button type="button" class="min-w-0 justify-self-start border border-(--surface-search-focus-border) bg-(--surface-search) px-3 py-1.5 text-xs leading-none text-(--text) hover:bg-(--surface-input-focus)" onclick="copyRecoveryKey('recovery-complete-key', 'recovery-complete-feedback')">Copy key</button>
|
|
||||||
</div>
|
|
||||||
<p class="mt-2 min-h-5 text-xs leading-normal text-(--text-faint)" id="recovery-complete-feedback" aria-live="polite"></p>
|
|
||||||
<p class="m-0 text-xs leading-normal text-(--text-faint)">Replace your old recovery key with this one.</p>
|
|
||||||
<p class="mt-5 mb-0 text-center text-sm text-(--text-muted)">
|
|
||||||
<a href="/login" class="inline-flex min-h-10 items-center justify-center border border-(--surface-search-focus-border) bg-(--surface-search) px-4 text-(--text) no-underline hover:bg-(--panel-soft) hover:no-underline">Go to login</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ templ Layout(title string, showHeader bool) {
|
|||||||
<script src="/dist/discover.js" defer></script>
|
<script src="/dist/discover.js" defer></script>
|
||||||
<script src="/dist/anime.js" defer></script>
|
<script src="/dist/anime.js" defer></script>
|
||||||
<script src="/dist/timezone.js" defer></script>
|
<script src="/dist/timezone.js" defer></script>
|
||||||
<script src="/dist/auth.js" defer></script>
|
|
||||||
<script src="/dist/player.js" defer></script>
|
<script src="/dist/player.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="min-h-screen bg-(--bg) text-(--text) font-(--font) text-sm leading-normal">
|
<body class="min-h-screen bg-(--bg) text-(--text) font-(--font) text-sm leading-normal">
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
export {}
|
|
||||||
|
|
||||||
function copyRecoveryKey(keyElementId: string, feedbackElementId: string): void {
|
|
||||||
const keyElement = document.getElementById(keyElementId)
|
|
||||||
const feedbackElement = document.getElementById(feedbackElementId)
|
|
||||||
|
|
||||||
if (!keyElement || !feedbackElement) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = keyElement.textContent || ''
|
|
||||||
navigator.clipboard
|
|
||||||
.writeText(key)
|
|
||||||
.then((): void => {
|
|
||||||
feedbackElement.textContent = 'Recovery key copied.'
|
|
||||||
})
|
|
||||||
.catch((): void => {
|
|
||||||
feedbackElement.textContent = 'Copy failed. Select and copy manually.'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function confirmDangerAction(message: string): boolean {
|
|
||||||
return window.confirm(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
;(window as Window & { copyRecoveryKey?: typeof copyRecoveryKey; confirmDangerAction?: typeof confirmDangerAction }).copyRecoveryKey = copyRecoveryKey
|
|
||||||
;(window as Window & { copyRecoveryKey?: typeof copyRecoveryKey; confirmDangerAction?: typeof confirmDangerAction }).confirmDangerAction = confirmDangerAction
|
|
||||||
Reference in New Issue
Block a user