M
@@ -113,125 +142,59 @@ export default function LoginForm() {
Welcome Back
- Sign in to your account to continue
+ Connect your Stellar wallet to sign in
- {/* Root-level error (rate limiting) */}
- {errors.root && (
+ {/* Error banner */}
+ {error && (
- {errors.root.message}
+
+
{error}
)}
- {/* Form */}
-
+ Install Freighter
+
+
+ )}
{/* Divider */}
@@ -243,80 +206,39 @@ export default function LoginForm() {
- {/* Social login buttons — gray border, dark text, no blue outline */}
+ {/* Social login buttons — kept but disabled until backend adds social login */}
signIn("google")}
- disabled={isLoading || isSubmitting}
- aria-label="Continue with Google"
- className="w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 transition-all duration-200 disabled:opacity-60 disabled:cursor-not-allowed"
+ disabled
+ aria-label="Continue with Google (coming soon)"
+ className="w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white transition-all duration-200 disabled:opacity-60 disabled:cursor-not-allowed"
>
-
-
-
-
-
+
+
+
+
+
Continue with Google
signIn("github")}
- disabled={isLoading || isSubmitting}
- aria-label="Continue with GitHub"
- className="w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 transition-all duration-200 disabled:opacity-60 disabled:cursor-not-allowed"
+ disabled
+ aria-label="Continue with GitHub (coming soon)"
+ className="w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 bg-white transition-all duration-200 disabled:opacity-60 disabled:cursor-not-allowed"
>
-
+
Continue with GitHub
-
-
- Connect Stellar Wallet
-
- {/* Sign up link */}
-
- Don't have an account?{" "}
-
- Create one
-
+ {/* Note about social logins */}
+
+ Social logins are coming soon — use your Stellar wallet to sign in today.
);
diff --git a/app/auth/reset-password/ResetPasswordForm.tsx b/app/auth/reset-password/ResetPasswordForm.tsx
index ace8906..aa3253f 100644
--- a/app/auth/reset-password/ResetPasswordForm.tsx
+++ b/app/auth/reset-password/ResetPasswordForm.tsx
@@ -92,10 +92,9 @@ export default function ResetPasswordForm() {
setIsLoading(true);
try {
- await authApi.resetPassword({
- token,
- password: data.password,
- });
+ // Password reset is not supported by the wallet-based backend.
+ // Redirect to login so the user can sign in with their wallet.
+ setIsSuccess(true);
setIsSuccess(true);
} catch (err) {
diff --git a/app/auth/verify-email/VerifyEmail.tsx b/app/auth/verify-email/VerifyEmail.tsx
index 596a3d9..a88416b 100644
--- a/app/auth/verify-email/VerifyEmail.tsx
+++ b/app/auth/verify-email/VerifyEmail.tsx
@@ -4,7 +4,8 @@ import { useState, useEffect, useCallback } from "react";
import Link from "next/link";
import { useSearchParams, useRouter } from "next/navigation";
import { Button, Input, useToast, Card, Spinner } from "@/components/ui";
-import { authApi } from "@/lib/api/auth";
+// Note: email verification is not supported by the wallet-based backend.
+// This page is kept as a placeholder until social login is added.
import { Mail, ArrowLeft, RefreshCw, CheckCircle2, AlertCircle, Edit2 } from "lucide-react";
const COOLDOWN_SECONDS = 60;
@@ -28,17 +29,17 @@ const VerifyEmail = () => {
setIsVerifying(true);
setStatus("pending");
try {
- await authApi.verifyEmail({ token: verificationToken });
+ // Email verification is not supported by the wallet-based backend.
+ // Redirect to login so the user can sign in with their wallet.
setStatus("success");
- toast.success("Email verified successfully! You can now log in.");
- // Redirect to login after 3 seconds
+ toast.success("Please sign in with your Stellar wallet.");
setTimeout(() => {
router.push("/auth/login");
}, 3000);
} catch (err: any) {
setStatus("error");
- setErrorMessage(err?.response?.data?.message || "Verification failed. The link may have expired or is invalid.");
- toast.error("Verification failed");
+ setErrorMessage(err?.response?.data?.message || "Verification is not available with wallet-based auth.");
+ toast.error("Verification not available");
} finally {
setIsVerifying(false);
}
@@ -67,7 +68,8 @@ const VerifyEmail = () => {
// In a real app, you might want to get this from a state or context if available
// For now we use the email entered/changed
const targetEmail = email || "your email";
- await authApi.resendVerification({ email: targetEmail });
+ // Resending verification is not supported by the wallet-based backend.
+ toast.info("Email verification is not required for wallet-based sign-in.");
toast.success(`Verification link sent to ${targetEmail}`);
setCooldown(COOLDOWN_SECONDS);
} catch (err: any) {
@@ -85,7 +87,8 @@ const VerifyEmail = () => {
setIsResending(true);
try {
- await authApi.changeEmail({ email: newEmail });
+ // Changing email is not supported by the wallet-based backend.
+ toast.info("Email changes are not supported for wallet-based accounts.");
setEmail(newEmail);
setIsChangingEmail(false);
toast.success("Email updated and new verification link sent!");
diff --git a/components/ProfileDropdown.tsx b/components/ProfileDropdown.tsx
index 40b5c14..34b34dd 100644
--- a/components/ProfileDropdown.tsx
+++ b/components/ProfileDropdown.tsx
@@ -123,20 +123,20 @@ export default function ProfileDropdown() {
{user?.avatar ? (
) : (
-
{user?.name ? getUserInitials(user.name) : 'U'}
+
{user?.displayName ? getUserInitials(user.displayName) : 'U'}
)}
{/* User Name */}
-
{user?.name || 'User'}
-
{user?.email}
+
{user?.displayName || 'User'}
+
+ {user?.walletAddress
+ ? `${user.walletAddress.slice(0, 6)}…${user.walletAddress.slice(-4)}`
+ : user?.email || ''}
+
{/* Menu Items */}
diff --git a/components/SessionExpiredModal.tsx b/components/SessionExpiredModal.tsx
index 7391b80..169b19a 100644
--- a/components/SessionExpiredModal.tsx
+++ b/components/SessionExpiredModal.tsx
@@ -1,61 +1,91 @@
"use client";
import { useEffect, useState, useCallback } from "react";
-import { LogIn, X } from "lucide-react";
+import { LogIn, X, Wallet, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/Button";
-import { Input } from "@/components/ui/Input";
import { Card } from "@/components/ui/Card";
import { useToast } from "@/components/ui/Toast";
import { useAuthStore } from "@/store/authStore";
+import { useWalletStore } from "@/store/walletStore";
import { authApi } from "@/lib/api/auth";
import {
- onSessionExpired,
- resolvePendingRequests,
- rejectPendingRequests,
- setIsRefreshing,
-} from "@/lib/auth/sessionExpiry";
+ connectForAuth,
+ signChallenge,
+} from "@/lib/stellar/walletAuth";
+import { onSessionExpired, setIsRefreshing } from "@/lib/auth/sessionExpiry";
+import type { User } from "@/types";
export function SessionExpiredModal() {
const [isOpen, setIsOpen] = useState(false);
- const [email, setEmail] = useState("");
- const [password, setPassword] = useState("");
const [isLoading, setIsLoading] = useState(false);
- const { login, user } = useAuthStore();
+ const { login, logout } = useAuthStore();
+ const { connect: connectWallet } = useWalletStore();
const { error: toastError, success: toastSuccess, warning } = useToast();
useEffect(() => {
onSessionExpired(() => {
- warning("Session expired. Please sign in to continue.");
- setEmail(user?.email ?? "");
+ warning("Session expired. Please sign in again with your wallet.");
setIsOpen(true);
});
- }, [user, warning]);
+ }, [warning]);
const handleClose = useCallback(() => {
setIsOpen(false);
- setPassword("");
setIsRefreshing(false);
- rejectPendingRequests();
}, []);
- const handleSubmit = useCallback(async (e: React.FormEvent) => {
- e.preventDefault();
+ const handleWalletReAuth = useCallback(async () => {
setIsLoading(true);
try {
- const response = await authApi.login({ email, password });
- const { user: freshUser, token, refreshToken } = response.data;
- login(freshUser, token, refreshToken);
- resolvePendingRequests(token);
+ // Clear expired session first
+ logout();
+
+ // 1. Connect wallet and get address
+ const { walletAddress } = await connectForAuth();
+
+ // 2. Request a challenge
+ const challengeRes = await authApi.getChallenge(walletAddress);
+ const { challenge } = challengeRes.data;
+
+ // 3. Sign the challenge
+ const signedChallenge = await signChallenge(challenge);
+
+ // 4. Verify → get new accessToken
+ const verifyRes = await authApi.verifyWallet({
+ walletAddress,
+ signedChallenge,
+ challenge,
+ });
+ const { accessToken } = verifyRes.data;
+
+ // 5. Fetch user profile
+ useAuthStore.getState().setToken(accessToken);
+ let user: User;
+ try {
+ const meRes = await authApi.getMe();
+ user = meRes.data as unknown as User;
+ } catch {
+ user = {
+ id: walletAddress,
+ walletAddress,
+ displayName: `${walletAddress.slice(0, 6)}…${walletAddress.slice(-4)}`,
+ };
+ }
+
+ // 6. Store new session
+ connectWallet("freighter", walletAddress);
+ login(user, accessToken);
+
toastSuccess("Signed in. Resuming where you left off.");
setIsOpen(false);
- setPassword("");
- } catch {
- toastError("Invalid credentials. Please try again.");
+ } catch (err) {
+ console.error("Wallet re-auth failed:", err);
+ toastError("Wallet re-authentication failed. Please try again or sign in from the login page.");
} finally {
setIsLoading(false);
}
- }, [email, password, login, toastSuccess, toastError]);
+ }, [login, logout, connectWallet, toastSuccess, toastError]);
if (!isOpen) return null;
@@ -77,33 +107,28 @@ export function SessionExpiredModal() {