Django: Intervention when raising errors when creating User via social authentication - python

I'm setting up social authentication with the package social-auth-app-django. I have the below ValueError raised when attempting to create a user when an email isn't supplied.
users.py
if not email:
message = "Ooops, it doesn\'t look like an email was specified. If you are signing in via one of your social media account, please ensure there is an email address associated to your account."
raise ValueError(message)
It wasn't really necessary originally as I would handle the form validation on the front end with standard form validation. However, when integrating social sign-ins this causes an issue. In development this is great - but essentially at this stage I would like to fling the error back to an errors template which says that they must supply an email address. I believe on platforms, such as Twitter, a user can initially register without an email address associated to the account. That's where I spotted this conflict. Essentially, social-auth-app-django isn't throwing an error before this - so how at this point, do I send the user back to a specified template which I can build to handle such errors by passing them back via the context?
Looking and looking through the documentation, I can't see anything I would be confident in running with...any advise would be greatly appreciated!

The python-social-auth module supports a pipeline - so defining a method in the end of your SOCIAL_AUTH_PIPELINE in settings.py and then implementing it in pipeline.py would give you a way to define the desired behavior in case, when your users have no email associated with their social users:
SOCIAL_AUTH_PIPELINE = (
# any other pipeline handlers ...
'custom.signup.pipeline.prevent_without_email'
)
Then in your custom/signup/pipeline.py you'll need to implement the actual partial for prevent_without_email - You can see it here in more detail http://python-social-auth-docs.readthedocs.io/en/latest/pipeline.html
If you need a specific handler, you can use Django signals - create a signal in signals.py, emit it in pipeline.py and let the handler in callbacks.py to handle it, when an email is not specified.

Related

django authentication model - login with emailed link?

Is there a way to set up the django authentication model where, instead of a password, users put in their email address, and then are emailed a link that they click on to login with? If so, are there any tutorials on how to set this up?
Yes, there is.
You'll either need to hope there is already a module out for this, otherwise you will have to write your own way of authenticating.
I'll give a rough estimate of how it's gonna work.
First, you will need to create a class inheriting from AbstractBaseUser
Set the username field to email, still include password fields. They are required.
Then, you will need to create a manager for that custom base user.
the user manager must have two methods: create_user() and create_superuser()
Then, in a view, have user enter their email address, and then generate a token with Django's default_token_generator, and send that token via e-mail to the user.
Create a view which accepts the token, and logs the user in.
I highly advise you to take the docs as your guide. This might get relatively complicated.
More info on the Django Docs
Side note: This will not be as secure as email and password validation. If a user's email gets hacked, the hackers will instantly know not only which site they can target, but also get a free pass to access.
Alternatively; check out Django AllAuth, they provide lots of ways to authenticate, including with Gmail or Facebook. There are some great tutorials online, but you'll have to do some googling. ;)

Creating auth token for non user object django rest framework

I am looking to generate a django authtoken for a non user object. previously I had easily generated auth tokens for user objects like this
email = request.data.get('email')
user = User.objects.get(email=email)
Token.objects.create(user=user)
but if I am trying this for non user object it is not getting generated.
device_id = request.data.get('device_id')
tablet = Table.objects.get(device_id=device_id)
Token.objects.create(user=tablet)
Here Table is simple model holding various device_ids.
I just want to generate an auth token for each tablet like we do for each user.
If you are linking devices to users, and need a "per device" token where a user has >1 device (e.g. desktop, tablet, phone, etc) that are logged in separately and where the tokens can be revoked, then look at the Knox App:
Django Knox (https://github.com/James1345/django-rest-knox)
Otherwise, authentication tokens are normally used to log in a user. If you don't have a user then they aren't much use as far as the standard infrastructure is concerned.
If you want something custom, then you'll have to write your own solution, which might include:
A custom middleware if:
you want/need to set request.device=, like request.user
you want a custom user object (below)
Decide if you want a "fake" user like DeviceUser
Implement the User interface (see AnonymousUser's example)
Has is_authenticated=True
Has permissions (?)
Has is_device_user=True so you can distinguish
Be really careful not to rely on request.user to have a user_id
Possibly a new Permission class (e.g. a new IsAuthenticated)
The main problem I see is with things that expect a non-anonymous User object (in request) to be a real user with a pk. If you are careful then this might not be too big an issue, but you'll need to start implementing to be sure how it affects you.

apache authentication against django application

I have the following problem:
I wrote an application in Django, basically it’s some kind of management platform for my choir. Now we have some files that we want to provide to our members, but only to them (i.e. registered users). Additionally, there are some files that should only be accessible to the management. Ideally, I want to be able to define in the database who can access which files.
Here is some information about the environment:
Server OS Debian 8.7
Python version 2.7
Django version 1.10
Mod_wsgi version 4.3
What have I tried so far?
Django documentation provides a very, very small example of how to authenticate against the database.
The part where I only validate whether the user is a valid user works. Although it kind of sucks, since the users have to login again every time they access a file.
The part where I validate whether the user is a group doesn't work at all. Apache says:
Unknown Authz provider: group
Then I started to look up this problem, which led me here. When I use the option wsgi-group the apache configuration doesn't cause any problems, but it apparently doesn't validate whether the user is in the specified group either. (I tried to access the file with a user who is not in the specified group and succeeded...)
Now here are some questions I have to this problem:
Has anyone else experienced these problems with the Require group/Require wsgi-group option and has found a solution?
Is there a possibility to "link" the session of the website with the session of the apache file access? As I already mentioned, it is not very user-friendly if the users have to login again every time they want to access a file..
Is there a possibility to write a custom authentication function? Technically, looking at an example implementation for the authentication method check_password (here "auth.wsgi"), I could overwrite this function, provided that the environ variable contains the URL that the user wants to access. (does it?)
This would probably be the most dynamic solution, but I don't quite know if this is possible. The basic idea ist that I render a button in the view for each downloadable file, that sends a POST to the server. The server checks whether the session user has the rights to access this file, and, if true, returns the file. This would mean that the file can only be accessed by the application and not through the apache. Is this possible?
I hope someone can help me, thank you in advance anyways =)
Edit: I did some research and found an answer for my last question here (Having Django serve downloadable files). With the mod_xsendfile this is apparently manageable pretty easily. That leaves just 3 more questions ;)
I have also used X-Send for various purposes within my Django App (usually to serve up files stored in FileFields) but for hosting a suite of static pages such as sphinx-generated pages and supporting media, proxying via X-Send has lead me into more problems than solutions.
As I was researching my own question I discovered yours and realized that mine is the solution to your question #1 and #2: Use mod_wsgi's WSGIAccessScript config with the allow_access(environ, host) function to instantiate a Django WSGI Request which can be programatically distilled to a boolean using whatever business logic you require. I hope someone else can answer my more specific concerns about the approach, but it is functional.
Your question #3 suggests I point you towards Django's Custom Authentication Backend documentation. I have written a custom backend that uses email addresses to authenticate a user rather than requiring users to remember an arbitrary username.
#Django Imports
from django.conf import settings
from django.contrib.auth.models import User
class EmailAuthBackend(object):
"""
Email Authentication Backend
Allows a user to sign in using an email/password pair rather than
a username/password pair.
"""
def authenticate(self, username=None, password=None):
""" Authenticate a user based on email address as the user name. """
try:
user = User.objects.get(email__iexact=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
""" Get a User object from the user_id. """
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None

Django complementary external source authentication

I'm trying to build a Django website that will be maintained and used by university students mainly. I need to restrict access to a few pages for certain approved students, but it would be very unmaintainable if I needed to create a new Django user for every student that wants to log in. Luckily, the university provides an API to check whether a username/password combination is correct. So I had the idea to create an authentication model complementary to Django's model, where users' university account can get approved by an admin, after which it is a valid login to view certain pages.
So essentially, some users may use a Django account (if they're in charge for the content of the website), and other users may just log in to view some pages with their uni account. For the uni account, the minimum amount of info should be stored (in other words, only the username is really required to approve certain users).
I can't seem to figure out how to build such a system in Django. I cannot use the standard User object because it stores data that is completely redundant, and I cannot substitute the user model because that would only make things incredibly complex. It seems reasonable to forget the User model altogether, but Authenticate needs to return a valid user. This makes me wonder, can I create regular Django users with as little information filled in as possible (dummy data except for the username), and then authenticate them with the API? Probably, but that hardly seems like a good idea.
To authenticate users using the university API, all you need to do is to write an authentication backend. You can then create a local user for these uni users the first time they login, since there is only two required fields: username and password. You can use set_unusable_password() so check_password() for this user will never return True.
The Django admin system is tightly coupled to the Django User object
described at the beginning of this document. For now, the best way to
deal with this is to create a Django User object for each user that
exists for your backend (e.g., in your LDAP directory, your external
SQL database, etc.) You can either write a script to do this in
advance, or your authenticate method can do it the first time a user
logs in.

How do you find the login provider in Django-Allauth?

I saw the code in the accepted answer for this question:
How to access user names and profiles with django-allauth
But when I run a template with {{user.get_provider}}, nothing appears. I was expecting it to say either "LinkedIn Oauth2" or maybe "native". (Those are my two ways to log in.)
Are there special things you need to get the template calls working? Other template items are working fine, such as account.get_avatar_url.
To my knowledge the user profile doesn't record which credential was used to establish the current session, nor as far as I am aware does a list of a particular account's associated credential types automatically populate into the user context object (I'm not sure which you were trying to get from the question you asked).
You can access what credentials an account has available to it in python & export these to the context. See the socialaccount/connections.html template that comes with django-allauth as an example.

Categories