AgentSkillsCN

frontend-supabase-auth

当您需要实施身份验证、路由守卫,或进行用户会话管理时,可使用此技能。

SKILL.md
--- frontmatter
name: frontend-supabase-auth
description: Use when implementing authentication, route guards, or user session management

Frontend: Supabase Authentication

Authentication is handled entirely through TanStack Router's beforeLoad guards with Supabase session management. Never implement auth checks in components or create custom auth stores.

Core Principles

  1. Route-level authentication only - Use TanStack Router's async beforeLoad
  2. Supabase manages sessions - Never create custom auth stores
  3. Fresh session checks - Always use supabase.auth.getSession() (async)
  4. Preserve redirect paths - Maintain user's intended destination
  5. Security-critical queries - Direct database checks for whitelist/membership

Authentication Pattern

Always check authentication in route files using beforeLoad:

tsx
export const Route = createFileRoute("/_protected")({
    beforeLoad: async ({ location }) => {
        // 1. Check for valid session
        const {
            data: { session },
        } = await supabase.auth.getSession();

        if (!session) {
            // 2. Preserve path + search params for post-login redirect
            const redirectPath = `${location.pathname}${location.search ? `?${new URLSearchParams(location.search).toString()}` : ""}`;

            throw redirect({
                to: "/login",
                search: { redirect: redirectPath },
            });
        }

        // 3. Additional checks (whitelist, membership, etc.)
    },
});

Sign Out Pattern

Use Supabase's signOut() method with proper error handling:

tsx
const handleSignOut = async () => {
    try {
        const { error } = await supabase.auth.signOut();
        if (error) throw error;

        message.success("Signed out successfully");
        navigate({ to: "/login" });
    } catch (error) {
        console.error("Error signing out:", error);
        message.error("Failed to sign out. Please try again.");
    }
};

Key Rules

  • beforeLoad is the ONLY security boundary - Components assume access is granted
  • No cached session data - Always query fresh for security-critical checks
  • Direct database queries - Use supabase.from() for whitelist/membership verification
  • Redirect on failure - Never render protected content on auth failure
  • Async pattern - getSession() must be awaited

Common Patterns

Whitelist Check:

tsx
const { data: profile } = await supabase
    .from("profiles")
    .select("whitelisted")
    .eq("id", session.user.id)
    .single();

if (!profile?.whitelisted) {
    throw redirect({ to: "/not-whitelisted" });
}

Membership Guard:

tsx
const { data: membership } = await supabase
    .from("organization_members")
    .select("*")
    .eq("user_id", session.user.id)
    .eq("organization_id", organizationId)
    .maybeSingle();

if (!membership) {
    throw redirect({ to: "/access-denied" });
}

Reference

For implementation examples: .claude/skills/frontend-supabase-auth/examples.md For anti-patterns: .claude/skills/frontend-supabase-auth/reference.md