Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/oauthlib/oauth2/rfc6749/endpoints/metadata.py: 21%
96 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:22 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:22 +0000
1"""
2oauthlib.oauth2.rfc6749.endpoint.metadata
3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5An implementation of the `OAuth 2.0 Authorization Server Metadata`.
7.. _`OAuth 2.0 Authorization Server Metadata`: https://tools.ietf.org/html/rfc8414
8"""
9import copy
10import json
11import logging
13from .. import grant_types, utils
14from .authorization import AuthorizationEndpoint
15from .base import BaseEndpoint, catch_errors_and_unavailability
16from .introspect import IntrospectEndpoint
17from .revocation import RevocationEndpoint
18from .token import TokenEndpoint
20log = logging.getLogger(__name__)
23class MetadataEndpoint(BaseEndpoint):
25 """OAuth2.0 Authorization Server Metadata endpoint.
27 This specification generalizes the metadata format defined by
28 `OpenID Connect Discovery 1.0` in a way that is compatible
29 with OpenID Connect Discovery while being applicable to a wider set
30 of OAuth 2.0 use cases. This is intentionally parallel to the way
31 that OAuth 2.0 Dynamic Client Registration Protocol [`RFC7591`_]
32 generalized the dynamic client registration mechanisms defined by
33 OpenID Connect Dynamic Client Registration 1.0
34 in a way that is compatible with it.
36 .. _`OpenID Connect Discovery 1.0`: https://openid.net/specs/openid-connect-discovery-1_0.html
37 .. _`RFC7591`: https://tools.ietf.org/html/rfc7591
38 """
40 def __init__(self, endpoints, claims={}, raise_errors=True):
41 assert isinstance(claims, dict)
42 for endpoint in endpoints:
43 assert isinstance(endpoint, BaseEndpoint)
45 BaseEndpoint.__init__(self)
46 self.raise_errors = raise_errors
47 self.endpoints = endpoints
48 self.initial_claims = claims
49 self.claims = self.validate_metadata_server()
51 @catch_errors_and_unavailability
52 def create_metadata_response(self, uri, http_method='GET', body=None,
53 headers=None):
54 """Create metadata response
55 """
56 headers = {
57 'Content-Type': 'application/json',
58 'Access-Control-Allow-Origin': '*',
59 }
60 return headers, json.dumps(self.claims), 200
62 def validate_metadata(self, array, key, is_required=False, is_list=False, is_url=False, is_issuer=False):
63 if not self.raise_errors:
64 return
66 if key not in array:
67 if is_required:
68 raise ValueError("key {} is a mandatory metadata.".format(key))
70 elif is_issuer:
71 if not utils.is_secure_transport(array[key]):
72 raise ValueError("key {}: {} must be an HTTPS URL".format(key, array[key]))
73 if "?" in array[key] or "&" in array[key] or "#" in array[key]:
74 raise ValueError("key {}: {} must not contain query or fragment components".format(key, array[key]))
76 elif is_url:
77 if not array[key].startswith("http"):
78 raise ValueError("key {}: {} must be an URL".format(key, array[key]))
80 elif is_list:
81 if not isinstance(array[key], list):
82 raise ValueError("key {}: {} must be an Array".format(key, array[key]))
83 for elem in array[key]:
84 if not isinstance(elem, str):
85 raise ValueError("array {}: {} must contains only string (not {})".format(key, array[key], elem))
87 def validate_metadata_token(self, claims, endpoint):
88 """
89 If the token endpoint is used in the grant type, the value of this
90 parameter MUST be the same as the value of the "grant_type"
91 parameter passed to the token endpoint defined in the grant type
92 definition.
93 """
94 self._grant_types.extend(endpoint._grant_types.keys())
95 claims.setdefault("token_endpoint_auth_methods_supported", ["client_secret_post", "client_secret_basic"])
97 self.validate_metadata(claims, "token_endpoint_auth_methods_supported", is_list=True)
98 self.validate_metadata(claims, "token_endpoint_auth_signing_alg_values_supported", is_list=True)
99 self.validate_metadata(claims, "token_endpoint", is_required=True, is_url=True)
101 def validate_metadata_authorization(self, claims, endpoint):
102 claims.setdefault("response_types_supported",
103 list(filter(lambda x: x != "none", endpoint._response_types.keys())))
104 claims.setdefault("response_modes_supported", ["query", "fragment"])
106 # The OAuth2.0 Implicit flow is defined as a "grant type" but it is not
107 # using the "token" endpoint, as such, we have to add it explicitly to
108 # the list of "grant_types_supported" when enabled.
109 if "token" in claims["response_types_supported"]:
110 self._grant_types.append("implicit")
112 self.validate_metadata(claims, "response_types_supported", is_required=True, is_list=True)
113 self.validate_metadata(claims, "response_modes_supported", is_list=True)
114 if "code" in claims["response_types_supported"]:
115 code_grant = endpoint._response_types["code"]
116 if not isinstance(code_grant, grant_types.AuthorizationCodeGrant) and hasattr(code_grant, "default_grant"):
117 code_grant = code_grant.default_grant
119 claims.setdefault("code_challenge_methods_supported",
120 list(code_grant._code_challenge_methods.keys()))
121 self.validate_metadata(claims, "code_challenge_methods_supported", is_list=True)
122 self.validate_metadata(claims, "authorization_endpoint", is_required=True, is_url=True)
124 def validate_metadata_revocation(self, claims, endpoint):
125 claims.setdefault("revocation_endpoint_auth_methods_supported",
126 ["client_secret_post", "client_secret_basic"])
128 self.validate_metadata(claims, "revocation_endpoint_auth_methods_supported", is_list=True)
129 self.validate_metadata(claims, "revocation_endpoint_auth_signing_alg_values_supported", is_list=True)
130 self.validate_metadata(claims, "revocation_endpoint", is_required=True, is_url=True)
132 def validate_metadata_introspection(self, claims, endpoint):
133 claims.setdefault("introspection_endpoint_auth_methods_supported",
134 ["client_secret_post", "client_secret_basic"])
136 self.validate_metadata(claims, "introspection_endpoint_auth_methods_supported", is_list=True)
137 self.validate_metadata(claims, "introspection_endpoint_auth_signing_alg_values_supported", is_list=True)
138 self.validate_metadata(claims, "introspection_endpoint", is_required=True, is_url=True)
140 def validate_metadata_server(self):
141 """
142 Authorization servers can have metadata describing their
143 configuration. The following authorization server metadata values
144 are used by this specification. More details can be found in
145 `RFC8414 section 2`_ :
147 issuer
148 REQUIRED
150 authorization_endpoint
151 URL of the authorization server's authorization endpoint
152 [`RFC6749#Authorization`_]. This is REQUIRED unless no grant types are supported
153 that use the authorization endpoint.
155 token_endpoint
156 URL of the authorization server's token endpoint [`RFC6749#Token`_]. This
157 is REQUIRED unless only the implicit grant type is supported.
159 scopes_supported
160 RECOMMENDED.
162 response_types_supported
163 REQUIRED.
165 Other OPTIONAL fields:
166 jwks_uri,
167 registration_endpoint,
168 response_modes_supported
170 grant_types_supported
171 OPTIONAL. JSON array containing a list of the OAuth 2.0 grant
172 type values that this authorization server supports. The array
173 values used are the same as those used with the "grant_types"
174 parameter defined by "OAuth 2.0 Dynamic Client Registration
175 Protocol" [`RFC7591`_]. If omitted, the default value is
176 "["authorization_code", "implicit"]".
178 token_endpoint_auth_methods_supported
180 token_endpoint_auth_signing_alg_values_supported
182 service_documentation
184 ui_locales_supported
186 op_policy_uri
188 op_tos_uri
190 revocation_endpoint
192 revocation_endpoint_auth_methods_supported
194 revocation_endpoint_auth_signing_alg_values_supported
196 introspection_endpoint
198 introspection_endpoint_auth_methods_supported
200 introspection_endpoint_auth_signing_alg_values_supported
202 code_challenge_methods_supported
204 Additional authorization server metadata parameters MAY also be used.
205 Some are defined by other specifications, such as OpenID Connect
206 Discovery 1.0 [`OpenID.Discovery`_].
208 .. _`RFC8414 section 2`: https://tools.ietf.org/html/rfc8414#section-2
209 .. _`RFC6749#Authorization`: https://tools.ietf.org/html/rfc6749#section-3.1
210 .. _`RFC6749#Token`: https://tools.ietf.org/html/rfc6749#section-3.2
211 .. _`RFC7591`: https://tools.ietf.org/html/rfc7591
212 .. _`OpenID.Discovery`: https://openid.net/specs/openid-connect-discovery-1_0.html
213 """
214 claims = copy.deepcopy(self.initial_claims)
215 self.validate_metadata(claims, "issuer", is_required=True, is_issuer=True)
216 self.validate_metadata(claims, "jwks_uri", is_url=True)
217 self.validate_metadata(claims, "scopes_supported", is_list=True)
218 self.validate_metadata(claims, "service_documentation", is_url=True)
219 self.validate_metadata(claims, "ui_locales_supported", is_list=True)
220 self.validate_metadata(claims, "op_policy_uri", is_url=True)
221 self.validate_metadata(claims, "op_tos_uri", is_url=True)
223 self._grant_types = []
224 for endpoint in self.endpoints:
225 if isinstance(endpoint, TokenEndpoint):
226 self.validate_metadata_token(claims, endpoint)
227 if isinstance(endpoint, AuthorizationEndpoint):
228 self.validate_metadata_authorization(claims, endpoint)
229 if isinstance(endpoint, RevocationEndpoint):
230 self.validate_metadata_revocation(claims, endpoint)
231 if isinstance(endpoint, IntrospectEndpoint):
232 self.validate_metadata_introspection(claims, endpoint)
234 # "grant_types_supported" is a combination of all OAuth2 grant types
235 # allowed in the current provider implementation.
236 claims.setdefault("grant_types_supported", self._grant_types)
237 self.validate_metadata(claims, "grant_types_supported", is_list=True)
238 return claims