Skip to content

Instantly share code, notes, and snippets.

@parijke
Forked from kunicmarko20/Login.php
Created January 14, 2020 17:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save parijke/0d7c563aede7671c98c2a9ffd63ba718 to your computer and use it in GitHub Desktop.
Save parijke/0d7c563aede7671c98c2a9ffd63ba718 to your computer and use it in GitHub Desktop.
Symfony OAuth with Leage OAuth2 library
<a href="{{ googleAuthorizationUrl }}">
Login
</a>
<?php
declare(strict_types=1);
namespace App;
use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;
use League\OAuth2\Client\Provider\Google as GoogleProvider;
final class Login
{
/**
* @var Environment
*/
private $twig;
/**
* @var GoogleProvider
*/
private $provider;
public function __construct(Environment $twig, GoogleProvider $provider)
{
$this->twig = $twig;
$this->provider = $provider;
}
public function __invoke(): Response
{
return new Response($this->twig->render(
'login.html.twig',
[
'googleAuthorizationUrl' => $this->provider->getAuthorizationUrl(),
]
));
}
}
<?php
declare(strict_types=1);
namespace App;
use League\OAuth2\Client\Provider\Google as GoogleProvider;
use League\OAuth2\Client\Provider\GoogleUser;
use League\OAuth2\Client\Token\AccessTokenInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
final class OAuthAuthenticator extends AbstractGuardAuthenticator
{
private const ROUTE_LOGIN = 'auth.login';
private const ROUTE_OAUTH_LOGIN = 'auth.connect.google';
private const ROUTE_DASHBOARD = 'index';
/**
* @var GoogleProvider
*/
private $provider;
/**
* @var UrlGeneratorInterface
*/
private $urlGenerator;
public function __construct(GoogleProvider $provider, UrlGeneratorInterface $urlGenerator)
{
$this->provider = $provider;
$this->urlGenerator = $urlGenerator;
}
public function start(Request $request, AuthenticationException $authException = null): RedirectResponse
{
return new RedirectResponse(
$this->urlGenerator->generate(self::ROUTE_LOGIN)
);
}
public function supports(Request $request): bool
{
return $request->attributes->get('_route') === self::ROUTE_OAUTH_LOGIN;
}
public function getCredentials(Request $request): AccessTokenInterface
{
if (!$request->query->has('code')) {
throw new CustomUserMessageAuthenticationException('Missing code.');
}
return $this->provider->getAccessToken('authorization_code', [
'code' => $request->query->get('code')
]);
}
public function getUser($credentials, UserProviderInterface $userProvider): UserInterface
{
$googleUser = $this->provider->getResourceOwner($credentials);
\assert($googleUser instanceof GoogleUser);
try {
$email = $googleUser->getEmail();
\assert(\is_string($email));
return $userProvider->loadUserByUsername($email);
} catch (UserDoesNotExist $exception) {
throw new CustomUserMessageAuthenticationException($exception->getMessage());
}
}
public function checkCredentials($credentials, UserInterface $user): bool
{
return true;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): RedirectResponse
{
$session = $request->getSession();
\assert($session instanceof SessionInterface);
$session->set(Security::AUTHENTICATION_ERROR, $exception);
return new RedirectResponse(
$this->urlGenerator->generate(self::ROUTE_LOGIN)
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): RedirectResponse
{
return new RedirectResponse(
$this->urlGenerator->generate(self::ROUTE_DASHBOARD)
);
}
public function supportsRememberMe(): bool
{
return true;
}
}
auth.login:
path: /login
controller: App\Login
methods: [GET]
auth.logout:
path: /logout
methods: [GET]
auth.connect.google:
path: /connect/google
methods: [GET]
security:
providers:
users:
id: App\UserProvider
firewalls:
login:
pattern: /login
anonymous: true
admin:
pattern: ^/
provider: users
guard:
authenticators:
- App\OAuthAuthenticator
logout:
path: auth.logout
target: auth.login
main:
anonymous: ~
access_control:
- { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/connect/google, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/.*, role: IS_AUTHENTICATED_FULLY }
<?php
declare(strict_types=1);
namespace App;
use App\User;
use Symfony\Component\Security\Core\User\UserInterface;
final class SecurityUser implements UserInterface
{
/**
* @var string
*/
private $email;
/**
* @var string[]
*/
private $roles;
public function __construct(User $user)
{
$this->email = $user->email();
$this->roles = $user->roles();;
}
/**
* @return string[]
*/
public function getRoles(): array
{
return $this->roles;
}
public function getPassword(): string
{
return '';
}
public function getSalt(): string
{
return '';
}
public function getUsername(): string
{
return $this->email->toString();
}
public function eraseCredentials(): void
{
}
}
services:
_defaults:
autowire: true
public: false
autoconfigure: true
League\OAuth2\Client\Provider\Google:
arguments:
$options:
clientId: '%env(APP_OAUTH_GOOGLE_CLIENT_ID)%'
clientSecret: '%env(APP_OAUTH_GOOGLE_CLIENT_SECRET)%'
redirectUri: '%env(APP_OAUTH_GOOGLE_REDIRECT_URI)%'
<?php
declare(strict_types=1);
namespace App;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
final class UserProvider implements UserProviderInterface
{
/**
* @var UserRepository
*/
private $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
/**
* @param string $email
*/
public function loadUserByUsername($email): SecurityUser
{
return new SecurityUser($this->userRepository->getOneByEmail(new Email($email)));
}
public function refreshUser(UserInterface $user): SecurityUser
{
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class): bool
{
return $class === SecurityUser::class;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment