Supabase Auth + Flutter (Google ile Giriş & Şifre Sıfırlama) — Entegrasyon Rehberi#
Bu rehber, Flutter mobil uygulaması için Supabase Auth kullanarak:
Google ile giriş (OAuth)
Şifre sıfırlama (password recovery) akışlarını derin bağlantı (deep link) ile uçtan uca anlatır. Aşağıdaki örnekler, senin projen için uyarlanmıştır:
Project URL:
https://fajdjdlecqokklpdgnfl.supabase.co**Redirect Deep Links **:
- Google Login Callback:
intronauts://login-callback - Password Reset Callback:
intronauts://reset-password
- Google Login Callback:
Not: Anahtar/şifre gibi gizli değerleri paylaşmayın; örneklerde
YOUR_SUPABASE_ANON_KEYşeklinde yer tutucular kullanılmıştır.
0) Özet Akışlar#
Google ile Giriş (Mobil)#
- Flutter:
signInWithOAuth(Provider.google, redirectTo: 'intronauts://login-callback') - Tarayıcı → Google hesabı seçimi
- Supabase callback →
intronauts://login-callback?access_token=...&refresh_token=... - Flutter deep link’i yakalar → token’ı session olarak set eder → kullanıcıyı uygulama içinde oturum açar.
Şifre Sıfırlama (Mobil)#
- Flutter/Postman:
POST /auth/v1/recover?redirect_to=intronauts://reset-password(body: email) - Kullanıcıya e-posta gelir (SMTP/Resend ile)
- Kullanıcı maildeki linke tıklar →
intronauts://reset-password?access_token=...&type=recovery - Flutter deep link’i yakalar → kullanıcı yeni şifreyi girer →
auth.updateUser({ password })
1) Supabase & Google & SMTP Ön Hazırlık (Yapildi)#
Supabase → Auth Ayarları#
Authentication → Providers → Google
- Enable: açık
- Client ID / Secret: Google Cloud’dan aldığın Web application kimlik bilgileri
- Callback URL (Supabase tarafı sabit):
https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/callback
Authentication → URL Configuration → Redirect URLs
Aşağıdaki deep link adreslerini Add URL ile ekle:
intronauts://login-callbackintronauts://reset-password
Authentication → Settings → Email (SMTP)
Custom SMTP: ON
Örn. Resend SMTP:
- Host:
smtp.resend.com - Port:
587 - Username:
resend - Password: Resend SMTP Key
- Sender email:
noreply@intronauts.online(domain verified olmalı)
- Host:
Google Cloud (OAuth Client — Web)#
Application type: Web application
Authorized redirect URIs:
https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/callback
Çıkan Client ID ve Client Secret’ı Supabase Google provider ekranına gir.
Resend (SMTP) — Deliverability#
- Domains: Domainini ekle → Verified olmalı (SPF/DKIM/Return-Path).
- Click Tracking kapatmanı öneririz (transactional e-posta için).
Kritik Bilgiler: Resend API Key, SMTP şifresi ve domain yapılandırması için güvenli dosyaya erişin
2) Deep Link (Mobil) Kurulumu (Yapilacak)#
Scheme olarak intronauts kullandığımızı varsayıyoruz.
Android — AndroidManifest.xml#
<!-- android/app/src/main/AndroidManifest.xml -->
<activity ... >
<!-- ... -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="intronauts" android:host="login-callback" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="intronauts" android:host="reset-password" />
</intent-filter>
</activity>iOS — Info.plist#
<!-- ios/Runner/Info.plist -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>intronauts</string>
</array>
</dict>
</array>Flutter — Deep Link Dinleme#
pubspec.yaml:
dependencies:
uni_links: ^0.5.1
supabase_flutter: ^2.6.0 # (uygun en güncel sürümü kullan)import 'dart:async';
import 'package:flutter/material.dart';
import 'package:uni_links/uni_links.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
class DeepLinkListener extends StatefulWidget {
final Widget child;
const DeepLinkListener({super.key, required this.child});
@override
State<DeepLinkListener> createState() => _DeepLinkListenerState();
}
class _DeepLinkListenerState extends State<DeepLinkListener> {
StreamSubscription? _sub;
@override
void initState() {
super.initState();
_sub = linkStream.listen((String? link) async {
if (link == null) return;
final uri = Uri.parse(link);
// Google Login Callback
if (uri.host == 'login-callback' && uri.queryParameters['access_token'] != null) {
final accessToken = uri.queryParameters['access_token']!;
final refreshToken = uri.queryParameters['refresh_token'] ?? '';
await Supabase.instance.client.auth.setSession(
RefreshTokenSession(accessToken: accessToken, refreshToken: refreshToken),
);
// Kullanıcıyı uygulama ana ekranına al
if (mounted) Navigator.pushReplacementNamed(context, '/home');
return;
}
// Password Recovery Callback
if (uri.host == 'reset-password' && uri.queryParameters['access_token'] != null) {
final recoveryToken = uri.queryParameters['access_token']!;
// Token’ı session olarak set et (gerekirse)
await Supabase.instance.client.auth.setSession(
RecoveryTokenSession(accessToken: recoveryToken),
);
if (mounted) Navigator.pushNamed(context, '/set-new-password', arguments: recoveryToken);
}
}, onError: (_) {});
}
@override
void dispose() {
_sub?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) => widget.child;
}Not: Supabase bazen
#fragmentyerine query param döndürebilir.uni_linksile gelen string’iUri.parseüzerindequeryParametersile okuyorsun — bu mobil deep link’te doğru yöntemdir.
3) Google ile Giriş — Backend/Frontend Detayları#
Mantık (Backend’te olan)#
- Flutter
signInWithOAuth(google)çağırır → Supabase Google’a yönlendirir. - Google onayı sonrası Supabase JWT üretir → redirect_to adresine
access_token,refresh_tokenekleyerek döner.
Flutter (Frontend) Gerekenler#
redirect_todeep link’i whitelist’e ekli (Supabase).uni_linksile deep link yakalanır →access_token/refresh_tokenalınır →auth.setSession(...).
Flutter Kod (Buton + Çağrı)#
final supabase = Supabase.instance.client;
Future<void> signInWithGoogle() async {
final res = await supabase.auth.signInWithOAuth(
Provider.google,
redirectTo: 'intronauts://login-callback',
);
// res.data.url → tarayıcıda açılır; Flutter SDK otomatik yönlendirir.
}cURL (Tarayıcı testi için)#
** Postman Koleksiyonu:**
# Google'a yönlendiren URL (tarayıcıda açılır)
open "https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/authorize?provider=google&redirect_to=intronauts://login-callback"Login Sonrası Kullanıcıyı Doğrulama (cURL)#
curl -X GET "https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/user" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "apikey: YOUR_SUPABASE_ANON_KEY"4) Şifre Sıfırlama — Backend/Frontend Detayları#
Mantık (Backend’te olan)#
POST /auth/v1/recoverçağrılır → Supabase SMTP sağlayıcına (Resend) e-posta bırakır.- E-postada şu formatta link olur:
https://.../auth/v1/verify?token=<...>&type=recovery&redirect_to=intronauts://reset-password - Kullanıcı linke tıklayınca Supabase token’ı doğrular → redirect_to ile uygulamaya döndürür.
E-Posta İçeriği (Örnek)#
From: Intronauts Destek Ekibi <noreply@intronauts.online>
Subject: Reset Your Password
<h2>Reset Password</h2>
<p>Follow this link to reset the password for your user:</p>
<p><a href="https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/verify?token=...&type=recovery&redirect_to=intronauts://reset-password">Reset Password</a></p>Postman/cURL — Reset Mail Gönderme (Doğru Biçim)#
Dikkat:
redirect_toURL query parametresi olmalı.
** Postman Koleksiyonu:**
curl -X POST \
"https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/recover?redirect_to=intronauts://reset-password" \
-H "apikey: YOUR_SUPABASE_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{ "email": "kullanici@mail.com" }'Flutter — Reset Mail Gönderme#
await Supabase.instance.client.auth.resetPasswordForEmail(
'kullanici@mail.com',
redirectTo: 'intronauts://reset-password',
);Flutter — Yeni Şifre Kaydetme Ekranı#
Future<void> submitNewPassword(String newPassword) async {
// recovery token deep link ile session’a set edilmiş olmalı
final res = await Supabase.instance.client.auth.updateUser(
UserAttributes(password: newPassword),
);
if (res.user != null) {
// başarı → kullanıcıyı login sayfasına veya home’a yönlendir
}
}cURL — Yeni Şifre Kaydetme (Token ile)#
curl -X POST "https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/user" \
-H "Authorization: Bearer <RECOVERY_ACCESS_TOKEN>" \
-H "apikey: YOUR_SUPABASE_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{ "password": "YeniSifre123!" }'5) Test Planı (Mobil)#
Google Login
- Uygulamada “Google ile Giriş” butonuna bas → tarayıcı açılır → Google onayı → app’e döner.
- Deep link yakalanır →
auth.setSession(...)→/homeekranı açılır. supabase.auth.getUser()ile kullanıcıyı al.
Şifre Sıfırlama
- Uygulamada mail adresini gir →
resetPasswordForEmail(redirectTo: 'intronauts://reset-password') - Mail gelir (Resend Logs’ta görünmeli).
- Linke tıkla → app açılır → yeni şifre ekranı →
auth.updateUser({ password }).
- Uygulamada mail adresini gir →
Postman
POST /auth/v1/recover?redirect_to=intronauts://reset-password→ 200- Resend Logs:
POST /emails200 + “Delivered” - Gerekirse spam → “Not Spam” işaretle (ilk günlerde normal).
6) Hata Ayıklama (Checklist)#
Mail gelmiyor
- Resend Domain = Verified (SPF/DKIM/Return-Path)
- Supabase SMTP host/port/user/pass doğru (Resend: host
smtp.resend.com, port587, userresend) - Supabase Auth → Logs içinde
smtp/errorara - Spam’i kontrol et; DMARC ekle (öneri:
v=DMARC1; p=quarantine; aspf=r; adkim=r)
Redirect yanlış/localhost
redirect_toURL query parametre olarak verilmeli- Authentication → URL Configuration → Redirect URLs listesinde deep link ekli olmalı
- Mobil deep link scheme/host app’te tanımlı olmalı
Google “redirect_uri_mismatch”
- Google Cloud → OAuth client → Authorized redirect URIs listesinde:
https://...supabase.co/auth/v1/callbackbirebir olmalı.
- Google Cloud → OAuth client → Authorized redirect URIs listesinde:
7) Güvenlik Notları#
- E-postadaki recovery token tek kullanımlık ve kısa ömürlüdür (Supabase yönetir).
- Deep link üzerinden gelen token’ı sadece reset/login akışında kullanın, kalıcı saklamayın.
- Üretimde HTTPS ve güncel SDK sürümlerini kullanın.
8) Hızlı Referans — Endpoint & cURL#
Google OAuth (tarayıcı testi)#
open "https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/authorize?provider=google&redirect_to=intronauts://login-callback"Kullanıcıyı Getir (token doğrulama)#
curl -X GET "https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/user" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "apikey: YOUR_SUPABASE_ANON_KEY"Şifre Sıfırlama Maili Gönder#
curl -X POST \
"https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/recover?redirect_to=intronauts://reset-password" \
-H "apikey: YOUR_SUPABASE_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{ "email": "kullanici@mail.com" }'Yeni Şifre Kaydet (recovery token ile)#
curl -X POST "https://fajdjdlecqokklpdgnfl.supabase.co/auth/v1/user" \
-H "Authorization: Bearer <RECOVERY_ACCESS_TOKEN>" \
-H "apikey: YOUR_SUPABASE_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{ "password": "YeniSifre123!" }'9) Flutter’da Minimal UI Parçaları#
Google ile Giriş Butonu
ElevatedButton(
onPressed: () => Supabase.instance.client.auth.signInWithOAuth(
Provider.google,
redirectTo: 'intronauts://login-callback',
),
child: const Text('Google ile Giriş'),
)Yeni Şifre Ekranı (özet)
class SetNewPasswordPage extends StatefulWidget {
final String recoveryToken;
const SetNewPasswordPage({super.key, required this.recoveryToken});
@override
State<SetNewPasswordPage> createState() => _SetNewPasswordPageState();
}
class _SetNewPasswordPageState extends State<SetNewPasswordPage> {
final _ctrl = TextEditingController();
Future<void> _submit() async {
await Supabase.instance.client.auth.updateUser(
UserAttributes(password: _ctrl.text.trim()),
);
if (mounted) Navigator.pop(context); // veya /login
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Yeni Şifre')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(controller: _ctrl, obscureText: true),
const SizedBox(height: 12),
ElevatedButton(onPressed: _submit, child: const Text('Kaydet')),
],
),
),
);
}