Handle content-type header charset value for streaming API
[twitter-api-cdsw] / oauthlib / oauth2 / rfc6749 / grant_types / resource_owner_password_credentials.py
1 # -*- coding: utf-8 -*-
2 """
3 oauthlib.oauth2.rfc6749.grant_types
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 """
6 from __future__ import unicode_literals, absolute_import
7
8 import json
9 import logging
10
11 from .base import GrantTypeBase
12 from .. import errors
13 from ..request_validator import RequestValidator
14
15 log = logging.getLogger(__name__)
16
17
18 class ResourceOwnerPasswordCredentialsGrant(GrantTypeBase):
19
20     """`Resource Owner Password Credentials Grant`_
21
22     The resource owner password credentials grant type is suitable in
23     cases where the resource owner has a trust relationship with the
24     client, such as the device operating system or a highly privileged
25     application.  The authorization server should take special care when
26     enabling this grant type and only allow it when other flows are not
27     viable.
28
29     This grant type is suitable for clients capable of obtaining the
30     resource owner's credentials (username and password, typically using
31     an interactive form).  It is also used to migrate existing clients
32     using direct authentication schemes such as HTTP Basic or Digest
33     authentication to OAuth by converting the stored credentials to an
34     access token::
35
36             +----------+
37             | Resource |
38             |  Owner   |
39             |          |
40             +----------+
41                  v
42                  |    Resource Owner
43                 (A) Password Credentials
44                  |
45                  v
46             +---------+                                  +---------------+
47             |         |>--(B)---- Resource Owner ------->|               |
48             |         |         Password Credentials     | Authorization |
49             | Client  |                                  |     Server    |
50             |         |<--(C)---- Access Token ---------<|               |
51             |         |    (w/ Optional Refresh Token)   |               |
52             +---------+                                  +---------------+
53
54     Figure 5: Resource Owner Password Credentials Flow
55
56     The flow illustrated in Figure 5 includes the following steps:
57
58     (A)  The resource owner provides the client with its username and
59             password.
60
61     (B)  The client requests an access token from the authorization
62             server's token endpoint by including the credentials received
63             from the resource owner.  When making the request, the client
64             authenticates with the authorization server.
65
66     (C)  The authorization server authenticates the client and validates
67             the resource owner credentials, and if valid, issues an access
68             token.
69
70     .. _`Resource Owner Password Credentials Grant`: http://tools.ietf.org/html/rfc6749#section-4.3
71     """
72
73     def __init__(self, request_validator=None, refresh_token=True):
74         """
75         If the refresh_token keyword argument is False, do not return
76         a refresh token in the response.
77         """
78         self.request_validator = request_validator or RequestValidator()
79         self.refresh_token = refresh_token
80
81     def create_token_response(self, request, token_handler):
82         """Return token or error in json format.
83
84         If the access token request is valid and authorized, the
85         authorization server issues an access token and optional refresh
86         token as described in `Section 5.1`_.  If the request failed client
87         authentication or is invalid, the authorization server returns an
88         error response as described in `Section 5.2`_.
89
90         .. _`Section 5.1`: http://tools.ietf.org/html/rfc6749#section-5.1
91         .. _`Section 5.2`: http://tools.ietf.org/html/rfc6749#section-5.2
92         """
93         headers = {
94             'Content-Type': 'application/json',
95             'Cache-Control': 'no-store',
96             'Pragma': 'no-cache',
97         }
98         try:
99             if self.request_validator.client_authentication_required(request):
100                 log.debug('Authenticating client, %r.', request)
101                 if not self.request_validator.authenticate_client(request):
102                     log.debug('Client authentication failed, %r.', request)
103                     raise errors.InvalidClientError(request=request)
104             elif not self.request_validator.authenticate_client_id(request.client_id, request):
105                 log.debug('Client authentication failed, %r.', request)
106                 raise errors.InvalidClientError(request=request)
107             log.debug('Validating access token request, %r.', request)
108             self.validate_token_request(request)
109         except errors.OAuth2Error as e:
110             log.debug('Client error in token request, %s.', e)
111             return headers, e.json, e.status_code
112
113         token = token_handler.create_token(request, self.refresh_token)
114         log.debug('Issuing token %r to client id %r (%r) and username %s.',
115                   token, request.client_id, request.client, request.username)
116         return headers, json.dumps(token), 200
117
118     def validate_token_request(self, request):
119         """
120         The client makes a request to the token endpoint by adding the
121         following parameters using the "application/x-www-form-urlencoded"
122         format per Appendix B with a character encoding of UTF-8 in the HTTP
123         request entity-body:
124
125         grant_type
126                 REQUIRED.  Value MUST be set to "password".
127
128         username
129                 REQUIRED.  The resource owner username.
130
131         password
132                 REQUIRED.  The resource owner password.
133
134         scope
135                 OPTIONAL.  The scope of the access request as described by
136                 `Section 3.3`_.
137
138         If the client type is confidential or the client was issued client
139         credentials (or assigned other authentication requirements), the
140         client MUST authenticate with the authorization server as described
141         in `Section 3.2.1`_.
142
143         The authorization server MUST:
144
145         o  require client authentication for confidential clients or for any
146             client that was issued client credentials (or with other
147             authentication requirements),
148
149         o  authenticate the client if client authentication is included, and
150
151         o  validate the resource owner password credentials using its
152             existing password validation algorithm.
153
154         Since this access token request utilizes the resource owner's
155         password, the authorization server MUST protect the endpoint against
156         brute force attacks (e.g., using rate-limitation or generating
157         alerts).
158
159         .. _`Section 3.3`: http://tools.ietf.org/html/rfc6749#section-3.3
160         .. _`Section 3.2.1`: http://tools.ietf.org/html/rfc6749#section-3.2.1
161         """
162         for param in ('grant_type', 'username', 'password'):
163             if not getattr(request, param, None):
164                 raise errors.InvalidRequestError(
165                     'Request is missing %s parameter.' % param, request=request)
166
167         for param in ('grant_type', 'username', 'password', 'scope'):
168             if param in request.duplicate_params:
169                 raise errors.InvalidRequestError(description='Duplicate %s parameter.' % param, request=request)
170
171         # This error should rarely (if ever) occur if requests are routed to
172         # grant type handlers based on the grant_type parameter.
173         if not request.grant_type == 'password':
174             raise errors.UnsupportedGrantTypeError(request=request)
175
176         log.debug('Validating username %s.', request.username)
177         if not self.request_validator.validate_user(request.username,
178                                                     request.password, request.client, request):
179             raise errors.InvalidGrantError(
180                 'Invalid credentials given.', request=request)
181         else:
182             if not hasattr(request.client, 'client_id'):
183                 raise NotImplementedError(
184                     'Validate user must set the '
185                     'request.client.client_id attribute '
186                     'in authenticate_client.')
187         log.debug('Authorizing access to user %r.', request.user)
188
189         # Ensure client is authorized use of this grant type
190         self.validate_grant_type(request)
191
192         if request.client:
193             request.client_id = request.client_id or request.client.client_id
194         self.validate_scopes(request)

Benjamin Mako Hill || Want to submit a patch?