diff options
| author | Devin Sevilla <dasevilla@gmail.com> | 2013-05-30 19:47:35 -0700 |
|---|---|---|
| committer | Devin Sevilla <dasevilla@gmail.com> | 2013-05-30 19:47:35 -0700 |
| commit | fa0dbbb3b127eec9dbc86208d8fbde1d903f13ea (patch) | |
| tree | 2246d1744a771759db137a4e67caa287ecc0fadd /docs/oauth2/server.rst | |
| parent | ba39888bf4d0224bc0bb9281f037402afbc46e12 (diff) | |
| download | oauthlib-fa0dbbb3b127eec9dbc86208d8fbde1d903f13ea.tar.gz | |
Organize documentation into directories
Diffstat (limited to 'docs/oauth2/server.rst')
| -rw-r--r-- | docs/oauth2/server.rst | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/docs/oauth2/server.rst b/docs/oauth2/server.rst new file mode 100644 index 0000000..5c50d1a --- /dev/null +++ b/docs/oauth2/server.rst @@ -0,0 +1,297 @@ +============================ +OAuth 2: Creating a Provider +============================ + +OAuthLib is a dependency free library that may be used with any web +framework. That said, there are framework specific helper libraries +to make your life easier. + +- For Django there is `django-oauth-toolkit`_. +- For Flask there is `flask-oauthlib`_. + +If there is no support for your favourite framework and you are interested +in providing it then you have come to the right place. OAuthLib can handle +the OAuth logic and leave you to support a few framework and setup specific +tasks such as marshalling request objects into URI, headers and body arguments +as well as provide an interface for a backend to store tokens, clients, etc. + +.. _`django-oauth-toolkit`: https://github.com/evonove/django-oauth-toolkit +.. _`flask-oauthlib`: https://github.com/lepture/flask-oauthlib + +**1. Create your datastore models** + + These models will represent various OAuth specific concepts. There are a few + important links between them that the security of OAuth is based on. Below + is a suggestion for models and why you need certain properties. There is + also example Django model fields which should be straightforward to + translate to other ORMs such as SQLAlchemy and the Appengine Datastore. + + **User (or Resource Owner)** + The user of your site which resources might be access by clients upon + authorization from the user. In our example we will re-use the User + model provided in django.contrib.auth.models. How the user authenticates + is orthogonal from OAuth and may be any way you prefer:: + + from django.contrib.auth.models import User + + **Client (or Consumer)** + The client interested in accessing protected resources. + + **Client Identifier**: + Required. The identifier the client will use during the OAuth + workflow. Structure is up to you and may be a simple UUID:: + + client_id = django.db.models.CharField(max_length=100, unique=True) + + **User**: + Recommended. It is common practice to link each client with one of + your existing users. Whether you do associate clients and users or + not, ensure you are able to protect yourself against malicious + clients:: + + user = django.db.models.ForeignKey(User) + + **Grant Type**: + Required. The grant type the client may utilize. This should only be + one per client as each grant type has different security properties + and it is best to keep them separate to avoid mistakes:: + + # max_length and choices depend on which grants you support + grant_type = django.db.models.CharField(max_length=18, + choices=[('authorization_code', 'Authorization code')]) + + **Response Type**: + Required, if using a grant type with an associated response type + (eg. Authorization Code Grant) or using a grant which only utilizes + response types (eg. Implicit Grant):: + + # max_length and choices depend on which response types you support + response_type = django.db.models.CharField(max_length=4, + choices=[('code', 'Authorization code')]) + + **Scopes**: + Required. The list of scopes the client may request access to. If + you allow multiple types of grants this will vary related to their + different security properties. For example, the Implicit Grant might + only allow read-only scopes but the Authorization Grant also allow + writes:: + + # You could represent it either as a list of keys or by serializing + # the scopes into a string. + scopes = django.db.models.TextField() + + # You might also want to mark a certain set of scopes as default + # scopes in case the client does not specify any in the authorization + default_scopes = django.db.models.TextField() + + **Redirect URIs**: + These are the absolute URIs that a client may use to redirect to after + authorization. You should never allow a client to redirect to a URI + that has not previously been registered:: + + # You could represent the URIs either as a list of keys or by + # serializing them into a string. + redirect_uris = django.db.models.TextField() + + # You might also want to mark a certain URI as default in case the + # client does not specify any in the authorization + default_redirect_uri = django.db.models.TextField() + + **Bearer Token (OAuth 2 Standard Token)** + The most common type of OAuth 2 token. Through the documentation this + will be considered an object with several properties, such as token type + and expiration date, and distinct from the access token it contains. + Think of OAuth 2 tokens as containers and access tokens and refresh + tokens as text. + + **Client**: + Association with the client to whom the token was given:: + + client = django.db.models.ForeignKey(Client) + + **User**: + Association with the user to which protected resources this token + grants access:: + + user = django.db.models.ForeignKey(User) + + **Scopes**: + Scopes to which the token is bound. Attempt to access protected + resources outside these scopes will be denied:: + + # You could represent it either as a list of keys or by serializing + # the scopes into a string. + scopes = django.db.models.TextField() + + **Access Token**: + An unguessable unique string of characters:: + + access_token = django.db.models.CharField(max_length=100, unique=True) + + **Refresh Token**: + An unguessable unique string of characters. This token is only + supplied to confidential clients. For example the Authorization Code + Grant or the Resource Owner Password Credentials Grant:: + + refresh_token = django.db.models.CharField(max_length=100, unique=True) + + **Expiration time**: + Exact time of expiration. Commonly this is one hour after creation:: + + expires_at = django.db.models.DateTimeField() + + **Authorization Code** + This is specific to the Authorization Code grant and represent the + temporary credential granted to the client upon successful + authorization. It will later be exchanged for an access token, when that + is done it should cease to exist. It should have a limited life time, + less than ten minutes. This model is similar to the Bearer Token as it + mainly acts a temporary storage of properties to later be transferred to + the token. + + **Client**: + Association with the client to whom the token was given:: + + client = django.db.models.ForeignKey(Client) + + **User**: + Association with the user to which protected resources this token + grants access:: + + user = django.db.models.ForeignKey(User) + + **Scopes**: + Scopes to which the token is bound. Attempt to access protected + resources outside these scopes will be denied:: + + # You could represent it either as a list of keys or by serializing + # the scopes into a string. + scopes = django.db.models.TextField() + + **Authorization Code**: + An unguessable unique string of characters:: + + code = django.db.models.CharField(max_length=100, unique=True) + + **Expiration time**: + Exact time of expiration. Commonly this is under ten minutes after + creation:: + + expires_at = django.db.models.DateTimeField() + +**2. Implement a validator** + + The majority of the work involved in implementing an OAuth 2 provider + relates to mapping various validation and persistence methods to a storage + backend. The not very accurately named interface you will need to implement + is called a :doc:`RequestValidator <validator>` (name suggestions welcome). + + An example of a very basic implementation of the validate_client_id method + can be seen below:: + + from oauthlib.oauth2 import RequestValidator + + # From the previous section on models + from my_models import Client + + class MyRequestValidator(RequestValidator): + + def validate_client_id(self, client_id, request): + try: + Client.objects.get(client_id=client_id) + return True + except Client.DoesNotExist: + return False + + The full API you will need to implement is available in the + :doc:`RequestValidator <validator>` section. You might not need to implement + 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: + + .. toctree:: + :maxdepth: 1 + + validator + security + + +**3. Create your composite endpoint** + + Each of the endpoints can function independently from each other, however + for this example it is easier to consider them as one unit. An example of a + pre-configured all-in-one Authorization Code Grant endpoint is given below:: + + # From the previous section on validators + from my_validator import MyRequestValidator + + from oauthlib.oauth2 import WebApplicationServer + from oauthlib.oauth2.ext.django import OAuth2ProviderDecorator + + validator = MyRequestValidator() + server = WebApplicationServer(validator) + + Relevant sections include: + + .. toctree:: + :maxdepth: 1 + + preconfigured_servers + + +**4. Create your endpoint views** + + We are implementing support for the Authorization Code Grant and will + therefore need two views for the authorization, pre- and post-authorization + together with the token view. We also include an error page to redirect + users to if the client supplied invalid credentials in their redirection, + for example an invalid redirect URI. + + The rest of this section will come soon. + +**5. Protect your APIs using scopes** + + The definition of ``protected_resource_view`` will come soon. Please see + the framework specific extensions outlined in the beginning for now. + + At this point you are ready to protect your API views with OAuth. Take some + time to come up with a good set of scopes as they can be very powerful in + controlling access:: + + @provider.protected_resource_view(scopes=['images']) + def i_am_protected(request, client, resource_owner, **kwargs): + # One of your many OAuth 2 protected resource views + # Returns whatever you fancy + # May be bound to various scopes of your choosing + return HttpResponse('pictures of cats') + + The set of scopes that protects a view may also be dynamically configured + at runtime by a function, rather then by a list:: + + def dynamic_scopes(request): + # Place code here to dynamically determine the scopes + # and return as a list + return ['images'] + + @provider.protected_resource_view(scopes=dynamic_scopes) + def i_am_also_protected(request, client, resource_owner, **kwargs) + # A view that has its views functionally set. + return HttpResponse('pictures of cats') + +**6. Let us know how it went!** + + Drop a line in our `G+ community`_ or open a `GitHub issue`_ =) + + .. _`G+ community`: https://plus.google.com/communities/101889017375384052571 + .. _`GitHub issue`: https://github.com/idan/oauthlib/issues/new + + If you run into issues it can be helpful to enable debug logging:: + + import logging + log = logging.getLogger('oauthlib') + log.addHandler(logging.StreamHandler(sys.stdout)) + log.setLevel(logging.DEBUG) |
