FOSUserBundle icon indicating copy to clipboard operation
FOSUserBundle copied to clipboard

Register/Login with email only

Open gavinwilliams opened this issue 13 years ago • 56 comments

Is there any way to get FOSUserBundle to register/login by email only and disable/remove the username property?

gavinwilliams avatar Feb 27 '12 20:02 gavinwilliams

A recent project of mine required exactly what you're trying to do, rather then disabling/removing the username I made it a hidden field that was filled in with a random set of numbers during registration and enabled the email log in functionality found here: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/logging_by_username_or_email.md.

Not really what you're looking for but maybe it helps.

nathanw-jc avatar Mar 07 '12 18:03 nathanw-jc

Another way to do this is to hide the username field and in the prePersist/preUpdate for your User entity set the username to the email address. That fixes the logging in by username or email issue too since the username is the same as the email address.

treethinking avatar Jun 23 '12 22:06 treethinking

@treethinking the prePersist/preUpate won't help with the validation of setting the username. The form validates before the Hooks are executed, thus the registration will fail without further modifications

weyandch avatar Aug 10 '12 09:08 weyandch

I just did it for the project I'm working on, what I did is :

  • Override the register.html.twig template by placing my own in AcmeBundle/Resources/views/Registration/

  • Override the FormType, replace the buildForm method by only what I wanted (email and the two passwords) /!\ Do not execute parent!

  • Override the FormHandler, simply replace process by its own code in FOSUserBundle's equivalent class, but adding the username you want there :

            $user = $this->userManager->createUser();//Original FOSUser line
    $user->setUsername(uniqid("u", true));//Creating my own uid for the username
    $this->form->setData($user);//Original FOSUser line
    

    and leave the rest of the method as it is defined in FOSUserBundle RegistrationFormHandler.php

And I think that's about it, I was able to register correctly with my email only.

GTheron avatar Sep 04 '12 09:09 GTheron

@GTheron : It's just some naming, why don't you simply use the email as username?

bamarni avatar Sep 04 '12 10:09 bamarni

You should not extend your user class from the FOS\UserBundle\Entity\Userbut you should implement the FOS\UserBundle\Model\UserInterface instead.

Baachi avatar Sep 04 '12 10:09 Baachi

@Baachi why ?

stof avatar Sep 04 '12 10:09 stof

@stof If you extend from FOS\UserBundle\Entity\User you have a empty column named username. To avoid this, you should implement the UserInterface instead. :)

Baachi avatar Sep 04 '12 10:09 Baachi

@Baachi but you have to do all the mapping yourself. It is not always worth it

stof avatar Sep 04 '12 10:09 stof

Yes, i know. That sucks, but to create a column that always empty is very dirty.

Baachi avatar Sep 04 '12 10:09 Baachi

@bamarni If a user wants to change their email, the username one remains valid for login, which can be seen as a problem in some situations, as the username cannot (or at least should not) be changed, which is why it seemed good to have a uid in the username. Note that it can always be used in "emergency" situations.

GTheron avatar Sep 04 '12 11:09 GTheron

From a general viewpoint, when you think about it, asking users to come up with a unique username when their email address already satisfies the very same uniqueness constraint is really not that great a thing to do in many cases. It would therefore be great to have support for "email usernames" added to this bundle.

As for my use case, the general concept of what GTheron wrote above (assigning a unique internal ID as a username) appears to be a good approach for the time being, with the difference that I would get my IDs from a non-random source.

advancingu avatar Sep 06 '12 02:09 advancingu

Fyi, for those who want to set their programmatic username only after registration form validation, it will be necessary to create and apply a custom validation_groups configuration, completely replacing the default Registration validation group configured for registration forms. In this new validation group, the only username field value allowed must be null. All the other validation settings can be copied over.

The issue is that it doesn't seem to be possible to simply override only the username validation in a new validation group and add that group behind the existing one, e.g. validation_groups: [Registration, RegistrationNoUsername]. It seems Registration always has precedence.

advancingu avatar Sep 06 '12 04:09 advancingu

@advancingu As a side note, the unique_id is not actually random, as it's based on a unique identifier corresponding to the machine time on which it's produced, therefore it cannot appear twice.

GTheron avatar Sep 19 '12 10:09 GTheron

@GTheron You're right that unique_id is not random as I said, however I would assume that if you run your application on more than one server in parallel you might still get duplicates with unique_id. Of course, chances are small but they're greater than zero.

advancingu avatar Sep 26 '12 02:09 advancingu

I agree with @advancingu. We're upgrading to sf2.1 right now and revisiting our FOSUserBundle integration. The dual username/email requirement is the only thing that requires a non-obvious workaround. It would be nice if "email as username" was baked-in as a feature.

nurikabe avatar Nov 14 '12 16:11 nurikabe

@nurikabe me too, vote for the new feature

seltzlab avatar Nov 20 '12 10:11 seltzlab

Wouldn't it be better to simply make the "main login fields" fully configurable?

FOSUserBundle is aiming at flexibility. Should it simply not assume anything about the role of fields, except password and some internal needs? There would be defaults, easily configurable by some yaml pairs:

fields:
  login: [email, company_registration_number]
  non_editable: [email, surname, firstname]

@stof: i tried to find the answer to the following question on GIT and stackoverflow, but couldn't: can we expect FOSUserBundle to at least make the login field configurable some day? Or isn't that planned for the moment? Thank you :)

NinjDS avatar Nov 23 '12 10:11 NinjDS

@NinjDS Stof started a PR for FOSUserBundle 2.

And i think your idea is out of the scope from FOSUserBundle.

Baachi avatar Nov 23 '12 15:11 Baachi

Indeed, i saw the PR some minute later. But why do you think it's not the purpose of FOSUserBundle? It's not named "FOSUsernameBundle" :)

Anyway, you may be right because i start to realize that FOSUserBundle is not what i need. It's very specific, aimed at particular cases (which fits in 90% of the cases i agree). But as soon as your user management is not too common, it's probably better to write it by yourself rather than overwrite 50% of the bundle's code.

In my case i need minimalistic user structure all drive by the email, and to which one or more "shop" entities ar attached, as a part of the user account. It requires me to tweak all FOSUserBundle form types and validations to deal with the username trick, and override the process methods everywhere to manage the "shop" links.

I'm not yet too confortable with this bundle so i don't see the whole job it caries. In other words, i don't see how much code i'll have to write if i want my own user management bundle to replace it taking my specifications into account, but i feel like it will be a better solution to go with.

NinjDS avatar Nov 23 '12 15:11 NinjDS

@NinjDS configuring form fields in YAML is out of the scope of the bundle. For the edit form, we are using the Form component, and we are making the form type configurable. It allows to do approximately anything in the edit form, which is far more flexible than your non_editable suggestion.

stof avatar Nov 23 '12 19:11 stof

@stof Hi, You are right, the way it's done makes it very configurable but not flexible. Flexible would mean that no assumption is made about what a user account is. It's the opposite here, starting with the fact that a user profile is defined primarly by a username and integrates an e-mail. I'm not telling FOSUB is bad or anything, no, really, it's great i'm sure, when your needs gets in its way. Not my case i'm afraid ;)

NinjDS avatar Nov 23 '12 21:11 NinjDS

Hey guys, if you afraid that unique_id is not unique than you must use sluggable!

spolischook avatar Apr 05 '13 12:04 spolischook

What I always thought about this: Why not removing setUsername(), getUsername(), setUsernameCanonical() and getUsernameCanonical() complety and replace it with a single getIdentity()? The intention is, that the one, who wants to use the email as login/identifier can just return it, as it is

public function getIdentity()
{
  return $this->getEmailCanonical();
}

I think nowadays this is more common, than a username. But those, who wants to use usernames instead, shouldn't have any problems adding this instead. And when somebody wants to use the real name of the user instead, it is easily usable too :) (Isn't useful because they are usually not unique, but it is possible).

What I want to say: Separating the login-name from the username and/or email would make the UserInterface more flexible against chances, what the login-name should be.

Any suggestions?

kingcrunch avatar Jun 21 '13 22:06 kingcrunch

i think UserInterface itself should be changed as you suggested @KingCrunch , but in symfony not just for FOSUserBundle

ghost avatar Jun 21 '13 22:06 ghost

Ah, havent thought this far... Right, getUsername() is part of \Symfony\Component\Security\Core\User\UserInterface ... Well, I thik for a change of a Symfony-Core interface it is too late (2.3 is out). In this case I would treat getUsername() like the getIdentity() I mentioned before and just remove the setters and getUsernameCanonical().

kingcrunch avatar Jun 21 '13 22:06 kingcrunch

You can create your own custom provider:

# app/config/security.yml
providers:
    fos_userbundle:
        id: my_custom.user_provider.email

# UserBundle/Resources/config/services.yml
services:
    my_custom.user_provider.email:
        class: UserBundle\Security\EmailProvider
        public: false
        arguments: [@fos_user.user_manager]

# UserBundle\Security\EmailProvider
namespace UserBundle\Security;

use FOS\UserBundle\Security\UserProvider;

class EmailProvider extends UserProvider
{
    protected function findUser($username)
    {
        return $this->userManager->findUserByEmail($username);
    }
}

sfblaauw avatar Jul 02 '13 18:07 sfblaauw

This works out quite simply in FOSUserBundle V2.0, first set the UserProvider to allow username or email:

    providers:
    fos_userbundle:
        id: fos_user.user_provider.username_email

Then add an EventListener for Registration Initialization

    public static function getSubscribedEvents()
    {
        return array(
            FOSUserEvents::REGISTRATION_INITIALIZE => 'onRegistrationInit',
        );
    }

/**
 * take action when registration is initialized
 * set the username to a unique id
 * @param \FOS\UserBundle\Event\FormEvent $event
 */    
    public function onRegistrationInit(UserEvent $userevent)
    {
        $user = $userevent->getUser();
        $user->setUsername(uniqid());
    }

And override forms and templates to get rid of references to the user name.

PeterWooster avatar Jul 08 '13 20:07 PeterWooster

Just to add my two cents (sorry if this has already been discussed), the bare minimum to register a user in any online application is an email address and a password. There are a considerable number of applications where a username is not appropriate.

From a UX perspective (especially in commerce) usernames are difficult for users to remember, whereas an email address is arguably easier to remember.

As it stands to achieve what I think is a better practice requires work-around on the bundle's defaults, and leaves us with an unused DB column (a minor issue for the majority of small projects, but still not ideal).

I think that usernames should be an optional extra on the bundle rather than the default, and the bundle would be a lot more useful if that were the case.

The suggestion from @KingCrunch could be a good approach, because the bare minimum (i.e. a means of confirming an account and password resetting) for account registration in a web app could easily replace email for a cellphone number for example.

peterjmit avatar Aug 13 '13 18:08 peterjmit

I remembered this issue and I thought about a PR, but first: This would obviously to a BC-break. 2.x is currently in dev, so from the versioning point of view there is no problem. However, this bundle in version 2 seems to be quite widespreaded, so it may (actually I have no idea) affect many users. But again: It is a dev-version :confused:

I would remove all "username"-related properties and setters/getters and redirect getUsername() to getEmailCanonical() like mentioned above 2 months earlier.

public function getUsername()
{
    return $this->getEmailCanonical();
}

What remains: Should I also provide an additional User-class with username, to make it easier for those, who want to keep it? Or maybe a trait? :wink:

What is the opinion of the maintainers?

kingcrunch avatar Aug 27 '13 21:08 kingcrunch