From da9d7fb8ec19f289d9d2777738a45007c41a1289 Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 1 Mar 2013 02:08:58 +0100 Subject: Add the OAuth2Authentication class --- rest_framework/authentication.py | 85 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) (limited to 'rest_framework/authentication.py') diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 14b2136b..c20d9cb5 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -6,6 +6,7 @@ from django.contrib.auth import authenticate from django.utils.encoding import DjangoUnicodeDecodeError from rest_framework import exceptions, HTTP_HEADER_ENCODING from rest_framework.compat import CsrfViewMiddleware +from rest_framework.compat import oauth2_provider, oauth2 from rest_framework.authtoken.models import Token import base64 @@ -155,4 +156,86 @@ class TokenAuthentication(BaseAuthentication): return 'Token' -# TODO: OAuthAuthentication +class OAuth2Authentication(BaseAuthentication): + """ + OAuth 2 authentication backend using `django-oauth2-provider` + """ + require_active = True + + def __init__(self, **kwargs): + super(OAuth2Authentication, self).__init__(**kwargs) + if oauth2_provider is None: + raise ImproperlyConfigured("The 'django-oauth2-provider' package could not be imported. It is required for use with the 'OAuth2Authentication' class.") + + def authenticate(self, request): + """ + The Bearer type is the only finalized type + + Read the spec for more details + http://tools.ietf.org/html/rfc6749#section-7.1 + """ + auth = request.META.get('HTTP_AUTHORIZATION', '').split() + print auth + if not auth or auth[0].lower() != "bearer": + return None + + if len(auth) != 2: + raise exceptions.AuthenticationFailed('Invalid token header') + + return self.authenticate_credentials(request, auth[1]) + + def authenticate_credentials(self, request, access_token): + """ + :returns: two-tuple of (user, auth) if authentication succeeds, or None otherwise. + """ + + # authenticate the client + oauth2_client_form = oauth2.forms.ClientAuthForm(request.REQUEST) + if not oauth2_client_form.is_valid(): + raise exceptions.AuthenticationFailed("Client could not be validated") + client = oauth2_client_form.cleaned_data.get('client') + + # retrieve the `oauth2.models.OAuth2AccessToken` instance from the access_token + auth_backend = oauth2.backends.AccessTokenBackend() + token = auth_backend.authenticate(access_token, client) + if token is None: + raise exceptions.AuthenticationFailed("Invalid token") # does not exist or is expired + + # TODO check scope + # try: + # self.validate_token(request, consumer, token) + # except oauth2.Error, e: + # print "got e" + # raise exceptions.AuthenticationFailed(e.message) + + if not self.check_active(token.user): + raise exceptions.AuthenticationFailed('User not active: %s' % token.user.username) + + if client and token: + request.user = token.user + return (request.user, None) + + raise exceptions.AuthenticationFailed( + 'You are not allowed to access this resource.') + + return None + + def authenticate_header(self, request): + """ + Bearer is the only finalized type currently + + Check details on the `OAuth2Authentication.authenticate` method + """ + return 'Bearer' + + def check_active(self, user): + """ + Ensures the user has an active account. + + Optimized for the ``django.contrib.auth.models.User`` case. + """ + if not self.require_active: + # Ignore & move on. + return True + + return user.is_active -- cgit v1.2.3 From 9d5c3060386cc8deb4ee55eda022f0a134e897c0 Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 1 Mar 2013 11:53:30 +0100 Subject: Improve the `django-oauth2-provider` import block to avoid naming collision with `oauth2` used for OAuth 1 --- rest_framework/authentication.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'rest_framework/authentication.py') diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index c20d9cb5..c94af405 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -6,7 +6,7 @@ from django.contrib.auth import authenticate from django.utils.encoding import DjangoUnicodeDecodeError from rest_framework import exceptions, HTTP_HEADER_ENCODING from rest_framework.compat import CsrfViewMiddleware -from rest_framework.compat import oauth2_provider, oauth2 +from rest_framework.compat import oauth2_provider from rest_framework.authtoken.models import Token import base64 @@ -190,13 +190,13 @@ class OAuth2Authentication(BaseAuthentication): """ # authenticate the client - oauth2_client_form = oauth2.forms.ClientAuthForm(request.REQUEST) + oauth2_client_form = oauth2_provider.forms.ClientAuthForm(request.REQUEST) if not oauth2_client_form.is_valid(): raise exceptions.AuthenticationFailed("Client could not be validated") client = oauth2_client_form.cleaned_data.get('client') - # retrieve the `oauth2.models.OAuth2AccessToken` instance from the access_token - auth_backend = oauth2.backends.AccessTokenBackend() + # retrieve the `oauth2_provider.models.OAuth2AccessToken` instance from the access_token + auth_backend = oauth2_provider.backends.AccessTokenBackend() token = auth_backend.authenticate(access_token, client) if token is None: raise exceptions.AuthenticationFailed("Invalid token") # does not exist or is expired @@ -204,7 +204,7 @@ class OAuth2Authentication(BaseAuthentication): # TODO check scope # try: # self.validate_token(request, consumer, token) - # except oauth2.Error, e: + # except oauth2_provider.Error, e: # print "got e" # raise exceptions.AuthenticationFailed(e.message) -- cgit v1.2.3 From d4c2267187128c60927931c685a5c41a95c300bd Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Fri, 1 Mar 2013 12:08:28 +0100 Subject: Clean up some print and comments --- rest_framework/authentication.py | 6 ------ 1 file changed, 6 deletions(-) (limited to 'rest_framework/authentication.py') diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index c94af405..c74078fc 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -175,7 +175,6 @@ class OAuth2Authentication(BaseAuthentication): http://tools.ietf.org/html/rfc6749#section-7.1 """ auth = request.META.get('HTTP_AUTHORIZATION', '').split() - print auth if not auth or auth[0].lower() != "bearer": return None @@ -202,11 +201,6 @@ class OAuth2Authentication(BaseAuthentication): raise exceptions.AuthenticationFailed("Invalid token") # does not exist or is expired # TODO check scope - # try: - # self.validate_token(request, consumer, token) - # except oauth2_provider.Error, e: - # print "got e" - # raise exceptions.AuthenticationFailed(e.message) if not self.check_active(token.user): raise exceptions.AuthenticationFailed('User not active: %s' % token.user.username) -- cgit v1.2.3 From c449dd4f4d8c9602c826e906870a87c13d6689de Mon Sep 17 00:00:00 2001 From: Pierre Dulac Date: Sat, 2 Mar 2013 20:17:14 +0100 Subject: Properly fail to wrong Authorization token type --- rest_framework/authentication.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'rest_framework/authentication.py') diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index c74078fc..d4ba7967 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -176,7 +176,7 @@ class OAuth2Authentication(BaseAuthentication): """ auth = request.META.get('HTTP_AUTHORIZATION', '').split() if not auth or auth[0].lower() != "bearer": - return None + raise exceptions.AuthenticationFailed('Invalid Authorization token type') if len(auth) != 2: raise exceptions.AuthenticationFailed('Invalid token header') @@ -212,8 +212,6 @@ class OAuth2Authentication(BaseAuthentication): raise exceptions.AuthenticationFailed( 'You are not allowed to access this resource.') - return None - def authenticate_header(self, request): """ Bearer is the only finalized type currently -- cgit v1.2.3