Flutter Google Login ohne Firebase - Laravel Backend Integration Tutorial
Google Sign-In ist eine der beliebtesten Authentifizierungsmethoden für mobile Apps. Die meisten Nutzer sind faul und wählen gerne die 1-click Lösung um sich anzumelden. Kenne ich auch von mir. Also warum nicht Google Login anbieten? Ich hab Google Login in meiner App Twork implementiert und dadurch haben sich die täglich registrierten Nutzer deutlich verbessert. Wie ich das gemacht habe - und vor allem ohne Firebase - zeige ich dir in diesem Blogpost.
Warum Google Sign-In ohne Firebase?
Firebase ist großartig, aber nicht immer die beste Lösung. Und zugegeben bin ich kein großer Fan von Firebase. Ich nutze zwar Laravel als Backend aber das Konzept ist für alle arten von Backend ausgelegt.
Mein Firebase freie Ansatz:
- Flutter App holt sich den ID-Token von Google
- Flutter App sendet den Token an dein Laravel Backend
- Laravel Backend validiert den Token bei Google
- Laravel Backend erstellt/aktualisiert den User in der Datenbank
- Laravel Backend gibt ein eigenes Auth-Token zurück
Vorteile ohne Firebase:
- ✅ Keine zusätzlichen Kosten: Kein Firebase-Account nötig
- ✅ Volle Kontrolle: Alle User Daten in deiner Datenbank
- ✅ Einfachere Architektur: Kein zusätzlicher Service
- ✅ Flexibilität: Laravel bietet mehr Anpassungsmöglichkeiten
- ✅ Ein Backend: Alles in Laravel, keine Microservices
Google Cloud Console Setup
1. Projekt erstellen
Gehe zur Google Cloud Console und erstelle ein neues Projekt oder wähle ein bestehendes aus.
2. OAuth 2.0 Credentials erstellen
- Navigiere zu APIs & Services (APIs und Dienste) → Credentials (Anmeldedaten)
- Klicke auf Create Credentials (Anmeldedaten erstellen) → OAuth client ID
- Erstelle Credentials für:
- Web application (für Laravel Backend)
- iOS (wenn du iOS unterstützt)
- Android (wenn du Android unterstützt)
Wichtig: Notiere dir die Client IDs und den Client Secret für die Web Application.
Tipp: Ich hab für Develop und Production separate Credentials erstellt. z.B: dev-android, prod-android, dev-ios, prod-ios, dev-web, prod-web.
3. Android SHA-1 Fingerprint hinzufügen
Für Android brauchst du den SHA-1 Fingerprint deiner App. Dieser wird ebenfalls unterteilt in einen für Development und einen für Production. Hast du die App schon im Play Store veröffentlicht, dann nutze den vom Play Store. Diesen findest du unter Testen und Veröffentlichen → App Integrität → Play App-Signatur → Einstellungen.
Für Development und wenn du kein Google Play App Signing verwendest, kannst du diesen Befehl nutzen:
# Develop Keystore
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
# Release Keystore
keytool -list -v -keystore /pfad/zu/deinem/keystore.jks -alias dein-alias
Füge den SHA-1 Fingerprint in der Google Cloud Console zu den jeweiligen Android OAuth client hinzu.
Flutter Implementation
1. Dependencies hinzufügen
Füge folgende Packages zu deiner pubspec.yaml hinzu:
dependencies:
flutter:
sdk: flutter
google_sign_in: ^7.2.0
Führe dann aus:
flutter pub get
2. Google Service Klasse erstellen
Erstelle eine Service Klasse für Google Sign-In. Diese Klasse kümmert sich um die Authentifizierung und das Abrufen des ID-Tokens, der dann zum Backend geschickt wird. Hier ist meine Implementierung aus der twork App:
import 'dart:io';
import 'package:google_sign_in/google_sign_in.dart';
class GoogleService {
GoogleSignIn get _googleInstance => GoogleSignIn.instance;
bool _isGoogleSignInInitialized = false;
GoogleService() {
_initializeGoogleSignIn();
}
Future<void> _initializeGoogleSignIn() async {
String? clientId;
if (Platform.isIOS) {
clientId = ''; // iOS Client ID hier einfügen
} else if (Platform.isAndroid) {
clientId = ''; // Android Client ID hier einfügen
}
await _googleInstance.initialize(
clientId: clientId,
serverClientId: const String.fromEnvironment('SERVER_CLIENT_ID'),
);
_isGoogleSignInInitialized = true;
}
/// Stelle sicher, dass Google Sign-In initialisiert ist
Future<void> _ensureGoogleSignInInitialized() async {
if (!_isGoogleSignInInitialized) {
await _initializeGoogleSignIn();
}
}
/// Prüfe, ob Google Sign-In unterstützt wird
bool checkGoogleSignInSupport() {
return _googleInstance.supportsAuthenticate();
}
/// Führe Google Sign-In aus und gib den ID-Token zurück
Future<String?> signIn() async {
try {
await _ensureGoogleSignInInitialized();
final GoogleSignInAccount account = await _googleInstance.authenticate();
final GoogleSignInAuthentication authentication = account.authentication;
if (authentication.idToken == null) {
throw ApiException('Failed to get ID token from Google');
}
return authentication.idToken;
} on GoogleSignInException catch (e) {
throw ApiException(e.description ?? 'Google Sign-In failed');
} catch (e) {
throw ApiException('An unexpected error occurred during Google Sign-In');
}
}
}
Wichtige Hinweise:
serverClientIdist die Web Client ID aus der Google Cloud Console- Der
idTokenist das, was wir ans Backend senden
3. Environment Variable beim Build setzen
Für die Web Client ID musst du eine Environment Variable setzen. Das mache ich immer über eine config.json Datei,
die ich dann über --dart-define-from-file=config.json einbinde. Alternativ kannst du es auch direkt beim Run angeben.
flutter run --dart-define=SERVER_CLIENT_ID=deine-web-client-id.apps.googleusercontent.com
4. Android Configuration
Für Android musst du fast nichts weiter konfigurieren, außer sicherzustellen, dass die minSdkVersion auf mindestens 21 gesetzt ist.
5. iOS Configuration
Für iOS musst du die URL Scheme in ios/Runner/Info.plist hinzufügen:
<key>GIDClientID</key>
<string>DEINE-CLIENT-ID.apps.googleusercontent.com</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.DEINE-CLIENT-ID</string>
</array>
</dict>
</array>
Laravel Backend Implementation
Laravel Socialite macht die Integration von Social Login extrem einfach. Normalerweise wird Socialite für OAuth Flows mit Redirects genutzt, also ist eher für Webapps gedacht. Aber wir können es auch nutzen, um den ID-Token zu validieren.
1. Laravel Socialite installieren
Installiere Laravel Socialite:
composer require laravel/socialite
2. Environment Variablen
Füge in .env deine Web Client ids hinzu:
GOOGLE_CLIENT_ID=deine-web-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=dein-client-secret
3. Config für Google Services
Füge in config/services.php deine Google Client Ids hinzu:
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
],
3. Auth Controller mit Laravel Socialite erstellen
Jetzt kommt der eigentliche Controller. Hier nutzen wir Laravel Socialite. So ähnlich hab ich es in der twork App umgesetzt:
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Laravel\Socialite\Facades\Socialite;
class GoogleAuthController extends Controller
{
/**
* Handle Google Sign-In with ID Token from Flutter
*/
public function googleSignIn(Request $request)
{
$request->validate([
'id_token' => 'required|string',
]);
try {
// Socialite validiert den Token automatisch bei Google
$googleUser = Socialite::driver('google')
->stateless()
->userFromToken($request->id_token);
// Email normalisieren (z.B. für Gmail-Aliase)
$email = $this->normalizeEmail($googleUser->getEmail());
// 1. Prüfe ob Nutzer mit Google ID existiert
$user = User::where('google_id', $googleUser->getId())->first();
if ($user) {
// Wenn ja, gib Auth Token und Nutzer zurück
return response()->json([
'access_token' => $user->createToken('auth_token')->plainTextToken,
'user' => $user,
'message' => 'Successfully authenticated with Google'
], 200);
}
// 2. Prüfe ob Nutzer mit einer Google Email existiert aber noch kein Google Sign-In hat
$user = User::where('email', $email)->first();
if ($user) {
// User mit Email gefunden - Google ID hinzufügen
$user->google_id = $googleUser->getId();
$user->save();
return response()->json([
'access_token' => $user->createToken('auth_token')->plainTextToken,
'user' => $user,
'message' => 'Successfully authenticated with Google'
], 200);
}
// 3. Neuen User erstellen
$user = User::create([
'name' => $googleUser->getName(),
'email' => $email,
'google_id' => $googleUser->getId(),
'password' => Hash::make(str()->random(32)),
]);
return response()->json([
'access_token' => $user->createToken('auth_token')->plainTextToken,
'user' => $user,
'message' => 'Successfully authenticated with Google'
], 201);
} catch (\Exception $e) {
return response()->json([
'message' => 'Failed to authenticate with Google',
'error' => config('app.debug') ? $e->getMessage() : 'Authentication failed'
], 500);
}
}
/**
* Normalize email (handle Gmail aliases)
*/
private function normalizeEmail(string $email): string
{
$email = Str::lower(Str::trim($email));
// Replace googlemail.com with gmail.com
if (Str::endsWith($email, '@googlemail.com')) {
$email = Str::replace('@googlemail.com', '@gmail.com', $email);
}
return $email;
}
}
Ok kommen wir zu den Ablauf:
- Zuerst wird mit dem
id_tokender Nutzer bei Google validiert. - Wenn das geklappt hat, schauen wir in unserer Datenbank ob ein Nutzer mit der Google ID existiert.
- Wenn ja, geben wir den Nutzer mit unseren Backend Token zurück.
- Wenn nicht, dann prüfen wir ob ein Nutzer mit der Google Email existiert.
- Wenn ja, fügen wir die Google ID zum Nutzer hinzu und geben dann den Nutzer zurück.
- Wenn kein Nutzer gefunden wurde, erstellen wir einen neuen Nutzer mit den Google Daten.
Beachte auch die normalizeEmail Methode. Diese Funktion ist eine Email-Normalisierung für Gmail Aliase,
damit Nutzer mit einer googlemail.com Adresse nicht doppelt angelegt werden.
Tatsächlich ist googlemail.com und gmail.com dasselbe.
Diese Funktion bring einige Vorteile mit sich:
- ✅ Verhindert doppelte Accounts für Nutzer mit Gmail-Aliasen
- ✅ Verknüpft bestehende Google Email-Adressen mit dem Google Account
- ✅ Alles ist bei uns in der Datenbank gespeichert
- ✅ volle Kontrolle
Flutter - Backend Integration
Jetzt verbinden wir Flutter mit dem Laravel Backend:
class AuthRepository {
final String baseUrl = 'https://deine-api.com/api';
final GoogleService _googleService;
AuthRepository(this._googleService);
Future<AuthResponse> signInWithGoogle() async {
try {
// 1. Hole ID Token von Google
final idToken = await _googleService.signIn();
if (idToken == null) {
throw ApiException('Failed to get Google ID token');
}
// 2. Sende ID Token an Laravel Backend
final response = await http.post(
Uri.parse('$baseUrl/auth/google'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'idToken': idToken}),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
// 3. Speichere Token für zukünftige Requests
final authToken = data['token'];
final user = User.fromJson(data['user']);
// Token speichern (z.B. mit flutter_secure_storage)
await _secureStorage.write(key: 'auth_token', value: authToken);
return AuthResponse(user: user, token: authToken);
} else {
throw 'Backend authentication failed';
}
} catch (error) {
throw ApiException(error.toString());
}
}
}
Der dargestellte Code entspricht nicht unbedingt den Code, den ich in der Twork App verwende, aber das Grundprinzip ist dasselbe. In Twork wird noch viel mehr mit Bloc und DataProviders gearbeitet.
UI Implementation
Die UI Implementierung erspare ich mir hier. Im Grunde brauchst du nur ein Button, der die signInWithGoogle Methode
aufruft. Ob ihr das mit Bloc, Provider oder Riverpod macht, ist egal.
Ich kann ja mal mein Bloc Setup hier als beispiel zeigen.
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:twork_app/data/repositories/auth_repository.dart';
import 'package:flutter/material.dart';
part 'login_event.dart';
part 'login_state.dart';
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final AuthRepository _authRepository;
LoginBloc({
required AuthRepository authRepository,
}) : _authRepository = authRepository,
super(LoginInitial()) {
on<LoginGoogleSubmitted>((event, emit) async {
try {
emit(LoginInProgress());
await _authRepository.googleLogin();
emit(LoginSuccess());
} catch (e) {
emit(LoginFailure(error: e.toString()));
}
});
}
}
In der UI wird über den trigger LoginGoogleSubmitted die Bloc Methode aufgerufen und der Login Prozess gestartet.
Bei dir kann das natürlich anders aussehen, aber das Grundprinzip bleibt gleich.
Troubleshooting
Problem: “Invalid ID token”
Lösung:
- Prüfe, ob die Client IDs korrekt konfiguriert sind
- Stelle sicher, dass du die Web Client ID als
serverClientIdverwendest - Prüfe, ob der Token nicht abgelaufen ist
Problem: “Email not verified”
Lösung:
- User muss Email bei Google verifizieren
- Du kannst diese Prüfung in Entwicklung temporär deaktivieren
Problem: SHA-1 Fehler auf Android
Lösung:
- Füge sowohl Debug als auch Release SHA-1 in Google Console hinzu
- Stelle sicher, dass Package Name korrekt ist
Was ist mit Google Firebase Authentication?
Viele fragen sich: “Brauche ich nicht Firebase für Google Login?” Nein!
Firebase ist nur eine von vielen Möglichkeiten. Wenn du bereits ein Laravel Backend hast (oder planst eines zu bauen), ist der direkte Weg über Laravel Socialite eine durchaus gute Alternative.
Firebase macht Sinn wenn:
- Du kein eigenes Backend möchtest
- Du alle Google Services nutzt (Firestore, Cloud Functions, etc.)
Laravel macht Sinn wenn:
- Du bereits ein Laravel Backend hast
- Du volle Kontrolle über deine Daten willst
- Du komplexere Business-Logik brauchst
- Du Kosten sparen möchtest
Fazit
Mit dieser Implementation hast du eine sichere Google Sign-In Integration in deiner Flutter App OHNE Firebase mit Laravel Backend Validierung. Die wichtigsten Punkte nochmal zusammengefasst:
- Kein Firebase: Spart Kosten und Komplexität
- Laravel Socialite: Einfache Integration mit
userFromToken() - Client-Side: Flutter holt ID Token von Google
- Server-Side: Laravel validiert Token bei Google
- Database: User wird mit Google ID in deiner DB gespeichert
- Security: Token wird serverseitig validiert
- UX: Einfacher One-Tap Login für User
- Gmail-Normalisierung: Verhindert Duplicate-Accounts
Diese Methode verwende ich produktiv in meiner Twork App und sie hat sich als sehr zuverlässig und sicher erwiesen! Bereits in den ersten Wochen haben sich die täglichen Anmeldungen verdoppelt. Ok zugegeben von 0-1 auf 1-2, aber hey, es ist ein Anfang! 🚀
Weiterführende Ressourcen
Hast du Fragen oder Probleme bei der Implementation? Kontaktiere mich direkt!
Newsletter abonnieren
Erhalte Updates zu neuen Blog-Posts, Tutorials und Tipps zu Flutter, Laravel und mehr.