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

Skip to content

[Security] Decoupling password from UserInterface #23081

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

Closed
Vinorcola opened this issue Jun 6, 2017 · 18 comments
Closed

[Security] Decoupling password from UserInterface #23081

Vinorcola opened this issue Jun 6, 2017 · 18 comments

Comments

@Vinorcola
Copy link

Q A
Bug report? no
Feature request? yes
BC Break report? no
RFC? no
Symfony version 3.x

I think we should decouple password from UserInterface (having a PasswordInterface for example) in order to be able to use them with the password encoding system. Currently, you need to pass a whole UserInterface for encoding password, while a simple password/salt is enough.

This would be usefull to implement some system where you manager password history. Right now, you need the "PasswordHistory" object to implement UserInterface in order to be used with the PasswordEncoder. But having a username or roles into a PasswordHistory object doesn't make any sense...

@Hanmac
Copy link
Contributor

Hanmac commented Jun 6, 2017

i find your idea interesting but i would have used the UserName with the Password in the Password history like:
[id] [user] [password] [salt]

for that i would have used a UserToken that just implements UserInterface.
getRoles returns empty string and eraseCredentials does nothing.

hm with that you can also use it to tell the user that he already used a password like that (with same hash)

@ogizanagi
Copy link
Contributor

ogizanagi commented Jun 6, 2017

@Vinorcola : The PasswordEncoderInterface does not require a UserInterface instance on the contrary of the UserPasswordEncoderInterface.

Instead of using UserPasswordEncoderInterface or the security.password_encoder service, try using the security.encoder_factory to create the encoder you expect to inject in your services/controller:

services:
    Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface:
        factory: 'Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface:getEncoder'
        # or
        factory: 'security.encoder_factory:getEncoder'
        arguments: ['AppBundle\Entity\User']

@linaori
Copy link
Contributor

linaori commented Jun 7, 2017

Personally, I'd rather see the UserInterface disappear. Beyond the username, you don't really need anything anymore besides for authentication.

@xabbuh xabbuh added the Security label Jun 7, 2017
@Hanmac
Copy link
Contributor

Hanmac commented Jun 9, 2017

@Vinorcola for that do you currently plan to use "PasswordHistory"?
do you plan in using it in such a "you already used that password before" mechanic?

because i would be interested in such thing too

@Vinorcola
Copy link
Author

@Hanmac Yes that's excatly why I want to use it. I don't need the username since PasswordHistory has a relation with the user account entity.

It is not that diffcult: when the user change his password, before registering it, you just try to match it to all its password history. If one match, then you refuse the given password.

@ogizanagi Thanks, good idea to create a service with a factory!

@Hanmac
Copy link
Contributor

Hanmac commented Jun 9, 2017

@Vinorcola interesting, would like to see if its ready to bundle
hm i would probalby still use my idea there with, and implement UserInterface
[id] [user] [password] [salt]
getUsername for example could point be done like getUser()->getUsername()

would it work with FOSUserbundle too?

@curry684
Copy link
Contributor

curry684 commented Jun 30, 2017

Big upvote in general, and highly agree with @iltar that UserInterface in general should be refactored or even removed. It hurts my purist mind that even the documentation lightly suggests to just implement getPassword, getSalt etc. as empty functions, without seeing the implication that this means the base mechanism is broken there.

In a modern application with OAuth or other SSO mechanisms the Symfony authentication layer is enforcing way too much crap on the developer that should be opt-in.

@Spomky
Copy link
Contributor

Spomky commented Sep 14, 2018

Hi,

I fully agree with @curry684 and @iltar. There are more and more cases today where users are no longer authenticated via username/password pair anymore.
I am actively working on PHP implementations for the Webauthn and OAuth2/OIDC specifications.

  • With the first specification, security tokens are associated to the user account and so the password (and associated logic such as password encoders...) becomes completely useless.
  • With the second one, there is no notion of user but resource owner. The resource owner could be a end user, the client itself or any other client. Resources are manipulated by a client that received a delegation from the resource owner. In this case the entire user interface becomes useless.

@craigh
Copy link

craigh commented Feb 5, 2020

I would also like to see UserInterface decoupled from Password/Salt/eraseCredentials I also like the idea of these items moving to a separate Interface. In our application, the UserEntity does not include authentication information which is handled by another bundle. I would like to use UserInterface to utilize ROLES and so forth, but can't implement a getPassword method when my UserEntity doesn't have one to get.

@linaori
Copy link
Contributor

linaori commented Feb 5, 2020

Please don't orient the usage of the UserInterface around entities, you can implement an intermediate DTO for this purpose, this keeps your entity clean.

@craigh
Copy link

craigh commented Feb 8, 2020

@linaori so you are saying should not follow Symfony's own docs on how to create a user? Seems like an odd recommendation.

as for an intermediate DTO, that may be a fine solution for a new app, but I am working with a very old code base with existing users and changing to add a new "layer" is much harder than simply implementing an interface on an existing Entity. It just seems silly that to do so, I would need to leave Password/Salt/eraseCredentials blank!

@linaori
Copy link
Contributor

linaori commented Feb 8, 2020

so you are saying should not follow Symfony's own docs on how to create a user? Seems like an odd recommendation.

Yes.

as for an intermediate DTO, that may be a fine solution for a new app, but I am working with a very old code base with existing users and changing to add a new "layer" is much harder than simply implementing an interface on an existing Entity. It just seems silly that to do so, I would need to leave Password/Salt/eraseCredentials blank!

Shortcuts are usually easier and take more time on the long run, and often cause a headache in the process.

@curry684
Copy link
Contributor

curry684 commented Feb 9, 2020

I would need to leave Password/Salt/eraseCredentials blank!

I have never implemented those functions in any project. I just copy/paste these lines along:

    /**
     * {@inheritdoc}
     */
    public function getPassword()
    {
        return '';
    }

    /**
     * {@inheritdoc}
     */
    public function getSalt()
    {
        return '';
    }

    /**
     * {@inheritdoc}
     */
    public function eraseCredentials()
    {
        // Noop
    }

In the end, Symfony's docs are correctly documenting an authentication layer that worked fine during the early Symfony 2.x days but hasn't really kept up recently with the world of OAuth, federated identities et al. So yes, deviate from them by all means, the beauty of a flexible framework is that it lets you. And the beauty of open source is that this issue is about removing this obsolete boilerplate stuff.

@faizanakram99
Copy link
Contributor

Not sure if this has been proposed before.

I think we should rather have IdentityInterface instead of UserInterface with just one method say getIdentifier or getIdentification. We don't necessarily need a User object, the app can be accessed programmatically say by another app.

UserInterface can extend IdentityInterface though (if needed at all)

@Spomky
Copy link
Contributor

Spomky commented Mar 14, 2020

IdentityInterface is a good idea. However when an other app is accessing the that, it is strange to associate it with an Identity. An application is not supposed to have an identity as users have.

I prefer the generic term resource owner. The web is all about resources and we can easily implement a ResourceOwnerInterface to User or Apllication objects.

@carsonbot
Copy link

Thank you for this suggestion.
There has not been a lot of activity here for a while. Would you still like to see this feature?

@wouterj
Copy link
Member

wouterj commented Feb 19, 2021

Note that although this is closed, work is being done here. A PR is coming soonish!

@chalasr
Copy link
Member

chalasr commented Feb 21, 2021

See #40267

fabpot added a commit that referenced this issue Mar 6, 2021
…asr)

This PR was merged into the 5.3-dev branch.

Discussion
----------

[Security] Decouple passwords from UserInterface

| 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.

Commits
-------

2764225 [Security] Decouple passwords from UserInterface
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests