Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Security] AuthenticatorManager to make "authenticators" first-class security#33558

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged

Conversation

wouterj
Copy link
Member

@wouterjwouterj commentedSep 11, 2019
edited
Loading

QA
Branch?master
Bug fix?no
New feature?yes
Deprecations?no
Tickets-
LicenseMIT
Doc PRtbd

The tl;dr

The old authentication listener + authentication provider system was replaced by a new "authenticator" system (similar to Guard authentication). All existing "auth systems" (e.g.form_login are now written as an "authenticator" in core).

Instead of each "authentication system" registering its own listener in theFirewall, there is now only one listener:AuthenticatorManagerListener

  • Firewall -> executesAuthenticatorManagerListener
  • AuthenticatorManagerListener -> callsAuthenticatorManager
  • AuthenticatorManager -> calls each authenticator

This PR containsno deprecations and the "new system" ismarked as experimental. This allows to continue to develop the new Security system during the 5.x release cycle without disturbing Symfony users. In 5.4, we can deprecate "old" Security and remove it completely in 6.0.

Important Decisions

  • A)The new authentication manager -AuthenticatorManager - now dispatches 3 important "hook" events:

    • VerifyAuthenticatorCredentialsEvent: occurs at the point when a "password" needs to be checked. Allows us to centralize password checking, CSRF validation, password upgrading and the "user checker" logic.
    • LoginSuccessEvent: Dispatched after a successful authentication. E.g. used by remember me listener.
    • LoginFailedEvent: Dispatched after an unsuccessful authentication. Also used by remember me (and in theory could be used for login throttling).
  • B)getCredentials(),getUser() andcheckCredentials() methods from old Guard are gone: their logic is centralized.
    Authenticators now have anauthenticate(Request $request): PassportInterface method. A passport contains the user object, the credentials and any other add-in Security badges (e.g. CSRF):

    publicfunctionauthenticate(Request$request):PassportInterface{returnnewPassport($user,newPasswordCredentials($request->get('_password')),        [newCsrfBadge($request->get('_token'))        ]    );}

    All badges (including the credentials) need to be resolved by listeners toVerifyAuthenticatorCredentialsEvent. There is build-in core support for the following badges/credentials:

    • PasswordCredentials: validated using the password encoder factory
    • CustomCredentials: allows a closure to do credentials checking
    • CsrfTokenBadge: automatic CSRF token verification
    • PasswordUpgradeBadge: enables password migration
    • RememberMeBadge: enables remember-me support for this authenticator
  • C)AuthenticatorManager contains all logic to authenticate
    As authenticators always relate to HTTP, theAuthenticatorManager contains all logic to authenticate. It has three methods, the most important two are:

    • authenticateRequest(Request $request): TokenInterface: Doing what is previously done by a listener and an authentication provider;
    • authenticateUser(UserInterface $user, AuthenticatorInterface $authenticator, Request $request, array $badges = []) for manual login in e.g. a controller.
  • D)One AuthenticatorManager per firewall
    In the old system, there was 1 authentication manager containing all providers and each firewall had a specific firewall listener. In the new system, each firewall has a specific authentication manager.

  • E)Pre-authentication tokens are dropped.
    As everything is now handled insideAuthenticatorManager and everything is stored in the SecurityPassport, there was no need for a token anymore (removing lots of confusion about what information is inside the token).

    This change deprecates 2 authentication calls: one inAuthorizationChecker#isGranted() and one inAccessListener. These seem now to be mis-used to reload users (e.g. re-authenticate the user after you change their roles). This (some "way" to change a user's roleswithout logging them out) needs to be "fixed"/added in another PR.

  • F)The remember me service now usesall user providers
    Previously, only user providers of authentication providers listening on that firewall were used. This change is due to practical reasons and we don't think it is common to have 2 user providers supporting the same user instance. In any case, you can always explicitly configure the user provider underremember_me.

  • G)Auth Providers No Longer Clear the Token on Auth Failure
    Previously, authentication providers did$this->tokenStorage->setToken(null) upon authentication failure. This is not yet implemented: our reasoning is that if you've authenticated successfully using e.g. the login form, why should you be logged out if you visit the same login form and enter wrong credentials?
    The pre-authenticated authenticators are an exception here, they do reset the token upon authentication failure, just like the old system.

  • H)CSRF Generator Service ID No Longer Configurable
    The old Form login authentication provider allowed you to configure the CSRF generator service ID. This is no longer possible with the automated CSRF listener. This feature was introduced in the first CSRF commit and didn't get any updates ever since, so we don't think this feature is required. This could also be accomplished by checking CSRF manually in your authenticator, instead of using the automated check.

Future Considerations

  • Remove Security sub-components: Move CSRF toSymfony\Component\Csrf (just like mime); Deprecated Guard; Put HTTP + Core assymfony/security. This means moving the new classes toSymfony\Component\Security

  • Convert LDAP to the new system

  • This is fixed (and merged) by[Security] Refactor logout listener to dispatch an event instead #36243There is a need for some listeners to listen for events on one firewall, but not another (e.g.RememberMeListener). This is now fixed by checking the$providerKey. We thought it might be nice to introduce a feature to the event dispatcher:

    • Create one event dispatcher per firewall;
    • Extend thekernel.event_subscriber tag, so that you can optionally specify the dispatcher service ID (to allow listening on events for a specific dispatcher);
    • Add a listener that always also triggers the events on the main event dispatcher, in case you want a listener that is listening on all firewalls.
  • Drop theAnonymousToken andAnonymousAuthenticator: Anonymous authentication has never made much sense and complicates things (e.g. the user can be a string). For access control, an anonymous user has the same meaning as an un-authenticated one (null). This require changes in theAccessListener andAuthorizationChecker and probably also a new Security attribute (to replaceIS_AUTHENTICATED_ANONYMOUSLY). Related issues:[Security] deprecate non UserInterface users #34909,[Security][RFC] Exception thrown on call to isGranted without token #30609

How to test

  1. Install the Symfony demo application (or any Symfony application)
  2. Clone my Symfony fork (git clone git@github.com:wouterj/symfony) and checkout my branch (git checkout security/deprecate-providers-listeners)
  3. Use the link utility to link my fork to the Symfony application:/path/to/symfony-fork/link /path/to/project
  4. Enable the new system by settingsecurity.enable_authenticator_manager totrue

fbourigault, romaricdrigon, kbond, dbrumann, chr-hertel, doubbz, 94noni, dsentker, iscluisgarcia, colinodell, and 4 more reacted with thumbs up emojijeremyFreeAgent and pamil reacted with heart emojichalasr, yceruto, apfelbox, sstok, mikemilano, iscluisgarcia, hvt, and pamil reacted with rocket emoji
@wouterjwouterjforce-pushed thesecurity/deprecate-providers-listeners branch 2 times, most recently from8960e3a tob485767CompareSeptember 11, 2019 19:25
@nicolas-grekasnicolas-grekas added this to thenext milestoneSep 12, 2019
@wouterjwouterjforce-pushed thesecurity/deprecate-providers-listeners branch fromb485767 to93ce631CompareSeptember 15, 2019 11:52
@Nyholm
Copy link
Member

Just note:
A user should also be able to use the provider/listener/handler/factory implementation. I love Guard but sometimes it is not enough to work with my super special authentication.

I recently had a scenario where I get a user if passed with an authenticated jwt token. I should create a user entity if it missing (by using data in auth0), if email is valid I create an authenticated token, if not, I will ask the user to login (with different authenticator).

I was not able to write a nice solution with guard for this scenario, so I had to use the “low level code”.

I’m all for promoting guard and trying to make it better, but I think it would be use the more flexible solution when needed.

@Koc
Copy link
Contributor

Koc commentedSep 17, 2019

Good movement, but AFIK it is impossible to define custom config nodes with guards. Should we add method similar toaddConfiguration(NodeDefinition $builder) insideGuardFactoryInterface like already done inSecurityFactoryInterface?

@wouterj
Copy link
MemberAuthor

I’m all for promoting guard and trying to make it better, but I think it would be use the more flexible solution when needed.

Hmm, interesting. Our assumption has always been that Guard is not less flexible than the current listener-provider solution. Guard only combines them into one class, which makes it more difficult to reuse partial systems (i.e. the DaoAuthenticationProvider).

I do not fully follow your requirements, but it would be interesting to see whether or not Guard (or a modification in Guard) could have fixed your requirements.


For now, I would say it doesn't matter as this PR is not about deprecating the old system. It's only introducing a new system next to it, to allow experimenting (and discovering exactly cases like you mentioned). For that reason, it's maybe a good idea to mark the new classes as@experimental (or do we only mark complete components as experimental?)

@fabpot@nicolas-grekas@weaverryan what should we do to get this into the next release (probably not 4.4, but 5.0?)

@wouterj
Copy link
MemberAuthor

wouterj commentedSep 21, 2019
edited
Loading

Good movement, but AFIK it is impossible to define custom config nodes with guards. Should we add method similar toaddConfiguration(NodeDefinition $builder) insideGuardFactoryInterface like already done inSecurityFactoryInterface?

In order to be usable a class should as of now implement both interfaces, so theaddConfiguration() method is added by the other interface. For clarity, we can add the methods to the new interface as well.

@fabpot
Copy link
Member

Making it experimental means that it cannot be part of 4.4. But it can be part of 5.0.

@ghostghost deleted a comment fromwouterjOct 10, 2019
@wouterjwouterjforce-pushed thesecurity/deprecate-providers-listeners branch from93ce631 tob21b514CompareDecember 13, 2019 16:37
@wouterjwouterj changed the base branch from4.4 tomasterDecember 14, 2019 11:50
@wouterjwouterjforce-pushed thesecurity/deprecate-providers-listeners branch 3 times, most recently from959fbbf todc68936CompareJanuary 26, 2020 14:52
@wouterjwouterjforce-pushed thesecurity/deprecate-providers-listeners branch 2 times, most recently from2b2d17f to7799078CompareFebruary 12, 2020 23:09
@wouterjwouterjforce-pushed thesecurity/deprecate-providers-listeners branch 2 times, most recently from6ba8ec6 to028baf0CompareMarch 1, 2020 10:38
@fabpot
Copy link
Member

Thank you@wouterj.

wouterj and weaverryan reacted with laugh emojiwouterj, weaverryan, javiereguiluz, chalasr, OlivierToussaint, hvt, and chapterjason reacted with hooray emojiwouterj, weaverryan, javiereguiluz, chalasr, mykiwi, recchia, jeremyFreeAgent, and db306 reacted with heart emoji

@fabpotfabpot merged commit1abdcbb intosymfony:masterApr 21, 2020
@wouterjwouterj deleted the security/deprecate-providers-listeners branchApril 21, 2020 12:46
@wouterj
Copy link
MemberAuthor

wouterj commentedApr 21, 2020
edited
Loading

Fyi,@noniagriconomie, I like your review and we've not forgotten your comments. However, we wanted to finally get this one in master, so I'll submit a PR fixing your comments later this day

phpfour, chalasr, skmedix, 94noni, and pamil reacted with thumbs up emoji

@noniagriconomie
Copy link
Contributor

@wouterj understood, good to have the security part upgraded :)

chalasr added a commit that referenced this pull requestApr 21, 2020
This PR was merged into the 5.1-dev branch.Discussion----------[Security] Fix missing nullable in CsrfTokenBadge| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | yes| New feature?  | no| Deprecations? | no| Tickets       || License       | MIT| Doc PR        |Related to#33558 I noticed a minor 🤏  bug with the method return-type.Commits-------5cb633c Update CsrfTokenBadge.php
wouterj added a commit to wouterj/symfony that referenced this pull requestApr 21, 2020
wouterj added a commit to wouterj/symfony that referenced this pull requestApr 21, 2020
chalasr added a commit that referenced this pull requestApr 21, 2020
…wouterj)This PR was merged into the 5.1-dev branch.Discussion----------[Security] Apply left-over review comments from#33558| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | no| Deprecations? | no| Tickets       | -| License       | MIT| Doc PR        | n/aThis applies the review comments of@noniagriconomie in#33558. It's mostly doc fixes and one extra security-safeguard by resetting the plaintext password early (similair to what is done in `PasswordCredentials`).Commits-------be3a9a9 Applied left-over review comments from#33558
fabpot added a commit that referenced this pull requestApr 30, 2020
…ultiple authenticators (wouterj)This PR was squashed before being merged into the 5.1-dev branch.Discussion----------[Security] Require entry_point to be configured with multiple authenticators| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | no| Deprecations? | no| Tickets       | -| License       | MIT| Doc PR        | tbdSee@weaverryan's comment at#33558 (comment):> I have it on my list to look at the entrypoint stuff more closely. But my gut reaction is this: let's fix them (or try to... or maybe in a PR after this) :). What I mean is this:>> -    It's always been confusing that your firewall may have multiple auth mechanisms that have their own "entry point"... and one is chosen seemingly at random :). I know it's not random, but why does the entrypoint from `form_login` "win" over `http_basic` if I have both defined under my firewall?>> -    Since we're moving to a new system, why not throw an exception the _moment_ that a firewall has multiple entrypoints available to it. Then we _force_ the user to choose the _one_ entrypoint that should be used.---**Before** (one authenticator)```yamlsecurity:  enable_authenticator_manager: true  firewalls:    main:      form_login: ...# form login is your entry point```**After**Same as before---**Before** (multiple authenticators)```yamlsecurity:  enable_authenticator_manager: true  firewalls:    main:      http_basic: ...      form_login: ...# for some reason, FormLogin is now your entry point! (config order doesn't matter)```**After**```yamlsecurity:  enable_authenticator_manager: true  firewalls:    main:      http_basic: ...      form_login: ...      entry_point: form_login```---**Before** (custom entry point service)```yamlsecurity:  enable_authenticator_manager: true  firewalls:    main:      http_basic: ...      form_login: ...      entry_point: App\Security\CustomEntryPoint```**After**Same as beforeCommits-------7e86169 [Security] Require entry_point to be configured with multiple authenticators
symfony-splitter pushed a commit to symfony/security-bundle that referenced this pull requestApr 30, 2020
…ultiple authenticators (wouterj)This PR was squashed before being merged into the 5.1-dev branch.Discussion----------[Security] Require entry_point to be configured with multiple authenticators| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | no| Deprecations? | no| Tickets       | -| License       | MIT| Doc PR        | tbdSee@weaverryan's comment atsymfony/symfony#33558 (comment):> I have it on my list to look at the entrypoint stuff more closely. But my gut reaction is this: let's fix them (or try to... or maybe in a PR after this) :). What I mean is this:>> -    It's always been confusing that your firewall may have multiple auth mechanisms that have their own "entry point"... and one is chosen seemingly at random :). I know it's not random, but why does the entrypoint from `form_login` "win" over `http_basic` if I have both defined under my firewall?>> -    Since we're moving to a new system, why not throw an exception the _moment_ that a firewall has multiple entrypoints available to it. Then we _force_ the user to choose the _one_ entrypoint that should be used.---**Before** (one authenticator)```yamlsecurity:  enable_authenticator_manager: true  firewalls:    main:      form_login: ...# form login is your entry point```**After**Same as before---**Before** (multiple authenticators)```yamlsecurity:  enable_authenticator_manager: true  firewalls:    main:      http_basic: ...      form_login: ...# for some reason, FormLogin is now your entry point! (config order doesn't matter)```**After**```yamlsecurity:  enable_authenticator_manager: true  firewalls:    main:      http_basic: ...      form_login: ...      entry_point: form_login```---**Before** (custom entry point service)```yamlsecurity:  enable_authenticator_manager: true  firewalls:    main:      http_basic: ...      form_login: ...      entry_point: App\Security\CustomEntryPoint```**After**Same as beforeCommits-------7e861698e7 [Security] Require entry_point to be configured with multiple authenticators
fabpot added a commit that referenced this pull requestMay 3, 2020
…m (wouterj)This PR was merged into the 5.1-dev branch.Discussion----------[Security] Removed anonymous in the new security system| Q             | A| ------------- | ---| Branch?       | master| Bug fix?      | no| New feature?  | yes| Deprecations? | no| Tickets       | -| License       | MIT| Doc PR        | tbdThis was one of the "Future considerations" of#33558:> Drop the AnonymousToken and AnonymousAuthenticator: Anonymous authentication has never made much sense and complicates things (e.g. the user can be a string). For access control, an anonymous user has the same meaning as an un-authenticated one (null). This require changes in the AccessListener and AuthorizationChecker and probably also a new Security attribute (to replace IS_AUTHENTICATED_ANONYMOUSLY). Related issues:#34909,#30609This new experimental system is probably a once-in-a-lifetime change to make this change.@weaverryan and I have had some brainstorming about this. Some reasons why we think it makes 100% sense to do this change:* From a Security perspective, **a user that is not authenticated is similar to an "unknown" user**: They both have no rights at all.* **The higher level consequences of the AnonymousToken are confusing and inconsistent**:  * It's hard to explain people new to Symfony Security that not being logged in still means you're authenticated within the Symfony app  * To counter this, some higher level APIs explicitly mark anonymous tokens as not being authenticated, see e.g. the [`is_authenticated()` expression language function](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguageProvider.php#L33-L37)  * The anonymous authentication resulted in the `IS_AUTHENTICATED` security attribute being removed from#35854, as there was no clear consensus on what its meaning should be* **Spring Security, which is where this originated from, makes Anonymous a very special case**:  > Finally, there is an AnonymousAuthenticationFilter, which is chained after the normal authentication mechanisms and automatically adds an AnonymousAuthenticationToken to the SecurityContextHolder if there is no existing Authentication held there.  >  > Note that there is no real conceptual difference between a user who is “anonymously authenticated” and an unauthenticated user. Spring Security's anonymous authentication just gives you a more convenient way to configure your access-control attributes. Calls to servlet API calls such as getCallerPrincipal, for example, will still return null even though there is actually an anonymous authentication object in the SecurityContextHolder.* Symfony uses AnonymousToken much more than "just for convience in access-control attributes". **Removing anonymous tokens allows us to move towards only allowing `UserInterface` users**:#34909---Removing anonymous tokens do have an impact on `AccessListener` and `AuthorizationChecker`. These currently throw an exception if there is no token in the storage, instead of treating them like "unknown users" (i.e. no roles). See#30609 on a RFC about removing this exception. We can also see e.g. the [Twig `is_granted()` function explicitly catching this exception](https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php#L37-L52).* **To make the changes in `AccessListener` and `AuthorizationChecker` BC, a flag has been added - default enabled - to throw an exception when no token is present** (which is automatically disabled when the new system is used). In Symfony 5.4 (or whenever the new system is no longer experimental), we can deprecate this flag and in 6.0 we can never throw the exception anymore.* **`anonymous: lazy` has been deprecated in favor of `{ anonymous: true, lazy: true }`** This fixes the dependency on `AnonymousFactory` from the `SecurityExtension` and allows removing the `anonymous` option.* **Introduced `PUBLIC_ACCESS` Security attribute** as alternative of `IS_AUTHENTICATED_ANONYMOUSLY`. Both work in the new system, the latter only triggers a deprecation notice (but may be usefull to allow switching back and forth between old and new system).cc@javiereguiluz you might be interested, as I recently talked with you about this topicCommits-------ac84a6c Removed AnonymousToken from the authenticator system
@nicolas-grekasnicolas-grekas modified the milestones:next,5.1May 4, 2020
@fabpotfabpot mentioned this pull requestMay 5, 2020
@curry684curry684 mentioned this pull requestJun 15, 2020
@AkashicSeer

This comment has been minimized.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@javiereguiluzjaviereguiluzjaviereguiluz left review comments

@rosierrosierrosier left review comments

@nicolas-grekasnicolas-grekasnicolas-grekas left review comments

@stofstofstof left review comments

@noniagriconomienoniagriconomienoniagriconomie left review comments

@fabpotfabpotfabpot approved these changes

@weaverryanweaverryanweaverryan approved these changes

@chalasrchalasrchalasr approved these changes

Assignees
No one assigned
Projects
None yet
Milestone
5.1
Development

Successfully merging this pull request may close these issues.

13 participants
@wouterj@Nyholm@Koc@fabpot@weaverryan@javiereguiluz@noniagriconomie@AkashicSeer@rosier@nicolas-grekas@stof@chalasr@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp