guard authentication: powerful, beautiful security
TRANSCRIPT
![Page 1: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/1.jpg)
Guard Authentication: Powerful, Beautiful Security
by your friend:
Ryan Weaver @weaverryan
![Page 2: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/2.jpg)
KnpUniversity.comgithub.com/weaverryan
Who is this guy?
> Lead for the Symfony documentation
> KnpLabs US - Symfony Consulting, training & general Kumbaya
> Writer for KnpUniversity.com Tutorials
> Husband of the much more talented @leannapelham
![Page 3: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/3.jpg)
Introducing…
@weaverryan
![Page 4: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/4.jpg)
sfGuardPlugin!
@weaverryan
![Page 5: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/5.jpg)
What’s the hardestpart of Symfony?
@weaverryan
![Page 6: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/6.jpg)
Authentication
Who are you?
@weaverryan
![Page 7: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/7.jpg)
Authorization
Do you have access to do X?
@weaverryan
![Page 8: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/8.jpg)
VOTERS!
@weaverryan
![Page 9: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/9.jpg)
Authenticationin Symfony sucks
@weaverryan
![Page 10: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/10.jpg)
1) Grab information from the request
@weaverryan
![Page 11: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/11.jpg)
2) Load a User
@weaverryan
![Page 12: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/12.jpg)
3) Validate if the credentials are valid
@weaverryan
![Page 13: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/13.jpg)
4) authentication success…now what?
@weaverryan
![Page 14: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/14.jpg)
5) authentication failure …dang, now what?!
@weaverryan
![Page 15: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/15.jpg)
6) How do we “ask” the user to login?
@weaverryan
![Page 16: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/16.jpg)
6 Steps
5 Different Classes
@weaverryan
![Page 17: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/17.jpg)
security: firewalls: main: anonymous: ~ logout: ~ form_login: ~ http_basic: ~ some_invented_system_i_created: ~
Each activates a system of these 5 classes
@weaverryan
![Page 18: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/18.jpg)
On Guard!
@weaverryan
![Page 19: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/19.jpg)
interface GuardAuthenticatorInterface{ public function getCredentials(Request $request); public function getUser($credentials, $userProvider); public function checkCredentials($credentials, UserInterface $user); public function onAuthenticationFailure(Request $request); public function onAuthenticationSuccess(Request $request, $token);
public function start(Request $request); public function supportsRememberMe();}
@weaverryan
![Page 20: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/20.jpg)
Bad News…
@weaverryan
![Page 21: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/21.jpg)
It’s getting coal for Christmas!
![Page 22: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/22.jpg)
You still have to do work (sorry Laravel people)
@weaverryan
![Page 23: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/23.jpg)
But it will be simple
@weaverryan
https://github.com/knpuniversity/guard-presentation
![Page 24: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/24.jpg)
You need a User class
(This has nothing todo with Guard)
@weaverryan
![Page 25: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/25.jpg)
@weaverryan
use Symfony\Component\Security\Core\User\UserInterface; class User implements UserInterface{ }
![Page 26: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/26.jpg)
class User implements UserInterface{ private $username; public function __construct($username) { $this->username = $username; } public function getUsername() { return $this->username; } public function getRoles() { return ['ROLE_USER']; } // …}
a unique identifier(not really used anywhere)
![Page 27: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/27.jpg)
@weaverryan
class User implements UserInterface{ // … public function getPassword() { } public function getSalt() { } public function eraseCredentials() { }}
These are only used for users thathave an encoded password
![Page 28: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/28.jpg)
The Hardest Example Ever:
Form Login
@weaverryan
![Page 29: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/29.jpg)
A traditional login form setup
@weaverryan
![Page 30: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/30.jpg)
class SecurityController extends Controller{ /** * @Route("/login", name="security_login") */ public function loginAction() { return $this->render('security/login.html.twig'); } /** * @Route("/login_check", name="login_check") */ public function loginCheckAction() { // will never be executed } }
![Page 31: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/31.jpg)
<form action="{{ path('login_check') }}” method="post"> <div> <label for="username">Username</label> <input name="_username" /> </div> <div> <label for="password">Password:</label> <input type="password" name="_password" /> </div> <button type="submit">Login</button> </form>
![Page 32: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/32.jpg)
Let’s create an authenticator!
@weaverryan
![Page 33: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/33.jpg)
class FormLoginAuthenticator extends AbstractGuardAuthenticator{ public function getCredentials(Request $request) { } public function getUser($credentials, UserProviderInterface $userProvider) { } public function checkCredentials($credentials, UserInterface $user) { } public function onAuthenticationFailure(Request $request) { } public function onAuthenticationSuccess(Request $request, TokenInterface $token) { } public function start(Request $request, AuthenticationException $e = null) { } public function supportsRememberMe() { }}
![Page 34: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/34.jpg)
public function getCredentials(Request $request) { if ($request->getPathInfo() != '/login_check') { return; } return [ 'username' => $request->request->get('_username'), 'password' => $request->request->get('_password'), ];}
Grab the “login” credentials!
@weaverryan
![Page 35: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/35.jpg)
public function getUser($credentials, UserProviderInterface $userProvider) { $username = $credentials['username']; $user = new User(); $user->setUsername($username); return $user; }
Create/Load that User!
@weaverryan
![Page 36: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/36.jpg)
public function checkCredentials($credentials, UserInterface $user) { $password = $credentials['password']; if ($password == 'santa' || $password == 'elves') { return; } return true;}
Are the credentials correct?
@weaverryan
![Page 37: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/37.jpg)
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { $url = $this->router->generate('security_login'); return new RedirectResponse($url);}
Crap! Auth failed! Now what!?
@weaverryan
![Page 38: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/38.jpg)
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) { $url = $this->router->generate('homepage'); return new RedirectResponse($url);}
Amazing. Auth worked. Now what?
@weaverryan
![Page 39: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/39.jpg)
public function start(Request $request) { $url = $this->router->generate('security_login'); return new RedirectResponse($url); }
Anonymous user went to /adminnow what?
@weaverryan
![Page 40: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/40.jpg)
Register as a service
services: form_login_authenticator: class: AppBundle\Security\FormLoginAuthenticator arguments: [‘@router’]
@weaverryan
![Page 41: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/41.jpg)
Activate in your firewall
security: firewalls: main: anonymous: ~ logout: ~ guard: authenticators: - form_login_authenticator
@weaverryan
![Page 42: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/42.jpg)
AbstractFormLoginAuthenticator
Free login form authenticator code
@weaverryan
![Page 43: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/43.jpg)
User Providers
(This has nothing todo with Guard)
@weaverryan
![Page 44: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/44.jpg)
Every App has a User class
@weaverryan
And the Christmas spirit
![Page 45: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/45.jpg)
Each User Class Needs 1 User Provider
@weaverryan
![Page 46: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/46.jpg)
class FestiveUserProvider implements UserProviderInterface{ public function loadUserByUsername($username) { // "load" the user - e.g. load from the db $user = new User(); $user->setUsername($username); return $user; } public function refreshUser(UserInterface $user) { return $user; } public function supportsClass($class) { return $class == 'AppBundle\Entity\User'; }}
![Page 47: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/47.jpg)
services: festive_user_provider: class: AppBundle\Security\FestiveUserProvider
@weaverryan
![Page 48: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/48.jpg)
security: providers: elves: id: festive_user_provider firewalls: main: anonymous: ~ logout: ~ # this is optional as there is only 1 provider provider: elves guard: authenticators: [form_login_authenticator]
Boom!
Optional Boom!
![Page 49: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/49.jpg)
class FestiveUserProvider implements UserProviderInterface{ public function loadUserByUsername($username) { // "load" the user - e.g. load from the db $user = new User(); $user->setUsername($username); return $user; } public function refreshUser(UserInterface $user) { return $user; } public function supportsClass($class) { return $class == 'AppBundle\Entity\User'; }}
But why!?
![Page 50: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/50.jpg)
class FestiveUserProvider implements UserProviderInterface{ public function loadUserByUsername($username) { // "load" the user - e.g. load from the db $user = new User(); $user->setUsername($username); return $user; } public function refreshUser(UserInterface $user) { return $user; } public function supportsClass($class) { return $class == 'AppBundle\Entity\User'; }}
refresh from the session
![Page 51: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/51.jpg)
class FestiveUserProvider implements UserProviderInterface{ public function loadUserByUsername($username) { // "load" the user - e.g. load from the db $user = new User(); $user->setUsername($username); return $user; } public function refreshUser(UserInterface $user) { return $user; } public function supportsClass($class) { return $class == 'AppBundle\Entity\User'; }}
switch_user, remember_me
![Page 52: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/52.jpg)
Slightly more wonderful:
Loading a User from the Database
@weaverryan
![Page 53: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/53.jpg)
class FestiveUserProvider implements UserProviderInterface{ public function loadUserByUsername($username) { $user = $this->em->getRepository('AppBundle:User') ->findOneBy(['username' => $username]); if (!$user) { throw new UsernameNotFoundException(); } return $user; } }
@weaverryan
(of course, the “entity” user provider does this automatically)
![Page 54: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/54.jpg)
public function getUser($credentials, UserProviderInterface $userProvider) { $username = $credentials['username']; //return $userProvider->loadUserByUsername($username); return $this->em ->getRepository('AppBundle:User') ->findOneBy(['username' => $username]);}
FormLoginAuthenticator
you can use this if you want to… or don’t!
![Page 55: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/55.jpg)
Easiest Example ever:
Api Token Authentication
@weaverryan
Jolliest
![Page 56: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/56.jpg)
1) Client sends a token on an X-API-TOKEN header
2) We load a User associated with that token
class User implements UserInterface{ /** * @ORM\Column(type="string") */ private $apiToken; // ...}
![Page 57: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/57.jpg)
class ApiTokenAuthenticator extends AbstractGuardAuthenticator{ public function getCredentials(Request $request) { } public function getUser($credentials, UserProviderInterface $userProvider) { } public function checkCredentials($credentials, UserInterface $user) { } public function onAuthenticationFailure(Request $request) { } public function onAuthenticationSuccess(Request $request, TokenInterface $token) { } public function start(Request $request, AuthenticationException $e = null) { } public function supportsRememberMe() { }}
![Page 58: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/58.jpg)
public function getCredentials(Request $request) { return $request->headers->get('X-API-TOKEN'); }
@weaverryan
![Page 59: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/59.jpg)
public function getUser($credentials, UserProviderInterface $userProvider) { $apiToken = $credentials; return $this->em ->getRepository('AppBundle:User') ->findOneBy(['apiToken' => $apiToken]);}
@weaverryan
![Page 60: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/60.jpg)
OR
If you use JWT, get the payload from the token and load the User from it
@weaverryan
![Page 61: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/61.jpg)
public function checkCredentials($credentials, UserInterface $user) { // no credentials to check return true; }
@weaverryan
![Page 62: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/62.jpg)
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { return new JsonResponse([ 'message' => $exception->getMessageKey() ], 401); }
@weaverryan
![Page 63: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/63.jpg)
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) { // let the request continue to the controller return; }
@weaverryan
![Page 64: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/64.jpg)
Register as a service
services: api_token_authenticator: class: AppBundle\Security\ApiTokenAuthenticator arguments: - '@doctrine.orm.entity_manager'
@weaverryan
![Page 65: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/65.jpg)
Activate in your firewallsecurity: # ... firewalls: main: # ... guard: authenticators: - form_login_authenticator - api_token_authenticator entry_point: form_login_authenticator
which “start” method should be called
![Page 66: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/66.jpg)
curl http://localhost:8000/secure
<!DOCTYPE html><html> <head> <meta charset="UTF-8" /> <meta http-equiv="refresh" content="1;url=/login" />
<title>Redirecting to /login</title> </head> <body> Redirecting to <a href="/login">/login</a>. </body></html>
@weaverryan
![Page 67: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/67.jpg)
curl \--header "X-API-TOKEN: BAD" \http://localhost:8000/secure
{"message":"Username could not be found."}
@weaverryan
![Page 68: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/68.jpg)
curl \--header "X-API-TOKEN: GOOD" \http://localhost:8000/secure
{"message":"Hello from the secureAction!"}
@weaverryan
![Page 69: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/69.jpg)
Social Login!
@weaverryan
![Page 70: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/70.jpg)
!
AUTHENTICATOR
/facebook/check?code=abc
give me user info!
load a User object
" User
!
![Page 71: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/71.jpg)
composer require league/oauth2-facebook
@weaverryan
![Page 72: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/72.jpg)
@weaverryan
services: app.facebook_provider: class: League\OAuth2\Client\Provider\Facebook arguments: - clientId: %facebook_app_id% clientSecret: %facebook_app_secret% graphApiVersion: v2.3 redirectUri: "..."
@=service('router').generate('connect_facebook_check', {}, true)
![Page 73: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/73.jpg)
@weaverryan
public function connectFacebookAction() { // redirect to Facebook $facebookOAuthProvider = $this->get('app.facebook_provider'); $url = $facebookOAuthProvider->getAuthorizationUrl([ // these are actually the default scopes 'scopes' => ['public_profile', 'email'], ]); return $this->redirect($url); } /** * @Route("/connect/facebook-check", name="connect_facebook_check") */public function connectFacebookActionCheck() { // will not be reached!}
![Page 74: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/74.jpg)
class FacebookAuthenticator extends AbstractGuardAuthenticator{ public function getCredentials(Request $request) { } public function getUser($credentials, UserProviderInterface $userProvider) { } public function checkCredentials($credentials, UserInterface $user) { } public function onAuthenticationFailure(Request $request) { } public function onAuthenticationSuccess(Request $request, TokenInterface $token) { } public function start(Request $request, AuthenticationException $e = null) { } public function supportsRememberMe() { }}
![Page 75: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/75.jpg)
public function getCredentials(Request $request) { if ($request->getPathInfo() != '/connect/facebook-check') { return; } return $request->query->get('code'); }
@weaverryan
![Page 76: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/76.jpg)
public function getUser($credentials, …){ $authorizationCode = $credentials; $facebookProvider = $this->container->get('app.facebook_provider'); $accessToken = $facebookProvider->getAccessToken( 'authorization_code', ['code' => $authorizationCode] ); /** @var FacebookUser $facebookUser */ $facebookUser = $facebookProvider->getResourceOwner($accessToken); // ...}
@weaverryan
![Page 77: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/77.jpg)
Now, have some hot chocolate!
@weaverryan
![Page 78: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/78.jpg)
public function getUser($credentials, …) { // ... /** @var FacebookUser $facebookUser */ $facebookUser = $facebookProvider->getResourceOwner($accessToken); // ... $em = $this->container->get('doctrine')->getManager(); // 1) have they logged in with Facebook before? Easy! $user = $em->getRepository('AppBundle:User') ->findOneBy(array('email' => $facebookUser->getEmail())); if ($user) { return $user; } // ...}
@weaverryan
![Page 79: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/79.jpg)
public function getUser($credentials, ...){ // ... // 2) no user? Perhaps you just want to create one // (or redirect to a registration) $user = new User(); $user->setUsername($facebookUser->getName()); $user->setEmail($facebookUser->getEmail()); $em->persist($user); $em->flush();
return $user; }
@weaverryan
![Page 80: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/80.jpg)
public function checkCredentials($credentials, UserInterface $user) { // nothing to do here!} public function onAuthenticationFailure(Request $request ...){ // redirect to login} public function onAuthenticationSuccess(Request $request ...){ // redirect to homepage / last page}
@weaverryan
![Page 81: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/81.jpg)
Extra Treats(no coal)
@weaverryan
![Page 82: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/82.jpg)
Can I control the error message?
@weaverryan
![Page 83: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/83.jpg)
@weaverryan
If authentication failed, it is becausean AuthenticationException(or sub-class) was thrown
(This has nothing todo with Guard)
![Page 84: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/84.jpg)
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { return new JsonResponse([ 'message' => $exception->getMessageKey() ], 401); }
@weaverryan
Christmas miracle! The exception is passedwhen authentication fails
AuthenticationException has a hardcodedgetMessageKey() “safe” string
Invalid credentials.
![Page 85: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/85.jpg)
public function getCredentials(Request $request) { } public function getUser($credentials, UserProviderInterface $userProvider) { } public function checkCredentials($credentials, UserInterface $user) { }
Throw an AuthenticationException at anytime in these 3 methods
![Page 86: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/86.jpg)
How can I customize the message?
@weaverryan
Create a new sub-class of AuthenticationException for each message
and override getMessageKey()
![Page 87: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/87.jpg)
CustomUserMessageAuthenticationException
@weaverryan
![Page 88: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/88.jpg)
public function getUser($credentials, ...){ $apiToken = $credentials; $user = $this->em ->getRepository('AppBundle:User') ->findOneBy(['apiToken' => $apiToken]); if (!$user) { throw new CustomUserMessageAuthenticationException( 'That API token is not very jolly' ); } return $user; }
@weaverryan
![Page 89: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/89.jpg)
I need to manually authenticate my user
@weaverryan
![Page 90: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/90.jpg)
public function registerAction(Request $request) { $user = new User(); $form = // ... if ($form->isValid()) { // save the user $guardHandler = $this->container ->get('security.authentication.guard_handler'); $guardHandler->authenticateUserAndHandleSuccess( $user, $request, $this->get('form_login_authenticator'), 'main' // the name of your firewall ); // redirect } // ...}
![Page 91: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/91.jpg)
I want to save a lastLoggedInAt
field on my user no matter *how* they login
@weaverryan
![Page 92: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/92.jpg)
Chill… that was already possible
SecurityEvents::INTERACTIVE_LOGIN
@weaverryan
![Page 93: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/93.jpg)
class LastLoginSubscriber implements EventSubscriberInterface{ public function onInteractiveLogin(InteractiveLoginEvent $event) { /** @var User $user */ $user = $event->getAuthenticationToken()->getUser(); $user->setLastLoginTime(new \DateTime()); $this->em->persist($user); $this->em->flush($user); } public static function getSubscribedEvents() { return [ SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin' ]; }}
@weaverryan
![Page 94: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/94.jpg)
All of these featuresare available now!
@weaverryan
Thanks 2.8!
![Page 95: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/95.jpg)
KnpUGuardBundle
@weaverryan
For those on 2.7
![Page 96: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/96.jpg)
knpuniversity.com/guard
![Page 97: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/97.jpg)
@weaverryan
Ok, what just happened?
![Page 98: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/98.jpg)
1. User implements UserInterface
@weaverryan
![Page 99: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/99.jpg)
2. UserProvider
@weaverryan
![Page 100: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/100.jpg)
3. Create your authenticator(s)
@weaverryan
![Page 101: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/101.jpg)
Authentication#
@weaverryan
![Page 102: Guard Authentication: Powerful, Beautiful Security](https://reader034.vdocuments.us/reader034/viewer/2022042619/58ee351b1a28abb42c8b4631/html5/thumbnails/102.jpg)
@weaverryan
PHP & Symfony Video Tutorials KnpUniversity.com
Thank You!