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

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.

Related

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!

Access request object from within django admin's authenticate()

Is this anyhow possible to access the request object from within the django admin's authenticate() in custom auth backend?
The reason for this is I want to keep and re-use the user's password (or a Kerberos ticket - not sure yet) to authorize an operation in another system, which uses the same LDAP backend. For this purpose I wanted to use request.session - but it doesn't seem to be accessible, at least not the easy way.
If there's any other possibility to pass a value from the auth to the model, I'd be happy to get a suggestion as well.
Thanks.
I believe it's a bad idea to store raw password in session for security reasons, but some ideas (just ideas, never tried it):
Access session directly https://docs.djangoproject.com/en/1.10/topics/http/sessions/#using-sessions-out-of-views
Make sure your session won't be flushed by django.contrib.auth.login. See details in implementation.
user_logged_in signal has access to request and user object.
user_logged_in.send(sender=user.__class__, request=request, user=user)
Looks like you can add attribute to user object inside authentication backend method and later attach it to session.

Using Flask-Security to authenticate REST API

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.

Implementation of sso with django auth. Is this safe?

I'm trying to implement single sign-on using only django auth.
Let's assume two django projects, on different sub-domains: site.com(auth) and app1.site.com(app1)
The auth table in site.com is master. site.com handles: login, logout, account registration, etc.
site.com sets SESSION_COOKIE_DOMAIN to .site.com to allow it to be read by subdomains
app1 will have login_url set to a view in the app1 project, which does the following:
retrieves site.com's session_id value(from cookie)
validates session_id by making a request to: site.com/validate/[session_id]/
If False, redirects to site.com/login?next=[...]
If True, request user data to: site.com/attributes/[session_id]/
site.com/attributes/ delivers a dictionary with all the User values, encrypted using a shared SSO_KEY(encryption done the same way django encodes and decodes session_id)
Now, app1 has a model SSO_User which has two fields, a foreign key to User model and an integer field. The SSO_User models links local auth User to the id of master auth table.
Using the id retrieved from site.com, we check SSO_User for existing local user, if true we simply update the values and login; if non existing, we create the user and SSO_User and login.
app1(or any other sub-domain) can keep their own profile information, without interfering with anything.
It seems simple to implement and safe, but before implementing I wanted some opinions. What do you think?
I don't profess to be a web security expert, but I've done something similar in the past. As long as you properly set the cookie domain (which you claim to be doing) I don't really see any security issues, especially since both sites are on the same domain.
If you really want to be safe I suppose you could set up your own OAuth portal or something, but quite frankly that seems to be overkill.

Python flask session per user and their own object

I'm hopeful someone can point me at the proper way to implement this situation.
I'm building a flask app for multiple concurrent users. The app essentially wraps a back-end RESTful service that requires authentication and will allow commands to be sent. The users do not need to login the flask app itself, but they do provide credentials that I use to authenticate an object to the Restful service. As such, each user needs their own unique object for their particular session. For ease of use/laziness I'm also storing some trivial information in a few session['x'] variables.
The session information is not unique to each user and it appears the last user to "login" clobbers the previous users' authentication object.
What is the proper way to build this? I could move the session variables into the object, but that still leaves the problem that all users require their own back-end object. Do I need to implement a true flask-login?
I have used the flask-login extension in the past to implement login and session management systems.
One issue with storing user information in a python object like session is that you will never be able to scale your application beyond one process. In almost all cases you will want to have more than one process handling requests. Each process will be running with its own python interpreter and will not share the session object. You really want a database that is shared by all processes to store session information, redis works great for this purpose.
For flask-login you need to implement a user class and two functions (best implemented as methods of the user class):
A login function that will authenticate the provided username and password against your service and then generate a large random number (16-32 bytes) using a cryptographically secure pseudo-random number generator (i.e. os.urandom()) to use as a session id. Use the generated session id as the key to the user's session information in a sessions table or database and as the id of your User class. The login function should return your User object. I normally implement it as a classmethod of the User class.
A load_user callback that will take a session id generated in the above function and return the class representing your user populated with information from the database that is storing your session information
Flask-login will handle securely storing (as a HMAC) your session id on the user's computer as a cookie and at the beginning of requests it will call your load_user callback to retrieve the user's information. Logged in users' information will be available by importing the current_user proxy from flask_login (from flask_login import current_user). This object is also available in jinja templates.

Categories

Resources