gaurv007 commited on
Commit
4bd4c03
·
verified ·
1 Parent(s): b55fbd6

Fix auth redirect URLs: use NEXT_PUBLIC_SITE_URL instead of window.location.origin (fixes localhost in verification emails)

Browse files
web/app/auth/forgot-password/page.tsx CHANGED
@@ -2,6 +2,7 @@
2
 
3
  import { useState } from "react";
4
  import { createClient } from "@/lib/supabase/client";
 
5
  import Link from "next/link";
6
  import { ArrowLeft, Mail, Check } from "lucide-react";
7
 
@@ -18,7 +19,7 @@ export default function ForgotPasswordPage() {
18
  setError("");
19
 
20
  const { error } = await supabase.auth.resetPasswordForEmail(email, {
21
- redirectTo: `${window.location.origin}/auth/reset-password`,
22
  });
23
 
24
  if (error) { setError(error.message); }
@@ -33,7 +34,7 @@ export default function ForgotPasswordPage() {
33
 
34
  const { error } = await supabase.auth.signInWithOtp({
35
  email,
36
- options: { emailRedirectTo: `${window.location.origin}/auth/callback` },
37
  });
38
 
39
  if (error) { setError(error.message); }
 
2
 
3
  import { useState } from "react";
4
  import { createClient } from "@/lib/supabase/client";
5
+ import { getBaseUrl } from "@/lib/auth-url";
6
  import Link from "next/link";
7
  import { ArrowLeft, Mail, Check } from "lucide-react";
8
 
 
19
  setError("");
20
 
21
  const { error } = await supabase.auth.resetPasswordForEmail(email, {
22
+ redirectTo: `${getBaseUrl()}/auth/reset-password`,
23
  });
24
 
25
  if (error) { setError(error.message); }
 
34
 
35
  const { error } = await supabase.auth.signInWithOtp({
36
  email,
37
+ options: { emailRedirectTo: `${getBaseUrl()}/auth/callback` },
38
  });
39
 
40
  if (error) { setError(error.message); }
web/app/auth/login/page.tsx CHANGED
@@ -2,6 +2,7 @@
2
 
3
  import { useState } from "react";
4
  import { createClient } from "@/lib/supabase/client";
 
5
  import Link from "next/link";
6
  import { ArrowLeft, Mail } from "lucide-react";
7
 
@@ -24,7 +25,8 @@ export default function LoginPage() {
24
  if (!email) { setError("Enter your email first."); return; }
25
  setLoading(true); setError("");
26
  const { error } = await supabase.auth.signInWithOtp({
27
- email, options: { emailRedirectTo: `${window.location.origin}/auth/callback` },
 
28
  });
29
  if (error) { setError(error.message); }
30
  else { setMagicSent(true); }
@@ -33,7 +35,8 @@ export default function LoginPage() {
33
 
34
  async function handleOAuth(provider: "google" | "github") {
35
  await supabase.auth.signInWithOAuth({
36
- provider, options: { redirectTo: `${window.location.origin}/auth/callback` },
 
37
  });
38
  }
39
 
 
2
 
3
  import { useState } from "react";
4
  import { createClient } from "@/lib/supabase/client";
5
+ import { getBaseUrl } from "@/lib/auth-url";
6
  import Link from "next/link";
7
  import { ArrowLeft, Mail } from "lucide-react";
8
 
 
25
  if (!email) { setError("Enter your email first."); return; }
26
  setLoading(true); setError("");
27
  const { error } = await supabase.auth.signInWithOtp({
28
+ email,
29
+ options: { emailRedirectTo: `${getBaseUrl()}/auth/callback` },
30
  });
31
  if (error) { setError(error.message); }
32
  else { setMagicSent(true); }
 
35
 
36
  async function handleOAuth(provider: "google" | "github") {
37
  await supabase.auth.signInWithOAuth({
38
+ provider,
39
+ options: { redirectTo: `${getBaseUrl()}/auth/callback` },
40
  });
41
  }
42
 
web/app/auth/signup/page.tsx CHANGED
@@ -2,7 +2,9 @@
2
 
3
  import { useState } from "react";
4
  import { createClient } from "@/lib/supabase/client";
 
5
  import Link from "next/link";
 
6
 
7
  export default function SignupPage() {
8
  const [email, setEmail] = useState("");
@@ -14,14 +16,21 @@ export default function SignupPage() {
14
 
15
  async function handleSignup(e: React.FormEvent) {
16
  e.preventDefault(); setLoading(true); setError("");
17
- const { error } = await supabase.auth.signUp({ email, password });
 
 
 
 
 
 
18
  if (error) { setError(error.message); } else { setDone(true); }
19
  setLoading(false);
20
  }
21
 
22
  async function handleOAuth(provider: "google" | "github") {
23
  await supabase.auth.signInWithOAuth({
24
- provider, options: { redirectTo: `${window.location.origin}/auth/callback` },
 
25
  });
26
  }
27
 
@@ -30,7 +39,7 @@ export default function SignupPage() {
30
  <div className="min-h-screen flex items-center justify-center bg-white px-4">
31
  <div className="text-center max-w-xs">
32
  <h2 className="text-xl font-semibold">Check your email</h2>
33
- <p className="mt-2 text-sm text-zinc-500">We sent a confirmation link to {email}.</p>
34
  <Link href="/auth/login" className="mt-4 inline-block text-sm text-zinc-600 hover:underline">Back to login</Link>
35
  </div>
36
  </div>
@@ -41,15 +50,17 @@ export default function SignupPage() {
41
  <div className="min-h-screen flex items-center justify-center bg-white px-4">
42
  <div className="w-full max-w-sm">
43
  <div className="mb-8">
44
- <Link href="/" className="text-sm text-zinc-400 hover:text-zinc-600">← Back</Link>
 
 
45
  <h1 className="mt-4 text-xl font-semibold">Create an account</h1>
46
  </div>
47
 
48
  <div className="space-y-2.5">
49
  <button onClick={() => handleOAuth("google")}
50
- className="w-full px-4 py-2.5 border border-zinc-200 rounded-md text-sm hover:bg-zinc-50">Continue with Google</button>
51
  <button onClick={() => handleOAuth("github")}
52
- className="w-full px-4 py-2.5 border border-zinc-200 rounded-md text-sm hover:bg-zinc-50">Continue with GitHub</button>
53
  </div>
54
 
55
  <div className="flex items-center gap-3 my-6">
@@ -58,12 +69,12 @@ export default function SignupPage() {
58
 
59
  <form onSubmit={handleSignup} className="space-y-3">
60
  <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required
61
- placeholder="Email" className="w-full px-3 py-2.5 border border-zinc-200 rounded-md text-sm focus:outline-none focus:border-zinc-400" />
62
  <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} required minLength={8}
63
- placeholder="Password (8+ characters)" className="w-full px-3 py-2.5 border border-zinc-200 rounded-md text-sm focus:outline-none focus:border-zinc-400" />
64
  {error && <p className="text-xs text-red-600">{error}</p>}
65
  <button type="submit" disabled={loading}
66
- className="w-full bg-zinc-900 text-white py-2.5 rounded-md text-sm font-medium hover:bg-zinc-800 disabled:opacity-40">
67
  {loading ? "Creating..." : "Create account"}
68
  </button>
69
  </form>
 
2
 
3
  import { useState } from "react";
4
  import { createClient } from "@/lib/supabase/client";
5
+ import { getBaseUrl } from "@/lib/auth-url";
6
  import Link from "next/link";
7
+ import { ArrowLeft } from "lucide-react";
8
 
9
  export default function SignupPage() {
10
  const [email, setEmail] = useState("");
 
16
 
17
  async function handleSignup(e: React.FormEvent) {
18
  e.preventDefault(); setLoading(true); setError("");
19
+ const { error } = await supabase.auth.signUp({
20
+ email,
21
+ password,
22
+ options: {
23
+ emailRedirectTo: `${getBaseUrl()}/auth/callback`,
24
+ },
25
+ });
26
  if (error) { setError(error.message); } else { setDone(true); }
27
  setLoading(false);
28
  }
29
 
30
  async function handleOAuth(provider: "google" | "github") {
31
  await supabase.auth.signInWithOAuth({
32
+ provider,
33
+ options: { redirectTo: `${getBaseUrl()}/auth/callback` },
34
  });
35
  }
36
 
 
39
  <div className="min-h-screen flex items-center justify-center bg-white px-4">
40
  <div className="text-center max-w-xs">
41
  <h2 className="text-xl font-semibold">Check your email</h2>
42
+ <p className="mt-2 text-sm text-zinc-500">We sent a confirmation link to <span className="font-medium text-zinc-700">{email}</span>.</p>
43
  <Link href="/auth/login" className="mt-4 inline-block text-sm text-zinc-600 hover:underline">Back to login</Link>
44
  </div>
45
  </div>
 
50
  <div className="min-h-screen flex items-center justify-center bg-white px-4">
51
  <div className="w-full max-w-sm">
52
  <div className="mb-8">
53
+ <Link href="/" className="inline-flex items-center gap-1.5 text-sm text-zinc-400 hover:text-zinc-600">
54
+ <ArrowLeft className="w-3.5 h-3.5" /> Back
55
+ </Link>
56
  <h1 className="mt-4 text-xl font-semibold">Create an account</h1>
57
  </div>
58
 
59
  <div className="space-y-2.5">
60
  <button onClick={() => handleOAuth("google")}
61
+ className="w-full px-4 py-2.5 border border-zinc-200 rounded-lg text-sm hover:bg-zinc-50 transition-colors">Continue with Google</button>
62
  <button onClick={() => handleOAuth("github")}
63
+ className="w-full px-4 py-2.5 border border-zinc-200 rounded-lg text-sm hover:bg-zinc-50 transition-colors">Continue with GitHub</button>
64
  </div>
65
 
66
  <div className="flex items-center gap-3 my-6">
 
69
 
70
  <form onSubmit={handleSignup} className="space-y-3">
71
  <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required
72
+ placeholder="Email" className="w-full px-3 py-2.5 border border-zinc-200 rounded-lg text-sm focus:outline-none focus:border-zinc-400" />
73
  <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} required minLength={8}
74
+ placeholder="Password (8+ characters)" className="w-full px-3 py-2.5 border border-zinc-200 rounded-lg text-sm focus:outline-none focus:border-zinc-400" />
75
  {error && <p className="text-xs text-red-600">{error}</p>}
76
  <button type="submit" disabled={loading}
77
+ className="w-full bg-zinc-900 text-white py-2.5 rounded-lg text-sm font-medium hover:bg-zinc-800 disabled:opacity-40 transition-colors">
78
  {loading ? "Creating..." : "Create account"}
79
  </button>
80
  </form>
web/lib/auth-url.ts ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Get the base URL for auth redirects.
3
+ * Uses NEXT_PUBLIC_SITE_URL in production, falls back to window.location.origin for local dev.
4
+ */
5
+ export function getBaseUrl(): string {
6
+ if (process.env.NEXT_PUBLIC_SITE_URL) {
7
+ return process.env.NEXT_PUBLIC_SITE_URL;
8
+ }
9
+ if (typeof window !== "undefined") {
10
+ return window.location.origin;
11
+ }
12
+ return "http://localhost:3000";
13
+ }