Google OAuth-Integration für Symfony
Ich zeige dir in diesem Artikel eine vollständige Implementierung für die Google OAuth-Integration (SSO – Single Sign On) in deinem Symfony Projekt. Ich verwende die LTS Version 6.4. Dies ermöglicht Benutzern, sich mit ihrem Google-Konto bei dir zu registrieren und anzumelden.
Übersicht
Wir werden die Google OAuth-Integration mit dem KnpUOAuth2ClientBundle implementieren, das eine gute Integration mit Symfony Security bietet. Die Lösung wird folgende Komponenten umfassen:
- Installation und Konfiguration der benötigten Pakete
- Erstellung eines Google OAuth-Clients
- Anpassung des Benutzermodells
- Implementierung der Authentication-Handler
- Frontend-Integration (Login-Button)
Was du schon haben solltest:
- Grundlegende Symfony/PHP Erfahrung
- Ein Symfony Projekt am besten ab Version 6.4
- Einen fertigen Login und Registrierungsprozess (Symfony User)
- Einen Google Account
Vorbereitende Aufgaben bei Google
Google OAuth Einrichtungsanleitung
Google Cloud Console Projekt erstellen
- Besuche die [Google Cloud Console] (https://console.cloud.google.com/)
- Erstelle ein neues Projekt oder wähle ein bestehendes Projekt aus
- Navigiere zum „OAuth consent screen“ (OAuth-Zustimmungsbildschirm) im Menü „APIs & Services“
- Wähle den Benutzertyp (extern oder intern) und gib die erforderlichen Informationen ein:
– App-Name
– Benutzer-Support-E-Mail
– Entwickler-Kontakt-Informationen - Speichere die Einstellungen
OAuth-Anmeldedaten erstellen
- Navigiere zu „Credentials“ (Anmeldedaten) im Menü „APIs & Services“
- Klicke auf „Create Credentials“ (Anmeldedaten erstellen) und wähle „OAuth client ID“ (OAuth-Client-ID)
- Wähle als Anwendungstyp „Web application“ (Webanwendung)
- Gib einen Namen für den Client ein
- Füge autorisierte Weiterleitungs-URIs hinzu:
– Für die Entwicklungsumgebung: `http://localhost:8000/connect/google/check`
– Für die Produktionsumgebung: `https://deine-domain.de/connect/google/check` - Klicke auf „Create“ (Erstellen)
- Notiere dir die „Client ID“ und das „Client Secret“
Umgebungsvariablen konfigurieren
Füge die Client ID und das Client Secret zu deiner `.env.local` Datei hinzu:
GOOGLE_CLIENT_ID=deine-client-id
GOOGLE_CLIENT_SECRET=dein-client-secret
Google OAuth API aktivieren
- Navigiere zu „Library“ (Bibliothek) im Menü „APIs & Services“
- Suche nach „Google People API“
- Aktiviere diese API für dein Projekt
Google OAuth-Integration in Symfony
Installation der benötigten Pakete
# Installation des KnpUOAuth2ClientBundle
composer require knpuniversity/oauth2-client-bundle
# Installation des Google OAuth2 Providers
composer require league/oauth2-google
# Falls noch nicht vorhanden, stelle sicher, dass die Symfony Security-Komponenten installiert sind
composer require symfony/security-bundle
Als Nächstes müssen wir die Konfiguration für das OAuth2-Bundle einrichten
# config/packages/knpu_oauth2_client.yaml
knpu_oauth2_client:
clients:
google:
type: google
client_id: '%env(GOOGLE_CLIENT_ID)%'
client_secret: '%env(GOOGLE_CLIENT_SECRET)%'
redirect_route: connect_google_check
redirect_params: {}
Das Benutzermodell
Wir konfigurieren nun unser Benutzermodell, damit es die von Google bereitgestellten Nutzerdaten – wie Name, E-Mail-Adresse und Profilbild – aufnehmen und verwalten kann. Das heißt wir müssen unsere User Entity anpassen. Öffne dazu die src/Entity/User.php
und führe folgende Änderungen durch:
// Hier muss nullable: true hinzugefügt werden
#[ORM\Column(nullable: true)]
private ?string $password = null;
// Folgende Felder müssen hinzugefügt werden
#[ORM\Column(length: 255, nullable: true)]
private ?string $googleId = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $locale = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $avatarUrl = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $registrationSource = null;
// Folgende Getter und Setter hinzufügen
public function getGoogleId(): ?string
{
return $this->googleId;
}
public function setGoogleId(?string $googleId): static
{
$this->googleId = $googleId;
return $this;
}
public function getAvatarUrl(): ?string
{
return $this->avatarUrl;
}
public function setAvatarUrl(?string $avatarUrl): static
{
$this->avatarUrl = $avatarUrl;
return $this;
}
public function getLocale(): ?string
{
return $this->locale;
}
public function setLocale(?string $locale): static
{
$this->locale = $locale;
return $this;
}
public function getRegistrationSource(): ?string
{
return $this->registrationSource;
}
public function setRegistrationSource(?string $registrationSource): static
{
$this->registrationSource = $registrationSource;
return $this;
}
public function isGoogleUser(): bool
{
return $this->googleId !== null;
}
Migration der neuen Felder in der User Entity
# Migration erstellen
php bin/console make:migration
# Migration ausführen
php bin/console doctrine:migrations:migrate
Google Authenticator für Google OAuth
Folgende PHP Datei erstellen wir unter src/Security/GoogleAuthenticator.php
<?php
namespace App\Security;
use App\Entity\User;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\OAuth2Authenticator;
use League\OAuth2\Client\Provider\GoogleUser;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
class GoogleAuthenticator extends OAuth2Authenticator implements AuthenticationEntryPointInterface
{
private ClientRegistry $clientRegistry;
private EntityManagerInterface $entityManager;
private RouterInterface $router;
private UserRepository $userRepository;
public function __construct(
ClientRegistry $clientRegistry,
EntityManagerInterface $entityManager,
RouterInterface $router,
UserRepository $userRepository
) {
$this->clientRegistry = $clientRegistry;
$this->entityManager = $entityManager;
$this->router = $router;
$this->userRepository = $userRepository;
}
public function supports(Request $request): ?bool
{
// Prüfen, ob der aktuelle Request der Google-Callback ist
return $request->attributes->get('_route') === 'connect_google_check';
}
public function authenticate(Request $request): Passport
{
$client = $this->clientRegistry->getClient('google');
$accessToken = $this->fetchAccessToken($client);
return new SelfValidatingPassport(
new UserBadge($accessToken->getToken(), function() use ($accessToken, $client) {
/** @var GoogleUser $googleUser */
$googleUser = $client->fetchUserFromToken($accessToken);
// Email ist die wichtigste Information
$email = $googleUser->getEmail();
// Prüfen, ob der User bereits existiert
$existingUser = $this->userRepository->findOneBy(['email' => $email]);
if ($existingUser) {
// User existiert bereits
if (!$existingUser->getGoogleId()) {
// Nur wenn der User noch nicht mit Google verbunden ist, setzen wir die Google-ID
$existingUser->setGoogleId($googleUser->getId());
$existingUser->setRegistrationSource('google');
}
// Hier: Immer die Benutzerdaten aktualisieren, unabhängig davon, ob die Google-ID bereits gesetzt war
$this->updateUserFromGoogleData($existingUser, $googleUser);
$this->entityManager->persist($existingUser);
$this->entityManager->flush();
return $existingUser;
}
// Neuen User erstellen
$user = new User();
$user->setEmail($email);
$user->setGoogleId($googleUser->getId());
$user->setRegistrationSource('google');
$user->setPassword($password ?? '');
$user->setRoles(['ROLE_USER']);
$this->updateUserFromGoogleData($user, $googleUser);
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
})
);
}
private function updateUserFromGoogleData(User $user, GoogleUser $googleUser): void
{
// Name kann als "given_name" und "family_name" oder in "name" enthalten sein
$user->setFirstName($googleUser->getFirstName());
$user->setLastName($googleUser->getLastName());
// Avatar-URL und Locale, falls verfügbar
if (method_exists($googleUser, 'getAvatar')) {
$user->setAvatarUrl($googleUser->getAvatar());
}
if (method_exists($googleUser, 'getLocale')) {
$user->setLocale($googleUser->getLocale());
}
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
// Weiterleitung nach erfolgreicher Authentifizierung
return new RedirectResponse($this->router->generate('app_homepage'));
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());
return new Response($message, Response::HTTP_FORBIDDEN);
}
public function start(Request $request, AuthenticationException $authException = null): Response
{
return new RedirectResponse(
$this->router->generate('app_login'),
Response::HTTP_TEMPORARY_REDIRECT
);
}
}
Passe die entsprechenden Routen an, falls sich diese von deinen unterscheiden.
Controller für Google OAuth-Integration
Als weiteren wichtigen Schritt benötigen wir noch einen Controller:
php bin/console make:controller Google
Folgenden Code fügen wir in die GoogleController.php
Datei:
<?php
namespace App\Controller;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class GoogleController extends AbstractController
{
/**
* Link zur Initiierung des OAuth-Flows mit Google
*/
#[Route('/connect/google', name: 'connect_google')]
public function connectAction(ClientRegistry $clientRegistry): RedirectResponse
{
// Weiterleitung zur Google Authorization URL
return $clientRegistry
->getClient('google')
->redirect([
'profile',
'email' // Die benötigten Scopes
]);
}
/**
* Diese Route wird nach erfolgreicher Authentifizierung bei Google aufgerufen
* Sie wird vom Google-Authenticator behandelt und nicht von dieser Methode
*/
#[Route('/connect/google/check', name: 'connect_google_check')]
public function connectCheckAction(Request $request, ClientRegistry $clientRegistry)
{
// Diese Methode wird nie aufgerufen, da der GoogleAuthenticator
// die Anfrage abfängt und verarbeitet
return $this->redirectToRoute('app_homepage');
}
}
Passe auch hier die entsprechende Route an.
Security-Konfiguration anpassen
Es wird Zeit unseren GoogleAuthenticator zu integrieren. Dazu öffnen wir unsere security.yaml
unter config/packages/security.yaml
und fügen folgende Zeilen hinzu:
security:
firewalls:
main:
# Google OAuth-Authenticator aktivieren
custom_authenticators:
- App\Security\GoogleAuthenticator
entry_point: form_login
Google Login-Button
Im Login und Registrierungs-Template fügen wir dann noch den Button für die Google Authentifikation ein.
{# templates/security/login.html.twig #}
{# templates/registration/register.html.twig #}
<div class="text-center my-4">
<a href="{{ path('connect_google') }}" class="btn btn-outline-danger w-100 mb-3 d-flex align-items-center justify-content-center">
Mit Google anmelden
</a>
</div>
Zusammenfassung der Google OAuth-Integration für Symfony
Wir haben jetzt eine vollständige Implementierung für die Google OAuth-Integration in deinem Symfony Projekt erstellt. Hier ist eine Übersicht der Implementierung:
Installierte Pakete
- KnpUOAuth2ClientBundle für die OAuth2-Integration
- League/OAuth2-Google als Google OAuth2 Provider
Hauptkomponenten
- User Entity mit Google-Unterstützung:
- Speichert wichtige Google-Informationen wie googleId, Profilbild, Name
- Unterstützt sowohl herkömmliche Passwort-Authentifizierung als auch Google OAuth
- GoogleAuthenticator:
- Verarbeitet den Google OAuth-Flow
- Erstellt neue Benutzer oder verbindet bestehende Konten
- Übernimmt die Authentifizierung für die Security-Komponente
- Controller für Login und Google-Verbindung:
- GoogleController mit Routen für OAuth-Initiierung und Callback
- SecurityController für klassisches Login
- RegistrationController für manuelle Registrierung
- Templates mit Google-Button:
- Login- und Registrierungsseiten mit Google-Anmeldebutton
Funktionalitäten
- Single Sign-On (SSO) mit Google:
- Benutzer können sich mit einem Klick über ihren Google-Account anmelden
- Profildaten werden aus dem Google-Konto übernommen
- Automatisches Konto-Linking:
- Wenn ein Benutzer mit der gleichen E-Mail-Adresse bereits existiert, wird sein Konto mit Google verknüpft
- Hybride Authentifizierung:
- Unterstützt sowohl herkömmliche E-Mail/Passwort-Authentifizierung als auch Google OAuth
- Speichert die Registrierungsquelle (Google oder Formular)
Tipps für Produktionsumgebungen
- Achte auf sichere HTTPS-Verbindungen
- Stelle sicher, dass Weiterleitungs-URLs in der Google Cloud Console korrekt konfiguriert sind
- Für hohe Benutzerzahlen (>100) ist eine vollständige Verifizierung des Google-Projekts erforderlich
Diese Implementierung ist zukunftssicher und kann einfach um weitere OAuth-Provider erweitert werden. Die Benutzerinformationen werden konsistent gespeichert, unabhängig davon, ob ein Benutzer sich über Google anmeldet oder traditionell registriert.
Hast du Fragen oder kommst an einer Stelle nicht weiter, schreibe deine Erfahrungen gerne in die Kommentare.
Weitere Beiträge
PHP 7 | Ein einfacher Fileupload mit PHP
TYPO3 Ressourcen Image auslesen und anzeigen ab Version 6.2
TYPO3 Subheader (Untertitel) im Backend benutzen.
Eigenes Inhaltselement für TYPO3 7.6.x - 9.5.x