Using Flask-Security to authenticate REST API - python

I am using Flask-Security to build a web app that has a public REST API. I am trying to figure out how to add user registration and login using REST calls only.
It is fairly easy to create a user using user_datastore.create_user. But how can I then login the user, using a REST call?
If flask_security.utils.login_user took username+password or a token as an argument, it would be easy, but it takes a user object instead?
The documentation shows how to register and login using forms and views, but I need to be able to register and login from an IOS device (using RESTkit).

You will either want to use flask_security.decorators.auth_token_required along with SECURITY_TOKEN_AUTHENTICATION_KEY or SECURITY_TOKEN_AUTHENTICATION_HEADER (depending on whether you want to pass the token in the URL or in a header) or you can override flask_security.core.UserMixin.get_auth_token for your User class and Flask-Security will do the right thing.

[Writing an answer since I do not have enough credentials to comment on answer provided by Sean Vieira]
I looked a bit of Flask-Security code - it uses Flask-Login's LoginManager for this.
Flask-Login in turn expects the user to define token_loader (as well as implement get_auth_token in User class)
Does Flask-Security provide "default" token_loader functionality ? Otherwise - it is same as Flask-Login
Edit:
It turns out Flask-Security works just fine. I do not need to write my own token_loader.
I had security code in a separate file, and that is how "magic" broke.
I brought back the security code into myapp/init.py - and documented code "works"
Edit 2:
Refering to answer provided by Sean above. I don't think it is one or the other. One must use auth_token_required decorator.
Overriding get_auth_token in User class is optional, in case you want different implementation for token generation (I think)
Overriding get_auth_token in User class is not sufficient.

Related

Do `Users` need to exist in Django for SSO?

Basically, I am implementing the following functionality where the Front end will send signed JWTs to the backend. If the backend is able to decode and hence validate the Token (i.e. the signature, claims, audience, etc), then it will give access to the protected API resources:
Now the users already exist in Azure AD (so I don't want to have to create/manage users in the Django DB as well). So essentially all I want to do is protect the Django Restful API endpoints with validated Access Tokens. I was looking at Custom Authentication (by extending rest_framework.authentication.BaseAuthentication) but it seems that the authenticate method expects a User to be matched (Hence Users to exist) and returned for a successful authentication (I would like to just pass if JWT validation is a success or raise Exception is there is an error).
Django docs:
Either way, authenticate() should check the credentials it gets and return a user object that matches those credentials if the credentials are valid. If they’re not valid, it should return None.
I have achieved my desired flow (i.e. having no Django Users) using method_decorator but this does not seem like a correct pattern for authentication:
class ComponentViewSet(viewsets.ModelViewSet):
queryset = Component.objects.all()
serializer_class = ComponentSerializer
#method_decorator(validate_jwt)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
Django docs also state that:
The Django admin is tightly coupled to the Django User object. 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.
I have also done a separate implementation of the Custom Authentication by creating Users on the fly in the authenticate method just as the documentation recommended.
So my question is, is having Users exist in Django a strongly recommended design (or must according to the docs) even when you have a centrally managed Authentication system like LDAP/AD etc? Or is it also common not to use the Users model and use some other kind of implementation (for example the method decorator I used) when you don't intend to hold any kind of User information in the backend because I see it as redundant? Are there any advantages of duplicating a users database in the Django backend if it is strongly recommended. And do these users also require passwords if they extend django.contrib.auth.models.User?
Rather than needing to use a decorator on every method, just make your own authentication backend. When a view calls authenticate(), Django calls all backends specified in AUTHENTICATION_BACKENDS until one doesn't return None.
In regards to storing users, Django assumes by default that you want to store session data in the backend, to not require login. If you're fine with reauthenticating on every request, then you can feasibly just return a new instance of BaseUser object, without saving to the database, although other Django functionality might break in unexpected ways.
You might want to explore creating your own user model based on AbstractUser or even AbstractBaseUser. If you're not getting a new token on every request, it makes sense to store the token in the user model, along with a when_authenticated, time_to_expiry, etc.
A new user OR a returning user who's when_authenticated + time_to_expiry < now() access token will be validated with the auth server. On success, update the necessary time fields, along with current_access_token. Subsequent requests within your refresh window merely have to do a fast equality check, as opposed to another request to the server.
This will save your auth server from lots of requests, speed up your API (no auth server validation on every call), and still let you use some of the awesome functionality Django lets you have with users.

Python Login Multi Solution

After reading a lot of articles I came here to ask a question related to flask login with different types of users(two usermixin classes).
I have 2 types of users(as mentioned two user classes) - 1.Let’s call them regular users(email and password) 2.Social Network users(Facebook, Twitter, Google) and after a successful OAuth login with Facebook, Twitter and Google API I need to disable access to the profile page(regular users have access) and off course to stay logged and log them out later.
Here is the thing, I tried with https://pypi.org/project/Flask-Login-Multi/. Simple, I made 2 blueprints for auth.py where I defined logins for both types of users but it is not working(it crashes when I put #login_required)
I can provide my code here but I just want someone to help me with this if possible? Thanks in advance, regards.
There's a difference between authentication and authorization. Authentication determines who is allowed in and authorization determines what your users are allowed to see.
#login_required will handle authentication, but your question as asked deals with authorization. This means you will want to use #roles_accepted as well. Create multiple roles based on your intended behavior and then assign these users the appropriate role as needed. Using #roles_accepted will then deal with authorization, only allowing authorized users to access that route based on their role.
Flask principal with roles did the job and one more important thing to say is that I needed to define a login type in the session in order to have a conditional Flask login for both types of users(regular and social network).

Adding a Flask user login requirement

I am using Flask-Security, which in turn uses Flask-Login. I have a site which requires that a user both confirm their email address AND subsequently be approved by an admin. Without both of these things, I don't want the user to be able to use the site.
I've turned on SECURITY_CONFIRMABLE and the email with the confirmation token is being sent correctly. I've set SECURITY_POST_CONFIRM_VIEW, and the user is directed to the right view once they click the confirmation link in the email. However, the user is logged in as part of the confirmation process. I don't want the user to have access until they are approved by an admin. (Interestingly, this line is not present in the develop branch.)
I can think of four ways to get around this, and none are particularly appealing:
Allow the user to log in after confirmation, but decorate every login_required view with a function which checks the user's permissions and sends them to an error page if they aren't allowed. I already have 2 of these, and I'd prefer to avoid adding a third to every view. Plus, it feels wrong to allow login when the user can't use the site until they're approved.
Turn off SECURITY_CONFIRMABLE and email/confirm the token myself. This will require copying a lot of code and the chances of an error are higher than I would like. And are there perhaps other ways a user can login which would get around this check? Say through the password reset flow? Or if a future admin turned on passwordless login with tokens?
Monkey patch Flask-Security's login_user or Flask-Login's login_user to do my check. Seems quite hacky, and I don't know how to ensure that my patch is installed before any other code connects to the original function.
Fork one of these libraries and insert a callback which I can implement in my code. Sledgehammer, meet nut. Plus maintainability issues. Unless perhaps I managed to get a PR accepted.
I hope there is another answer I'm overlooking. This can't be that uncommon a setup!
Fortunately, the solution was MUCH easier than I feared. UserMixin has an is_active property which gets consulted at the very beginning of Flask-Login's login_user. So my code simply looks like this:
class User(UserMixin, Base):
#property
def is_active(self):
return (super(UserMixin, self).is_active and not self.has_role('pending'))
Now no user can sign in who has a 'pending' role.

Change user used to access database connected to django website in runtime

Currently I implemented a login routine for the website I am working on, according to This Tutorial.
Also I am not authenticating the user with djangos own system, because I am using authentication against the LDAP of my company (django-auth-ldap).
Currently I am using a general user to login to the database, which has universal access to all data, which also gives full access to any user logging in to the website.
To avoid that I would like to know how I can connect to the database as the individual user, who just logged in to the website.
Thanks in advance and sorry for bad english
Restricting user access to functionality and authenticating with the DB are handled separately in Django. You might be able to read the privileges of your users from the DB and map them to Django permissions but this is non-trivial (about Permissions see https://docs.djangoproject.com/en/2.1/topics/auth/default/#permissions-and-authorization).
In a UI/UX that has functionalities restricted depending on authorization, the frontend and backend need to be aware that permissions need to be checked and missing authorization needs to be communicated in some way or other to the user.
Example:
Users in group A are allowed to delete X. They see the "delete" button and there might also be an AJAX call that can delete X.
Users in group B are not allowed to delete X. They do not see the delete button and the AJAX call that can delete X needs to check for that permission and/or user group membership.
If you are only using a DB level authorization layer than - how would you know if the "delete" button should be displayed and for what to check in the AJAX call?
hi!
If I'm getting your problem correctly, the user you are creating is a Super User every time right?
Well if you are using Django auth.User model, you can just make User_object.is_super to False and then restrict the access of users though if-else in view! (User_object is the object of the auth.User model)
Does that made any sense?
//BTW, a side-note, a mistake I made while making my first custom user model: make sure to store your passwords hashed using Django hashes and salts!

Flask authentication using LDAP

I've searched an answer for this but have found nothing - maybe because it's so obvious. But I want to make sure.
What is the correct way to authenticate users using an LDAP server in a Flask web app?
This link describes 4 authentication methods, so along the same lines, do I just need to write an LDAP auth decorator?
(The reason I'm asking is to see whether I can make a Flask clone of my Django app)
Thanks for any help and suggestions.
Yes, you have to write your own decorator which checks the authentication.
In this decorator you should call the wrapped function if a user is authenticated. If not you should return a default page reminding the user to login.
I don't think you need a decorator, but it's a sensible thing to do, as it makes it easy to "tag" those routes that you want to require authentication.
Otherwise you'll be adding a lot more code whenever you want to implement some sort of authentication for a route.

Categories

Resources