We have a website running on Apache, access to which has a number of static pages protected via HTTP Basic authentication.
I've written a new part of the site with Django using Django's built in support for user management.
The problem I have is that users have to log in once via the HTTP Basic authentication and then again using a Django login form. This both clumsy and very confusing for users.
I was wondering if anyone had found a way to make Django log a user in using the HTTP Basic authentication information.
I not expecting to pass a password to Django, but rather if a user dave has been authenticated by Apache then they should be automatically logged into Django as dave too.
(One option would be to make Apache and Django share a user store to ensure common usernames and passwords but this would still involve two login prompts which is what I'm trying to avoid.)
For just supporting basic auth on some requests (and not mucking with the web server -- which is how someone might interpret your question title), you will want to look here:
http://www.djangosnippets.org/snippets/243/
This has been added to the Django 1.3 release. See more current documentation for this here:
http://docs.djangoproject.com/en/dev/howto/auth-remote-user/
Do check out Oli's links. You basically see the authenticated username as verified by Basic HTTP Authentication in Django by looking at request.META['REMOTE_USER'].
Update: Tested the proposed patch for ticket #689, which is available up-to-date in telenieko's git repository here. It applies cleanly at least on revision 9084 of Django.
Activate the remote user authentication backend by
adding the RemoteUserAuthMiddleware after AuthenticationMiddleware
adding the setting AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.RemoteUserAuthBackend',)
If you use lighttpd and FastCGI like I do, activate mod_auth, create credentials for a test user (I called it testuser and set 123 as the password) and configure the Django site to require basic authentication.
The following urls.py can be used to test the setup:
from django.conf.urls.defaults import *
from django.http import HttpResponse
from django.contrib.auth.models import User
urlpatterns = patterns('',
url(regex='^$',
view=lambda request: HttpResponse(repr(request), 'text/plain')),
url(regex='^user/$',
view=lambda request: HttpResponse(repr(request.user), 'text/plain')),
url(regex='^users/$',
view=lambda request: HttpResponse(
','.join(u.username for u in User.objects.all()),
'text/plain')),
)
After reloading lighty and the Django FCGI server, loading the root of the site now asks for authentication and accepts the testuser credentials, and then outputs a dump of the request object. In request.META these new properties should be present:
'AUTH_TYPE': 'Basic'
'HTTP_AUTHORIZATION': 'Basic dGVzdHVzZXI6MTIz'
'REMOTE_USER': 'testuser'
The /user/ URL can be used to check that you're indeed logged in as testuser:
<User: testuser>
And the /users/ URL now lists the automatically added testuser (here the admin user I had created when doing syncdb is also shown):
admin,testuser
If you don't want to patch Django, it's trivial to detach the RemoteUserAuthBackend and RemoteUserAuthMiddleware classes into a separate module and refer to that in the Django settings.
Yes you can use basic autorization with django as something similar:
def post(self, request):
auth_header = request.META.get('HTTP_AUTHORIZATION', '')
token_type, _, credentials = auth_header.partition(' ')
import base64
expected = base64.b64encode(b'<username>:<password>').decode()
if token_type != 'Basic' or credentials != expected:
return HttpResponse(status=401)
authorization success flow code ...
request.META contains key HTTP_AUTHORIZATION in which your Autorization is present.
In case if you are using apache with modWSGI, the key HTTP_AUTHORIZATION might not be present. You need to add below line in your WSGI config
WSGIPassAuthorization On
Refer this detailed answer:
Passing apache2 digest authentication information to a wsgi script run by mod_wsgi
Hope it is useful for someone who is wondering why HTTP_AUTHORIZATION key is not present
There is httpauth.py. I'm still a complete newb with Django so I've no idea how it fits in exactly, but it should do what you're looking for.
Edit: here's a longer bug thread on the subject.
Because django can be run in several ways, and only modpython gives you close integration with Apache, I don't believe there is a way for django to log you in basic on Apache's basic auth. Authentication should really be done at the application level as it'll give you much more control and will be simpler. You really don't want the hassle of sharing a userdata between Python and Apache.
If you don't mind using a patched version of Django then there is a patch at http://www.djangosnippets.org/snippets/56/ which will give you some middleware to support basic auth.
Basic auth is really quite simple - if the user isn't logged in you return a 401 authentication required status code. This prompts the browser to display a login box. The browser will then supply the username and password as bas64 encoded strings. The wikipedia entry http://en.wikipedia.org/wiki/Basic_access_authentication is pretty good.
If the patch doesn't do what you want then you could implement basic auth yourself quite quickly.
This seems to be a task for custom AuthenticationBackend - see Django documentation on this subject, djangosnippets.org has some real-life examples of such code (see 1 or 2) (and this is not really a hard thing).
AuthenticationBackend subclasses have to have only 2 methods defined and their code is pretty straightforward: one has to return User object for user ID, the second has to perform credentials check and return User object if the credentials are valid.
Related
Cant find a solution to a simple problem from current SO questions.
I have 2 apps in a django project.
App1 is from the graph tutorial found here
App2 will allow the users to list data from a DB in this case it will be branch names.
If I try and access a page with #login_required decorator then the url route has /accounts/login/ added and I get the usual cant find error.
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/accounts/login/?next=/calendar
Using the URLconf defined in graph_project.urls, Django tried these URL patterns, in this order:
[name='home']
about [name='about']
signin [name='signin']
signout [name='signout']
calendar [name='calendar']
callback [name='callback']
branches/
admin/
branches/
The current path, accounts/login/, didn't match any of these.
If I am reading the django docs correctly then this is default and I can redirect the login path using the LOGIN_URL in the project settings. When I set that to the signin function created in the tutorial for App1
def sign_in(request):
# Get the sign-in URL
sign_in_url, state = get_sign_in_url()
# Save the expected state so we can validate in the callback
request.session['auth_state'] = state
# Redirect to the Azure sign-in page
return HttpResponseRedirect(sign_in_url)
It will forever cycle the MS OAuth login but never access the requested page once completed. If I leave out the LOGIN_URL from settings it adds the accounts/login/ to the url as that is the default.
What is it that I am not understanding as to have login/logout requests handled by the functions in App1 for any requests made in other Apps when the request is behind a Login_Required decorator? And why does it not check if I am already authenticated when I can see It holds my name/email/calendar calls if I do not have a #Login_Required decorator and move between pages.
Thanks
I reached out to the support team on Django-Project.
One of the maintainers had a look over my source and all seemed fine on my end, he also checked the tutorial link I provided in the question.
His exact words were
>Personally, I’m a bit suspicious with how they’re doing this. I’ve read through the >tutorial and it looks to me like they’ve taken some shortcuts with managing the user in >the session.
That is enough for me to drop that route and look somewhere else, he offered these as a suggestion should anyone care to look into what I moved on to using.
[Azure AD pypi][1]
[Django Auth Docs][2]
[MS Django Auth][3]
[1]: https://pypi.org/project/django-azure-ad-auth/
[2]: https://django-auth-adfs.readthedocs.io/en/latest/
[3]: https://pypi.org/project/django-microsoft-auth/
I have an AngularJS Single Page Application that uses a Django backend API based on the Django Rest Framework. The API is protected via django-rest-framework-jwt. I would like to use django-allauth for account management and authentication on the server side.
I am just missing one single piece in the flow: How does my Oauth-Token from the client get transferred into a JWT-token? Basically, I would like to do as described here http://blog.wizer.fr/2013/11/angularjs-facebook-with-a-django-rest-api/ based on python-social-auth.
So my question is, how do I implement the ObtainAuthToken class from the link with django-allauth?
There are usually two login flows with social login: client-side ("Javascript SDK") and server-side. If your server needs to be authorised, it's usually a lot easier to go through the server-side flow. And that's also what all-auth does I think (and you didn't mention you use a frontend library like the blogpost you mentioned does).
Now the challenge is to provide the token from the server to the frontend. You would probably load the token in the HTML of the initialisation of the SPA, and then from Angular save the token client side (cookie, localStorage, etc.) so the session isn't lost on a refresh.
If you don't want the user to leave your app, you can open your /accounts/login/ or /accounts/signup/ url in a new window. In that new window they authorise your app, and your server receives the token upon return. There, you will have to generate a JWT token manually, and render that into the template so that javascript can access it. With js in that popup window, you can then communicate with your app that opened the popup and pass it the token – see this SO answer for an example – so it can save it.
Django-allauth provides signals that let you hook into the social login process. In your case, I would recommend subscribing to the allauth.socialaccount.signals.pre_social_login signal. The code will look something like this:
from allauth.socialaccount.signals import pre_social_login
#receiver(pre_social_login)
def create_jwt_token(sender, request, sociallogin, **kwargs):
# dig into the sociallogin object to find the new access token.
We used hello.js for O-Auth at the company I worked at.
You provide a shim on the Python end and get the refresh token and whatever other data needed once the user connects their social account.
We redirect them via Django to the page they attempted to access from their OAuth provider's page.
Each user still has their own email account which is needed for the JWT, but you could assume that whatever email is in the scope of their social account is their email then use django's functionality to create new users: User.objects.create(email=emailStringFromOauthData) etc.
I used Django Tastypie to build my api and i'm thinking in the correct way to create a login form so the user can login in the application, right now what I do is send a GET request with username/password he submited in the form as filtering options, but i'm pretty sure thats not secure at all. How can i do the same using POST request?
When i open the console with firebug:
GET URL/app/api/v1/user/?email=USER&password=PASS
Api-Auth and Content-Type are on the header.
#leosilvano
Don't handle user authentication and authorization using angular js not that its impossible but just that its not too secure and implementing also take some effort when Django's provides something which far easy than this.
I happen to be using django + angularjs + tastypie (REST API ). If you like take a look at my way of implementation.
Include your index.html of the angularJs in your templates ( Django Templates ) and place your directives, controllers, js, css and etc in the static folder ( Django Static ). Make your API calls after the auth processes. This will work seamlessly and you will run into less issues as well.
Reasons:
user_auth models becomes so handy while registering and logging in using templates and you don't need to sweat trying to write your own authentication which i'm sure you have to do when you go with Angular Js login auth implementation.
Use of decorators like for a view lets say "profile" if you need to check if the user is logged in all you need to do it something like the following
#login_required(login_url='/login/')
def profile(request):
return render_to_response('profile.html')
Passing password through "GET" is bad .. and passing auth values through "POST" and getting it via JSON .. is also not a good idea. Because you will be susceptible to middle-man attacks ..
Remember you have to take measures for CORS requests when using Angularjs for login since anyone can view the json response and they will be able to reproduce the same structure. Implementing Perm-Mixins and Groups is way more easier when using Django templates.
Handling exceptions like 404 or if you want to handle only post requests and thereby take user to a custom page ( actions like redirect ) becomes difficult. I am aware of SPA's but still if there happens to be redirection .. in my case i needed to redirect to another site. Following shows how simple it can be achieved including http statuses.
if request.method != 'POST':
return HttpResponseNotFound(render_to_response('404.html', { 'message' : 'Only POST Requests are allowed for authentication process.', 'baseurl' :request.build_absolute_uri('/').rstrip('/')}))
Solution:
Use Angular Js and REST (Tastypie) interaction to happen after you login. Use Django template for login authorization. Make use of the django modules .. it saves a lot of time.
If you still want to login using REST API .. by send post to django .. please take a look at the following post
How can I login to django using tastypie
You can POST data using the $http module
Just do:
$http.post(url, data)
Always send authentication data through https or your password will be sent in clear text.
Given:
A default-configured django service, and;
A default-configured django rest framework:
How do I log in?
By "default-configured", I mean that I followed the suggested tutorials of both websites.
However, neither django nor the django rest framework discusses how to use the authentication system as a user. They often seem to discuss authentication from the point of view of the python code running within the django framework.
Where can I read concise, clear documentation that tells me how the user requests the website with correct authentication?
I know that my DEFAULT_PERMISSION_CLASSES in 'REST_FRAMEWORK' is set to: rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly
Which implies that I am probably using whatever authentication is set in the django webservice by default.
And I know that my django instance has "Authenticationmiddleware" and "CsrfViewMiddleware" and the like, whatever they mean. (Ok, CSRF is easy to google, but that's besides the point)
The relevant django documentation seems to be https://docs.djangoproject.com/en/1.5/topics/auth/default/
However, that doesn't tell me how to actually log INTO the website so that I can POST some data.
Where can I find simple, concise instructions that tell me how to:
GET some data as a particular user, and.
POST some data as a particular user.
In the mean time, I will continue perusing the documentation.
So far I have attempted to do:
Obtain the csrf cookie by using GET /api-auth/login/
Perform the login by using POST /api-auth/login/ and providing the cookie obtained in the previous step.
However, Django still detects forgery.
Here is how to do it:
django-rest-framework provides a login page for you if you follow the documentation at http://django-rest-framework.org/
django by default, at least in version 1.5, uses CSRF tokens for security.
In order to login, you need to:
Obtain the cookies by visiting the login page, at /api-auth/login/, including the csrftoken and sessionid. This will be something like: csrftoken=123411231234123; sessionid=143212341234123412
Send the login page as a http POST using the above cookies, and setting the POST as a form with username, password, and csrfmiddlewaretoken as the form elements.
** The value of the csrfmiddleware should be that of the csrftoken value.
So, to re-iterate, the following must be set:
All the original cookies must be set in the POST headers.
The username in the POST form.
The password in the POST form.
The csrfmiddlewaretoken in the POST form.
I'm using django with apache mod_python. I authenticate users via the basic auth handler (django.contrib.auth.handlers.modpython) [1]. My views get only an instance of AnonymousUser passed in request.user. What am I doing wrong?
[1]: it's an API that is https only, so it shouldn't be a security problem.
I figured it out by myself now: In case you need to do something user specific in a view that is exposed via basic authentication don't let apache handle authentication for you.
If you want HTTP Basic Authentication for all your views write (or get) a Middleware. If, like me, you want to expose only a few views via basic auth create a decorator.
http://djangosnippets.org/snippets/243/ is a good start.