F.R Michel
Posted on August 24, 2020
Integrate Oauth2 Linkedin for Symfony (or other framework) is easy.
Install the lib oauth2-linkedin with composer:
composer req league/oauth2-linkedin
I am going to define the keys provided by linkedin in .env file like this:
LINKEDIN_CLIENT_ID=yourId
LINKEDIN_CLIENT_SECRET=yourSecretId
Then we modify the config/services.yaml :
# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
linkedin_client_id: '%env(LINKEDIN_CLIENT_ID)%'
linkedin_client_secret: '%env(LINKEDIN_CLIENT_SECRET)%'
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
Let's create the controller that will manage the login.
<?php
namespace App\Controller;
use App\Entity\User;
use App\Service\LinkedinService;
use App\Service\UserService;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use League\OAuth2\Client\Provider\LinkedInResourceOwner;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Class SecurityController
* @package App\Controller
*/
class SecurityController extends AbstractController
{
/**
* @Route("/login", name="app_login", methods={"GET", "POST"})
* @param AuthenticationUtils $authenticationUtils
* @return Response
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
if ($this->getUser()) {
return $this->redirectToRoute('home_page');
}
return $this->render(
'security/login.html.twig',
[
'last_username' => $authenticationUtils->getLastUsername(),
'error' => $authenticationUtils->getLastAuthenticationError()
]
);
}
/**
* @Route("/login/linkedin", name="app_login_linkedin", methods={"GET", "POST"})
* @param Request $request
* @param LinkedinService $service
* @param EntityManagerInterface $em
* @return Response
*/
public function loginLinkedIn(Request $request, LinkedinService $service, EntityManagerInterface $em): Response
{
if ($this->getUser()) {
throw new AccessDeniedHttpException();
}
$code = $request->query->get('code');
if ($code === null) {
return $service->redirectToLinkedin();
}
if (!$service->isAuthenticated($request)) {
$this->addFlash('error', 'Une erreur est survenue');
return $this->redirectToRoute('home_page');
}
try {
/**
* @var LinkedInResourceOwner $userLinkedIn
*/
$userLinkedIn = $service->getUser($code);
$user = $em->getRepository(User::class)->findOneBy(['email' => $userLinkedIn->getEmail()]);
if (!$user instanceof User) {
$user = (new User())
->setEmail($userLinkedIn->getEmail());
$em->persist($user);
$em->flush();
}
// Generate UsernamePasswordToken to login user
/*
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->tokenStorage->setToken($token);
$this->session->set('_security_main',
serialize($token));
*/
} catch (IdentityProviderException $e) {
$this->addFlash('error', 'Une erreur est survenue');
}
return $this->redirectToRoute('home_page');
}
}
To finish we create the service which manage oauth2-linkedin:
<?php
namespace App\Service;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Provider\LinkedIn;
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
use League\OAuth2\Client\Token\AccessToken;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* Class UserService
* @package App\Service
*/
class LinkedinService
{
/**
* @var LinkedIn
*/
private $provider;
/**
* @var SessionInterface
*/
private $session;
/**
* UserService constructor.
* @param ParameterBagInterface $bag
* @param RouterInterface $router
* @param SessionInterface $session
*/
public function __construct(ParameterBagInterface $bag, RouterInterface $router, SessionInterface $session)
{
$this->provider = new LinkedIn([
'clientId' => $bag->get('linkedin_client_id'),
'clientSecret' => $bag->get('linkedin_client_secret'),
'redirectUri' => $router->generate('app_login_linkedin', [], UrlGeneratorInterface::ABSOLUTE_URL),
]);
$this->session = $session;
}
public function redirectToLinkedin(): Response
{
$url =$this->provider->getAuthorizationUrl();
$this->session->set('oauth2state', $this->provider->getState());
return new RedirectResponse($url);
}
/**
* @param Request $request
* @return bool
*/
public function isAuthenticated(Request $request): bool
{
$state = $request->query->get('state');
$isAuthenticated = ($state !== null && $state === $this->session->get('oauth2state'));
if ($isAuthenticated === false) {
$this->session->remove('oauth2state');
}
return $isAuthenticated;
}
/**
* @param string $code
* @return ResourceOwnerInterface
* @throws IdentityProviderException
*/
public function getUser(string $code):ResourceOwnerInterface
{
/**
* @var AccessToken $token
*/
$token = $this->provider->getAccessToken('authorization_code', [
'code' => $code
]);
return $this->provider->getResourceOwner($token);
}
}
Simple and easy!
💖 💪 🙅 🚩
F.R Michel
Posted on August 24, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
symfony Symfony Station Communiqué — 15 November 2024. A look at Symfony, Drupal, PHP, and programming news!
November 18, 2024
symfony Symfony Station Communiqué — 08 November 2024. A look at Symfony, Drupal, PHP, and programming news!
November 11, 2024