---
title: "Supabase Auth ile React'te Kullanıcı Yönetimi: Kayıt, Giriş ve Rol Sistemi"
description: "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..."
date: 2026-04-15
category: react-supabase
tags: ["supabase", "react", "authentication", "typescript", "session", "rol-yonetimi"]
url: https://mikroerp.dev/blog/supabase-auth-react-kullanici-yonetimi/
---

## 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

```bash
npm install @supabase/supabase-js
```

```typescript
// 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

```tsx
// 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

```tsx
// 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:

```tsx
// 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}</>;
}
```

```tsx
// 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:

| Alan | Kim değiştirebilir? | Kullanım |
|------|---------------------|----------|
| `user_metadata` | Kullanıcı kendisi | Profil bilgileri |
| `app_metadata` | Sadece backend (service_role) | **Roller ve yetkiler** |

```sql
-- 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';
```

```tsx
// 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:

```sql
-- 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:

| Event | Ne Zaman? |
|-------|----------|
| `SIGNED_IN` | Kullanıcı giriş yaptı |
| `SIGNED_OUT` | Kullanıcı çıkış yaptı |
| `TOKEN_REFRESHED` | JWT token yenilendi |
| `USER_UPDATED` | Kullanı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

- [Supabase RLS Yetkilendirme](/blog/supabase-rls-cok-katmanli-yetkilendirme/) — 9 rol, 8 şube RLS
- [SECURITY DEFINER vs INVOKER](/blog/postgresql-security-definer-vs-invoker/) — Fonksiyon güvenliği
- [.env Güvenli Yönetimi](/blog/env-dosyalari-guvenli-yonetim/) — Key sızıntısı önleme
- [React Query + Supabase Cache](/blog/react-query-supabase-cache-stratejisi/) — Auth + data cache

---

*Bu rehber [Supabase Auth Docs](https://supabase.com/docs/guides/auth) ve [React Context API](https://react.dev/reference/react/createContext) dokümantasyonuna dayanarak hazırlanmıştır.*