1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
|
"""authlib.oauth2.rfc6749.errors.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Implementation for OAuth 2 Error Response. A basic error has
parameters:
Error:
REQUIRED. A single ASCII [USASCII] error code.
error_description
OPTIONAL. Human-readable ASCII [USASCII] text providing
additional information, used to assist the client developer in
understanding the error that occurred.
error_uri
OPTIONAL. A URI identifying a human-readable web page with
information about the error, used to provide the client
developer with additional information about the error.
Values for the "error_uri" parameter MUST conform to the
URI-reference syntax and thus MUST NOT include characters
outside the set %x21 / %x23-5B / %x5D-7E.
state
REQUIRED if a "state" parameter was present in the client
authorization request. The exact value received from the
client.
https://tools.ietf.org/html/rfc6749#section-5.2
:copyright: (c) 2017 by Hsiaoming Yang.
"""
from authlib.common.security import is_secure_transport
from authlib.oauth2.base import OAuth2Error
__all__ = [
"OAuth2Error",
"InsecureTransportError",
"InvalidRequestError",
"InvalidClientError",
"UnauthorizedClientError",
"InvalidGrantError",
"UnsupportedResponseTypeError",
"UnsupportedGrantTypeError",
"InvalidScopeError",
"AccessDeniedError",
"MissingAuthorizationError",
"UnsupportedTokenTypeError",
"MissingCodeException",
"MissingTokenException",
"MissingTokenTypeException",
"MismatchingStateException",
]
class InsecureTransportError(OAuth2Error):
error = "insecure_transport"
description = "OAuth 2 MUST utilize https."
@classmethod
def check(cls, uri):
"""Check and raise InsecureTransportError with the given URI."""
if not is_secure_transport(uri):
raise cls()
class InvalidRequestError(OAuth2Error):
"""The request is missing a required parameter, includes an
unsupported parameter value (other than grant type),
repeats a parameter, includes multiple credentials,
utilizes more than one mechanism for authenticating the
client, or is otherwise malformed.
https://tools.ietf.org/html/rfc6749#section-5.2
"""
error = "invalid_request"
class InvalidClientError(OAuth2Error):
"""Client authentication failed (e.g., unknown client, no
client authentication included, or unsupported
authentication method). The authorization server MAY
return an HTTP 401 (Unauthorized) status code to indicate
which HTTP authentication schemes are supported. If the
client attempted to authenticate via the "Authorization"
request header field, the authorization server MUST
respond with an HTTP 401 (Unauthorized) status code and
include the "WWW-Authenticate" response header field
matching the authentication scheme used by the client.
https://tools.ietf.org/html/rfc6749#section-5.2
"""
error = "invalid_client"
status_code = 400
def get_headers(self):
headers = super().get_headers()
if self.status_code == 401:
error_description = self.get_error_description()
# safe escape
error_description = error_description.replace('"', "|")
extras = [
f'error="{self.error}"',
f'error_description="{error_description}"',
]
headers.append(("WWW-Authenticate", "Basic " + ", ".join(extras)))
return headers
class InvalidGrantError(OAuth2Error):
"""The provided authorization grant (e.g., authorization
code, resource owner credentials) or refresh token is
invalid, expired, revoked, does not match the redirection
URI used in the authorization request, or was issued to
another client.
https://tools.ietf.org/html/rfc6749#section-5.2
"""
error = "invalid_grant"
class UnauthorizedClientError(OAuth2Error):
"""The authenticated client is not authorized to use this
authorization grant type.
https://tools.ietf.org/html/rfc6749#section-5.2
"""
error = "unauthorized_client"
class UnsupportedResponseTypeError(OAuth2Error):
"""The authorization server does not support obtaining
an access token using this method.
"""
error = "unsupported_response_type"
def __init__(self, response_type, *args, **kwargs):
super().__init__(*args, **kwargs)
self.response_type = response_type
def get_error_description(self):
return f"response_type={self.response_type} is not supported"
class UnsupportedGrantTypeError(OAuth2Error):
"""The authorization grant type is not supported by the
authorization server.
https://tools.ietf.org/html/rfc6749#section-5.2
"""
error = "unsupported_grant_type"
def __init__(self, grant_type):
super().__init__()
self.grant_type = grant_type
def get_error_description(self):
return f"grant_type={self.grant_type} is not supported"
class InvalidScopeError(OAuth2Error):
"""The requested scope is invalid, unknown, malformed, or
exceeds the scope granted by the resource owner.
https://tools.ietf.org/html/rfc6749#section-5.2
"""
error = "invalid_scope"
description = "The requested scope is invalid, unknown, or malformed."
class AccessDeniedError(OAuth2Error):
"""The resource owner or authorization server denied the request.
Used in authorization endpoint for "code" and "implicit". Defined in
`Section 4.1.2.1`_.
.. _`Section 4.1.2.1`: https://tools.ietf.org/html/rfc6749#section-4.1.2.1
"""
error = "access_denied"
description = "The resource owner or authorization server denied the request"
# -- below are extended errors -- #
class ForbiddenError(OAuth2Error):
status_code = 401
def __init__(self, auth_type=None, realm=None):
super().__init__()
self.auth_type = auth_type
self.realm = realm
def get_headers(self):
headers = super().get_headers()
if not self.auth_type:
return headers
extras = []
if self.realm:
extras.append(f'realm="{self.realm}"')
extras.append(f'error="{self.error}"')
error_description = self.description
extras.append(f'error_description="{error_description}"')
headers.append(("WWW-Authenticate", f"{self.auth_type} " + ", ".join(extras)))
return headers
class MissingAuthorizationError(ForbiddenError):
error = "missing_authorization"
description = "Missing 'Authorization' in headers."
class UnsupportedTokenTypeError(ForbiddenError):
error = "unsupported_token_type"
# -- exceptions for clients -- #
class MissingCodeException(OAuth2Error):
error = "missing_code"
description = "Missing 'code' in response."
class MissingTokenException(OAuth2Error):
error = "missing_token"
description = "Missing 'access_token' in response."
class MissingTokenTypeException(OAuth2Error):
error = "missing_token_type"
description = "Missing 'token_type' in response."
class MismatchingStateException(OAuth2Error):
error = "mismatching_state"
description = "CSRF Warning! State not equal in request and response."
|