Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/oauthlib/oauth2/rfc6749/endpoints/metadata.py: 22%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

97 statements  

1""" 

2oauthlib.oauth2.rfc6749.endpoint.metadata 

3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

4 

5An implementation of the `OAuth 2.0 Authorization Server Metadata`. 

6 

7.. _`OAuth 2.0 Authorization Server Metadata`: https://tools.ietf.org/html/rfc8414 

8""" 

9import copy 

10import json 

11import logging 

12 

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 

19 

20log = logging.getLogger(__name__) 

21 

22 

23class MetadataEndpoint(BaseEndpoint): 

24 

25 """OAuth2.0 Authorization Server Metadata endpoint. 

26 

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. 

35 

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 """ 

39 

40 def __init__(self, endpoints, claims={}, raise_errors=True): 

41 assert isinstance(claims, dict) # noqa: S101 

42 for endpoint in endpoints: 

43 assert isinstance(endpoint, BaseEndpoint) # noqa: S101 

44 

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() 

50 

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 

61 

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 

65 

66 if key not in array: 

67 if is_required: 

68 raise ValueError("key {} is a mandatory metadata.".format(key)) 

69 

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])) 

75 

76 elif is_url: 

77 if not array[key].startswith("http"): 

78 raise ValueError("key {}: {} must be an URL".format(key, array[key])) 

79 

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)) 

86 

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"]) 

96 

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) 

100 

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"]) 

105 

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") 

111 

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 

118 

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) 

123 

124 def validate_metadata_revocation(self, claims, endpoint): 

125 claims.setdefault("revocation_endpoint_auth_methods_supported", 

126 ["client_secret_post", "client_secret_basic"]) 

127 

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) 

131 

132 def validate_metadata_introspection(self, claims, endpoint): 

133 claims.setdefault("introspection_endpoint_auth_methods_supported", 

134 ["client_secret_post", "client_secret_basic"]) 

135 

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) 

139 

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`_ : 

146 

147 issuer 

148 REQUIRED 

149 

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. 

154 

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. 

158 

159 scopes_supported 

160 RECOMMENDED. 

161 

162 response_types_supported 

163 REQUIRED. 

164 

165 Other OPTIONAL fields: 

166 jwks_uri, 

167 registration_endpoint, 

168 response_modes_supported 

169 

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"]". 

177 

178 token_endpoint_auth_methods_supported 

179 

180 token_endpoint_auth_signing_alg_values_supported 

181 

182 service_documentation 

183 

184 ui_locales_supported 

185 

186 op_policy_uri 

187 

188 op_tos_uri 

189 

190 revocation_endpoint 

191 

192 revocation_endpoint_auth_methods_supported 

193 

194 revocation_endpoint_auth_signing_alg_values_supported 

195 

196 introspection_endpoint 

197 

198 introspection_endpoint_auth_methods_supported 

199 

200 introspection_endpoint_auth_signing_alg_values_supported 

201 

202 code_challenge_methods_supported 

203 

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`_]. 

207 

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) 

222 

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) 

233 

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