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