Thanks to visit codestin.com
Credit goes to github.com

Skip to content

New Guard Authentication System (e.g. putting the joy back into security) #14673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Sep 24, 2015

Conversation

weaverryan
Copy link
Member

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets at least partially: #14300, #11158, #11451, #10035, #10463, #8606, probably more
License MIT
Doc PR symfony/symfony-docs#5265

Hi guys!

Though it got much easier in 2.4 with pre_auth, authentication is a pain in Symfony. This introduces a new authentication provider called guard, with one goal in mind: put everything you need for any authentication system into one spot.

How it works

With guard, you can perform custom authentication just by implementing the GuardAuthenticatorInterface and registering it as a service. It has methods for every part of a custom authentication flow I can think of.

For a working example, see https://github.com/weaverryan/symfony-demo/tree/guard-auth. This uses 2 authenticators simultaneously, creating a system that handles form login and api token auth with a respectable amount of code. The security.yml is also quite simple.

This also supports "manual login" without jumping through hoops: https://github.com/weaverryan/symfony-demo/blob/guard-auth/src/AppBundle/Controller/SecurityController.php#L45

I've also tested with "remember me" and "switch user" - no problems with either.

I hope you like it :).

What's Needed

  1. Other Use-Cases?: Please think about the code and try it. What use-cases are we not covering? I want Guard to be simple, but cover the 99.9% use-cases.

  2. Remember me functionality cannot be triggered via manual login. That's true now, and it's not fixed, and it's tricky.

Deprecations?

This is a new feature, so no deprecations. But, creating a login form with a guard authenticator is a whole heck of a lot easier to understand than form_login or even simple_form. In a perfect world, we'd either deprecate those or make them use "guard" internally so that we have just one way of performing authentication.

Thanks!


// check the AdvancedUserInterface methods!
$this->userChecker->checkPreAuth($user);
$this->userChecker->checkPostAuth($user);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the checkPostAuth() be done after createAuthenticatedToken()?

I have a small concern regarding the user checker though. Say that before querying anything to find a user, I want to check if the IP has a time out, how would I do it here?

In the old situation I would have created my own simple-authenticator which did this. After this PR, I'd have to put it in a GuardAuthenticatorInterface. Neither of those solutions seems feasible for me as I would have to create either a weird inheritance tree or duplicate code in my authenticators. It would be great for you (and others ofc!) to think with me on the following points:

Regarding those points, after an x amount of failed attempts, I have to show a captcha which has to be displayed until either valid or another x attempts have failed. This bundle would add the 'captcha' type to a form which you can then re-use for both the validation and display. I want to do this before the authentication takes place (why query the database for a blocked IP). https://github.com/Gregwar/CaptchaBundle

Just a random brainfart
I can also imagine the possibility to automate certain validation points by using a FormType with constraints; With a data transformer to put the user in there automatically via a user provider based on the username.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot here :)

  1. Actually, checkPostAuth() is right, but checkPreAuth() was wrong - it should happen right after fetching the User, but before checking credentials (see UserAuthenticationProvider). I've just split a method on the interface to make this possible and moved the checkPreAuth() in the provider. Thanks for asking about this - I hadn't really realized I had things in the wrong spot.

  2. About the IP timeout idea, was this simpler with the simple-authenticator? Or are you saying that in both cases, you'd need to use inheritance or duplication to put the code into the "simple authenticator" or the "guard authenticator", so it's the same, but no better? I just commented on symfony/security - tagging UserCheckerInterface #11090 - I think it can be solved there.

  3. We should avoid using forms or validation anywhere. But more importantly, the "guard authenticators" are so simple that you can do whatever you want, including using forms and validation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding point 2, It will be similar. In my case I've actually implement a custom UserChecker, which I manually had to inject (can't configure that at all). I've made a custom method called checkPreUserLoad() in a custom UserCheckerInterface implementation and created an abstract authenticator that all our different authentication classes (simple-form, simple-pre-auth) extend. In your implementation, I would probably put half of this inside the part where you get the values from the request and pass the captcha valid/invalid/not required from there.

I was wondering if it would be possible to tackle that problem while we are at it.

    public function checkPreUserLoad()
    {
        $request  = $this->request_stack->getCurrentRequest();
        $resolved = $this->login_ip_resolver->resolveStatus($request->getClientIp());

        if ($resolved->requiresBlock()) {
            throw new BlockThresholdExceededException('Too many failed login attempts, ip blocked');
        }

        if (!$resolved->requiresCaptcha()) {
            return;
        }
        // captcha is required
        $form = $this->form_factory->create('login', null, ['add_captcha' => true]);
        $form->handleRequest($request);

        // only check the captcha, there is currently no username/password validation
        if ($request->request->has('login') && $form->has('captcha')) {
            if ($form->isSubmitted() && !$form->get('captcha')->isValid()) {
                throw new InvalidCaptchaException('Provided captcha was not valid');
            }

            // captcha is valid, we can skip it
            return;
        }

        throw new CaptchaThresholdExceededException('Too many failed login attempts, captcha required');
    }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the user checker stuff in #11090 solve this? If not, what do you propose? This looks like a lot of business logic to me, and putting that either into the user checker or somewhere in the authenticator (or the authenticator calls out to another service which holds the logic) seems right and simple to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the solution I proposed in #11090 will in fact solve this issue. That is Backwards compatible, just need to modify the configuration for this factory.

Eventually I can use a service decorator to replace the security.user_checker and voila, everything magically works without needing to change this code.

@linaori
Copy link
Contributor

linaori commented May 18, 2015

I really love this PR! 👍 It makes things a lot simpler. As you can see I have already worked my way through the changes to comment. It's really nice to see the difference between the pre and post authentication tokens.

Edit: also fixes #14300

@javiereguiluz
Copy link
Member

👍 congrats @weaverryan! It's a very nice move towards reducing perceived complexity of the Security component. I'd like to make two quick comments regarding the naming and code of some methods:

1) Are you planning to add new methods to get credentials from different sources than the Request object? If not, maybe the ...FromRequest part of the method name is redundant:

// redundant method name?
public function getCredentialsFromRequest(Request $request) { ... }

// acceptable alternative?
public function getCredentials(Request $request) { ... }

2) The autoLogin() code looks very convoluted to me. In an ideal world, I'd like to do the following:

$security->login($user);

I know that we can't do that for Symfony, but could we simplify that code as much as possible?

* @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, $guardAuthenticators, LoggerInterface $logger = null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the docblock, $guardAuthenticators is an array of GuardAuthenticatorInterface. Should the argument be array $guardAuthenticators, or is this left out intentionally?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not intentional at all - thanks for catching it

@weaverryan
Copy link
Member Author

@javiereguiluz

  • Good idea on checkCredentials() - I've changed that.
  • For autoLogin, I hear you. The system is already a big improvement over before, where you had not access to your normal "success listener Response" nor an automatic way to dispatch the "interactive login" event, which now happens. What we're missing to make it easier is "user-land" access to which firewall we are currently under. I'm not aware of any way to access this currently, which is why we have that ugly secured_area thing in there.

Thanks!

* @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, array $guardAuthenticators, LoggerInterface $logger = null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything preventing you from using a NullLogger instance if not provided ? In order to avoid such things:

if (null !== $this->logger) {
    ...
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing technical preventing that, but NullLogger isn't used anywhere in core currently - having an optional argument like this is used :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I think he means is the following:

$this->logger = $logger ?: new NullLogger();

IMO this is a cleaner solution as you don't have to worry about if($this->logger) statements. You can just log away safely.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #14682

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree with @iltar it does simplify a little bit IMO

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👎 for a NullLogger, Symfony should take options for most performance.

@ogizanagi
Copy link
Contributor

Wow ! This looks great ! 👍


public function setAuthenticated($authenticated)
{
throw new \LogicException('The PreAuthenticationGuardToken is *always* not authenticated.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know it's quite common in English, but being dutch, "always not" is very confusing for me. What about replacing it with "never"?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either is *never* authenticated, or is *always* unauthenticated makes more sense to me.

private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null)
{
if (!$guardAuthenticator->supportsRememberMe()) {
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should you not inform that the guard doesn't support rembember-me? And do this check after checking if remember-me was actually enabled for the firewall.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable to me :) making that change

@OskarStark
Copy link
Contributor

great work 👍

@ad3n
Copy link

ad3n commented Jul 20, 2015

very nice 👍

@malas
Copy link

malas commented Jul 23, 2015

I waited for this so long! Hope it gets into core ASAP

@giovkanata
Copy link

Well done! This was one of the most lacked features in symfony until now.

@nielvrom
Copy link

Very nice! In the beginning of learning Symfony the authentication was not easy for me! This makes it so much easier for new users!

fabpot added a commit that referenced this pull request Sep 24, 2015
…back into security) (weaverryan)

This PR was merged into the 2.8 branch.

Discussion
----------

New Guard Authentication System (e.g. putting the joy back into security)

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | at least partially: #14300, #11158, #11451, #10035, #10463, #8606, probably more
| License       | MIT
| Doc PR        | symfony/symfony-docs#5265

Hi guys!

Though it got much easier in 2.4 with `pre_auth`, authentication is a pain in Symfony. This introduces a new authentication provider called guard, with one goal in mind: put everything you need for *any* authentication system into one spot.

### How it works

With guard, you can perform custom authentication just by implementing the [GuardAuthenticatorInterface](https://github.com/weaverryan/symfony/blob/guard/src/Symfony/Component/Security/Guard/GuardAuthenticatorInterface.php) and registering it as a service. It has methods for every part of a custom authentication flow I can think of.

For a working example, see https://github.com/weaverryan/symfony-demo/tree/guard-auth. This uses 2 authenticators simultaneously, creating a system that handles [form login](https://github.com/weaverryan/symfony-demo/blob/guard-auth/src/AppBundle/Security/FormLoginAuthenticator.php) and [api token auth](https://github.com/weaverryan/symfony-demo/blob/guard-auth/src/AppBundle/Security/TokenAuthenticator.php) with a respectable amount of code. The [security.yml](https://github.com/weaverryan/symfony-demo/blob/guard-auth/app/config/security.yml) is also quite simple.

This also supports "manual login" without jumping through hoops: https://github.com/weaverryan/symfony-demo/blob/guard-auth/src/AppBundle/Controller/SecurityController.php#L45

I've also tested with "remember me" and "switch user" - no problems with either.

I hope you like it :).

### What's Needed

1) **Other Use-Cases?**: Please think about the code and try it. What use-cases are we *not* covering? I want Guard to be simple, but cover the 99.9% use-cases.

2) **Remember me** functionality cannot be triggered via manual login. That's true now, and it's not fixed, and it's tricky.

### Deprecations?

This is a new feature, so no deprecations. But, creating a login form with a guard authenticator is a whole heck of a lot easier to understand than `form_login` or even `simple_form`. In a perfect world, we'd either deprecate those or make them use "guard" internally so that we have just **one** way of performing authentication.

Thanks!

Commits
-------

a01ed35 Adding the necessary files so that Guard can be its own installable component
d763134 Removing unnecessary override
e353833 fabbot
dd485f4 Adding a new exception and throwing it when the User changes
302235e Fixing a bug where having an authentication failure would log you out.
396a162 Tweaks thanks to Wouter
c9d9430 Adding logging  on this step and switching the order - not for any huge reason
31f9cae Adding a base class to assist with form login authentication
0501761 Allowing for other authenticators to be checked
293c8a1 meaningless author and license changes
81432f9 Adding missing factory registration
7a94994 Thanks again fabbot!
7de05be A few more changes thanks to @iltar
ffdbc66 Splitting the getting of the user and checking credentials into two steps
6edb9e1 Tweaking docblock on interface thanks to @iltar
d693721 Adding periods at the end of exceptions, and changing one class name to LogicException thanks to @iltar
eb158cb Updating interface method per suggestion - makes sense to me, Request is redundant
c73c32e Thanks fabbot!
6c180c7 Adding an edge case - this should not happen anyways
180e2c7 Properly handles "post auth" tokens that have become not authenticated
873ed28 Renaming the tokens to be clear they are "post" and "pre" auth - also adding an interface
a0bceb4 adding Guard tests
05af97c Initial commit (but after some polished work) of the new Guard authentication system
330aa7f Improving phpdoc on AuthenticationEntryPointInterface so people that implement this understand it
@fabpot
Copy link
Member

fabpot commented Sep 24, 2015

The subtree-split is now active for this repository and available here:

https://github.com/symfony/security-guard
https://packagist.org/packages/symfony/security-guard

@derrabus
Copy link
Member

@weaverryan Symfony 2.7 compatibility in composer.json wouldn't be too bad, so I could try out the guard in an existing application without pulling the entire framework from an unstable branch.

Apart from that: 👏 Great work, I'm glad to see this feature being merged. 😃

@derrabus
Copy link
Member

The unit tests pass, if I pull every required Symfony component from 2.7.4 but security-core. As far as I can tell, the only reason why security-core has to be ~2.8@dev is the usage of a new exception class (AuthenticationExpiredException). I'm not sure if you would want to work around this.

But what you can probably do is setting security-http to ~2.7.

@TomasVotruba
Copy link
Contributor

@weaverryan Great job!

@ad3n
Copy link

ad3n commented Sep 25, 2015

thanks @fabpot 👍

$this->event = null;
$this->logger = null;
$this->request = null;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing $this->rememberMeServices = null;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in #15920

@weaverryan weaverryan deleted the guard branch September 26, 2015 14:10
@weaverryan weaverryan mentioned this pull request Sep 26, 2015
@weaverryan
Copy link
Member Author

@derrabus I've opened #15920 to lower the http requirement. But I don't think we'll want to add a class_exists with the AuthenticationExpiredException to get 2.7 compatibility

fabpot added a commit that referenced this pull request Sep 27, 2015
…stof)

This PR was merged into the 2.8 branch.

Discussion
----------

Add the replace rules for the security-guard component

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

The update of composer replacements was forgotten in #14673

Commits
-------

5ef8abc Add the replace rules for the security-guard component
fabpot added a commit that referenced this pull request Sep 27, 2015
This PR was merged into the 2.8 branch.

Discussion
----------

Guard minor tweaks

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

Various completely minor things, most from suggestions on #14673

Commits
-------

869d5a7 tweaking message related to configuration edge case that we want to be helpful with
da4758a Minor tweaks - lowering the required security-http requirement and nulling out a test field
@rvanlaak
Copy link
Contributor

Nice! Would this also allow "simple" configuration via annotations? #13950

fabpot added a commit that referenced this pull request Oct 2, 2015
…tar)

This PR was squashed before being merged into the 2.8 branch (closes #14721).

Discussion
----------

[Security] Configuring a user checker per firewall

_Changed my base branch to avoid issues, closed old PR_

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed ticket | #11090 and helps #14673
| License       | MIT
| Doc PR        | symfony/symfony-docs/pull/5530

This pull request adds support for a configurable user checker per firewall. An example could be:

```yml
services:
    app.user_checker:
        class: App\Security\UserChecker
        arguments:
            - "@request_stack"

security:
    firewalls:
        secured_area:
            pattern: ^/
            anonymous: ~
            basic_auth: ~
            user_checker: app.user_checker

```
The above example will use the `UserChecker` defined as `app.user_checker`. If the `user_checker` option is left empty, `security.user_checker` will  be used. If the `user_checkers` option is not defined, it will fall back to the original behavior to not break backwards compatibility and will validate using the existing `UserChecker`: `security.user_checker`.

I left the default argument in the service definitions to be `security.user_checker` to include backwards compatibility for people who for some reason don't have the extension executed. You can obtain the checker for a specific firewall by appending the firewall name to it. For the firewall `secured_area`, this would be `security.user_checker.secured_area`.

Commits
-------

76bc662 [Security] Configuring a user checker per firewall
@Codenator81
Copy link

@weaverryan Super simple! Love it!

*
* @throws AuthenticationException
*/
public function checkCredentials($credentials, UserInterface $user);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes be nervous that this method, by default, results in a user being successfully authenticated and requires throwing an exception in the case that the user should NOT be authenticated. In the case that a developer accidentally writes a bug that doesn't throw an exception in a complex (or any) setup, the mistake results in _users being authenticated that shouldn't have been._

I think a safer way to do this would be to explicitly return a value (perhaps a boolean) indicating whether the credential check was successful or not. In the GuardAuthenticationProvider you could very easily check this boolean value for falseness (in the case that someone doesn't return) and then throw an AuthenticationException to keep the rest of the logic in the Provider the same.

TLDR; Which is worse? Authenticating a user who has invalid credentials? Or not authenticating a user who has valid credentials? I guess I'd rather not take a chance with security. Thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I agree - the same could be said for returning false. For example, if you collect the result in a boolean variable $valid which is returned, that variable could easily have the wrong truth value simply by mistaking && for || somewhere in your code.

The only way to prevent bugs like the one you described is by properly testing the authenticator. I don't think that throwing an exception vs. returning false makes any difference here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see #16395

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, maybe I should look at the tip of 2.8 before I go commenting on old PRs. Sorry everyone.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@patrick-mcdougle No, thank you! I was trying to find you at the conference - I made a pull request within an hour after your asked this very good question - and wanted to tell you about it :).

@linaori
Copy link
Contributor

linaori commented Nov 19, 2015

  • If you return a boolean, how will you know what went wrong?
  • How will you be able to trace back the reason of not being able to login, to this specific method implementation?

Imo this is a developer mistake and tests should've caught that. There's already a weird case with ParamConverters having to return a boolean for some reason which makes it far more confusion than necessary.

As long as it's stated that an exception should be thrown in the docs: 👎 from my side for this request.

Maybe result objects are a better suggestion, where you can specify a reason. It would conflict with the current flow of AuthenticationException though.

weaverryan added a commit to symfony/symfony-docs that referenced this pull request Nov 27, 2015
…eaverryan)

This PR was merged into the 2.8 branch.

Discussion
----------

Documentation for the new Guard authentication style

| Q             | A
| ------------- | ---
| Doc fix?      | no
| New docs?     | yes symfony/symfony#14673
| Applies to    | 2.8+
| Fixed tickets | n/a

Hi guys!

This is a WIP documentation for a proposed new authentication system. I've written just enough so people can understand how to use it, but will finish it later once the code has gotten reviewed.

Thanks!

Commits
-------

51720c7 Many fixes thanks to great review from ogizanagi, javiereguiluz and others
4752d4c adding one clarifying message
9782ff1 adding toc entries
62dcae3 Using JsonResponse + cleanup
440fe6f revamping Guard article
bfce91b Fixing minor comments
9e411fe I'm extending the abstract class - so mention that. Also adding anonymous
ac107c7 WIP documentation for the new guard auth
xabbuh added a commit to symfony/symfony-docs that referenced this pull request Feb 10, 2016
…okbook documentation (mheki)

This PR was squashed before being merged into the 2.8 branch (closes #5886).

Discussion
----------

[2.8] Add "How to Use Multiple Guard Authenticators" cookbook documentation

| Q             | A
| ------------- | ---
| Doc fix?      | no
| New docs?     | yes (symfony/symfony#14673)
| Applies to    | `2.8` onwards

Hi guys,
this is my first contribution to the symfony docs.
During my preparations for the Symfony Guard component workshops I have spent some time trying to figure out the problem described here.
I hope this cookbook entry will help others save their time.

cc @weaverryan

Thanks!

Commits
-------

121196d [2.8] Add "How to Use Multiple Guard Authenticators" cookbook documentation
@bogdaniel
Copy link

Question in your working example shouldn't be return true if else?
File: FormLoginAuthenticator.php
Line: 60

        if (!$passwordValid) {
            throw new BadCredentialsException();
        }

To

        if ($passwordValid)
            return true;
        else
            throw new BadCredentialsException();

Because i think it will always hit the user with a BadCredentialsException()

@wouterj
Copy link
Member

wouterj commented Mar 16, 2016

@bogdaniel this behaviour was changed after this PR (but before releasing 2.8.0) it seems like the demo hasn't been updated.

@derrabus
Copy link
Member

@wouterj This shouldn't be the place to maintain an up-to-date demo for this feature. imho, the PR should be seen as a documentation of the development process and the demo shows how the feature has worked at the time when the PR was merged.

@bogdaniel: Please see the docs for an up-to-date documentation on this feature: http://symfony.com/doc/current/cookbook/security/guard-authentication.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.