Skip to content

Commit

Permalink
add forgot password (mckaywrigley#1178)
Browse files Browse the repository at this point in the history
  • Loading branch information
mckaywrigley authored Jan 15, 2024
1 parent 7a620f9 commit e2edd4a
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 4 deletions.
7 changes: 6 additions & 1 deletion app/auth/callback/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ import { NextResponse } from "next/server"
export async function GET(request: Request) {
const requestUrl = new URL(request.url)
const code = requestUrl.searchParams.get("code")
const next = requestUrl.searchParams.get("next")

if (code) {
const cookieStore = cookies()
const supabase = createClient(cookieStore)
await supabase.auth.exchangeCodeForSession(code)
}

return NextResponse.redirect(requestUrl.origin)
if (next) {
return NextResponse.redirect(requestUrl.origin + next)
} else {
return NextResponse.redirect(requestUrl.origin)
}
}
32 changes: 29 additions & 3 deletions app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export default async function Login({
const signUp = async (formData: FormData) => {
"use server"

const origin = headers().get("origin")
const email = formData.get("email") as string
const password = formData.get("password") as string
const cookieStore = cookies()
Expand All @@ -80,6 +79,24 @@ export default async function Login({
// return redirect("/login?message=Check email to continue sign in process")
}

const handleResetPassword = async (formData: FormData) => {
"use server"

const origin = headers().get("origin")
const email = formData.get("email") as string
const cookieStore = cookies()
const supabase = createClient(cookieStore)

const { error } = await supabase.auth.resetPasswordForEmail(email, {
redirectTo: `${origin}/auth/callback?next=/login/password`
})

if (error) {
console.error(error)
return redirect("/login?message=Could not reset password")
}
}

return (
<div className="flex w-full flex-1 flex-col justify-center gap-2 px-8 sm:max-w-md">
<form
Expand All @@ -92,7 +109,7 @@ export default async function Login({
Email
</Label>
<Input
className="mb-6 rounded-md border bg-inherit px-4 py-2"
className="mb-3 rounded-md border bg-inherit px-4 py-2"
name="email"
placeholder="you@example.com"
required
Expand All @@ -106,7 +123,6 @@ export default async function Login({
type="password"
name="password"
placeholder="••••••••"
required
/>

<Button className="mb-2 rounded-md bg-blue-700 px-4 py-2 text-white">
Expand All @@ -120,6 +136,16 @@ export default async function Login({
Sign Up
</Button>

<div className="text-muted-foreground mt-1 flex justify-center text-sm">
<span className="mr-1">Forgot your password?</span>
<button
formAction={handleResetPassword}
className="text-primary ml-1 underline hover:opacity-80"
>
Reset
</button>
</div>

{searchParams?.message && (
<p className="bg-foreground/10 text-foreground mt-4 p-4 text-center">
{searchParams.message}
Expand Down
30 changes: 30 additions & 0 deletions app/login/password/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client"

import { ChangePassword } from "@/components/utility/change-password"
import { supabase } from "@/lib/supabase/browser-client"
import { useRouter } from "next/navigation"
import { useEffect, useState } from "react"

export default function ChangePasswordPage() {
const [loading, setLoading] = useState(true)

const router = useRouter()

useEffect(() => {
;(async () => {
const session = (await supabase.auth.getSession()).data.session

if (!session) {
router.push("/login")
} else {
setLoading(false)
}
})()
}, [])

if (loading) {
return null
}

return <ChangePassword />
}
63 changes: 63 additions & 0 deletions components/utility/change-password.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"use client"

import { supabase } from "@/lib/supabase/browser-client"
import { useRouter } from "next/navigation"
import { FC, useState } from "react"
import { Button } from "../ui/button"
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle
} from "../ui/dialog"
import { Input } from "../ui/input"

interface ChangePasswordProps {}

export const ChangePassword: FC<ChangePasswordProps> = () => {
const router = useRouter()

const [newPassword, setNewPassword] = useState("")
const [confirmPassword, setConfirmPassword] = useState("")

const handleResetPassword = async () => {
if (!newPassword) return alert("Please enter your new password.")

await supabase.auth.updateUser({ password: newPassword })

alert("Password changed successfully.")

router.push("/login")
}

return (
<Dialog open={true}>
<DialogContent className="h-[240px] w-[400px] p-4">
<DialogHeader>
<DialogTitle>Change Password</DialogTitle>
</DialogHeader>

<Input
id="password"
placeholder="New Password"
type="password"
value={newPassword}
onChange={e => setNewPassword(e.target.value)}
/>

<Input
id="confirmPassword"
placeholder="Confirm New Password"
type="password"
value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)}
/>

<DialogFooter>
<Button onClick={handleResetPassword}>Confirm Change</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}

0 comments on commit e2edd4a

Please sign in to comment.