From f5f87c30bee2b43b9e3c32a9e24c75df2217bbb7 Mon Sep 17 00:00:00 2001 From: Ib Lundgren Date: Tue, 26 Mar 2013 12:50:08 +0100 Subject: OAuth 2 skeleton provider --- docs/server2.rst | 6 +- examples/skeleton_oauth2_web_application_server.py | 147 +++++++++++++++++++++ oauthlib/oauth2/ext/django.py | 1 + 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 examples/skeleton_oauth2_web_application_server.py diff --git a/docs/server2.rst b/docs/server2.rst index ff1fd65..9b655f4 100644 --- a/docs/server2.rst +++ b/docs/server2.rst @@ -210,7 +210,11 @@ Note that OAuth 2 provider is still very much a work in progress, consider it a The full API you will need to implement is available in the :doc:`RequestValidator ` section. You might not need to implement - all methods depending on which grant types you wish to support. + all methods depending on which grant types you wish to support. A skeleton + validator listing the methods required for the WebApplicationServer is + available in the `examples`_ folder on GitHub. + + .. _`examples`: https://github.com/idan/oauthlib/blob/master/examples/skeleton_oauth2_web_application_server.py Relevant sections include: diff --git a/examples/skeleton_oauth2_web_application_server.py b/examples/skeleton_oauth2_web_application_server.py new file mode 100644 index 0000000..95f7db2 --- /dev/null +++ b/examples/skeleton_oauth2_web_application_server.py @@ -0,0 +1,147 @@ +# Skeleton for an OAuth 2 Web Application Server which is an OAuth +# provider configured for Authorization Code, Refresh Token grants and +# for dispensing Bearer Tokens. + +# This example is tailored for django but should translate to other +# web frameworks easily. + +# This example is meant to act as a supplement to the documentation, +# see http://oauthlib.readthedocs.org/en/latest/. + +from django.contrib.auth.decorators import login_required +from django.http import HttpResponse +from oauthlib.oauth2 import RequestValidator, WebApplicationServer +from oauthlib.oauth2.ext.django import OAuth2ProviderDecorator + + +class SkeletonValidator(RequestValidator): + + # Ordered roughly in order of apperance in the authorization grant flow + + # Pre- and Post-authorization. + + def validate_client_id(self, client_id, request, *args, **kwargs): + # Simple validity check, does client exist? Not banned? + pass + + def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs): + # Is the client allowed to use the supplied redirect_uri? i.e. has + # the client previously registered this EXACT redirect uri. + pass + + def get_default_redirect_uri(self, client_id, request, *args, **kwargs): + # The redirect used if none has been supplied. + # Prefer your clients to pre register a redirect uri rather than + # supplying one on each authorization request. + pass + + def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs): + # Is the client allowed to access the requested scopes? + pass + + def get_default_scopes(self, client_id, request, *args, **kwargs): + # Scopes a client will authorize for if none are supplied in the + # authorization request. + pass + + def validate_response_type(self, client_id, response_type, client, request, *args, **kwargs): + # Clients should only be allowed to use one type of response type, the + # one associated with their one allowed grant type. + # In this case it must be "code". + pass + + # Post-authorization + + def save_authorization_code(self, client_id, code, request, *args, **kwargs): + # Remember to associate it with request.scopes, request.redirect_uri + # request.client, request.state and request.user (the last is passed in + # post_authorization credentials, i.e. { 'user': request.user}. + pass + + # Token request + + def authenticate_client(self, request, *args, **kwargs): + # Whichever authentication method suits you, HTTP Basic might work + pass + + def authenticate_client_id(self, client_id, request, *args, **kwargs): + # Don't allow public (non-authenticated) clients + return False + + def validate_code(self, client_id, code, client, *args, **kwargs): + # Validate the code belongs to the client. Add associated scopes, + # state and user to request.scopes, request.state and request.user. + pass + + def confirm_redirect_uri(self, client_id, code, redirect_uri, client, *args, **kwargs): + # You did save the redirect uri with the authorization code right? + pass + + def validate_grant_type(self, client_id, grant_type, client, request, *args, **kwargs): + # Clients should only be allowed to use one type of grant. + # In this case, it must be "authorization_code" or "refresh_token" + pass + + def save_bearer_token(self, token, request, *args, **kwargs): + # Remeber to associate it with request.scopes, request.user and + # request.client. The two former will be set when you validate + # the authorization code. Don't forget to save both the + # access_token and the refresh_token and set expiration for the + # access_token to now + expires_in seconds. + pass + + def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs): + # Authorization codes are use once, invalidate it when a Bearer token + # has been acquired. + pass + + # Protected resource request + + def validate_bearer_token(self, token, scopes, request): + # Remember to check expiration and scope membership + pass + + # Token refresh request + + def confirm_scopes(self, refresh_token, scopes, request, *args, **kwargs): + # If the client requests a set of scopes, assure that those are the + # same as, or a subset of, the ones associated with the token earlier. + pass + + +validator = SkeletonValidator() +server = WebApplicationServer(validator) +provider = OAuth2ProviderDecorator('/error', server) + + +@login_required +@provider.pre_authorization_view +def authorize(request, scopes=None, client_id=None): + # The user might not want to provide access to all scopes, + # make it easy for them to opt-out. + response = HttpResponse() + response.write('

Authorize access to %s

' % client_id) + response.write('
') + for scope in scopes or []: + response.write(' %s' % (scope, scope)) + response.write('') + return response + + +@login_required +@provider.post_authorization_view +def authorization_response(request): + # Only return scopes the user actually authorized, i.e. the checked + # scope checkboxes from the authorize view. + return request.POST['scopes'], {'user': request.user} + + +@provider.access_token_view +def token_response(request): + # This dict will be available as request.extra_credentials in all + # validation methods, including save_bearer_token. + return {} + + +def error(request): + return HttpResponse('Bad client! Warn user!') diff --git a/oauthlib/oauth2/ext/django.py b/oauthlib/oauth2/ext/django.py index 9cc915d..5a580bd 100644 --- a/oauthlib/oauth2/ext/django.py +++ b/oauthlib/oauth2/ext/django.py @@ -43,6 +43,7 @@ class OAuth2ProviderDecorator(object): log.debug('Saving credentials to session, %r.', credentials) request.session['oauth2_credentials'] = credentials kwargs['scopes'] = scopes + kwargs.update(credentials) log.debug('Invoking view method, %r.', f) return f(request, *args, **kwargs) -- cgit v1.2.1