Supabase Auth ile React'te Kullanıcı Yönetimi: Kayıt, Giriş ve Rol Sistemi

Supabase Auth ile React'te kullanıcı kaydı, giriş, oturum yönetimi ve rol bazlı yetkilendirme nasıl yapılır? Protected routes, onAuthStateChange ve...

Sorun: “Kullanıcı Girişi Nasıl Yapılır?”

Her web uygulamasının ilk ihtiyacı: kullanıcı kaydı, giriş, çıkış ve oturum yönetimi. Supabase Auth bunu 5 dakikada çözer — ama production’da doğru yapılması gerekir.

Supabase Client Kurulumu

npm install @supabase/supabase-js
// src/lib/supabase.ts
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey);

⚠️ VITE_ prefix’i client-side’da erişilebilir yapar. service_role key’ini asla client’a koymayın.

Auth Context — Global Oturum Yönetimi

// src/contexts/AuthContext.tsx
import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { Session, User } from '@supabase/supabase-js';
import { supabase } from '../lib/supabase';

interface AuthContextType {
  session: Session | null;
  user: User | null;
  loading: boolean;
  signUp: (email: string, password: string) => Promise<void>;
  signIn: (email: string, password: string) => Promise<void>;
  signOut: () => Promise<void>;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export function AuthProvider({ children }: { children: ReactNode }) {
  const [session, setSession] = useState<Session | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // 1. Mevcut oturumu al
    supabase.auth.getSession().then(({ data: { session } }) => {
      setSession(session);
      setLoading(false);
    });

    // 2. Oturum değişikliklerini dinle
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (_event, session) => {
        setSession(session);
      }
    );

    // 3. Cleanup
    return () => subscription.unsubscribe();
  }, []);

  const signUp = async (email: string, password: string) => {
    const { error } = await supabase.auth.signUp({ email, password });
    if (error) throw error;
  };

  const signIn = async (email: string, password: string) => {
    const { error } = await supabase.auth.signInWithPassword({ email, password });
    if (error) throw error;
  };

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

  return (
    <AuthContext.Provider value={{
      session,
      user: session?.user ?? null,
      loading,
      signUp,
      signIn,
      signOut,
    }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth must be used within AuthProvider');
  return context;
}

Kullanım: Giriş Formu

// src/components/LoginForm.tsx
import { useState } from 'react';
import { useAuth } from '../contexts/AuthContext';

export function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const { signIn } = useAuth();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');
    try {
      await signIn(email, password);
      // Başarılı — onAuthStateChange otomatik günceller
    } catch (err: any) {
      setError(err.message);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={email} 
        onChange={e => setEmail(e.target.value)} 
        placeholder="E-posta" required />
      <input type="password" value={password} 
        onChange={e => setPassword(e.target.value)} 
        placeholder="Şifre" required />
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <button type="submit">Giriş Yap</button>
    </form>
  );
}

Protected Routes (Korumalı Sayfalar)

Giriş yapmamış kullanıcıları login sayfasına yönlendirin:

// src/components/ProtectedRoute.tsx
import { Navigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';

export function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { session, loading } = useAuth();

  if (loading) return <div>Yükleniyor...</div>;
  if (!session) return <Navigate to="/login" replace />;

  return <>{children}</>;
}
// App.tsx
<AuthProvider>
  <Routes>
    <Route path="/login" element={<LoginForm />} />
    <Route path="/dashboard" element={
      <ProtectedRoute>
        <Dashboard />
      </ProtectedRoute>
    } />
  </Routes>
</AuthProvider>

Rol Sistemi: user_metadata vs app_metadata

Supabase’de roller iki yerde saklanabilir:

AlanKim değiştirebilir?Kullanım
user_metadataKullanıcı kendisiProfil bilgileri
app_metadataSadece backend (service_role)Roller ve yetkiler
-- Kullanıcıya admin rolü atama (Supabase SQL Editor)
UPDATE auth.users 
SET raw_app_meta_data = raw_app_meta_data || '{"role": "admin"}'::jsonb
WHERE email = 'admin@example.com';
// React'te rol kontrolü
const { user } = useAuth();
const role = user?.app_metadata?.role || 'viewer';

if (role !== 'admin') {
  return <p>Bu sayfaya erişim yetkiniz yok.</p>;
}

RLS ile Auth Entegrasyonu

Supabase Auth + RLS birlikte kullanıldığında güvenlik veritabanı seviyesine iner:

-- Kullanıcı sadece kendi verilerini görsün
CREATE POLICY "Users see own data" ON user_notes
  FOR SELECT
  USING (user_id = auth.uid());

-- Admin her şeyi görsün
CREATE POLICY "Admins see all" ON user_notes
  FOR SELECT
  USING (
    (auth.jwt() -> 'app_metadata' ->> 'role') = 'admin'
  );

onAuthStateChange Event’leri

onAuthStateChange şu eventleri yakalar:

EventNe Zaman?
SIGNED_INKullanıcı giriş yaptı
SIGNED_OUTKullanıcı çıkış yaptı
TOKEN_REFRESHEDJWT token yenilendi
USER_UPDATEDKullanıcı bilgileri güncellendi
PASSWORD_RECOVERYŞifre sıfırlama linki tıklandı

Dikkat Edilecekler

  1. loading state’i: İlk render’da getSession() tamamlanmadan Protected Route yönlendirme yapmamalı.
  2. Token refresh: Supabase otomatik yapar ama onAuthStateChange dinlemelisiniz.
  3. anon key güvenliği: Client-side’da güvenle kullanılabilir — RLS ile korunur.
  4. Şifre politikası: Supabase varsayılan minimum 6 karakter. Dashboard’dan değiştirilebilir.

İlgili Yazılar


Bu rehber Supabase Auth Docs ve React Context API dokümantasyonuna dayanarak hazırlanmıştır.

📚 İlgili Yazılar