Slide 1

Slide 1 text

Symfony Security Demystified

Slide 2

Slide 2 text

Security is about user management

Slide 3

Slide 3 text

Security is about user management protecting resources

Slide 4

Slide 4 text

→ Who are you? → Are you allowed (to do that)?

Slide 5

Slide 5 text

→ Who are you? ➡ Authentication → Are you allowed (to do that)? ➡ Authorization

Slide 6

Slide 6 text

The Game Plan → authentication concepts → authorization concepts → how they're used in a framework project Example Project https://github.com/ocrampete16/symfony-security- demystified-talk

Slide 7

Slide 7 text

btw, who dis? ! Marco Petersen " Software Developer @ SensioLabs # @ocrampete16 $ marco.petersen@sensiolabs.de

Slide 8

Slide 8 text

Authentication Who are you?

Slide 9

Slide 9 text

User Providers load a user from a source $userProvider = new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd', 'roles' => ['ROLE_USER'], ], ]); $user = $userProvider->loadUserByUsername('marco');

Slide 10

Slide 10 text

User Providers load a user from a source $userProvider = new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd', 'roles' => ['ROLE_USER'], ], ]); $user = $userProvider->loadUserByUsername('marco');

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

The User Checker ensures that the user meets certain requirements $userChecker = new UserChecker(); $userChecker->checkPreAuth($user); // authenticate user here $userChecker->checkPostAuth($user);

Slide 13

Slide 13 text

The User Checker ensures that the user meets certain requirements $userChecker = new UserChecker(); $userChecker->checkPreAuth($user); // authenticate user here $userChecker->checkPostAuth($user);

Slide 14

Slide 14 text

The User Checker ensures that the user meets certain requirements $userChecker = new UserChecker(); $userChecker->checkPreAuth($user); // authenticate user here $userChecker->checkPostAuth($user);

Slide 15

Slide 15 text

The User Checker ensures that the user meets certain requirements $userChecker = new UserChecker(); $userChecker->checkPreAuth($user); // authenticate user here $userChecker->checkPostAuth($user);

Slide 16

Slide 16 text

What does the default User Checker do? The User Checker does not do anything by default. Implement your own to check for things like: → disabled accounts → accounts past the trial period → expired credentials

Slide 17

Slide 17 text

Password Encoders encode passwords $encoderFactory = new EncoderFactory([ User::class => new PlaintextPasswordEncoder(), ]); $encoder = $encoderFactory->getEncoder($user); $hash = $encoder->encodePassword($plaintext, $salt);

Slide 18

Slide 18 text

Password Encoders encode passwords $encoderFactory = new EncoderFactory([ User::class => new PlaintextPasswordEncoder(), ]); $encoder = $encoderFactory->getEncoder($user); $hash = $encoder->encodePassword($plaintext, $salt);

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

Putting it all together

Slide 21

Slide 21 text

const PROVIDER_KEY = 'default'; $unauthenticatedToken = new UsernamePasswordToken('marco', 'p4$$w0rd', PROVIDER_KEY); $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]); $authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);

Slide 22

Slide 22 text

const PROVIDER_KEY = 'default'; $unauthenticatedToken = new UsernamePasswordToken('marco', 'p4$$w0rd', PROVIDER_KEY); $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]); $authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);

Slide 23

Slide 23 text

const PROVIDER_KEY = 'default'; $unauthenticatedToken = new UsernamePasswordToken('marco', 'p4$$w0rd', PROVIDER_KEY); $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]); $authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);

Slide 24

Slide 24 text

const PROVIDER_KEY = 'default'; $unauthenticatedToken = new UsernamePasswordToken('marco', 'p4$$w0rd', PROVIDER_KEY); $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]); $authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);

Slide 25

Slide 25 text

const PROVIDER_KEY = 'default'; $unauthenticatedToken = new UsernamePasswordToken('marco', 'p4$$w0rd', PROVIDER_KEY); $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]); $authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);

Slide 26

Slide 26 text

Symfony uses the same class for authenticated and unauthenticated tokens // UserPasswordToken.php public function __construct( $user, $credentials, string $providerKey, array $roles = [] ) { // ... parent::setAuthenticated(\count($roles) > 0); }

Slide 27

Slide 27 text

Symfony uses the same class for authenticated and unauthenticated tokens // UserPasswordToken.php public function __construct( $user, $credentials, string $providerKey, array $roles = [] ) { // ... parent::setAuthenticated(\count($roles) > 0); }

Slide 28

Slide 28 text

$userProvider = new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd', 'roles' => ['ROLE_USER'], ], ]);

Slide 29

Slide 29 text

$userProvider = new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd', 'roles' => ['ROLE_USER'], ], ]);

Slide 30

Slide 30 text

$userProvider = new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd', 'roles' => [], ], ]); // ... // $token is unauthenticated! $token = $authenticationManager->authenticate($unauthenticatedToken);

Slide 31

Slide 31 text

$userProvider = new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd', 'roles' => [], ], ]); // ... // $token is unauthenticated! $token = $authenticationManager->authenticate($unauthenticatedToken);

Slide 32

Slide 32 text

$userProvider = new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd', 'roles' => [], ], ]); // ... // $token is unauthenticated! $token = $authenticationManager->authenticate($unauthenticatedToken);

Slide 33

Slide 33 text

Authorization Do you have permission?

Slide 34

Slide 34 text

Security voters vote whether an authenticated user can perform an action on a subject $voter = new BlogPostVoter(); // returns VoterInterface::ACCESS_GRANTED, // VoterInterface::ACCESS_ABSTAIN or VoterInterface::ACCESS_DENIED $vote = $voter->vote($token, $blogPost, ['edit']);

Slide 35

Slide 35 text

Security voters vote whether an authenticated user can perform an action on a subject $voter = new BlogPostVoter(); // returns VoterInterface::ACCESS_GRANTED, // VoterInterface::ACCESS_ABSTAIN or VoterInterface::ACCESS_DENIED $vote = $voter->vote($token, $blogPost, ['edit']);

Slide 36

Slide 36 text

The framework also uses Security Voters to check for roles and authentication states $roleVoter = new RoleVoter('ROLE_'); $authenticatedVoter = new AuthenticatedVoter( $authenticationTrustResolver ); // Does the user have the role `ROLE_USER`? $roleVoter->vote($token, null, ['ROLE_USER']); // Is the user logged in? $authenticatedVoter->vote($token, null, ['IS_AUTHENTICATED_FULLY']);

Slide 37

Slide 37 text

The framework also uses Security Voters to check for roles and authentication states $roleVoter = new RoleVoter('ROLE_'); $authenticatedVoter = new AuthenticatedVoter( $authenticationTrustResolver ); // Does the user have the role `ROLE_USER`? $roleVoter->vote($token, null, ['ROLE_USER']); // Is the user logged in? $authenticatedVoter->vote($token, null, ['IS_AUTHENTICATED_FULLY']);

Slide 38

Slide 38 text

The framework also uses Security Voters to check for roles and authentication states $roleVoter = new RoleVoter('ROLE_'); $authenticatedVoter = new AuthenticatedVoter( $authenticationTrustResolver ); // Does the user have the role `ROLE_USER`? $roleVoter->vote($token, null, ['ROLE_USER']); // Is the user logged in? $authenticatedVoter->vote($token, null, ['IS_AUTHENTICATED_FULLY']);

Slide 39

Slide 39 text

Putting it all together $accessDecisionManager = new AccessDecisionManager([ new RoleVoter(), new BlogPostVoter(), ]); $hasRole = $accessDecisionManager->decide( $authenticatedToken, ['ROLE_USER'] ); $canEditBlogPost = $accessDecisionManager->decide( $authenticatedToken, ['edit'], $blogPost );

Slide 40

Slide 40 text

Putting it all together $accessDecisionManager = new AccessDecisionManager([ new RoleVoter(), new BlogPostVoter(), ]); $hasRole = $accessDecisionManager->decide( $authenticatedToken, ['ROLE_USER'] ); $canEditBlogPost = $accessDecisionManager->decide( $authenticatedToken, ['edit'], $blogPost );

Slide 41

Slide 41 text

Putting it all together $accessDecisionManager = new AccessDecisionManager([ new RoleVoter(), new BlogPostVoter(), ]); $hasRole = $accessDecisionManager->decide( $authenticatedToken, ['ROLE_USER'] ); $canEditBlogPost = $accessDecisionManager->decide( $authenticatedToken, ['edit'], $blogPost );

Slide 42

Slide 42 text

Putting it all together $accessDecisionManager = new AccessDecisionManager([ new RoleVoter(), new BlogPostVoter(), ]); $hasRole = $accessDecisionManager->decide( $authenticatedToken, ['ROLE_USER'] ); $canEditBlogPost = $accessDecisionManager->decide( $authenticatedToken, ['edit'], $blogPost );

Slide 43

Slide 43 text

When using the framework

Slide 44

Slide 44 text

A simple security configuration security: providers: database_users: entity: { class: App\Entity\User, property: username } encoders: App\Entity\User: 'bcrypt' firewalls: main: anonymous: true form_login: true access_control: - { path: ^/profile, roles: ROLE_USER }

Slide 45

Slide 45 text

framework providers: database_users: entity: { class: App\Entity\User, property: username } standalone $userProvider = new EntityUserProvider( $registry, App\Entity\User::class, 'username' );

Slide 46

Slide 46 text

framework encoders: App\Entity\User: 'bcrypt' standalone $encoderFactory = new EncoderFactory([ App\Entity\User::class => new BCryptPasswordEncoder($cost), ]);

Slide 47

Slide 47 text

framework firewalls: main: anonymous: true form_login: true standalone $authenticationManager = new AuthenticationProviderManager([ new AnonymousAuthenticationProvider($secret), new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]);

Slide 48

Slide 48 text

framework access_control: - { path: ^/profile, roles: ROLE_USER } standalone $accessDecisionManager = new AccessDecisionManager([ new RoleVoter(), ]); // if request matches ^/profile $granted = $accessDecisionManager->decide( $authenticatedToken, ['ROLE_USER'] );

Slide 49

Slide 49 text

In Summary

Slide 50

Slide 50 text

The security workflow is comprised of two phases. Authentication asks "Who are you?" Authorization asks "Are you allowed to do that?"

Slide 51

Slide 51 text

User providers load users from a source. User checkers check loaded users. Password encoders encode passwords.

Slide 52

Slide 52 text

Security voters vote to grant or deny access (or abstain). Symfony uses Security Voters to check roles and authentication states.

Slide 53

Slide 53 text

$kernel->terminate($request, $response);

Slide 54

Slide 54 text

No content