Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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;
}
}
@kunicmarko20

This comment has been minimized.

Copy link
Owner Author

@kunicmarko20 kunicmarko20 commented Jan 14, 2020

Use League OAuth2 library directly with Symfony without the need for external bundles

@ruudk

This comment has been minimized.

Copy link

@ruudk ruudk commented Jan 14, 2020

Thanks for sharing! This is much simpler than having to rely on external dependencies like HWIOAuthBundle or knpuniversity/oauth2-client-bundle.

@Tojo-srg

This comment has been minimized.

Copy link

@Tojo-srg Tojo-srg commented Jan 15, 2020

Thanks for sharing!

@Mouerr

This comment has been minimized.

Copy link

@Mouerr Mouerr commented Jan 15, 2020

thanks for sharing, amazing job.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.