Implementation of sso with django auth. Is this safe? - python

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.

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.

When/where should openid tokens be validated in a pyramid application?

I am writing a small webapp using the pyramid framework. For authentication I use a service of the cloud infrastructure that supplies me with a javascript web token, which I can use to validate the identity of the user.
I am a bit unsure on how to incorporate this with pyramid, especially since most examples I found are either username-password based authentication frameworks, or they generate JWT tokens themselves (I just consume those by the identity provider).
Anyway, in the login view, I have currently a logic like this:
.....
user = validate_jwt_token_and_return_user(id_token, ** config)
if user:
headers = remember(self.request, user.id)
raise HTTPFound(location=next_url, headers=headers)
Now it seems that by this I store the user-ID as a session cookie (?) and the user visits the next view, the user id is taken from the cookie?
Wouldn't it be better to store the jwt_token in the cookie and use this upon the next request to check whether the user's login attempt is valid?
Sure, you can store the JWT token in a cookie. You choose the authentication policy - just write one that does that instead of using one that stores it in the session.

Sharing authenticated users between Django and django-rest-framework in the same project

I have a Django project that will ultimately consist of three apps. Two of which will be "normal" Django apps, the third is a djangorestframework app. I also plan on creating a desktop client for the project at some point.
I want the rest app to be the only entity communicating with the database. Hence I use requests to communicate with the rest endpoints from the views of the "normal" Django apps and I will do the same for the desktop client. I want all apps to be accessible only for authenticated users, so I'm using Django's authentication backend.
My question is how to pass on the authenticated user/session from the pure Django apps to the rest endpoints when using requests in the views.
I managed to authenticate on the rest API using request's HTTPBasicAuth, but that requires me to have the user's password at hand in plain text. Sure, I could create a technical user to do these requests. But that would also mean that each and every request would need to go through authentication first and that doesn't feel like the best approach.
I have tried to extract the session cookie from the request object that is provided to the views and pass it on through requests.get, but did not manage to put it into the requests.get call the right way.
As of now, using requests and the established sessions looks like my best bet, especially since that will be the way the desktop client will do things, too. So I'm currently looking for the right way to provide requests.get with the session cookie, but I'm certainly open for better solutions.
You should use tokens.
Basically any kind of authentication out of your django project should be managed with secure tokens.
And yes, authentication check should happen everytime you send a request. To make it faster, you can store tokens in memory. (You can use redis or maybe even load your db on memory or ... ) but this is the right and common way to it. Even django does this check everytime using it's builtin functions.
DRF docs recommended some few packages to manage these tokens:
DRF: Third party packages
I used knox for many projects and it's pretty good.
Basically to authenticate your users over all of your projects or microservices, you have to take the token from user, set it as header or ... for your request to the main database or authentication project.
Most of the apps use token in headers which you can simply add to all of your requests calls:
Requests docs: Custom Headers

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!

Allowing Users logged in with multiple accounts at once to the same Django site

I'm creating a widget that gets installed on various different sites and I need distinct users for each site. Problem is, the same person browsing might have 2 different sites open at once that use my widget. This means that I need users to be logged in with multiple accounts simultaneously to the same Django site.
From my understanding, Django usually assumes that only 1 user is logged in per session.
What's the simplest and most effective way to go about this?
I ended up storing the per-site data in the user's session, i.e. session['site_id_1'] = user_obj_1, session['site_id_2'] = user_obj_2, etc...
Instead of logging in, I just store the user data in the appropriate key. Instead of logging out, I delete the key for the site.
I think you have to rewrite the session middleware. In the default implementation the session is stored in the database, the session_key is saved in a cookie and the cookie_name is defined in settings.py.
So if you write your own session middleware, you can define different cookie names for every site and retrieve the session from the database accordingly. django.contrib.sessions.middleware.SessionMiddleware is a good template.

Categories

Resources