179 lines
6.2 KiB
Python
179 lines
6.2 KiB
Python
|
"""OAuth errors"""
|
||
|
from urllib.parse import quote
|
||
|
|
||
|
|
||
|
class OAuth2Error(Exception):
|
||
|
"""Base class for all OAuth2 Errors"""
|
||
|
|
||
|
error: str
|
||
|
description: str
|
||
|
|
||
|
def create_dict(self):
|
||
|
"""Return error as dict for JSON Rendering"""
|
||
|
return {
|
||
|
"error": self.error,
|
||
|
"error_description": self.description,
|
||
|
}
|
||
|
|
||
|
def __repr__(self) -> str:
|
||
|
return self.error
|
||
|
|
||
|
|
||
|
class RedirectUriError(OAuth2Error):
|
||
|
"""The request fails due to a missing, invalid, or mismatching
|
||
|
redirection URI (redirect_uri)."""
|
||
|
|
||
|
error = "Redirect URI Error"
|
||
|
description = (
|
||
|
"The request fails due to a missing, invalid, or mismatching"
|
||
|
" redirection URI (redirect_uri)."
|
||
|
)
|
||
|
|
||
|
|
||
|
class ClientIdError(OAuth2Error):
|
||
|
"""The client identifier (client_id) is missing or invalid."""
|
||
|
|
||
|
error = "Client ID Error"
|
||
|
description = "The client identifier (client_id) is missing or invalid."
|
||
|
|
||
|
|
||
|
class UserAuthError(OAuth2Error):
|
||
|
"""
|
||
|
Specific to the Resource Owner Password Credentials flow when
|
||
|
the Resource Owners credentials are not valid.
|
||
|
"""
|
||
|
|
||
|
error = "access_denied"
|
||
|
description = "The resource owner or authorization server denied the request."
|
||
|
|
||
|
|
||
|
class TokenIntrospectionError(OAuth2Error):
|
||
|
"""
|
||
|
Specific to the introspection endpoint. This error will be converted
|
||
|
to an "active: false" response, as per the spec.
|
||
|
See https://tools.ietf.org/html/rfc7662
|
||
|
"""
|
||
|
|
||
|
|
||
|
class AuthorizeError(OAuth2Error):
|
||
|
"""General Authorization Errors"""
|
||
|
|
||
|
_errors = {
|
||
|
# OAuth2 errors.
|
||
|
# https://tools.ietf.org/html/rfc6749#section-4.1.2.1
|
||
|
"invalid_request": "The request is otherwise malformed",
|
||
|
"unauthorized_client": "The client is not authorized to request an "
|
||
|
"authorization code using this method",
|
||
|
"access_denied": "The resource owner or authorization server denied "
|
||
|
"the request",
|
||
|
"unsupported_response_type": "The authorization server does not "
|
||
|
"support obtaining an authorization code "
|
||
|
"using this method",
|
||
|
"invalid_scope": "The requested scope is invalid, unknown, or " "malformed",
|
||
|
"server_error": "The authorization server encountered an error",
|
||
|
"temporarily_unavailable": "The authorization server is currently "
|
||
|
"unable to handle the request due to a "
|
||
|
"temporary overloading or maintenance of "
|
||
|
"the server",
|
||
|
# OpenID errors.
|
||
|
# http://openid.net/specs/openid-connect-core-1_0.html#AuthError
|
||
|
"interaction_required": "The Authorization Server requires End-User "
|
||
|
"interaction of some form to proceed",
|
||
|
"login_required": "The Authorization Server requires End-User "
|
||
|
"authentication",
|
||
|
"account_selection_required": "The End-User is required to select a "
|
||
|
"session at the Authorization Server",
|
||
|
"consent_required": "The Authorization Server requires End-User" "consent",
|
||
|
"invalid_request_uri": "The request_uri in the Authorization Request "
|
||
|
"returns an error or contains invalid data",
|
||
|
"invalid_request_object": "The request parameter contains an invalid "
|
||
|
"Request Object",
|
||
|
"request_not_supported": "The provider does not support use of the "
|
||
|
"request parameter",
|
||
|
"request_uri_not_supported": "The provider does not support use of the "
|
||
|
"request_uri parameter",
|
||
|
"registration_not_supported": "The provider does not support use of "
|
||
|
"the registration parameter",
|
||
|
}
|
||
|
|
||
|
def __init__(self, redirect_uri, error, grant_type):
|
||
|
super().__init__()
|
||
|
self.error = error
|
||
|
self.description = self._errors[error]
|
||
|
self.redirect_uri = redirect_uri
|
||
|
self.grant_type = grant_type
|
||
|
|
||
|
def create_uri(self, redirect_uri: str, state: str) -> str:
|
||
|
"""Get a redirect URI with the error message"""
|
||
|
description = quote(str(self.description))
|
||
|
|
||
|
# See:
|
||
|
# http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthError
|
||
|
hash_or_question = "#" if self.grant_type == "implicit" else "?"
|
||
|
|
||
|
uri = "{0}{1}error={2}&error_description={3}".format(
|
||
|
redirect_uri, hash_or_question, self.error, description
|
||
|
)
|
||
|
|
||
|
# Add state if present.
|
||
|
uri = uri + ("&state={0}".format(state) if state else "")
|
||
|
|
||
|
return uri
|
||
|
|
||
|
|
||
|
class TokenError(OAuth2Error):
|
||
|
"""
|
||
|
OAuth2 token endpoint errors.
|
||
|
https://tools.ietf.org/html/rfc6749#section-5.2
|
||
|
"""
|
||
|
|
||
|
_errors = {
|
||
|
"invalid_request": "The request is otherwise malformed",
|
||
|
"invalid_client": "Client authentication failed (e.g., unknown client, "
|
||
|
"no client authentication included, or unsupported "
|
||
|
"authentication method)",
|
||
|
"invalid_grant": "The provided authorization grant or refresh token is "
|
||
|
"invalid, expired, revoked, does not match the "
|
||
|
"redirection URI used in the authorization request, "
|
||
|
"or was issued to another client",
|
||
|
"unauthorized_client": "The authenticated client is not authorized to "
|
||
|
"use this authorization grant type",
|
||
|
"unsupported_grant_type": "The authorization grant type is not "
|
||
|
"supported by the authorization server",
|
||
|
"invalid_scope": "The requested scope is invalid, unknown, malformed, "
|
||
|
"or exceeds the scope granted by the resource owner",
|
||
|
}
|
||
|
|
||
|
def __init__(self, error):
|
||
|
super().__init__()
|
||
|
self.error = error
|
||
|
self.description = self._errors[error]
|
||
|
|
||
|
|
||
|
class BearerTokenError(OAuth2Error):
|
||
|
"""
|
||
|
OAuth2 errors.
|
||
|
https://tools.ietf.org/html/rfc6750#section-3.1
|
||
|
"""
|
||
|
|
||
|
_errors = {
|
||
|
"invalid_request": ("The request is otherwise malformed", 400),
|
||
|
"invalid_token": (
|
||
|
"The access token provided is expired, revoked, malformed, "
|
||
|
"or invalid for other reasons",
|
||
|
401,
|
||
|
),
|
||
|
"insufficient_scope": (
|
||
|
"The request requires higher privileges than provided by "
|
||
|
"the access token",
|
||
|
403,
|
||
|
),
|
||
|
}
|
||
|
|
||
|
def __init__(self, code):
|
||
|
super().__init__()
|
||
|
self.code = code
|
||
|
error_tuple = self._errors.get(code, ("", ""))
|
||
|
self.description = error_tuple[0]
|
||
|
self.status = error_tuple[1]
|