-
-
Notifications
You must be signed in to change notification settings - Fork 4
(SP: 3) [Backend][Frontend][UI] Implement secure authentication lifecycle with email verification and password recovery #137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| "use client"; | ||
| import { Link } from "@/i18n/routing"; | ||
| import { useState } from "react"; | ||
| import { useSearchParams } from "next/navigation"; | ||
| import { Button } from "@/components/ui/button"; | ||
|
|
||
| export default function ForgotPasswordPage() { | ||
| const searchParams = useSearchParams(); | ||
| const returnTo = searchParams.get("returnTo"); | ||
|
|
||
| const [loading, setLoading] = useState(false); | ||
| const [email, setEmail] = useState(""); | ||
| const [submitted, setSubmitted] = useState(false); | ||
| const [error, setError] = useState<string | null>(null); | ||
|
|
||
| async function onSubmit(e: React.FormEvent<HTMLFormElement>) { | ||
| e.preventDefault(); | ||
| setLoading(true); | ||
| setError(null); | ||
|
|
||
| const res = await fetch("/api/auth/password-reset", { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ email }), | ||
| }); | ||
|
|
||
| setLoading(false); | ||
|
|
||
| if (!res.ok) { | ||
| setError("Something went wrong. Please try again."); | ||
| return; | ||
| } | ||
|
|
||
| setSubmitted(true); | ||
| } | ||
|
|
||
| return ( | ||
| <div className="mx-auto max-w-sm py-12"> | ||
| <h1 className="mb-6 text-2xl font-semibold"> | ||
| Forgot password | ||
| </h1> | ||
|
|
||
| {submitted ? ( | ||
| <div className="rounded-md border border-green-400 bg-green-50 p-4 text-sm text-green-800"> | ||
| <p> | ||
| If an account for{" "} | ||
| <strong>{email}</strong> exists, we’ve sent a | ||
| password reset link. | ||
| </p> | ||
|
|
||
| <p className="mt-2"> | ||
| Please check your inbox and follow the | ||
| instructions to reset your password. | ||
| </p> | ||
|
|
||
| <Link | ||
| href={ | ||
| returnTo | ||
| ? `/login?returnTo=${encodeURIComponent(returnTo)}` | ||
| : "/login" | ||
| } | ||
| className="mt-4 inline-block underline" | ||
| > | ||
| Back to login | ||
| </Link> | ||
|
Comment on lines
+56
to
+65
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, find the login page and related files
find frontend -type f -name "*login*" | head -20Repository: DevLoversTeam/devlovers.net Length of output: 53 🏁 Script executed: # Search for returnTo usage across the codebase
rg -n "returnTo" --type ts --type tsx frontend/Repository: DevLoversTeam/devlovers.net Length of output: 98 🏁 Script executed: # Also check if there's any validation or redirect logic in api routes or components
rg -n "redirect\|encodeURI\|pathname" --type ts --type tsx frontend/app/ -A 2 -B 2 | head -100Repository: DevLoversTeam/devlovers.net Length of output: 98 🏁 Script executed: # Search for returnTo without file type restriction
rg -n "returnTo" frontend/Repository: DevLoversTeam/devlovers.net Length of output: 1924 🏁 Script executed: # Find and read the login page
find frontend -type f -name "*login*" -path "*/page.*"Repository: DevLoversTeam/devlovers.net Length of output: 53 🏁 Script executed: # Search for validation or redirect patterns
rg -n "redirect\|validate\|allowedDomains" frontend/ -A 3 | head -150Repository: DevLoversTeam/devlovers.net Length of output: 53 🏁 Script executed: # Read the login page to see the returnTo handling
cat -n frontend/app/[locale]/login/page.tsx | sed -n '1,110p'Repository: DevLoversTeam/devlovers.net Length of output: 4197 🏁 Script executed: # Check if there's any validation utility or helper
rg -n "validate\|isValidUrl\|isSafeUrl\|allowed" frontend/ -B 2 -A 2 | head -100Repository: DevLoversTeam/devlovers.net Length of output: 53 Fix open redirect vulnerability in login redirect logic. The login page directly redirects to the Implement validation to ensure function isSafeRedirectUrl(url: string, locale: string): boolean {
if (!url) return false;
// Allow relative paths starting with /
if (url.startsWith('/')) {
// Ensure it doesn't contain protocol indicators for external redirects
return !url.includes('://');
}
return false;
}Then use: |
||
| </div> | ||
| ) : ( | ||
| <form onSubmit={onSubmit} className="space-y-4"> | ||
| <p className="text-sm text-gray-600"> | ||
| Enter your email address and we’ll send | ||
| you a link to reset your password. | ||
| </p> | ||
|
|
||
| <input | ||
| type="email" | ||
| required | ||
| placeholder="Email" | ||
| value={email} | ||
| onChange={e => setEmail(e.target.value)} | ||
| className="w-full rounded border px-3 py-2" | ||
| /> | ||
|
|
||
| {error && ( | ||
| <p className="text-sm text-red-600"> | ||
| {error} | ||
| </p> | ||
| )} | ||
|
|
||
| <Button | ||
| type="submit" | ||
| disabled={loading} | ||
| className="w-full" | ||
| > | ||
| {loading | ||
| ? "Sending reset link..." | ||
| : "Send reset link"} | ||
| </Button> | ||
| </form> | ||
| )} | ||
|
|
||
| {!submitted && ( | ||
| <p className="mt-4 text-sm text-gray-600"> | ||
| Remembered your password?{" "} | ||
| <Link | ||
| href={ | ||
| returnTo | ||
| ? `/login?returnTo=${encodeURIComponent(returnTo)}` | ||
| : "/login" | ||
| } | ||
| className="underline" | ||
| > | ||
| Log in | ||
| </Link> | ||
| </p> | ||
| )} | ||
| </div> | ||
| ); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.