Django Tastypie: How to Authenticate with API Key - python

I'm making an internal API with TastyPie. I have
from tastypie.authentication import ApiKeyAuthentication
class MyResource(ModelResource):
Meta:
authentication = ApiKeyAuthentication()
With Auth rules disabled, my API works great. With it on, I get a 401 (UNAUTHORIZED) response no matter what I try.
I'm sure this is one of those things that's really obvious once you've see it in action, but in the meantime, please advise how to to make the request (a GET).

Add the username and api_key parameters to your GET variables. Make sure that you have the
curl http://localhost:8000/api/v1/books/?username=issackelly\&api_key=123456789adfljafal
Make sure to follow the other instructions from teh docs when setting it up:
ApiKeyAuthentication
As an alternative to requiring sensitive data like a password, the ApiKeyAuthentication allows you to collect just username & a machine-generated api key. Tastypie ships with a special Model just for this purpose, so you'll need to ensure tastypie is in INSTALLED_APPS.
Tastypie includes a signal function you can use to auto-create ApiKey objects. Hooking it up looks like:
from django.contrib.auth.models import User
from django.db import models
from tastypie.models import create_api_key
models.signals.post_save.connect(create_api_key, sender=User)

Related

Custom Authentication in Django Rest Framework

I have a django rest framework application with custom authentication scheme implemented. Now I want to allow external app call some methods of my application.
There's an endpoint for external app to login /external-app-login which implemented like this:
class ExternalAppLoginView(views.APIView):
def post(self, request):
if request.data.get('username') == EXTERNAL_APP_USER_NAME and request.data.get('password') == EXTERNAL_APP_PASSWORD:
user = models.User.objects.get(username=username)
login(request, user)
return http.HttpResponse(status=200)
return http.HttpResponse(status=401)
Now I want to add authentication. I implemented it like this:
class ExternalAppAuthentication(authentication.SessionAuthentication):
def authenticate(self, request):
return super().authenticate(request)
But authentication fails all the time. What is the correct way to do it? I want to store login/password of external app in variables in application, not in database.
The authentication fails, because it needs to return a registered user in your database to authenticate. However as the user info is all in variables instead of database, the issue arises.
There are more than one ways to overcome this issue. Firstly i would suggest you write the authentication code instead of using
return super().authenticate(request) as this would lead you to the real reason of the issue.
Also must give a read to this documentation link, it clears a lot of things regarding authentication.
https://www.django-rest-framework.org/api-guide/authentication/
Now after you have done all that, and you seek ways how to authenticate, then you can try either remote user authentication, or you can check for existing users in your variables and use anonymous user for authentication which resolves the issue.

Authentication with Django channels using Tokens - How do I use this mixin

My website hosts a lot of APIs(CRUDs) using DJR. I am using DJR Token based authentication and for testing I would add this header to postman
Key : Authorization
value : Token 826fdf3067b07afdf9edd89a6c9facd9920de8b8
and Django Rest Framework would easily be able to authenticate the user.
Now I inlcuded Django channels constantly 1.1.5 and wanted to know how I could do token based authentication. I read this post and it suggests that I copy this mixin to the project. I just started with Django-channels and am not sure how to include that mixin to my code. Currently I have something like this
#channel_session_user_from_http
def ws_connect(message):
user = message.user
message.reply_channel.send({"accept": True}) #Send back Acceptance response.
#channel_session_user
def chat_join(message):
user = message.user #Get authenticated user
I have the following two questions
1-How do I include that mixin in my current project ? I know you include mixins in classes using class classname(SomeMixin). How would I go about including this mixin into my code ?
2-Will I need to include an authentication token in my json message that I send to the websocket ?
Any suggestions would be great.
`

Generating single access token with Django OAuth2 Toolkit

I'm using the latest Django OAuth2 Toolkit (0.10.0) with Python 2.7, Django 1.8 and Django REST framework 3.3
While using the grant_type=password, I noticed some weird behavior that any time the user asks for a new access token:
curl -X POST -d "grant_type=password&username=<user_name>&password=<password>" -u"<client_id>:<client_secret>" http://localhost:8000/o/token/
A new access token and refresh token is created. The old access and refresh token are still usable until token timeout!
My Issues:
What I need is that every time a user asks for a new access token,
the old one will become invalid, unusable and will be removed.
Also, is there a way that the password grunt type wont create refresh
token. I don't have any use for that in my application.
One solution I found is that REST Framework OAuth provides a configuration for One Access Token at a time. I'm not eager to use that provider, but I might wont have a choice.
If you like to remove all previous access tokens before issuing a new one, there is a simple solution for this problem: Make your own token view provider!
The code bellow will probably help you to achieve that kind of functionality:
from oauth2_provider.models import AccessToken, Application
from braces.views import CsrfExemptMixin
from oauth2_provider.views.mixins import OAuthLibMixin
from oauth2_provider.settings import oauth2_settings
class TokenView(APIView, CsrfExemptMixin, OAuthLibMixin):
permission_classes = (permissions.AllowAny,)
server_class = oauth2_settings.OAUTH2_SERVER_CLASS
validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
oauthlib_backend_class = oauth2_settings.OAUTH2_BACKEND_CLASS
def post(self, request):
username = request.POST.get('username')
try:
if username is None:
raise User.DoesNotExist
AccessToken.objects.filter(user=User.objects.get(username=username), application=Application.objects.get(name="Website")).delete()
except Exception as e:
return Response(e.message,status=400)
url, headers, body, status = self.create_token_response(request)
return Response(body, status=status, headers=headers)
The part you should notice is the Try-Except block. In there we finding the Access tokens and removing them. All before we creating a new one.
You can look at how to create your own Provider using OAuthLib.
Also, this might be useful as well: TokenView in django-oauth-toolkit. You can see there the original Apiview. As you said, you were using this package.
As for the refresh_token, as previously mentioned in other answers here, you can't do what you are asking. When looking at the code of oauthlib password grunt type, you will see that in its initialization, refresh_token is set to True. Unless you change the Grunt type it self, it can't be done.
But you can do the same thing we did above with the access tokens.
Create the token and then delete the refresh token.
What I need is that every time a user asks for a new access token, the
old one will become invalid, unusable and will be removed.
Giving a new token when you ask for one seems like an expected behavior. Is it not possible for you to revoke the existing one before asking for the new one?
Update
If you are determined to keep just one token -
The class OAuth2Validator inherits OAuthLib's RequestValidator and overrides the method save_bearer_token. In this method before the code related to AccessToken model instance creation and its .save() method you can query (similar to this) to see if there is already an AccessToken saved in DB for this user. If found the existing token can be deleted from database.
I strongly suggest to make this change configurable, in case you change your mind in future (after all multiple tokens are issued for reasons like this)
A more simpler solution is to have your own validator class, probably one that inherits oauth2_provider.oauth2_validators.OAuth2Validator and overrides save_bearer_token. This new class should be given for OAUTH2_VALIDATOR_CLASS in settings.py
Also, is there a way that the password grunt type wont create refresh
token. I don't have any use for that in my application.
Django OAuth Toolkit depends on OAuthLib.
Making refresh_token optional boils down to create_token method in BearerToken class of oAuthLib at this line and the class for password grant is here. As you can see the __init__ method for this class takes refresh_token argument which by default is set to True. This value is used in create_token_response method of the same class at the line
token = token_handler.create_token(request, self.refresh_token)
create_token_response method in OAuthLibCore class of Django OAuth toolkit is the one, I believe, calls the corresponding create_token_response in OAuthLib. Observe the usage of self.server and its initialization in __init__ method of this class, which has just the validator passed as an argument but nothing related to refresh_token.
Compare this with OAuthLib Imlicit grant type's create_token_response method, which explicitly does
token = token_handler.create_token(request, refresh_token=False)
to not create refresh_token at all
So, unless I missed something here, tldr, I don't think Django OAuth toolkit exposes the feature of optional refresh_token.
Here's an example of just making it directly:
from oauthlib.common import generate_token
from oauth2_provider.models import AccessToken, Application
from django.utils import timezone
from dateutil.relativedelta import relativedelta
tok = generate_token()
app = Application.objects.first()
user = User.objects.first()
access_token = AccessToken.objects.create(user=user, application=app, expires=timezone.now() + relativedelta(hours=1), token=tok)
The accepted answer still fails to clear the RefreshToken. Below code should revoke both the refresh and access token.
from oauth2_provider.models import RefreshToken
def clear_token(user):
"""
Clear all user authorized tokens.
"""
for token in RefreshToken.objects.filter(user=user, revoked__isnull=True):
token.revoke()

Django rest-framework does not store authenticated user between calls

For the project I'm currently working on, I decided to write a custom authentication model. I'm using both the rest-framework and all-access. The local authentication is not implemented yet (it is, but in a different version, so no issue there).
The User model registers properly and OAuth authentication works. Users get logged alright.
When working with views, authentication works properly if I use django's View as base class. Not so much if I use rest-framework's APIView.
In my ProfileView (LOGIN_REDIRECT_URL redirects here) I receice a rest_framework.request.Request instance, which is OK by me. The problem is:
request.user is an instance of AnonymousUser (even after login)
I would guess the problem lies with my implementation, but then:
request._request.user is an authenticated user
(Note that request._request is the original django request)
I tend to think this is a rest-framework bug, but maybe one of you knows of a workaround?
Edit
Just to note that the login is made in a non-APIView class, but instead in a class that inherits from View. That's probably the reason the User instance is not passed to the APIView instance.
You need to set the DEFAULT_AUTHENTICATION_CLASSES in your settings.py. Else DRF will default to None as request.auth and AnonymousUser as request.user
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
Once you set that and the user is logged in, ensure that the session_id is available in the request headers. Browsers typically store the session information and pass it in the headers of all requests.
If you are using a non-browser client (Eg. a mobile app), you will need to set the session id manually in the header.
Also since you mentioned you use oauth to sign-in the user, for oauth2 you need to specify the oauth2 authorization header instead of the session id.
"Authorization: Bearer <your-access-token>"
Refer to Authenticating in Django Rest Framework

How can I create a session-local cookie-aware HTTP client in Django?

I'm using a web service backend to provide authentication to Django, and the get_user method must retain a cookie provided by the web service in order to associate with a session. Right now, I make my remote calls just by calling urllib2.urlopen(myTargetService) but this doesn't pass the cookie for the current session along.
I have created a session access middleware to store the session in the settings:
class SessionAccessMiddleware:
def process_request(self, request):
settings.current_session = request.session
So, I can access the request session in get_request and post_request, but I don't know how to have urllib2 remember my cookies in a session-specific way.
How do I do this?
Here: http://docs.python.org/library/cookielib.html#examples are examples of doing exactly what you try to do with urllib2 and cookielib. So according to docs you need to create cookielib.CookieJar, set cookie with correct data (from session), build an opener that uses your CookieJar and use it to fetch yourTargetService.
If settings in your middleware code means from django.conf import settings it's not good idea. Look at http://github.com/svetlyak40wt/django-globals/ for a place where you can safely store request-wide data for access from somewhere where request object is unaccessible. Also, it would be probably good idea to write custom authentication backend and use it with django.contrib.auth - instead of rolling your own auth system from scratch - which is covered here: http://docs.djangoproject.com/en/dev/topics/auth/#writing-an-authentication-backend .

Categories