Skip to content
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

[Security] Decouple passwords from UserInterface #40267

Merged
merged 1 commit into from
Mar 6, 2021

Conversation

chalasr
Copy link
Member

@chalasr chalasr commented Feb 21, 2021

Q A
Branch? 5.x
Bug fix? no
New feature? yes
Deprecations? yes
Tickets #23081, helps with #39308
License MIT
Doc PR todo

This PR addresses a long-standing issue of the Security component: UserInterface is coupled to passwords.
It does it by moving the getPassword() method from UserInterface to a PasswordAuthenticatedUserInterface, and the getSalt() method to a LegacyPasswordAuthenticatedUserInterface.

Steps:

  • In 5.3, we add the new interface and, at places where password-based authentication happens, trigger deprecation notices when a UserInterface object does not implement the new interface(s). The UserInterface is kept as-is until 6.0.
  • In 6.0, we can remove the methods from UserInterface as well as support for using password authentication with user objects not implementing the new interface(s).

As a side-effect, some password-related interfaces (UserPasswordHasherInterface and PasswordUpgraderInterface) must change their signatures to type-hint against the new interface.
That is done in a BC way, which is to make the concerned methods virtual until 6.0, with deprecation notices triggered from callers and concrete implementations.

Benefits:
In 6.0, applications that use password-less authentication (e.g. login links) won't need to write no-op getPassword() and getSalt() in order to fulfil the UserInterface contract.

For applications that do use password-based authentication, they will need to opt-in explicitly by implementing the relevant interface(s).

This build on great discussions with @wouterj and @nicolas-grekas, and it is part of the overall rework of the Security component.

@chalasr
Copy link
Member Author

chalasr commented Feb 21, 2021

Build failure with high-deps expected (will be fixed once merged).

@chalasr chalasr force-pushed the passwordless-user branch 3 times, most recently from fbdf20c to 08e04e6 Compare February 21, 2021 13:24
Copy link
Member

@derrabus derrabus left a comment

Choose a reason for hiding this comment

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

Thank you for working on this feature!

Copy link
Member

@wouterj wouterj left a comment

Choose a reason for hiding this comment

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

Looking great. It doesn't look like it causes much of change in the internals, which is great :). Just some small comments found while looking at the diff. I'll find some time this week to test it in a real project.

I think we should also add this to the guard. This is the most tricky, but I think we can trigger the deprecation in GuardAuthenticationProvider and GuardBridgeAuthenticator if we check for $guard instanceof PasswordAuthenticatedInterface && !$user instanceof PasswordAuthenticatedUserInterface (or maybe directly test whether the user's getPassword or getSalt don't return null?).

@chalasr chalasr force-pushed the passwordless-user branch 2 times, most recently from 8716067 to 1054ba7 Compare February 21, 2021 23:17
@chalasr
Copy link
Member Author

chalasr commented Feb 21, 2021

I think we can trigger the deprecation in GuardAuthenticationProvider and GuardBridgeAuthenticator if we check for $guard instanceof PasswordAuthenticatedInterface && !$user instanceof PasswordAuthenticatedUserInterface

Good idea. Done, thank you.

Note that UserPasswordHasher already triggers a deprecation notice when getPassword() is defined but the new interface is not implemented (and another for getSalt()). GuardAuthenticationProvider uses it for password upgrades, and userland authenticators created via make:auth also use it for password verification.
Guard is now well covered I guess :)

@chalasr chalasr force-pushed the passwordless-user branch 5 times, most recently from 770e25b to aacfbd0 Compare March 5, 2021 09:23
@chalasr
Copy link
Member Author

chalasr commented Mar 5, 2021

Added one compile-time deprecation to ease upgrading. Fixed psalm reports also.
This is ready.

@chalasr chalasr added the Ready label Mar 5, 2021
@chalasr chalasr force-pushed the passwordless-user branch 3 times, most recently from 5066319 to 8e1a5d6 Compare March 5, 2021 23:32
@fabpot
Copy link
Member

fabpot commented Mar 6, 2021

Thank you @chalasr.

@fabpot fabpot merged commit 8de664d into symfony:5.x Mar 6, 2021
@chalasr chalasr deleted the passwordless-user branch March 6, 2021 11:16
pamil added a commit to Sylius/Sylius that referenced this pull request Mar 18, 2021
…oic425)

This PR was merged into the 1.10-dev branch.

Discussion
----------

| Q               | A
| --------------- | -----
| Branch?         | 1.7, 1.8 or master <!-- see the comment below -->
| Bug fix?        | no/yes
| New feature?    | no/yes
| BC breaks?      | no/yes
| Deprecations?   | no/yes <!-- don't forget to update the UPGRADE-*.md file -->
| Related tickets | fixes #X, partially #Y, mentioned in #Z
| License         | MIT

<!--
 - Bug fixes must be submitted against the 1.7 or 1.8 branch (the lowest possible)
 - Features and deprecations must be submitted against the master branch
 - Make sure that the correct base branch is set

 To be sure you are not breaking any Backward Compatibilities, check the documentation:
 https://docs.sylius.com/en/latest/book/organization/backward-compatibility-promise.html
-->

When using a SSO such as Keycloak, you don't need a password. It's better to allow null instead of setting plain password with a random value.

Moreover, password will be decoupled from user interface on Symfony security user.
symfony/symfony#40267



Commits
-------

e20f871 Allow user password to be null
9daecf6 Move migration into core bundle
pamil added a commit to Sylius/SyliusCoreBundle that referenced this pull request Mar 18, 2021
…oic425)

This PR was merged into the 1.10-dev branch.

Discussion
----------

| Q               | A
| --------------- | -----
| Branch?         | 1.7, 1.8 or master <!-- see the comment below -->
| Bug fix?        | no/yes
| New feature?    | no/yes
| BC breaks?      | no/yes
| Deprecations?   | no/yes <!-- don't forget to update the UPGRADE-*.md file -->
| Related tickets | fixes #X, partially #Y, mentioned in #Z
| License         | MIT

<!--
 - Bug fixes must be submitted against the 1.7 or 1.8 branch (the lowest possible)
 - Features and deprecations must be submitted against the master branch
 - Make sure that the correct base branch is set

 To be sure you are not breaking any Backward Compatibilities, check the documentation:
 https://docs.sylius.com/en/latest/book/organization/backward-compatibility-promise.html
-->

When using a SSO such as Keycloak, you don't need a password. It's better to allow null instead of setting plain password with a random value.

Moreover, password will be decoupled from user interface on Symfony security user.
symfony/symfony#40267



Commits
-------

e20f871d9a49c6028ba7c1cd3708f7986ebe32aa Allow user password to be null
9daecf696b3c489dbe56f7ba26b162527a7948c9 Move migration into core bundle
chalasr added a commit that referenced this pull request Mar 28, 2021
…serIdentifier() (wouterj)

This PR was squashed before being merged into the 5.3-dev branch.

Discussion
----------

[Security] Rename UserInterface::getUsername() to getUserIdentifier()

| Q             | A
| ------------- | ---
| Branch?       | 5.x
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | yes
| Tickets       | Fix part of #39308
| License       | MIT
| Doc PR        | tbd

This continues the great work by @chalasr in #40267 and rounds up the `UserInterface` cleanup.

The `getUsername()` has been a point of confusion for many years. In today's applications, many domains no longer have a username, but instead rely on a user's email address or the like. Even more, this username has to be unique for all Security functionality to work correctly - so it's more confusing in complex applications relying on e.g. "username+company" to be unique.

**This PR proposes to rename the method to `getUserIdentifier()`**, to more clearly indicate the goal of the method (note: I'm open for any other suggestion).

For BC, the `getUsername()` method is deprecated in 5.3 and `getUserIdentifier()` will be added to the `UserInterface` in 6.0. PHPdocs are used to improve type-hinting for 5.3 & 5.4. Both authentication managers (the legacy and experimental one) check the authenticated user object and trigger a deprecation if `getUserIdentifier()` is not implemented.
* [x] **judge whether we need to have better deprecations for calling `getUsername()` in the application code.**

Consistent with these changes, I've also done the same BC change for `TokenInterface::getUsername()`.
* [x] **do the same for remember me's `PersistentTokenInterface::getUsername()`**
* [x] **also rename `UserProviderInterface::loadUserByUsername()` & `UsernameNotFoundException`**
* [x] **also rename `UserLoaderInterface::loadUserByUsername()`**

I'm very much looking forward for feedback and help for this important, but BC-heavy, rename. 😃

Commits
-------

8afd7a3 [Security] Rename UserInterface::getUsername() to getUserIdentifier()
weaverryan added a commit to symfony/maker-bundle that referenced this pull request Mar 30, 2021
…dUserInterface (jrushlow)

This PR was squashed before being merged into the 1.0-dev branch.

Discussion
----------

[make:user] user entities implement PasswordAuthenticatedUserInterface

Starting in Symfony 5.3 - PR symfony/symfony#40267 introduces the new `PasswordAuthenticatedUserInterface` ~& `LegacyPasswordAuthenticatedUserInterface`~ aimed at decoupling passwords from the `UserInterface`.

- Generated `User` entity implements `PasswordAuthenticatedUserInterface` if it exists.
- Adds correct type hint to the generated `UserRepository::upgradePassword()` method.

Authentication related changes are handled in #824

// cc @chalasr

Commits
-------

f9d9d03 [make:user] user entities implement PasswordAuthenticatedUserInterface
@fabpot fabpot mentioned this pull request Apr 18, 2021
nicolas-grekas added a commit that referenced this pull request Jul 9, 2021
…rInterface (chalasr)

This PR was merged into the 6.0 branch.

Discussion
----------

[Security] Remove getPassword() and getSalt() from UserInterface

| Q             | A
| ------------- | ---
| Branch?       | 6.0
| Bug fix?      | no
| New feature?  | no
| Deprecations? | no
| Tickets       | -
| License       | MIT
| Doc PR        | -

with BC layers from #40267

Commits
-------

30e2c00 [Security] Remove getPassword() and getSalt() from UserInterface
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.

None yet

8 participants