How to see whether user approved requested permission in Python-Social-Auth - python

I am using python-social-auth to log users in to my we application. Everything works as expected to create user accounts, log them in, etc.
Now I am also requesting the publish_actions permission from the user. When I do this, I see the request step when I try to log in, so I know Facebook is being asked for this permission properly. However, I can't figure out how to discover from the response whether the user approved this permission. I want to store this so that I only expose the right parts of the UI based on the user's choice to allow or deny the permission.
Here's how I request the permission:
SOCIAL_AUTH_FACEBOOK_SCOPE = [ 'email', 'publish_actions' ]
And for extra params I have the following:
SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
'fields': 'id,name,email',
}
I needed to add email to the list to expose the email value returned from FB to my authentication pipeline.
If I try to add publish_actions to this field, the pipeline is interrupted and the authentication is cancelled:
SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
'fields': 'id,name,email,publish_actions', # <-- this causes a failure in auth pipeline
}
Without publish_actions noted as an extra param, I can't see any other data that indicates whether the user approved the permission. How do I discover this?

You can make a request for /me/permissions/ using the user access token, that will show you which permissions the user has granted to your app.
You can also make the login dialog return the granted scopes directly in the direct URL (see parameter return_scopes) – whether or not that can easily be added to the login dialog from within your framework, I don’t know.

Related

Django: phone verification for inactive account

I'd like to implement phone verification with pyotp in my view class-based Django (2.5) project.
After new users sign up (specifying name, phone, e-mail and password) in RegisterView, they should be redirected to GetAccessCodeView with an input field for verification code and a hidden field with a secure token. For generating and sending the code and the token I have to pass there a newly created user instanse from RegisterView to GetAccessCodeView.
How can I do that? Currently newly created users have is_active field set to False (it should become True after code succesful verification), thus cannot be authorized by default, so without changing login procedure, it is impossible to use request.user directly. But if I let inactive users to log in, then all the login_required views will let unconfirmed users to open corresponding pages. Should I write is_active check for each view manually or maybe Django has some ready stuff like 'login_and_active_required'? Or maybe there is some different solution?

How to allow an inactive user to log in and use the /user/ endpoint

I am using rest-auth and I want to allow the users reactivate their accounts by their own.
I managed to allow the inactive user to log in by using AllowAllUsersModelBackend in settings.py
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.AllowAllUsersModelBackend",
]
And then I disallowed rest-auth to check respond with an error message by deleting these lines from the LoginSerializer:
if not user.is_active:
msg = _('User account is disabled.')
raise exceptions.ValidationError(msg)
I also customized the UserDetailSerializer to allow deactivation and activation by adding is_active field of the user model.
Now I can log in using a deactivated user and it sends me a JWT back, but when I try to use the /user/ endpoint it respond with that error:
{
"detail": "User account is disabled."
}
I want to allow them to use this endpoint to reactivate their account but without allowing them to use any other custom endpoint that requires authentication.

Django Rest Framework - For user registration getting authentication error

I am registering the user by using this post , In this case successfully i am creating the user.
After above step, I enabled token authentication by using this post, but the main problem is when i perform registration operation it is showing "detail": "Authentication credentials were not provided." this error.
Here i don't need authentication checking for user registration, need authentication checking for remaining apis.
Can anyone please help me out how to disable authentication for user registration.
Thanks in advance
Depending on how you want your endpoint to be accessed and the level of publicity you can use 1 of 2 permissions.
Either AllowAny or IsAuthenticatedOrReadOnly
AllowAny will allow for anyone accessing each of the methods you've defined for the viewset.
IsAuthenticatedOrReadOnly will, by default, allow anyone to use the safe http methods GET, HEAD or OPTIONS on your endpoint but in order to POST, PUT/PATCH or DELETE you would need to be authenticated.
You add these to each of your endpoints for fine grained control
from rest_framework import permissions
class MyViewSet(viewsets.GenericViewSet):
permissions_classes = (permissions.AllowAny, )

Create Django token-based Authentication [duplicate]

I am trying to figure out the best way to implement token based authentication in my django app. An external, non-django application is setting a cookie, with a token, and I have a webservice that can retrieve user information based off of that token. If the user has the cookie set, they should not need to authenticate on my site and should be automatically logged in based on the info passed back by the web service. As I see it, there are a few different options to perform the actual check and I'm not sure which is best:
Write a custom decorator like the one in this snippet and use it instead of
login_required.
Call a custom authenticate method inside base_site through an ajax call. On every page, a check would be made and if the cookie exists and is valid, then the user would be automatically logged in.
Add some javascript to the LOGIN_REDIRECT_URL page that will check/validate the cookie in an ajax call, and automatically redirect back to the referrer if the cookie authenticated.
Is there an option I am missing? Ideally, there would be a way to build this into login_required, without having to write a custom decorator.
Before searching for code, be sure you read the documentation. http://docs.djangoproject.com/en/1.2/topics/auth/#other-authentication-sources
Also read the supplied Django source.
You want to create three things.
Middleware to capture the token. This is where most of the work happens. It checks for the token, authenticates it (by confirming it with the identity manager) and then logs in the user.
Authentication backend to find Users. This is a stub. All it does is create users as needed. Your identity manager has the details. You're just caching the current version of the user on Django's local DB.
Here's the middleware (edited).
from django.contrib.auth import authenticate, login
class CookieMiddleware( object ):
"""Authentication Middleware for OpenAM using a cookie with a token.
Backend will get user.
"""
def process_request(self, request):
if not hasattr(request, 'user'):
raise ImproperlyConfigured()
if "thecookiename" not in request.COOKIES:
return
token= request.COOKIES["thecookiename"]
# REST request to OpenAM server for user attributes.
token, attribute, role = identity_manager.get_attributes( token )
user = authenticate(remote_user=attribute['uid'][0])
request.user = user
login(request, user)
The identity_manager.get_attributes is a separate class we wrote to validate the token and get details on the user from the IM source. This, of course, has to be mocked for testing purposes.
Here's a backend (edited)
class Backend( RemoteUserBackend ):
def authenticate(**credentials):
"""We could authenticate the token by checking with OpenAM
Server. We don't do that here, instead we trust the middleware to do it.
"""
try:
user= User.objects.get(username=credentials['remote_user'])
except User.DoesNotExist:
user= User.objects.create(username=credentials['remote_user'] )
# Here is a good place to map roles to Django Group instances or other features.
return user
This does not materially change the decorators for authentication or authorization.
To make sure of this, we actually refresh the User and Group information from our
identity manager.
Note that the middleware runs for every single request. Sometimes, it's okay to pass the token to the backed authenticate method. If the token exists in the local user DB, the request can proceed without contacting the identity manager.
We, however, have complex rules and timeouts in the identity manager, so we have to examine every token to be sure it's valid. Once the middleware is sure the token is valid, we can then allow the backend to do any additional processing.
This isn't our live code (it's a little too complex to make a good example.)

Django user impersonation by admin

I have a Django app. When logged in as an admin user, I want to be able to pass a secret parameter in the URL and have the whole site behave as if I were another user.
Let's say I have the URL /my-profile/ which shows the currently logged in user's profile. I want to be able to do something like /my-profile/?__user_id=123 and have the underlying view believe that I am actually the user with ID 123 (thus render that user's profile).
Why do I want that?
Simply because it's much easier to reproduce certain bugs that only appear in a single user's account.
My questions:
What would be the easiest way to implement something like this?
Is there any security concern I should have in mind when doing this? Note that I (obviously) only want to have this feature for admin users, and our admin users have full access to the source code, database, etc. anyway, so it's not really a "backdoor"; it just makes it easier to access a user's account.
I don't have enough reputation to edit or reply yet (I think), but I found that although ionaut's solution worked in simple cases, a more robust solution for me was to use a session variable. That way, even AJAX requests are served correctly without modifying the request URL to include a GET impersonation parameter.
class ImpersonateMiddleware(object):
def process_request(self, request):
if request.user.is_superuser and "__impersonate" in request.GET:
request.session['impersonate_id'] = int(request.GET["__impersonate"])
elif "__unimpersonate" in request.GET:
del request.session['impersonate_id']
if request.user.is_superuser and 'impersonate_id' in request.session:
request.user = User.objects.get(id=request.session['impersonate_id'])
Usage:
log in: http://localhost/?__impersonate=[USERID]
log out (back to admin): http://localhost/?__unimpersonate=True
It looks like quite a few other people have had this problem and have written re-usable apps to do this and at least some are listed on the django packages page for user switching. The most active at time of writing appear to be:
django-hijack puts a "hijack" button in the user list in the admin, along with a bit at the top of page for while you've hijacked an account.
impostor means you can login with username "me as other" and your own password
django-impersonate sets up URLs to start impersonating a user, stop, search etc
I solved this with a simple middleware. It also handles redirects (that is, the GET parameter is preserved during a redirect). Here it is:
class ImpersonateMiddleware(object):
def process_request(self, request):
if request.user.is_superuser and "__impersonate" in request.GET:
request.user = models.User.objects.get(id=int(request.GET["__impersonate"]))
def process_response(self, request, response):
if request.user.is_superuser and "__impersonate" in request.GET:
if isinstance(response, http.HttpResponseRedirect):
location = response["Location"]
if "?" in location:
location += "&"
else:
location += "?"
location += "__impersonate=%s" % request.GET["__impersonate"]
response["Location"] = location
return response
#Charles Offenbacher's answer is great for impersonating users who are not being authenticated via tokens. However, it will not work with clients side apps that use token authentication. To get user impersonation to work with apps using tokens, one has to directly set the HTTP_AUTHORIZATION header in the Impersonate Middleware. My answer basically plagiarizes Charles's answer and adds lines for manually setting said header.
class ImpersonateMiddleware(object):
def process_request(self, request):
if request.user.is_superuser and "__impersonate" in request.GET:
request.session['impersonate_id'] = int(request.GET["__impersonate"])
elif "__unimpersonate" in request.GET:
del request.session['impersonate_id']
if request.user.is_superuser and 'impersonate_id' in request.session:
request.user = User.objects.get(id=request.session['impersonate_id'])
# retrieve user's token
token = Token.objects.get(user=request.user)
# manually set authorization header to user's token as it will be set to that of the admin's (assuming the admin has one, of course)
request.META['HTTP_AUTHORIZATION'] = 'Token {0}'.format(token.key)
i don't see how that is a security hole any more than using su - someuser as root on a a unix machine. root or an django-admin with root/admin access to the database can fake anything if he/she wants to. the risk is only in the django-admin account being cracked at which point the cracker could hide tracks by becoming another user and then faking actions as the user.
yes, it may be called a backdoor, but as ibz says, admins have access to the database anyways. being able to make changes to the database in that light is also a backdoor.
Set up so you have two different host names to the same server. If you are doing it locally, you can connect with 127.0.0.1, or localhost, for example. Your browser will see this as three different sites, and you can be logged in with different users. The same works for your site.
So in addition to www.mysite.com you can set up test.mysite.com, and log in with the user there. I often set up sites (with Plone) so I have both www.mysite.com and admin.mysite.com, and only allow access to the admin pages from there, meaning I can log in to the normal site with the username that has the problems.

Categories

Resources