Bonjour à tous,
Je réalise une API Rest sous symfony, et je suis maintenant sur la partie Utilisteur/Sécurité et me retrouve face à un problème.
Pour l'identification des utilisateurs nous demandons de s'authentifier par facebook ou google. Nous utilisons le protocole Oauth2 pour faire cela.
Donc au niveau du fonctionnement, sur la partie Client ( pour l'instant app Android et site sous angular ) :
- L'utilisateur choisit sur quel réseau social il veut se connecter
- L'utilisateur est ensuite redirigé sur la page d'autorisation et doit autoriser notre application à accéder à ses données.
- Une fois l'autorisation obtenu, il est redirigé sur le client avec un code permettant l'échange contre un access_token.
- A partir de la l'utilisateur est considéré comme connecté sur la partie client.
Maintenant l'utilisateur va faire une action qui demande de contacter mon API , il faut que le client envoie dans le header de la requete un Bearer correspondant à l'access_token.
Du coté de l'API, j'ai fait un guard protégeant certaines routes :
Je n'ai pas de problème pour récupérer les infos de l'utilisateur et pouvoir l'authentifier sur l'api, mon problème est comme vous le voyez, que j'injecte pour le moment directement mon provider de Google, alors qu'il me faut utiliser soit le provider de google soit celui de Facebook.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6oauth: pattern: ^/ stateless: true simple_preauth: authenticator: Oauth_authenticator provider: provider.google_user
La seule doc de symfony la dessus ne m'a pas vraiment avancé : https://symfony.com/doc/3.4/security...nticators.html
Ma question est de savoir si quelqu’un à déjà eu ce problème et quelle est la bonne stratégie à adapter ? Faut il plusieurs authenticator liées chacun à un provider spécialement ? Ou un seul aunthenticor qui utilise deux providers ? Je n'arrive pas à comprendre comment faire pour qu'a la reception de la requete mon guard récupere le bon service associé ?
Je vous met mon authenticator et provider pour vous montrer ce que je fais pour le moment :
GoogleProvider:
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 class OauthAuthenticator implements SimplePreAuthenticatorInterface,AuthenticationFailureHandlerInterface { private $userProvider; function __construct(UserProviderInterface $userProvider) { $this->userProvider = $userProvider; } public function createToken(Request $request, $providerKey) { $bearer = $request->headers->get('Authorization'); $accessToken = substr($bearer, 7); return new PreAuthenticatedToken( 'anon.', $accessToken, $providerKey ); } public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) { $accessToken = $token->getCredentials(); $user = $userProvider->loadUserByUsername($accessToken); return new PreAuthenticatedToken( $user, $accessToken, $providerKey, ['ROLE_USER'] ); } /** * This is called when an interactive authentication attempt fails. This is * called by authentication listeners inheriting from * AbstractAuthenticationListener. * * @return Response The response to return, never null */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { return new Response("Authentication Failed ", 401); } public function supportsToken(TokenInterface $token, $providerKey) { return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey; } /** * @return UserProviderInterface */ public function getUserProvider(): UserProviderInterface { return $this->userProvider; } /** * @param UserProviderInterface $userProvider */ public function setUserProvider(UserProviderInterface $userProvider) { $this->userProvider = $userProvider; }
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
121
122 class GoogleProvider implements UserProviderInterface { private $client; private $serializer; private $router; private $userManager; function __construct(Client $client,Serializer $serializer,Router $router,UserManagerInterface $userManager) { $this->client = $client; $this->serializer = $serializer; $this->router = $router; $this->userManager = $userManager; } /** * Loads the user for the given username. * * This method must throw UsernameNotFoundException if the user is not * found. * * @param string $username The username * * @return UserInterface * * @throws UsernameNotFoundException if the user is not found */ public function loadUserByUsername($username) { $url = 'https://www.googleapis.com/oauth2/v1/userinfo?access_token='.$username; // dump($url);die(); try{ $response = $this->client->get($url); }catch (Exception $exception){ throw new \Exception($exception->getMessage(),404); } $res = $response->getBody()->getContents(); // dump(json_decode($res)); $userData = $this->serializer->deserialize($res,'array','json'); // dump($userData);die(); if (!$userData){ throw new LogicException('Did not managed to get your user info from social network'); } $existingUser = $this->userManager->getUserByGoogleAccount($userData['id']) ; if ($existingUser != null){ /** @var \AppBundle\Entity\Interfaces\UserInterface $existingUser */ $existingUser->setEmail($userData['email']); $existingUser->setGoogleId($userData['id']); $existingUser->setFullname($userData['name']); //dump($existingUser ); return $existingUser; } $user = new User(); $user->setEmail($userData['email']); $user->setGoogleId($userData['id']); $user->setFullname($userData['name']); // dump($user); $user = $this->userManager->createUser($user); // dump($user);die(); return $user; // TODO: Implement loadUserByUsername() method. } /** * Refreshes the user. * * It is up to the implementation to decide if the user data should be * totally reloaded (e.g. from the database), or if the UserInterface * object can just be merged into some internal array of users / identity * map. * * @return UserInterface * * @throws UnsupportedUserException if the user is not supported */ public function refreshUser(UserInterface $user) { $class = get_class($user); if (!$this->supportsClass($class)){ throw new \Symfony\Component\Security\Core\Exception\UnsupportedUserException(); } return $user;// TODO: Implement refreshUser() method. } /** * Whether this provider supports the given user class. * * @param string $class * * @return bool */ public function supportsClass($class) { // TODO: Implement supportsClass() method. return 'AppBundle\Entity\User' === $class; } }
Je dois passer mon diplome bientot et j'aimerai vraiment implémenter cette fonctionnalité , alors tout les retours seront vraiment les bienvenus !
Un grand merci à vous,
Cordialement,
Partager