PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/openid-connect-server/src/main/java/org/mitre/discovery/web/DiscoveryEndpoint.java

https://gitlab.com/jslee1/OpenID-Connect-Java-Spring-Server
Java | 372 lines | 176 code | 37 blank | 159 comment | 19 complexity | b790702e60c36861eec0a523cd20d746 MD5 | raw file
  1. /*******************************************************************************
  2. * Copyright 2016 The MITRE Corporation
  3. * and the MIT Internet Trust Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *******************************************************************************/
  17. package org.mitre.discovery.web;
  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.HashMap;
  21. import java.util.Map;
  22. import org.mitre.discovery.util.WebfingerURLNormalizer;
  23. import org.mitre.jwt.encryption.service.JWTEncryptionAndDecryptionService;
  24. import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
  25. import org.mitre.oauth2.service.SystemScopeService;
  26. import org.mitre.oauth2.web.IntrospectionEndpoint;
  27. import org.mitre.oauth2.web.RevocationEndpoint;
  28. import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
  29. import org.mitre.openid.connect.model.UserInfo;
  30. import org.mitre.openid.connect.service.UserInfoService;
  31. import org.mitre.openid.connect.view.HttpCodeView;
  32. import org.mitre.openid.connect.view.JsonEntityView;
  33. import org.mitre.openid.connect.web.DynamicClientRegistrationEndpoint;
  34. import org.mitre.openid.connect.web.JWKSetPublishingEndpoint;
  35. import org.mitre.openid.connect.web.UserInfoEndpoint;
  36. import org.slf4j.Logger;
  37. import org.slf4j.LoggerFactory;
  38. import org.springframework.beans.factory.annotation.Autowired;
  39. import org.springframework.http.HttpStatus;
  40. import org.springframework.http.MediaType;
  41. import org.springframework.stereotype.Controller;
  42. import org.springframework.ui.Model;
  43. import org.springframework.web.bind.annotation.RequestMapping;
  44. import org.springframework.web.bind.annotation.RequestParam;
  45. import org.springframework.web.util.UriComponents;
  46. import org.springframework.web.util.UriComponentsBuilder;
  47. import com.google.common.base.Function;
  48. import com.google.common.base.Strings;
  49. import com.google.common.collect.Collections2;
  50. import com.google.common.collect.Lists;
  51. import com.nimbusds.jose.Algorithm;
  52. import com.nimbusds.jose.JWSAlgorithm;
  53. /**
  54. *
  55. * Handle OpenID Connect Discovery.
  56. *
  57. * @author jricher
  58. *
  59. */
  60. @Controller
  61. public class DiscoveryEndpoint {
  62. public static final String WELL_KNOWN_URL = ".well-known";
  63. public static final String OPENID_CONFIGURATION_URL = WELL_KNOWN_URL + "/openid-configuration";
  64. public static final String WEBFINGER_URL = WELL_KNOWN_URL + "/webfinger";
  65. /**
  66. * Logger for this class
  67. */
  68. private static final Logger logger = LoggerFactory.getLogger(DiscoveryEndpoint.class);
  69. @Autowired
  70. private ConfigurationPropertiesBean config;
  71. @Autowired
  72. private SystemScopeService scopeService;
  73. @Autowired
  74. private JWTSigningAndValidationService signService;
  75. @Autowired
  76. private JWTEncryptionAndDecryptionService encService;
  77. @Autowired
  78. private UserInfoService userService;
  79. // used to map JWA algorithms objects to strings
  80. private Function<Algorithm, String> toAlgorithmName = new Function<Algorithm, String>() {
  81. @Override
  82. public String apply(Algorithm alg) {
  83. if (alg == null) {
  84. return null;
  85. } else {
  86. return alg.getName();
  87. }
  88. }
  89. };
  90. @RequestMapping(value={"/" + WEBFINGER_URL}, produces = MediaType.APPLICATION_JSON_VALUE)
  91. public String webfinger(@RequestParam("resource") String resource, @RequestParam(value = "rel", required = false) String rel, Model model) {
  92. if (!Strings.isNullOrEmpty(rel) && !rel.equals("http://openid.net/specs/connect/1.0/issuer")) {
  93. logger.warn("Responding to webfinger request for non-OIDC relation: " + rel);
  94. }
  95. if (!resource.equals(config.getIssuer())) {
  96. // it's not the issuer directly, need to check other methods
  97. UriComponents resourceUri = WebfingerURLNormalizer.normalizeResource(resource);
  98. if (resourceUri != null
  99. && resourceUri.getScheme() != null
  100. && resourceUri.getScheme().equals("acct")) {
  101. // acct: URI (email address format)
  102. // check on email addresses first
  103. UserInfo user = userService.getByEmailAddress(resourceUri.getUserInfo() + "@" + resourceUri.getHost());
  104. if (user == null) {
  105. // user wasn't found, see if the local part of the username matches, plus our issuer host
  106. user = userService.getByUsername(resourceUri.getUserInfo()); // first part is the username
  107. if (user != null) {
  108. // username matched, check the host component
  109. UriComponents issuerComponents = UriComponentsBuilder.fromHttpUrl(config.getIssuer()).build();
  110. if (!Strings.nullToEmpty(issuerComponents.getHost())
  111. .equals(Strings.nullToEmpty(resourceUri.getHost()))) {
  112. logger.info("Host mismatch, expected " + issuerComponents.getHost() + " got " + resourceUri.getHost());
  113. model.addAttribute(HttpCodeView.CODE, HttpStatus.NOT_FOUND);
  114. return HttpCodeView.VIEWNAME;
  115. }
  116. } else {
  117. // if the user's still null, punt and say we didn't find them
  118. logger.info("User not found: " + resource);
  119. model.addAttribute(HttpCodeView.CODE, HttpStatus.NOT_FOUND);
  120. return HttpCodeView.VIEWNAME;
  121. }
  122. }
  123. } else {
  124. logger.info("Unknown URI format: " + resource);
  125. model.addAttribute(HttpCodeView.CODE, HttpStatus.NOT_FOUND);
  126. return HttpCodeView.VIEWNAME;
  127. }
  128. }
  129. // if we got here, then we're good, return ourselves
  130. model.addAttribute("resource", resource);
  131. model.addAttribute("issuer", config.getIssuer());
  132. return "webfingerView";
  133. }
  134. @RequestMapping("/" + OPENID_CONFIGURATION_URL)
  135. public String providerConfiguration(Model model) {
  136. /*
  137. issuer
  138. REQUIRED. URL using the https scheme with no query or fragment component that the OP asserts as its Issuer Identifier.
  139. authorization_endpoint
  140. OPTIONAL. URL of the OP's Authentication and Authorization Endpoint [OpenID.Messages].
  141. token_endpoint
  142. OPTIONAL. URL of the OP's OAuth 2.0 Token Endpoint [OpenID.Messages].
  143. userinfo_endpoint
  144. RECOMMENDED. URL of the OP's UserInfo Endpoint [OpenID.Messages]. This URL MUST use the https scheme
  145. and MAY contain port, path, and query parameter components.
  146. check_session_iframe
  147. OPTIONAL. URL of an OP endpoint that provides a page to support cross-origin communications for session state information with
  148. the RP Client, using the HTML5 postMessage API. The page is loaded from an invisible iframe embedded in an RP page so that
  149. it can run in the OP's security context. See [OpenID.Session].
  150. end_session_endpoint
  151. OPTIONAL. URL of the OP's endpoint that initiates logging out the End-User. See [OpenID.Session].
  152. jwks_uri
  153. REQUIRED. URL of the OP's JSON Web Key Set [JWK] document. This contains the signing key(s) the Client uses to
  154. validate signatures from the OP. The JWK Set MAY also contain the Server's encryption key(s),
  155. which are used by Clients to encrypt requests to the Server. When both signing and encryption keys are made available,
  156. a use (Key Use) parameter value is REQUIRED for all keys in the document to indicate each key's intended usage.
  157. registration_endpoint
  158. RECOMMENDED. URL of the OP's Dynamic Client Registration Endpoint [OpenID.Registration].
  159. scopes_supported
  160. RECOMMENDED. JSON array containing a list of the OAuth 2.0 [RFC6749] scope values that this server supports.
  161. The server MUST support the openid scope value.
  162. response_types_supported
  163. REQUIRED. JSON array containing a list of the OAuth 2.0 response_type values that this server supports.
  164. The server MUST support the code, id_token, and the token id_token response type values.
  165. grant_types_supported
  166. OPTIONAL. JSON array containing a list of the OAuth 2.0 grant type values that this server supports.
  167. The server MUST support the authorization_code and implicit grant type values
  168. and MAY support the urn:ietf:params:oauth:grant-type:jwt-bearer grant type defined in OAuth JWT Bearer Token Profiles [OAuth.JWT].
  169. If omitted, the default value is ["authorization_code", "implicit"].
  170. acr_values_supported
  171. OPTIONAL. JSON array containing a list of the Authentication Context Class References that this server supports.
  172. subject_types_supported
  173. REQUIRED. JSON array containing a list of the subject identifier types that this server supports. Valid types include pairwise and public.
  174. userinfo_signing_alg_values_supported
  175. OPTIONAL. JSON array containing a list of the JWS [JWS] signing algorithms (alg values) [JWA] supported by the UserInfo Endpoint to
  176. encode the Claims in a JWT [JWT].
  177. userinfo_encryption_alg_values_supported
  178. OPTIONAL. JSON array containing a list of the JWE [JWE] encryption algorithms (alg values) [JWA] supported by the UserInfo Endpoint to
  179. encode the Claims in a JWT [JWT].
  180. userinfo_encryption_enc_values_supported
  181. OPTIONAL. JSON array containing a list of the JWE encryption algorithms (enc values) [JWA] supported by the UserInfo Endpoint to
  182. encode the Claims in a JWT [JWT].
  183. id_token_signing_alg_values_supported
  184. REQUIRED. JSON array containing a list of the JWS signing algorithms (alg values) supported by the Authorization Server for the
  185. ID Token to encode the Claims in a JWT [JWT].
  186. id_token_encryption_alg_values_supported
  187. OPTIONAL. JSON array containing a list of the JWE encryption algorithms (alg values) supported by the Authorization Server for the
  188. ID Token to encode the Claims in a JWT [JWT].
  189. id_token_encryption_enc_values_supported
  190. OPTIONAL. JSON array containing a list of the JWE encryption algorithms (enc values) supported by the Authorization Server for the
  191. ID Token to encode the Claims in a JWT [JWT].
  192. request_object_signing_alg_values_supported
  193. OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values) supported by the Authorization Server for
  194. the Request Object described in Section 2.9 of OpenID Connect Messages 1.0 [OpenID.Messages]. These algorithms are used both when
  195. the Request Object is passed by value (using the request parameter) and when it is passed by reference (using the request_uri parameter).
  196. Servers SHOULD support none and RS256.
  197. request_object_encryption_alg_values_supported
  198. OPTIONAL. JSON array containing a list of the JWE encryption algorithms (alg values) supported by the Authorization Server for
  199. the Request Object described in Section 2.9 of OpenID Connect Messages 1.0 [OpenID.Messages]. These algorithms are used both when
  200. the Request Object is passed by value and when it is passed by reference.
  201. request_object_encryption_enc_values_supported
  202. OPTIONAL. JSON array containing a list of the JWE encryption algorithms (enc values) supported by the Authorization Server for
  203. the Request Object described in Section 2.9 of OpenID Connect Messages 1.0 [OpenID.Messages]. These algorithms are used both when
  204. the Request Object is passed by value and when it is passed by reference.
  205. token_endpoint_auth_methods_supported
  206. OPTIONAL. JSON array containing a list of authentication methods supported by this Token Endpoint.
  207. The options are client_secret_post, client_secret_basic, client_secret_jwt, and private_key_jwt,
  208. as described in Section 2.2.1 of OpenID Connect Messages 1.0 [OpenID.Messages].
  209. Other authentication methods MAY be defined by extensions.
  210. If omitted, the default is client_secret_basic -- the HTTP Basic Authentication Scheme as specified in
  211. Section 2.3.1 of OAuth 2.0 [RFC6749].
  212. token_endpoint_auth_signing_alg_values_supported
  213. OPTIONAL. JSON array containing a list of the JWS signing algorithms (alg values) supported by the Token Endpoint for
  214. the private_key_jwt and client_secret_jwt methods to encode the JWT [JWT]. Servers SHOULD support RS256.
  215. display_values_supported
  216. OPTIONAL. JSON array containing a list of the display parameter values that the OpenID Provider supports.
  217. These values are described in Section 2.1.1 of OpenID Connect Messages 1.0 [OpenID.Messages].
  218. claim_types_supported
  219. OPTIONAL. JSON array containing a list of the Claim Types that the OpenID Provider supports.
  220. These Claim Types are described in Section 2.6 of OpenID Connect Messages 1.0 [OpenID.Messages].
  221. Values defined by this specification are normal, aggregated, and distributed.
  222. If not specified, the implementation supports only normal Claims.
  223. claims_supported
  224. RECOMMENDED. JSON array containing a list of the Claim Names of the Claims that the OpenID Provider MAY be able to supply values for.
  225. Note that for privacy or other reasons, this might not be an exhaustive list.
  226. service_documentation
  227. OPTIONAL. URL of a page containing human-readable information that developers might want or need to know when using the OpenID Provider.
  228. In particular, if the OpenID Provider does not support Dynamic Client Registration, then information on how to register Clients needs
  229. to be provided in this documentation.
  230. claims_locales_supported
  231. OPTIONAL. Languages and scripts supported for values in Claims being returned, represented as a JSON array of
  232. BCP47 [RFC5646] language tag values. Not all languages and scripts are necessarily supported for all Claim values.
  233. ui_locales_supported
  234. OPTIONAL. Languages and scripts supported for the user interface, represented as a JSON array of BCP47 [RFC5646] language tag values.
  235. claims_parameter_supported
  236. OPTIONAL. Boolean value specifying whether the OP supports use of the claims parameter, with true indicating support.
  237. If omitted, the default value is false.
  238. request_parameter_supported
  239. OPTIONAL. Boolean value specifying whether the OP supports use of the request parameter, with true indicating support.
  240. If omitted, the default value is false.
  241. request_uri_parameter_supported
  242. OPTIONAL. Boolean value specifying whether the OP supports use of the request_uri parameter, with true indicating support.
  243. If omitted, the default value is true.
  244. require_request_uri_registration
  245. OPTIONAL. Boolean value specifying whether the OP requires any request_uri values used to be pre-registered using
  246. the request_uris registration parameter. Pre-registration is REQUIRED when the value is true. If omitted, the default value is false.
  247. op_policy_uri
  248. OPTIONAL. URL that the OpenID Provider provides to the person registering the Client to read about the OP's requirements on
  249. how the Relying Party can use the data provided by the OP. The registration process SHOULD display this URL to the person registering
  250. the Client if it is given.
  251. op_tos_uri
  252. OPTIONAL. URL that the OpenID Provider provides to the person registering the Client to read about OpenID Provider's terms of service.
  253. The registration process SHOULD display this URL to the person registering the Client if it is given.
  254. */
  255. String baseUrl = config.getIssuer();
  256. if (!baseUrl.endsWith("/")) {
  257. logger.warn("Configured issuer doesn't end in /, adding for discovery: " + baseUrl);
  258. baseUrl = baseUrl.concat("/");
  259. }
  260. Collection<JWSAlgorithm> serverSigningAlgs = signService.getAllSigningAlgsSupported();
  261. Collection<JWSAlgorithm> clientSymmetricSigningAlgs = Lists.newArrayList(JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512);
  262. Collection<JWSAlgorithm> clientSymmetricAndAsymmetricSigningAlgs = Lists.newArrayList(JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512,
  263. JWSAlgorithm.RS256, JWSAlgorithm.RS384, JWSAlgorithm.RS512,
  264. JWSAlgorithm.ES256, JWSAlgorithm.ES384, JWSAlgorithm.ES512,
  265. JWSAlgorithm.PS256, JWSAlgorithm.PS384, JWSAlgorithm.PS512);
  266. Collection<Algorithm> clientSymmetricAndAsymmetricSigningAlgsWithNone = Lists.newArrayList(JWSAlgorithm.HS256, JWSAlgorithm.HS384, JWSAlgorithm.HS512,
  267. JWSAlgorithm.RS256, JWSAlgorithm.RS384, JWSAlgorithm.RS512,
  268. JWSAlgorithm.ES256, JWSAlgorithm.ES384, JWSAlgorithm.ES512,
  269. JWSAlgorithm.PS256, JWSAlgorithm.PS384, JWSAlgorithm.PS512,
  270. Algorithm.NONE);
  271. ArrayList<String> grantTypes = Lists.newArrayList("authorization_code", "implicit", "urn:ietf:params:oauth:grant-type:jwt-bearer", "client_credentials", "urn:ietf:params:oauth:grant_type:redelegate");
  272. Map<String, Object> m = new HashMap<>();
  273. m.put("issuer", config.getIssuer());
  274. m.put("authorization_endpoint", baseUrl + "authorize");
  275. m.put("token_endpoint", baseUrl + "token");
  276. m.put("userinfo_endpoint", baseUrl + UserInfoEndpoint.URL);
  277. //check_session_iframe
  278. //end_session_endpoint
  279. m.put("jwks_uri", baseUrl + JWKSetPublishingEndpoint.URL);
  280. m.put("registration_endpoint", baseUrl + DynamicClientRegistrationEndpoint.URL);
  281. m.put("scopes_supported", scopeService.toStrings(scopeService.getUnrestricted())); // these are the scopes that you can dynamically register for, which is what matters for discovery
  282. m.put("response_types_supported", Lists.newArrayList("code", "token")); // we don't support these yet: , "id_token", "id_token token"));
  283. m.put("grant_types_supported", grantTypes);
  284. //acr_values_supported
  285. m.put("subject_types_supported", Lists.newArrayList("public", "pairwise"));
  286. m.put("userinfo_signing_alg_values_supported", Collections2.transform(clientSymmetricAndAsymmetricSigningAlgs, toAlgorithmName));
  287. m.put("userinfo_encryption_alg_values_supported", Collections2.transform(encService.getAllEncryptionAlgsSupported(), toAlgorithmName));
  288. m.put("userinfo_encryption_enc_values_supported", Collections2.transform(encService.getAllEncryptionEncsSupported(), toAlgorithmName));
  289. m.put("id_token_signing_alg_values_supported", Collections2.transform(clientSymmetricAndAsymmetricSigningAlgsWithNone, toAlgorithmName));
  290. m.put("id_token_encryption_alg_values_supported", Collections2.transform(encService.getAllEncryptionAlgsSupported(), toAlgorithmName));
  291. m.put("id_token_encryption_enc_values_supported", Collections2.transform(encService.getAllEncryptionEncsSupported(), toAlgorithmName));
  292. m.put("request_object_signing_alg_values_supported", Collections2.transform(clientSymmetricAndAsymmetricSigningAlgs, toAlgorithmName));
  293. m.put("request_object_encryption_alg_values_supported", Collections2.transform(encService.getAllEncryptionAlgsSupported(), toAlgorithmName));
  294. m.put("request_object_encryption_enc_values_supported", Collections2.transform(encService.getAllEncryptionEncsSupported(), toAlgorithmName));
  295. m.put("token_endpoint_auth_methods_supported", Lists.newArrayList("client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt", "none"));
  296. m.put("token_endpoint_auth_signing_alg_values_supported", Collections2.transform(clientSymmetricAndAsymmetricSigningAlgs, toAlgorithmName));
  297. //display_types_supported
  298. m.put("claim_types_supported", Lists.newArrayList("normal" /*, "aggregated", "distributed"*/));
  299. m.put("claims_supported", Lists.newArrayList(
  300. "sub",
  301. "name",
  302. "preferred_username",
  303. "given_name",
  304. "family_name",
  305. "middle_name",
  306. "nickname",
  307. "profile",
  308. "picture",
  309. "website",
  310. "gender",
  311. "zoneinfo",
  312. "locale",
  313. "updated_at",
  314. "birthdate",
  315. "email",
  316. "email_verified",
  317. "phone_number",
  318. "phone_number_verified",
  319. "address"
  320. ));
  321. m.put("service_documentation", baseUrl + "about");
  322. //claims_locales_supported
  323. //ui_locales_supported
  324. m.put("claims_parameter_supported", false);
  325. m.put("request_parameter_supported", true);
  326. m.put("request_uri_parameter_supported", false);
  327. m.put("require_request_uri_registration", false);
  328. m.put("op_policy_uri", baseUrl + "about");
  329. m.put("op_tos_uri", baseUrl + "about");
  330. m.put("introspection_endpoint", baseUrl + IntrospectionEndpoint.URL); // token introspection endpoint for verifying tokens
  331. m.put("revocation_endpoint", baseUrl + RevocationEndpoint.URL); // token revocation endpoint
  332. model.addAttribute(JsonEntityView.ENTITY, m);
  333. return JsonEntityView.VIEWNAME;
  334. }
  335. }