Temporary users in Django











up vote
0
down vote

favorite
1












I would like to implement temporary users in my project. The reason is that people should be able to auth in my application via Facebook Oauth or Facebook Account Kit.



I've got 2 decisions, but each one is not ideal.



1) Create User and TemporaryUser models. The first one will have all info about regular user + unique db fields. This TemporaryUser will have only phone_number or facebook_id. Temporary user will be created on auth/ endpoint and response will have auth_token to be able perform registration later with all needed fields.



The issue is: how will I determine that user is temporary and his auth_token is legit only for registration/ endpoint?



2) Create basic User model with 2 types: regular or temporary. This model will have only general fields. Also there will be different models with OneToOne relation with User. Only Users with type regular will be able to have that instances with OneToOne relation.



The issue is: User model should have USERNAME_FIELD , REQUIRED_FIELDS to be able to login by admin panel + users with different types should have different managers.



UPDATED



class User(AbstractBaseUser, TimeStampedModel, PermissionsMixin):
is_regular = models.BooleanField(
default=False
)
id = models.CharField(
max_length=11,
primary_key=True,
default=custom_uuid,
editable=False
)
phone_number = PhoneNumberField(
null=True
)
facebook_id = models.CharField(
max_length=255,
null=True
)

objects = UserManager()
USERNAME_FIELD = 'phone_number'
REQUIRED_FIELDS =









share|improve this question
























  • Why FB users should be temporary? BTW, its not a good idea to use 2 auth models. I don't think you can authenticate a user if its not related to a User model (for 2nd solution)
    – ruddra
    Nov 7 at 10:26












  • @ruddra Flow: front-end authorize user by facebook and gives me facebook_token -> back-end do request with this token and facebook_id will be in return -> back-end checks whether user exists in db -> if yes - auth user, else - it should be created temp user with auth token (to be sure that it's the same user and there is no need to verify user one more tome on registration) -> front-end let user to fill in required fields and than register real user
    – Ernst
    Nov 7 at 10:31










  • in that case, you can create a user with dummy data, or override the Django's own User Model make fewer fields required.
    – ruddra
    Nov 7 at 10:34










  • @ruddra I can't change my fields, all of them should stay required. What if user will close app on screen with all needed fields for users? User will stay with that dummy data on the next login and he will skip registration screen. Also this solution will combine auth/registration endpoints that I don't like.
    – Ernst
    Nov 7 at 10:45















up vote
0
down vote

favorite
1












I would like to implement temporary users in my project. The reason is that people should be able to auth in my application via Facebook Oauth or Facebook Account Kit.



I've got 2 decisions, but each one is not ideal.



1) Create User and TemporaryUser models. The first one will have all info about regular user + unique db fields. This TemporaryUser will have only phone_number or facebook_id. Temporary user will be created on auth/ endpoint and response will have auth_token to be able perform registration later with all needed fields.



The issue is: how will I determine that user is temporary and his auth_token is legit only for registration/ endpoint?



2) Create basic User model with 2 types: regular or temporary. This model will have only general fields. Also there will be different models with OneToOne relation with User. Only Users with type regular will be able to have that instances with OneToOne relation.



The issue is: User model should have USERNAME_FIELD , REQUIRED_FIELDS to be able to login by admin panel + users with different types should have different managers.



UPDATED



class User(AbstractBaseUser, TimeStampedModel, PermissionsMixin):
is_regular = models.BooleanField(
default=False
)
id = models.CharField(
max_length=11,
primary_key=True,
default=custom_uuid,
editable=False
)
phone_number = PhoneNumberField(
null=True
)
facebook_id = models.CharField(
max_length=255,
null=True
)

objects = UserManager()
USERNAME_FIELD = 'phone_number'
REQUIRED_FIELDS =









share|improve this question
























  • Why FB users should be temporary? BTW, its not a good idea to use 2 auth models. I don't think you can authenticate a user if its not related to a User model (for 2nd solution)
    – ruddra
    Nov 7 at 10:26












  • @ruddra Flow: front-end authorize user by facebook and gives me facebook_token -> back-end do request with this token and facebook_id will be in return -> back-end checks whether user exists in db -> if yes - auth user, else - it should be created temp user with auth token (to be sure that it's the same user and there is no need to verify user one more tome on registration) -> front-end let user to fill in required fields and than register real user
    – Ernst
    Nov 7 at 10:31










  • in that case, you can create a user with dummy data, or override the Django's own User Model make fewer fields required.
    – ruddra
    Nov 7 at 10:34










  • @ruddra I can't change my fields, all of them should stay required. What if user will close app on screen with all needed fields for users? User will stay with that dummy data on the next login and he will skip registration screen. Also this solution will combine auth/registration endpoints that I don't like.
    – Ernst
    Nov 7 at 10:45













up vote
0
down vote

favorite
1









up vote
0
down vote

favorite
1






1





I would like to implement temporary users in my project. The reason is that people should be able to auth in my application via Facebook Oauth or Facebook Account Kit.



I've got 2 decisions, but each one is not ideal.



1) Create User and TemporaryUser models. The first one will have all info about regular user + unique db fields. This TemporaryUser will have only phone_number or facebook_id. Temporary user will be created on auth/ endpoint and response will have auth_token to be able perform registration later with all needed fields.



The issue is: how will I determine that user is temporary and his auth_token is legit only for registration/ endpoint?



2) Create basic User model with 2 types: regular or temporary. This model will have only general fields. Also there will be different models with OneToOne relation with User. Only Users with type regular will be able to have that instances with OneToOne relation.



The issue is: User model should have USERNAME_FIELD , REQUIRED_FIELDS to be able to login by admin panel + users with different types should have different managers.



UPDATED



class User(AbstractBaseUser, TimeStampedModel, PermissionsMixin):
is_regular = models.BooleanField(
default=False
)
id = models.CharField(
max_length=11,
primary_key=True,
default=custom_uuid,
editable=False
)
phone_number = PhoneNumberField(
null=True
)
facebook_id = models.CharField(
max_length=255,
null=True
)

objects = UserManager()
USERNAME_FIELD = 'phone_number'
REQUIRED_FIELDS =









share|improve this question















I would like to implement temporary users in my project. The reason is that people should be able to auth in my application via Facebook Oauth or Facebook Account Kit.



I've got 2 decisions, but each one is not ideal.



1) Create User and TemporaryUser models. The first one will have all info about regular user + unique db fields. This TemporaryUser will have only phone_number or facebook_id. Temporary user will be created on auth/ endpoint and response will have auth_token to be able perform registration later with all needed fields.



The issue is: how will I determine that user is temporary and his auth_token is legit only for registration/ endpoint?



2) Create basic User model with 2 types: regular or temporary. This model will have only general fields. Also there will be different models with OneToOne relation with User. Only Users with type regular will be able to have that instances with OneToOne relation.



The issue is: User model should have USERNAME_FIELD , REQUIRED_FIELDS to be able to login by admin panel + users with different types should have different managers.



UPDATED



class User(AbstractBaseUser, TimeStampedModel, PermissionsMixin):
is_regular = models.BooleanField(
default=False
)
id = models.CharField(
max_length=11,
primary_key=True,
default=custom_uuid,
editable=False
)
phone_number = PhoneNumberField(
null=True
)
facebook_id = models.CharField(
max_length=255,
null=True
)

objects = UserManager()
USERNAME_FIELD = 'phone_number'
REQUIRED_FIELDS =






django






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 8 at 11:43

























asked Nov 7 at 10:22









Ernst

74112




74112












  • Why FB users should be temporary? BTW, its not a good idea to use 2 auth models. I don't think you can authenticate a user if its not related to a User model (for 2nd solution)
    – ruddra
    Nov 7 at 10:26












  • @ruddra Flow: front-end authorize user by facebook and gives me facebook_token -> back-end do request with this token and facebook_id will be in return -> back-end checks whether user exists in db -> if yes - auth user, else - it should be created temp user with auth token (to be sure that it's the same user and there is no need to verify user one more tome on registration) -> front-end let user to fill in required fields and than register real user
    – Ernst
    Nov 7 at 10:31










  • in that case, you can create a user with dummy data, or override the Django's own User Model make fewer fields required.
    – ruddra
    Nov 7 at 10:34










  • @ruddra I can't change my fields, all of them should stay required. What if user will close app on screen with all needed fields for users? User will stay with that dummy data on the next login and he will skip registration screen. Also this solution will combine auth/registration endpoints that I don't like.
    – Ernst
    Nov 7 at 10:45


















  • Why FB users should be temporary? BTW, its not a good idea to use 2 auth models. I don't think you can authenticate a user if its not related to a User model (for 2nd solution)
    – ruddra
    Nov 7 at 10:26












  • @ruddra Flow: front-end authorize user by facebook and gives me facebook_token -> back-end do request with this token and facebook_id will be in return -> back-end checks whether user exists in db -> if yes - auth user, else - it should be created temp user with auth token (to be sure that it's the same user and there is no need to verify user one more tome on registration) -> front-end let user to fill in required fields and than register real user
    – Ernst
    Nov 7 at 10:31










  • in that case, you can create a user with dummy data, or override the Django's own User Model make fewer fields required.
    – ruddra
    Nov 7 at 10:34










  • @ruddra I can't change my fields, all of them should stay required. What if user will close app on screen with all needed fields for users? User will stay with that dummy data on the next login and he will skip registration screen. Also this solution will combine auth/registration endpoints that I don't like.
    – Ernst
    Nov 7 at 10:45
















Why FB users should be temporary? BTW, its not a good idea to use 2 auth models. I don't think you can authenticate a user if its not related to a User model (for 2nd solution)
– ruddra
Nov 7 at 10:26






Why FB users should be temporary? BTW, its not a good idea to use 2 auth models. I don't think you can authenticate a user if its not related to a User model (for 2nd solution)
– ruddra
Nov 7 at 10:26














@ruddra Flow: front-end authorize user by facebook and gives me facebook_token -> back-end do request with this token and facebook_id will be in return -> back-end checks whether user exists in db -> if yes - auth user, else - it should be created temp user with auth token (to be sure that it's the same user and there is no need to verify user one more tome on registration) -> front-end let user to fill in required fields and than register real user
– Ernst
Nov 7 at 10:31




@ruddra Flow: front-end authorize user by facebook and gives me facebook_token -> back-end do request with this token and facebook_id will be in return -> back-end checks whether user exists in db -> if yes - auth user, else - it should be created temp user with auth token (to be sure that it's the same user and there is no need to verify user one more tome on registration) -> front-end let user to fill in required fields and than register real user
– Ernst
Nov 7 at 10:31












in that case, you can create a user with dummy data, or override the Django's own User Model make fewer fields required.
– ruddra
Nov 7 at 10:34




in that case, you can create a user with dummy data, or override the Django's own User Model make fewer fields required.
– ruddra
Nov 7 at 10:34












@ruddra I can't change my fields, all of them should stay required. What if user will close app on screen with all needed fields for users? User will stay with that dummy data on the next login and he will skip registration screen. Also this solution will combine auth/registration endpoints that I don't like.
– Ernst
Nov 7 at 10:45




@ruddra I can't change my fields, all of them should stay required. What if user will close app on screen with all needed fields for users? User will stay with that dummy data on the next login and he will skip registration screen. Also this solution will combine auth/registration endpoints that I don't like.
– Ernst
Nov 7 at 10:45












1 Answer
1






active

oldest

votes

















up vote
1
down vote



accepted










From discussion, there are many ways to tackle this issue. But using two different models for authentication purpose is a bad idea. Because Django Auth was designed assuming one User Model.



This is how I would have approached:




  1. Rather than using facebook_id, I would have stored email from facebook's graph API. While creating the password, I would have used an unique UUID.

  2. Put a temporary flag(a Model Boolean Field) to track if the user is registered through social media. So when he tries to login to normal path or registration, I would know that he does not have a password, so I would send him to reset his password. After successful reset password, I would have removed that temporary flag.


  3. Other information which are absolute necessary, I would have used a separate model to store them. For example:



    class Profile(models.Model):
    ...
    phone_number = models.CharField(max_length=30)
    user = models.OneToOneField(settings.AUTH_USER_MODEL)


    And checked with login that if the User has a profile or not:



    if not hasattr(user, 'profile'):
    # redirect to more information page


  4. For other permissions for accessing admin site should be restricted creation through normal registration or social registration. For these users, those fields(is_staff, is_superuser etc should be False by default. For admin user creation, you can use createsuperuser command. For staff users, you can later assign a normal user to staff by making the is_staff flag True from adminsite or Django Shell.



Update(from comments...)



You can use custom Backend for authenticating facebook user:



from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

class CustomModelbackend(ModelBackend):
def authenticate(self, phone_no=None, password=None, facebook_id=None, **kwargs):
# Override
UserModel = User
try:
user = UserModel.objects.get(Q(phone_no=phone_no) | Q(facebook_id=facebook_id))
if password and user.check_password(password):
return user
elif facebook_id:
return user
except Exception as e:
# log exception


and add it to AUTHENTICATION_BACKENDS in settings.py:



AUTHENTICATION_BACKENDS = ['path.to.custom_backend.CustomBackend']


and call authenticate method like this:



 authenticate(phone_no=phone_number, password=password)  # for normal auth
authenticate(facebook_id=facebook_id) # for social auth


But you can't make your phone_no unique as there is chance of the phone_no being empty for social login, but you can put it in USERNAME_FIELD. So you will see warnings when you run django developer server(runserver command)



Update (2)



You can try like this:



NORMAL_USER = "N"
OAUTH_USER = "O"
AT_USER = "A"
USER_TYPES=(
(NORMAL_USER, 'Normal User'),
(OAUTH_USER, 'Oauth User'),
(AT_USER, 'Account Toolkit User')
)

class User(...):
username = models.CharField(max_length=255, default=uuid.uuid4, unique=True) # it might be unnecessary
account_type = models.CharField(max_length=2, choices = USER_TYPES, default=NORMAL_USER)
identifier = models.CharField(max_length=255)
...

class Meta:
unique_togather = ('account_type', 'identifier',)


# for Oauth Creation

User.objects.create(account_type=OAUTH_USER, identifier=facebook_id) # or email

# for Toolkit Creation

User.objects.create(account_type=AT_USER, identifier=phone_number)

# For normal User
User.objects.create(identifier=username, username=username)

#backend, and does not need to use ModelBackend, it will work with adminsite
class CustomBackend(...):
def authenticate(self, **kwargs):
try:
identifier= kwargs.get('username')
password=kwargs.get('password')
account_type=kwargs.get('account_type', NORMAL_USER)
user = User.objects.get(identifier=identifier, account_type=account_type)
if user.check_password(password):
return user
except Exception:
# log exception

# authentication
from django.contrib.auth import authenticate
user = authenticate(username=identifier, password=password, account_type=OAUTH_USER)





share|improve this answer























  • You wouldn't need a temporary flag for the initial password prompt; a user that hasn't set a password will have a password_hash of None, where a user that just forgot theirs will just have a mismatching hash when they input the wrong one. The Profile pattern is a good one, though, and very often a better way to go than a custom user model. I wouldn't use hasattr to check for a profile, though. I'm not sure if the ORM will set that to None or just not create it, and I wouldn't depend on that behavior. Profile.objects.get(user=user) might be a little heavy, but it's more concrete.
    – kungphu
    Nov 8 at 5:41










  • @kungphu I am not against CustomUser Model. Because the requirement here is to store some data which are absolutely necessary, but does not come through social login(from comments in question). Also, hasattr usage is per documentation found here (in the 2nd example).
    – ruddra
    Nov 8 at 5:48












  • @ruddra In my application users will not be able to perform registration by email, only Facebook Oauth2 or Account Kit. So I can create Basic User model with id, facebook_id and phone_number. User will be able to register by facebook_id or phone_number. I decided to add flag is_regular (to be sure that user finished registration and he can have profile and other info), but the main problem is that I need something for USERNAME_FIELD.
    – Ernst
    Nov 8 at 9:29










  • @Ernst please see my updated section in the answer.
    – ruddra
    Nov 8 at 9:55










  • @ruddra Than you for your help. I can't put phone_number into USERNAME_FIELD as facebook user can have only email, but not always phone_number. One of possible solution is to put username into USERNAME_FIELD as each regular user will have it, but I don't like idea to put there dummy data for temporary users
    – Ernst
    Nov 8 at 10:44











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53187522%2ftemporary-users-in-django%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
1
down vote



accepted










From discussion, there are many ways to tackle this issue. But using two different models for authentication purpose is a bad idea. Because Django Auth was designed assuming one User Model.



This is how I would have approached:




  1. Rather than using facebook_id, I would have stored email from facebook's graph API. While creating the password, I would have used an unique UUID.

  2. Put a temporary flag(a Model Boolean Field) to track if the user is registered through social media. So when he tries to login to normal path or registration, I would know that he does not have a password, so I would send him to reset his password. After successful reset password, I would have removed that temporary flag.


  3. Other information which are absolute necessary, I would have used a separate model to store them. For example:



    class Profile(models.Model):
    ...
    phone_number = models.CharField(max_length=30)
    user = models.OneToOneField(settings.AUTH_USER_MODEL)


    And checked with login that if the User has a profile or not:



    if not hasattr(user, 'profile'):
    # redirect to more information page


  4. For other permissions for accessing admin site should be restricted creation through normal registration or social registration. For these users, those fields(is_staff, is_superuser etc should be False by default. For admin user creation, you can use createsuperuser command. For staff users, you can later assign a normal user to staff by making the is_staff flag True from adminsite or Django Shell.



Update(from comments...)



You can use custom Backend for authenticating facebook user:



from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

class CustomModelbackend(ModelBackend):
def authenticate(self, phone_no=None, password=None, facebook_id=None, **kwargs):
# Override
UserModel = User
try:
user = UserModel.objects.get(Q(phone_no=phone_no) | Q(facebook_id=facebook_id))
if password and user.check_password(password):
return user
elif facebook_id:
return user
except Exception as e:
# log exception


and add it to AUTHENTICATION_BACKENDS in settings.py:



AUTHENTICATION_BACKENDS = ['path.to.custom_backend.CustomBackend']


and call authenticate method like this:



 authenticate(phone_no=phone_number, password=password)  # for normal auth
authenticate(facebook_id=facebook_id) # for social auth


But you can't make your phone_no unique as there is chance of the phone_no being empty for social login, but you can put it in USERNAME_FIELD. So you will see warnings when you run django developer server(runserver command)



Update (2)



You can try like this:



NORMAL_USER = "N"
OAUTH_USER = "O"
AT_USER = "A"
USER_TYPES=(
(NORMAL_USER, 'Normal User'),
(OAUTH_USER, 'Oauth User'),
(AT_USER, 'Account Toolkit User')
)

class User(...):
username = models.CharField(max_length=255, default=uuid.uuid4, unique=True) # it might be unnecessary
account_type = models.CharField(max_length=2, choices = USER_TYPES, default=NORMAL_USER)
identifier = models.CharField(max_length=255)
...

class Meta:
unique_togather = ('account_type', 'identifier',)


# for Oauth Creation

User.objects.create(account_type=OAUTH_USER, identifier=facebook_id) # or email

# for Toolkit Creation

User.objects.create(account_type=AT_USER, identifier=phone_number)

# For normal User
User.objects.create(identifier=username, username=username)

#backend, and does not need to use ModelBackend, it will work with adminsite
class CustomBackend(...):
def authenticate(self, **kwargs):
try:
identifier= kwargs.get('username')
password=kwargs.get('password')
account_type=kwargs.get('account_type', NORMAL_USER)
user = User.objects.get(identifier=identifier, account_type=account_type)
if user.check_password(password):
return user
except Exception:
# log exception

# authentication
from django.contrib.auth import authenticate
user = authenticate(username=identifier, password=password, account_type=OAUTH_USER)





share|improve this answer























  • You wouldn't need a temporary flag for the initial password prompt; a user that hasn't set a password will have a password_hash of None, where a user that just forgot theirs will just have a mismatching hash when they input the wrong one. The Profile pattern is a good one, though, and very often a better way to go than a custom user model. I wouldn't use hasattr to check for a profile, though. I'm not sure if the ORM will set that to None or just not create it, and I wouldn't depend on that behavior. Profile.objects.get(user=user) might be a little heavy, but it's more concrete.
    – kungphu
    Nov 8 at 5:41










  • @kungphu I am not against CustomUser Model. Because the requirement here is to store some data which are absolutely necessary, but does not come through social login(from comments in question). Also, hasattr usage is per documentation found here (in the 2nd example).
    – ruddra
    Nov 8 at 5:48












  • @ruddra In my application users will not be able to perform registration by email, only Facebook Oauth2 or Account Kit. So I can create Basic User model with id, facebook_id and phone_number. User will be able to register by facebook_id or phone_number. I decided to add flag is_regular (to be sure that user finished registration and he can have profile and other info), but the main problem is that I need something for USERNAME_FIELD.
    – Ernst
    Nov 8 at 9:29










  • @Ernst please see my updated section in the answer.
    – ruddra
    Nov 8 at 9:55










  • @ruddra Than you for your help. I can't put phone_number into USERNAME_FIELD as facebook user can have only email, but not always phone_number. One of possible solution is to put username into USERNAME_FIELD as each regular user will have it, but I don't like idea to put there dummy data for temporary users
    – Ernst
    Nov 8 at 10:44















up vote
1
down vote



accepted










From discussion, there are many ways to tackle this issue. But using two different models for authentication purpose is a bad idea. Because Django Auth was designed assuming one User Model.



This is how I would have approached:




  1. Rather than using facebook_id, I would have stored email from facebook's graph API. While creating the password, I would have used an unique UUID.

  2. Put a temporary flag(a Model Boolean Field) to track if the user is registered through social media. So when he tries to login to normal path or registration, I would know that he does not have a password, so I would send him to reset his password. After successful reset password, I would have removed that temporary flag.


  3. Other information which are absolute necessary, I would have used a separate model to store them. For example:



    class Profile(models.Model):
    ...
    phone_number = models.CharField(max_length=30)
    user = models.OneToOneField(settings.AUTH_USER_MODEL)


    And checked with login that if the User has a profile or not:



    if not hasattr(user, 'profile'):
    # redirect to more information page


  4. For other permissions for accessing admin site should be restricted creation through normal registration or social registration. For these users, those fields(is_staff, is_superuser etc should be False by default. For admin user creation, you can use createsuperuser command. For staff users, you can later assign a normal user to staff by making the is_staff flag True from adminsite or Django Shell.



Update(from comments...)



You can use custom Backend for authenticating facebook user:



from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

class CustomModelbackend(ModelBackend):
def authenticate(self, phone_no=None, password=None, facebook_id=None, **kwargs):
# Override
UserModel = User
try:
user = UserModel.objects.get(Q(phone_no=phone_no) | Q(facebook_id=facebook_id))
if password and user.check_password(password):
return user
elif facebook_id:
return user
except Exception as e:
# log exception


and add it to AUTHENTICATION_BACKENDS in settings.py:



AUTHENTICATION_BACKENDS = ['path.to.custom_backend.CustomBackend']


and call authenticate method like this:



 authenticate(phone_no=phone_number, password=password)  # for normal auth
authenticate(facebook_id=facebook_id) # for social auth


But you can't make your phone_no unique as there is chance of the phone_no being empty for social login, but you can put it in USERNAME_FIELD. So you will see warnings when you run django developer server(runserver command)



Update (2)



You can try like this:



NORMAL_USER = "N"
OAUTH_USER = "O"
AT_USER = "A"
USER_TYPES=(
(NORMAL_USER, 'Normal User'),
(OAUTH_USER, 'Oauth User'),
(AT_USER, 'Account Toolkit User')
)

class User(...):
username = models.CharField(max_length=255, default=uuid.uuid4, unique=True) # it might be unnecessary
account_type = models.CharField(max_length=2, choices = USER_TYPES, default=NORMAL_USER)
identifier = models.CharField(max_length=255)
...

class Meta:
unique_togather = ('account_type', 'identifier',)


# for Oauth Creation

User.objects.create(account_type=OAUTH_USER, identifier=facebook_id) # or email

# for Toolkit Creation

User.objects.create(account_type=AT_USER, identifier=phone_number)

# For normal User
User.objects.create(identifier=username, username=username)

#backend, and does not need to use ModelBackend, it will work with adminsite
class CustomBackend(...):
def authenticate(self, **kwargs):
try:
identifier= kwargs.get('username')
password=kwargs.get('password')
account_type=kwargs.get('account_type', NORMAL_USER)
user = User.objects.get(identifier=identifier, account_type=account_type)
if user.check_password(password):
return user
except Exception:
# log exception

# authentication
from django.contrib.auth import authenticate
user = authenticate(username=identifier, password=password, account_type=OAUTH_USER)





share|improve this answer























  • You wouldn't need a temporary flag for the initial password prompt; a user that hasn't set a password will have a password_hash of None, where a user that just forgot theirs will just have a mismatching hash when they input the wrong one. The Profile pattern is a good one, though, and very often a better way to go than a custom user model. I wouldn't use hasattr to check for a profile, though. I'm not sure if the ORM will set that to None or just not create it, and I wouldn't depend on that behavior. Profile.objects.get(user=user) might be a little heavy, but it's more concrete.
    – kungphu
    Nov 8 at 5:41










  • @kungphu I am not against CustomUser Model. Because the requirement here is to store some data which are absolutely necessary, but does not come through social login(from comments in question). Also, hasattr usage is per documentation found here (in the 2nd example).
    – ruddra
    Nov 8 at 5:48












  • @ruddra In my application users will not be able to perform registration by email, only Facebook Oauth2 or Account Kit. So I can create Basic User model with id, facebook_id and phone_number. User will be able to register by facebook_id or phone_number. I decided to add flag is_regular (to be sure that user finished registration and he can have profile and other info), but the main problem is that I need something for USERNAME_FIELD.
    – Ernst
    Nov 8 at 9:29










  • @Ernst please see my updated section in the answer.
    – ruddra
    Nov 8 at 9:55










  • @ruddra Than you for your help. I can't put phone_number into USERNAME_FIELD as facebook user can have only email, but not always phone_number. One of possible solution is to put username into USERNAME_FIELD as each regular user will have it, but I don't like idea to put there dummy data for temporary users
    – Ernst
    Nov 8 at 10:44













up vote
1
down vote



accepted







up vote
1
down vote



accepted






From discussion, there are many ways to tackle this issue. But using two different models for authentication purpose is a bad idea. Because Django Auth was designed assuming one User Model.



This is how I would have approached:




  1. Rather than using facebook_id, I would have stored email from facebook's graph API. While creating the password, I would have used an unique UUID.

  2. Put a temporary flag(a Model Boolean Field) to track if the user is registered through social media. So when he tries to login to normal path or registration, I would know that he does not have a password, so I would send him to reset his password. After successful reset password, I would have removed that temporary flag.


  3. Other information which are absolute necessary, I would have used a separate model to store them. For example:



    class Profile(models.Model):
    ...
    phone_number = models.CharField(max_length=30)
    user = models.OneToOneField(settings.AUTH_USER_MODEL)


    And checked with login that if the User has a profile or not:



    if not hasattr(user, 'profile'):
    # redirect to more information page


  4. For other permissions for accessing admin site should be restricted creation through normal registration or social registration. For these users, those fields(is_staff, is_superuser etc should be False by default. For admin user creation, you can use createsuperuser command. For staff users, you can later assign a normal user to staff by making the is_staff flag True from adminsite or Django Shell.



Update(from comments...)



You can use custom Backend for authenticating facebook user:



from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

class CustomModelbackend(ModelBackend):
def authenticate(self, phone_no=None, password=None, facebook_id=None, **kwargs):
# Override
UserModel = User
try:
user = UserModel.objects.get(Q(phone_no=phone_no) | Q(facebook_id=facebook_id))
if password and user.check_password(password):
return user
elif facebook_id:
return user
except Exception as e:
# log exception


and add it to AUTHENTICATION_BACKENDS in settings.py:



AUTHENTICATION_BACKENDS = ['path.to.custom_backend.CustomBackend']


and call authenticate method like this:



 authenticate(phone_no=phone_number, password=password)  # for normal auth
authenticate(facebook_id=facebook_id) # for social auth


But you can't make your phone_no unique as there is chance of the phone_no being empty for social login, but you can put it in USERNAME_FIELD. So you will see warnings when you run django developer server(runserver command)



Update (2)



You can try like this:



NORMAL_USER = "N"
OAUTH_USER = "O"
AT_USER = "A"
USER_TYPES=(
(NORMAL_USER, 'Normal User'),
(OAUTH_USER, 'Oauth User'),
(AT_USER, 'Account Toolkit User')
)

class User(...):
username = models.CharField(max_length=255, default=uuid.uuid4, unique=True) # it might be unnecessary
account_type = models.CharField(max_length=2, choices = USER_TYPES, default=NORMAL_USER)
identifier = models.CharField(max_length=255)
...

class Meta:
unique_togather = ('account_type', 'identifier',)


# for Oauth Creation

User.objects.create(account_type=OAUTH_USER, identifier=facebook_id) # or email

# for Toolkit Creation

User.objects.create(account_type=AT_USER, identifier=phone_number)

# For normal User
User.objects.create(identifier=username, username=username)

#backend, and does not need to use ModelBackend, it will work with adminsite
class CustomBackend(...):
def authenticate(self, **kwargs):
try:
identifier= kwargs.get('username')
password=kwargs.get('password')
account_type=kwargs.get('account_type', NORMAL_USER)
user = User.objects.get(identifier=identifier, account_type=account_type)
if user.check_password(password):
return user
except Exception:
# log exception

# authentication
from django.contrib.auth import authenticate
user = authenticate(username=identifier, password=password, account_type=OAUTH_USER)





share|improve this answer














From discussion, there are many ways to tackle this issue. But using two different models for authentication purpose is a bad idea. Because Django Auth was designed assuming one User Model.



This is how I would have approached:




  1. Rather than using facebook_id, I would have stored email from facebook's graph API. While creating the password, I would have used an unique UUID.

  2. Put a temporary flag(a Model Boolean Field) to track if the user is registered through social media. So when he tries to login to normal path or registration, I would know that he does not have a password, so I would send him to reset his password. After successful reset password, I would have removed that temporary flag.


  3. Other information which are absolute necessary, I would have used a separate model to store them. For example:



    class Profile(models.Model):
    ...
    phone_number = models.CharField(max_length=30)
    user = models.OneToOneField(settings.AUTH_USER_MODEL)


    And checked with login that if the User has a profile or not:



    if not hasattr(user, 'profile'):
    # redirect to more information page


  4. For other permissions for accessing admin site should be restricted creation through normal registration or social registration. For these users, those fields(is_staff, is_superuser etc should be False by default. For admin user creation, you can use createsuperuser command. For staff users, you can later assign a normal user to staff by making the is_staff flag True from adminsite or Django Shell.



Update(from comments...)



You can use custom Backend for authenticating facebook user:



from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

class CustomModelbackend(ModelBackend):
def authenticate(self, phone_no=None, password=None, facebook_id=None, **kwargs):
# Override
UserModel = User
try:
user = UserModel.objects.get(Q(phone_no=phone_no) | Q(facebook_id=facebook_id))
if password and user.check_password(password):
return user
elif facebook_id:
return user
except Exception as e:
# log exception


and add it to AUTHENTICATION_BACKENDS in settings.py:



AUTHENTICATION_BACKENDS = ['path.to.custom_backend.CustomBackend']


and call authenticate method like this:



 authenticate(phone_no=phone_number, password=password)  # for normal auth
authenticate(facebook_id=facebook_id) # for social auth


But you can't make your phone_no unique as there is chance of the phone_no being empty for social login, but you can put it in USERNAME_FIELD. So you will see warnings when you run django developer server(runserver command)



Update (2)



You can try like this:



NORMAL_USER = "N"
OAUTH_USER = "O"
AT_USER = "A"
USER_TYPES=(
(NORMAL_USER, 'Normal User'),
(OAUTH_USER, 'Oauth User'),
(AT_USER, 'Account Toolkit User')
)

class User(...):
username = models.CharField(max_length=255, default=uuid.uuid4, unique=True) # it might be unnecessary
account_type = models.CharField(max_length=2, choices = USER_TYPES, default=NORMAL_USER)
identifier = models.CharField(max_length=255)
...

class Meta:
unique_togather = ('account_type', 'identifier',)


# for Oauth Creation

User.objects.create(account_type=OAUTH_USER, identifier=facebook_id) # or email

# for Toolkit Creation

User.objects.create(account_type=AT_USER, identifier=phone_number)

# For normal User
User.objects.create(identifier=username, username=username)

#backend, and does not need to use ModelBackend, it will work with adminsite
class CustomBackend(...):
def authenticate(self, **kwargs):
try:
identifier= kwargs.get('username')
password=kwargs.get('password')
account_type=kwargs.get('account_type', NORMAL_USER)
user = User.objects.get(identifier=identifier, account_type=account_type)
if user.check_password(password):
return user
except Exception:
# log exception

# authentication
from django.contrib.auth import authenticate
user = authenticate(username=identifier, password=password, account_type=OAUTH_USER)






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 8 at 16:30

























answered Nov 8 at 3:47









ruddra

7,82832546




7,82832546












  • You wouldn't need a temporary flag for the initial password prompt; a user that hasn't set a password will have a password_hash of None, where a user that just forgot theirs will just have a mismatching hash when they input the wrong one. The Profile pattern is a good one, though, and very often a better way to go than a custom user model. I wouldn't use hasattr to check for a profile, though. I'm not sure if the ORM will set that to None or just not create it, and I wouldn't depend on that behavior. Profile.objects.get(user=user) might be a little heavy, but it's more concrete.
    – kungphu
    Nov 8 at 5:41










  • @kungphu I am not against CustomUser Model. Because the requirement here is to store some data which are absolutely necessary, but does not come through social login(from comments in question). Also, hasattr usage is per documentation found here (in the 2nd example).
    – ruddra
    Nov 8 at 5:48












  • @ruddra In my application users will not be able to perform registration by email, only Facebook Oauth2 or Account Kit. So I can create Basic User model with id, facebook_id and phone_number. User will be able to register by facebook_id or phone_number. I decided to add flag is_regular (to be sure that user finished registration and he can have profile and other info), but the main problem is that I need something for USERNAME_FIELD.
    – Ernst
    Nov 8 at 9:29










  • @Ernst please see my updated section in the answer.
    – ruddra
    Nov 8 at 9:55










  • @ruddra Than you for your help. I can't put phone_number into USERNAME_FIELD as facebook user can have only email, but not always phone_number. One of possible solution is to put username into USERNAME_FIELD as each regular user will have it, but I don't like idea to put there dummy data for temporary users
    – Ernst
    Nov 8 at 10:44


















  • You wouldn't need a temporary flag for the initial password prompt; a user that hasn't set a password will have a password_hash of None, where a user that just forgot theirs will just have a mismatching hash when they input the wrong one. The Profile pattern is a good one, though, and very often a better way to go than a custom user model. I wouldn't use hasattr to check for a profile, though. I'm not sure if the ORM will set that to None or just not create it, and I wouldn't depend on that behavior. Profile.objects.get(user=user) might be a little heavy, but it's more concrete.
    – kungphu
    Nov 8 at 5:41










  • @kungphu I am not against CustomUser Model. Because the requirement here is to store some data which are absolutely necessary, but does not come through social login(from comments in question). Also, hasattr usage is per documentation found here (in the 2nd example).
    – ruddra
    Nov 8 at 5:48












  • @ruddra In my application users will not be able to perform registration by email, only Facebook Oauth2 or Account Kit. So I can create Basic User model with id, facebook_id and phone_number. User will be able to register by facebook_id or phone_number. I decided to add flag is_regular (to be sure that user finished registration and he can have profile and other info), but the main problem is that I need something for USERNAME_FIELD.
    – Ernst
    Nov 8 at 9:29










  • @Ernst please see my updated section in the answer.
    – ruddra
    Nov 8 at 9:55










  • @ruddra Than you for your help. I can't put phone_number into USERNAME_FIELD as facebook user can have only email, but not always phone_number. One of possible solution is to put username into USERNAME_FIELD as each regular user will have it, but I don't like idea to put there dummy data for temporary users
    – Ernst
    Nov 8 at 10:44
















You wouldn't need a temporary flag for the initial password prompt; a user that hasn't set a password will have a password_hash of None, where a user that just forgot theirs will just have a mismatching hash when they input the wrong one. The Profile pattern is a good one, though, and very often a better way to go than a custom user model. I wouldn't use hasattr to check for a profile, though. I'm not sure if the ORM will set that to None or just not create it, and I wouldn't depend on that behavior. Profile.objects.get(user=user) might be a little heavy, but it's more concrete.
– kungphu
Nov 8 at 5:41




You wouldn't need a temporary flag for the initial password prompt; a user that hasn't set a password will have a password_hash of None, where a user that just forgot theirs will just have a mismatching hash when they input the wrong one. The Profile pattern is a good one, though, and very often a better way to go than a custom user model. I wouldn't use hasattr to check for a profile, though. I'm not sure if the ORM will set that to None or just not create it, and I wouldn't depend on that behavior. Profile.objects.get(user=user) might be a little heavy, but it's more concrete.
– kungphu
Nov 8 at 5:41












@kungphu I am not against CustomUser Model. Because the requirement here is to store some data which are absolutely necessary, but does not come through social login(from comments in question). Also, hasattr usage is per documentation found here (in the 2nd example).
– ruddra
Nov 8 at 5:48






@kungphu I am not against CustomUser Model. Because the requirement here is to store some data which are absolutely necessary, but does not come through social login(from comments in question). Also, hasattr usage is per documentation found here (in the 2nd example).
– ruddra
Nov 8 at 5:48














@ruddra In my application users will not be able to perform registration by email, only Facebook Oauth2 or Account Kit. So I can create Basic User model with id, facebook_id and phone_number. User will be able to register by facebook_id or phone_number. I decided to add flag is_regular (to be sure that user finished registration and he can have profile and other info), but the main problem is that I need something for USERNAME_FIELD.
– Ernst
Nov 8 at 9:29




@ruddra In my application users will not be able to perform registration by email, only Facebook Oauth2 or Account Kit. So I can create Basic User model with id, facebook_id and phone_number. User will be able to register by facebook_id or phone_number. I decided to add flag is_regular (to be sure that user finished registration and he can have profile and other info), but the main problem is that I need something for USERNAME_FIELD.
– Ernst
Nov 8 at 9:29












@Ernst please see my updated section in the answer.
– ruddra
Nov 8 at 9:55




@Ernst please see my updated section in the answer.
– ruddra
Nov 8 at 9:55












@ruddra Than you for your help. I can't put phone_number into USERNAME_FIELD as facebook user can have only email, but not always phone_number. One of possible solution is to put username into USERNAME_FIELD as each regular user will have it, but I don't like idea to put there dummy data for temporary users
– Ernst
Nov 8 at 10:44




@ruddra Than you for your help. I can't put phone_number into USERNAME_FIELD as facebook user can have only email, but not always phone_number. One of possible solution is to put username into USERNAME_FIELD as each regular user will have it, but I don't like idea to put there dummy data for temporary users
– Ernst
Nov 8 at 10:44


















 

draft saved


draft discarded



















































 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53187522%2ftemporary-users-in-django%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







這個網誌中的熱門文章

Tangent Lines Diagram Along Smooth Curve

Yusuf al-Mu'taman ibn Hud

Zucchini