Bonjour,

J'ai un soucis avec Oauth2. Il me retourne comme erreur "Invalid state parameter passed in callback URL."

Je suis sur symfony 6

J'ignore d'où ça peut provenir, en localhost tout fonctionne mais en production j'ai cette erreur. Lorsque j'ajoute use_state: true en paramètre une autre erreur m'est retourné: "Authentication failed! Did you authorize our app?"

J'ai cherché partout une solution sans rien trouver.

knpu_oauth2_client.yaml

Code yaml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
```lang-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

Code yaml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
```lang-yaml
security:
    enable_authenticator_manager: true
    # <a href="https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords" target="_blank">https://symfony.com/doc/current/secu...hing-passwords</a>
 
 
    # <a href="https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider" target="_blank">https://symfony.com/doc/current/secu...-user-provider</a>
    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
      # <a href="https://symfony.com/doc/current/security.html#firewalls-authentication" target="_blank">https://symfony.com/doc/current/secu...authentication</a>
 
      # <a href="https://symfony.com/doc/current/security/impersonating_user.html" target="_blank">https://symfony.com/doc/current/secu...ting_user.html</a>
      # 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

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
```lang-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

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
```lang-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
        );
    }
 
}
```