I have a problem with Oauth2. It returns me as error "Invalid state parameter passed in callback URL."
I'm on symfony 6
I don't know where this can come from, in localhost everything works but in production I have this error.
I looked everywhere for a solution without finding anything.
knpu_oauth2_client.yaml
knpu_oauth2_client:
clients:
azure:
type: azure
client_id: '%env(OAUTH_AZURE_CLIENT_ID)%'
client_secret: '%env(OAUTH_AZURE_CLIENT_SECRET)%'
redirect_route: connect_azure_check
redirect_params: {}
# scope: {}
tenant: '%env(AZURE_TENANT_ID)%'
security.yaml
security:
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# used to reload user from session & other features (e.g. switch_user)
users_in_memory: { memory: null }
my_provider:
entity: {class: App\Entity\User, property: uuid}
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: my_provider
custom_authenticators:
- App\Security\AzureAuthenticator
logout: true
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/connect/azure, role: PUBLIC_ACCESS }
- { path: ^/, roles: ROLE_USER}
AzureController.php
<?php
namespace App\Controller;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class AzureController extends AbstractController
{
/**
* Cette fonction effectue la connexion avec Azure
* Ex: Si vous allez sur cette route, un formulaire microsoft vous demandera de vous connecter
*/
#[Route('/connect/azure', name: 'connect_azure', )]
public function connectAction(ClientRegistry $clientRegistry)
{
return $clientRegistry
->getClient('azure')
->redirect([
'openid', 'profile', 'email'
], []);
}
/**
* Cette fonction permet de savoir si l'authentification à réussi
* Ex: Après vous être connecté ci-dessus, vous serez rediriger sur cette route qui vous redirigera à son tour vers la route home
*/
#[Route('/connect/azure/check', name: 'connect_azure_check', schemes:['http'])]
public function connectCheckAction(Request $request, ClientRegistry $clientRegistry)
{
try {
return $this->redirectToRoute('home');
} catch (IdentityProviderException $e) {
return new JsonResponse(array('status' => false, 'message' => "User not found!", 'error' => $e->getMessage()));
}
}
}
AzureAuthenticator.php
<?php
namespace App\Security;
use App\Entity\User;
use League\OAuth2\Client\Provider\azureUser;
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Security\Authenticator\OAuth2Authenticator;
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 AzureAuthenticator extends OAuth2Authenticator implements AuthenticationEntryPointInterface
{
private ClientRegistry $clientRegistry;
private EntityManagerInterface $entityManager;
private RouterInterface $router;
public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $entityManager, RouterInterface $router)
{
$this->clientRegistry = $clientRegistry;
$this->entityManager = $entityManager;
$this->router = $router;
}
/**
* Cette fonction renvoie true alors la fonction authenticate sera appelée
* @param Request $request
* @return bool|null
*/
public function supports(Request $request): ?bool
{
return $request->attributes->get('_route') === 'connect_azure_check';
}
/**
* Cette fonction permet de traiter les données et de les utiliser. Elle vérifie également si l'utilisateur est déjà existant en base de donnée, si se n'est pas le cas elle l'ajoute.
* @param Request $request
* @return Passport
*/
public function authenticate(Request $request): Passport
{
$client = $this->clientRegistry->getClient('azure');
$accessToken = $this->fetchAccessToken($client);
return new SelfValidatingPassport(
new UserBadge($accessToken->getToken(), function() use ($accessToken, $client) {
/** @var AzureUser $AzureUser */
$AzureUser = $client->fetchUserFromToken($accessToken);
// 1) have they logged in with Azure before? Easy!
$existingUser = $this->entityManager->getRepository(User::class)->findOneBy(['uuid' => $AzureUser->getId()]);
if ($existingUser) {
return $existingUser;
}
$user = new User();
$user->setUuid($AzureUser->getId());
$user->setNom($AzureUser->claim('family_name'));
$user->setPrenom($AzureUser->claim('given_name'));
$user->setEmail($AzureUser->claim('upn'));
$this->entityManager->persist($user);
$this->entityManager->flush();
return $user;
})
);
}
/**
* Si l'authentification réussi, l'utilisateur sera renvoyé sur la route home
* @param Request $request
* @param TokenInterface $token
* @param string $firewallName
* @return Response|null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$targetUrl = $this->router->generate('home');
return new RedirectResponse($targetUrl);
}
/**
* Si l'authentification échoue, l'utilisateur sera informé avec un message d'erreur
* @param Request $request
* @param AuthenticationException $exception
* @return Response|null
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());
return new Response($message, Response::HTTP_FORBIDDEN);
}
/**
* Cette fonction permet de rediriger l'utilisateur sur la route de connexion
* Dès que l'utilisateur sera sur une route qu'il n'a pas le droit d'avoir accès il sera rediriger à cet endroit (Dans le cas de notre application toutes les routes sont par défaut interdite)
* @param Request $request
* @param AuthenticationException|null $authException
* @return Response
*/
public function start(Request $request, AuthenticationException $authException = null): Response
{
return new RedirectResponse(
'/connect/azure',
Response::HTTP_TEMPORARY_REDIRECT
);
}
}
When I add use_state: true
in parameter another error is returned: "Authentication failed! Did you authorize our app?"