better-auth 0.10.0

The most comprehensive authentication framework for Rust
Documentation
"use client";

import { useSession, authClient } from "@/lib/auth-client";
import { useRouter } from "next/navigation";
import { useEffect, useState, useCallback } from "react";
import Link from "next/link";

interface SessionItem {
  id: string;
  token: string;
  userId: string;
  expiresAt: string;
  ipAddress?: string;
  userAgent?: string;
}

export default function SessionsPage() {
  const router = useRouter();
  const { data: session, isPending } = useSession();
  const [sessions, setSessions] = useState<SessionItem[]>([]);
  const [loadingSessions, setLoadingSessions] = useState(true);
  const [revoking, setRevoking] = useState<string | null>(null);
  const [revokingAll, setRevokingAll] = useState(false);
  const [error, setError] = useState("");
  const [message, setMessage] = useState("");

  useEffect(() => {
    if (!isPending && !session) {
      router.push("/sign-in");
    }
  }, [isPending, session, router]);

  const fetchSessions = useCallback(async () => {
    setLoadingSessions(true);
    setError("");
    const { data, error: fetchError } = await authClient.listSessions();
    if (fetchError) {
      setError(fetchError.message || "Failed to load sessions");
    } else if (data) {
      setSessions(data as unknown as SessionItem[]);
    }
    setLoadingSessions(false);
  }, []);

  useEffect(() => {
    if (session) {
      fetchSessions();
    }
  }, [session, fetchSessions]);

  async function handleRevokeSession(token: string) {
    setRevoking(token);
    setError("");
    setMessage("");

    const { error: revokeError } = await authClient.revokeSession({
      token,
    });

    if (revokeError) {
      setError(revokeError.message || "Failed to revoke session");
    } else {
      setMessage("Session revoked");
      await fetchSessions();
    }

    setRevoking(null);
  }

  async function handleRevokeOtherSessions() {
    setRevokingAll(true);
    setError("");
    setMessage("");

    const { error: revokeError } = await authClient.revokeOtherSessions();

    if (revokeError) {
      setError(revokeError.message || "Failed to revoke sessions");
    } else {
      setMessage("All other sessions revoked");
      await fetchSessions();
    }

    setRevokingAll(false);
  }

  function isCurrentSession(s: SessionItem) {
    return session?.session?.token === s.token;
  }

  if (isPending) {
    return (
      <div className="container" style={{ marginTop: "4rem" }}>
        <p style={{ textAlign: "center", color: "var(--muted)" }}>
          Loading...
        </p>
      </div>
    );
  }

  if (!session) {
    return null;
  }

  return (
    <div className="container" style={{ marginTop: "2rem" }}>
      <div style={{ marginBottom: "1.5rem" }}>
        <Link href="/dashboard" style={{ fontSize: "0.875rem" }}>
          ← Back to Dashboard
        </Link>
      </div>

      <h1>Active Sessions</h1>
      <p className="muted">
        Manage your active sessions across devices
      </p>

      {error && <div className="error">{error}</div>}
      {message && <div className="success">{message}</div>}

      {loadingSessions ? (
        <p style={{ color: "var(--muted)", textAlign: "center" }}>
          Loading sessions...
        </p>
      ) : (
        <>
          <div className="stack">
            {sessions.map((s) => (
              <div key={s.id} className="card">
                <div
                  style={{
                    display: "flex",
                    justifyContent: "space-between",
                    alignItems: "flex-start",
                  }}
                >
                  <div>
                    {isCurrentSession(s) && (
                      <span className="badge" style={{ marginBottom: "0.5rem", display: "inline-block" }}>
                        Current session
                      </span>
                    )}
                    <div className="info-row" style={{ borderBottom: "none", padding: "0.25rem 0" }}>
                      <span className="info-label" style={{ marginRight: "1rem" }}>
                        IP
                      </span>
                      <span>{s.ipAddress || "Unknown"}</span>
                    </div>
                    <div className="info-row" style={{ borderBottom: "none", padding: "0.25rem 0" }}>
                      <span className="info-label" style={{ marginRight: "1rem" }}>
                        Expires
                      </span>
                      <span style={{ fontSize: "0.75rem" }}>
                        {new Date(s.expiresAt).toLocaleString()}
                      </span>
                    </div>
                    {s.userAgent && (
                      <div
                        style={{
                          fontSize: "0.7rem",
                          color: "var(--muted)",
                          marginTop: "0.25rem",
                          wordBreak: "break-all",
                          maxWidth: "280px",
                        }}
                      >
                        {s.userAgent.length > 80
                          ? s.userAgent.slice(0, 80) + "..."
                          : s.userAgent}
                      </div>
                    )}
                  </div>
                  {!isCurrentSession(s) && (
                    <button
                      className="danger"
                      style={{ width: "auto", fontSize: "0.75rem", padding: "0.375rem 0.75rem" }}
                      onClick={() => handleRevokeSession(s.token)}
                      disabled={revoking === s.token}
                    >
                      {revoking === s.token ? "..." : "Revoke"}
                    </button>
                  )}
                </div>
              </div>
            ))}
          </div>

          {sessions.length > 1 && (
            <button
              className="danger"
              style={{ marginTop: "1rem" }}
              onClick={handleRevokeOtherSessions}
              disabled={revokingAll}
            >
              {revokingAll
                ? "Revoking..."
                : "Revoke All Other Sessions"}
            </button>
          )}
        </>
      )}
    </div>
  );
}