Permalink
Browse files

Added CSRF protection to OAuth authorization

  • Loading branch information...
1 parent 989d39b commit 96865c3da868b549eefe5db3a61ec81cf76fb5ff @james-emerton james-emerton committed Jun 1, 2009
Showing with 102 additions and 29 deletions.
  1. +1 −0 .hgignore
  2. +32 −25 piston/authentication.py
  3. +44 −0 piston/forms.py
  4. +15 −0 piston/templates/piston/authorize_token.html
  5. +10 −4 tests/test_project/apps/testapp/tests.py
View
@@ -11,3 +11,4 @@ downloads
develop-eggs
.DS_Store
.svn
+*.swp
View
@@ -6,9 +6,12 @@
from django.conf import settings
from django.core.urlresolvers import get_callable
from django.core.exceptions import ImproperlyConfigured
+from django.shortcuts import render_to_response
+from django.template import RequestContext
from django.utils.importlib import import_module
import oauth
+from piston import forms
class NoAuthentication(object):
"""
@@ -136,7 +139,12 @@ def oauth_request_token(request):
return response
def oauth_auth_view(request, token, callback, params):
- return HttpResponse("Just a fake view for auth. %s, %s, %s" % (token, callback, params))
+ form = forms.OAuthAuthenticationForm(initial={
+ 'oauth_token': token.key,
+ 'oauth_callback': callback,
+ })
+ return render_to_response('piston/authorize_token.html',
+ { 'form': form }, RequestContext(request))
@login_required
def oauth_user_auth(request):
@@ -156,35 +164,34 @@ def oauth_user_auth(request):
callback = None
if request.method == "GET":
- request.session['oauth'] = token.key
params = oauth_request.get_normalized_parameters()
- oauth_view = getattr(settings, 'OAUTH_AUTH_VIEW', 'oauth_auth_view')
-
- return get_callable(oauth_view)(request, token, callback, params)
+ oauth_view = getattr(settings, 'OAUTH_AUTH_VIEW', None)
+ if oauth_view is None:
+ return oauth_auth_view(request, token, callback, params)
+ else:
+ return get_callable(oauth_view)(request, token, callback, params)
elif request.method == "POST":
- if request.session.get('oauth', '') == token.key:
- request.session['oauth'] = ''
+ try:
+ form = forms.OAuthAuthenticationForm(request.POST)
+ if form.is_valid():
+ token = oauth_server.authorize_token(token, request.user)
+ args = '?'+token.to_string(only_key=True)
+ else:
+ args = '?error=%s' % 'Access not granted by user.'
- try:
- if int(request.POST.get('authorize_access', '0')):
- token = oauth_server.authorize_token(token, request.user)
- args = '?'+token.to_string(only_key=True)
- else:
- args = '?error=%s' % 'Access not granted by user.'
+ if not callback:
+ callback = getattr(settings, 'OAUTH_CALLBACK_VIEW')
+ return get_callable(callback)(request, token)
- if not callback:
- callback = getattr(settings, 'OAUTH_CALLBACK_VIEW')
- return get_callable(callback)(request, token)
-
- response = HttpResponseRedirect(callback+args)
-
- except oauth.OAuthError, err:
- response = send_oauth_error(err)
- else:
- response = HttpResponse('Action not allowed.')
+ response = HttpResponseRedirect(callback+args)
+
+ except oauth.OAuthError, err:
+ response = send_oauth_error(err)
+ else:
+ response = HttpResponse('Action not allowed.')
- return response
+ return response
def oauth_access_token(request):
oauth_server, oauth_request = initialize_server_request(request)
@@ -278,4 +285,4 @@ def is_valid_request(request):
def validate_token(request, check_timestamp=True, check_nonce=True):
oauth_server, oauth_request = initialize_server_request(request)
return oauth_server.verify_request(oauth_request)
-
+
View
@@ -1,4 +1,8 @@
+import hmac
+import base64
+
from django import forms
+from django.conf import settings
class Form(forms.Form):
pass
@@ -17,3 +21,43 @@ def merge_from_initial(self):
for field in filter(filt, getattr(self.Meta, 'fields', ())):
self.data[field] = self.initial.get(field, None)
+
+class OAuthAuthenticationForm(forms.Form):
+ oauth_token = forms.CharField(widget=forms.HiddenInput)
+ oauth_callback = forms.URLField(widget=forms.HiddenInput)
+ authorize_access = forms.BooleanField(required=True)
+ csrf_signature = forms.CharField(widget=forms.HiddenInput)
+
+ def __init__(self, *args, **kwargs):
+ forms.Form.__init__(self, *args, **kwargs)
+
+ self.fields['csrf_signature'].initial = self.initial_csrf_signature
+
+ def clean_csrf_signature(self):
+ sig = self.cleaned_data['csrf_signature']
+ token = self.cleaned_data['oauth_token']
+
+ sig1 = OAuthAuthenticationForm.get_csrf_signature(settings.SECRET_KEY, token)
+
+ if sig != sig1:
+ raise forms.ValidationError("CSRF signature is not valid")
+
+ return sig
+
+ def initial_csrf_signature(self):
+ token = self.initial['oauth_token']
+ return OAuthAuthenticationForm.get_csrf_signature(settings.SECRET_KEY, token)
+
+ @staticmethod
+ def get_csrf_signature(key, token):
+ # Check signature...
+ try:
+ import hashlib # 2.5
+ hashed = hmac.new(key, token, hashlib.sha1)
+ except:
+ import sha # deprecated
+ hashed = hmac.new(key, token, sha)
+
+ # calculate the digest base 64
+ return base64.b64encode(hashed.digest())
+
@@ -0,0 +1,15 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+<html>
+ <head>
+ <title>Authorize Token</title>
+ </head>
+ <body>
+ <h1>Authorize Token</h1>
+
+ <form action="{% url piston.authentication.oauth_user_auth %}" method="POST">
+ {{ form.as_table }}
+ </form>
+
+ </body>
+</html>
@@ -71,13 +71,19 @@ def test_handshake(self):
http_url='http://testserver/api/oauth/authorize')
request.sign_request(self.signature_method, oaconsumer, oatoken)
- # Place the token into the client session...
- session = self.client.session
- session['oauth'] = oatoken.key
- session.save()
+ # Request the login page
+# TODO: Parse the response to make sure all the fields exist
+# response = self.client.get('/api/oauth/authorize', {
+# 'oauth_token': oatoken.key,
+# 'oauth_callback': 'http://printer.example.com/request_token_ready',
+# })
+
+ from piston.forms import OAuthAuthenticationForm
+ from django.conf import settings
response = self.client.post('/api/oauth/authorize', {
'oauth_token': oatoken.key,
'oauth_callback': 'http://printer.example.com/request_token_ready',
+ 'csrf_signature': OAuthAuthenticationForm.get_csrf_signature(settings.SECRET_KEY, oatoken.key),
'authorize_access': 1,
})

0 comments on commit 96865c3

Please sign in to comment.