1"""
2oauthlib.oauth2.rfc6749.errors
3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5Error used both by OAuth 2 clients and providers to represent the spec
6defined error responses for all four core grant types.
7"""
8import json
9import inspect
10import sys
11
12from oauthlib.common import add_params_to_uri, urlencode
13
14
15class OAuth2Error(Exception):
16 error = None
17 status_code = 400
18 description = ''
19
20 def __init__(self, description=None, uri=None, state=None,
21 status_code=None, request=None):
22 """
23 :param description: A human-readable ASCII [USASCII] text providing
24 additional information, used to assist the client
25 developer in understanding the error that occurred.
26 Values for the "error_description" parameter
27 MUST NOT include characters outside the set
28 x20-21 / x23-5B / x5D-7E.
29
30 :param uri: A URI identifying a human-readable web page with information
31 about the error, used to provide the client developer with
32 additional information about the error. Values for the
33 "error_uri" parameter MUST conform to the URI- Reference
34 syntax, and thus MUST NOT include characters outside the set
35 x21 / x23-5B / x5D-7E.
36
37 :param state: A CSRF protection value received from the client.
38
39 :param status_code:
40
41 :param request: OAuthlib request.
42 :type request: oauthlib.common.Request
43 """
44 if description is not None:
45 self.description = description
46
47 message = '({}) {}'.format(self.error, self.description)
48 if request:
49 message += ' ' + repr(request)
50 super().__init__(message)
51
52 self.uri = uri
53 self.state = state
54
55 if status_code:
56 self.status_code = status_code
57
58 if request:
59 self.redirect_uri = request.redirect_uri
60 self.client_id = request.client_id
61 self.scopes = request.scopes
62 self.response_type = request.response_type
63 self.response_mode = request.response_mode
64 self.grant_type = request.grant_type
65 if state is None:
66 self.state = request.state
67 else:
68 self.redirect_uri = None
69 self.client_id = None
70 self.scopes = None
71 self.response_type = None
72 self.response_mode = None
73 self.grant_type = None
74
75 def in_uri(self, uri):
76 fragment = self.response_mode == "fragment"
77 return add_params_to_uri(uri, self.twotuples, fragment)
78
79 @property
80 def twotuples(self):
81 error = [('error', self.error)]
82 if self.description:
83 error.append(('error_description', self.description))
84 if self.uri:
85 error.append(('error_uri', self.uri))
86 if self.state:
87 error.append(('state', self.state))
88 return error
89
90 @property
91 def urlencoded(self):
92 return urlencode(self.twotuples)
93
94 @property
95 def json(self):
96 return json.dumps(dict(self.twotuples))
97
98 @property
99 def headers(self):
100 if self.status_code == 401:
101 """
102 https://tools.ietf.org/html/rfc6750#section-3
103
104 All challenges defined by this specification MUST use the auth-scheme
105 value "Bearer". This scheme MUST be followed by one or more
106 auth-param values.
107 """
108 authvalues = ['error="{}"'.format(self.error)]
109 if self.description:
110 authvalues.append('error_description="{}"'.format(self.description))
111 if self.uri:
112 authvalues.append('error_uri="{}"'.format(self.uri))
113 return {"WWW-Authenticate": "Bearer " + ", ".join(authvalues)}
114 return {}
115
116
117class TokenExpiredError(OAuth2Error):
118 error = 'token_expired'
119
120
121class InsecureTransportError(OAuth2Error):
122 error = 'insecure_transport'
123 description = 'OAuth 2 MUST utilize https.'
124
125
126class MismatchingStateError(OAuth2Error):
127 error = 'mismatching_state'
128 description = 'CSRF Warning! State not equal in request and response.'
129
130
131class MissingCodeError(OAuth2Error):
132 error = 'missing_code'
133
134
135class MissingTokenError(OAuth2Error):
136 error = 'missing_token'
137
138
139class MissingTokenTypeError(OAuth2Error):
140 error = 'missing_token_type'
141
142
143class FatalClientError(OAuth2Error):
144 """
145 Errors during authorization where user should not be redirected back.
146
147 If the request fails due to a missing, invalid, or mismatching
148 redirection URI, or if the client identifier is missing or invalid,
149 the authorization server SHOULD inform the resource owner of the
150 error and MUST NOT automatically redirect the user-agent to the
151 invalid redirection URI.
152
153 Instead the user should be informed of the error by the provider itself.
154 """
155
156
157class InvalidRequestFatalError(FatalClientError):
158 """
159 For fatal errors, the request is missing a required parameter, includes
160 an invalid parameter value, includes a parameter more than once, or is
161 otherwise malformed.
162 """
163 error = 'invalid_request'
164
165
166class InvalidRedirectURIError(InvalidRequestFatalError):
167 description = 'Invalid redirect URI.'
168
169
170class MissingRedirectURIError(InvalidRequestFatalError):
171 description = 'Missing redirect URI.'
172
173
174class MismatchingRedirectURIError(InvalidRequestFatalError):
175 description = 'Mismatching redirect URI.'
176
177
178class InvalidClientIdError(InvalidRequestFatalError):
179 description = 'Invalid client_id parameter value.'
180
181
182class MissingClientIdError(InvalidRequestFatalError):
183 description = 'Missing client_id parameter.'
184
185
186class InvalidRequestError(OAuth2Error):
187 """
188 The request is missing a required parameter, includes an invalid
189 parameter value, includes a parameter more than once, or is
190 otherwise malformed.
191 """
192 error = 'invalid_request'
193
194
195class MissingResponseTypeError(InvalidRequestError):
196 description = 'Missing response_type parameter.'
197
198
199class MissingCodeChallengeError(InvalidRequestError):
200 """
201 If the server requires Proof Key for Code Exchange (PKCE) by OAuth
202 public clients and the client does not send the "code_challenge" in
203 the request, the authorization endpoint MUST return the authorization
204 error response with the "error" value set to "invalid_request". The
205 "error_description" or the response of "error_uri" SHOULD explain the
206 nature of error, e.g., code challenge required.
207 """
208 description = 'Code challenge required.'
209
210
211class MissingCodeVerifierError(InvalidRequestError):
212 """
213 The request to the token endpoint, when PKCE is enabled, has
214 the parameter `code_verifier` REQUIRED.
215 """
216 description = 'Code verifier required.'
217
218
219class AccessDeniedError(OAuth2Error):
220 """
221 The resource owner or authorization server denied the request.
222 """
223 error = 'access_denied'
224
225
226class UnsupportedResponseTypeError(OAuth2Error):
227 """
228 The authorization server does not support obtaining an authorization
229 code using this method.
230 """
231 error = 'unsupported_response_type'
232
233
234class UnsupportedCodeChallengeMethodError(InvalidRequestError):
235 """
236 If the server supporting PKCE does not support the requested
237 transformation, the authorization endpoint MUST return the
238 authorization error response with "error" value set to
239 "invalid_request". The "error_description" or the response of
240 "error_uri" SHOULD explain the nature of error, e.g., transform
241 algorithm not supported.
242 """
243 description = 'Transform algorithm not supported.'
244
245
246class InvalidScopeError(OAuth2Error):
247 """
248 The requested scope is invalid, unknown, or malformed, or
249 exceeds the scope granted by the resource owner.
250
251 https://tools.ietf.org/html/rfc6749#section-5.2
252 """
253 error = 'invalid_scope'
254
255
256class ServerError(OAuth2Error):
257 """
258 The authorization server encountered an unexpected condition that
259 prevented it from fulfilling the request. (This error code is needed
260 because a 500 Internal Server Error HTTP status code cannot be returned
261 to the client via a HTTP redirect.)
262 """
263 error = 'server_error'
264
265
266class TemporarilyUnavailableError(OAuth2Error):
267 """
268 The authorization server is currently unable to handle the request
269 due to a temporary overloading or maintenance of the server.
270 (This error code is needed because a 503 Service Unavailable HTTP
271 status code cannot be returned to the client via a HTTP redirect.)
272 """
273 error = 'temporarily_unavailable'
274
275
276class InvalidClientError(FatalClientError):
277 """
278 Client authentication failed (e.g. unknown client, no client
279 authentication included, or unsupported authentication method).
280 The authorization server MAY return an HTTP 401 (Unauthorized) status
281 code to indicate which HTTP authentication schemes are supported.
282 If the client attempted to authenticate via the "Authorization" request
283 header field, the authorization server MUST respond with an
284 HTTP 401 (Unauthorized) status code, and include the "WWW-Authenticate"
285 response header field matching the authentication scheme used by the
286 client.
287 """
288 error = 'invalid_client'
289 status_code = 401
290
291
292class InvalidGrantError(OAuth2Error):
293 """
294 The provided authorization grant (e.g. authorization code, resource
295 owner credentials) or refresh token is invalid, expired, revoked, does
296 not match the redirection URI used in the authorization request, or was
297 issued to another client.
298
299 https://tools.ietf.org/html/rfc6749#section-5.2
300 """
301 error = 'invalid_grant'
302 status_code = 400
303
304
305class UnauthorizedClientError(OAuth2Error):
306 """
307 The authenticated client is not authorized to use this authorization
308 grant type.
309 """
310 error = 'unauthorized_client'
311
312
313class UnsupportedGrantTypeError(OAuth2Error):
314 """
315 The authorization grant type is not supported by the authorization
316 server.
317 """
318 error = 'unsupported_grant_type'
319
320
321class UnsupportedTokenTypeError(OAuth2Error):
322 """
323 The authorization server does not support the hint of the
324 presented token type. I.e. the client tried to revoke an access token
325 on a server not supporting this feature.
326 """
327 error = 'unsupported_token_type'
328
329
330class InvalidTokenError(OAuth2Error):
331 """
332 The access token provided is expired, revoked, malformed, or
333 invalid for other reasons. The resource SHOULD respond with
334 the HTTP 401 (Unauthorized) status code. The client MAY
335 request a new access token and retry the protected resource
336 request.
337 """
338 error = 'invalid_token'
339 status_code = 401
340 description = ("The access token provided is expired, revoked, malformed, "
341 "or invalid for other reasons.")
342
343
344class InsufficientScopeError(OAuth2Error):
345 """
346 The request requires higher privileges than provided by the
347 access token. The resource server SHOULD respond with the HTTP
348 403 (Forbidden) status code and MAY include the "scope"
349 attribute with the scope necessary to access the protected
350 resource.
351 """
352 error = 'insufficient_scope'
353 status_code = 403
354 description = ("The request requires higher privileges than provided by "
355 "the access token.")
356
357
358class ConsentRequired(OAuth2Error):
359 """
360 The Authorization Server requires End-User consent.
361
362 This error MAY be returned when the prompt parameter value in the
363 Authentication Request is none, but the Authentication Request cannot be
364 completed without displaying a user interface for End-User consent.
365 """
366 error = 'consent_required'
367
368
369class LoginRequired(OAuth2Error):
370 """
371 The Authorization Server requires End-User authentication.
372
373 This error MAY be returned when the prompt parameter value in the
374 Authentication Request is none, but the Authentication Request cannot be
375 completed without displaying a user interface for End-User authentication.
376 """
377 error = 'login_required'
378
379
380class CustomOAuth2Error(OAuth2Error):
381 """
382 This error is a placeholder for all custom errors not described by the RFC.
383 Some of the popular OAuth2 providers are using custom errors.
384 """
385 def __init__(self, error, *args, **kwargs):
386 self.error = error
387 super().__init__(*args, **kwargs)
388
389
390def raise_from_error(error, params=None):
391 kwargs = {
392 'description': params.get('error_description'),
393 'uri': params.get('error_uri'),
394 'state': params.get('state')
395 }
396 for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass):
397 if cls.error == error:
398 raise cls(**kwargs)
399 raise CustomOAuth2Error(error=error, **kwargs)