001/* 002* The contents of this file are subject to the terms of the Common Development and 003* Distribution License (the License). You may not use this file except in compliance with the 004* License. 005* 006* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007* specific language governing permission and limitations under the License. 008* 009* When distributing Covered Software, include this CDDL Header Notice in each file and include 010* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011* Header, with the fields enclosed by brackets [] replaced by your own identifying 012* information: "Portions copyright [year] [name of copyright owner]". 013* 014* Copyright 2014-2015 ForgeRock AS. 015*/ 016 017package org.forgerock.jaspi.modules.openid.resolvers; 018 019import static org.forgerock.caf.authentication.framework.AuthenticationFramework.LOG; 020 021import java.io.IOException; 022import java.net.MalformedURLException; 023import java.net.URL; 024import java.util.Map; 025 026import org.forgerock.jaspi.modules.openid.exceptions.FailedToLoadJWKException; 027import org.forgerock.jaspi.modules.openid.helpers.SimpleHTTPClient; 028import org.forgerock.json.JsonValue; 029import org.forgerock.json.jose.utils.Utils; 030 031/** 032 * This class creates JWKOpenIdResolverImpl's from a supplied 033 * well-known open id configuration url. 034 */ 035public class WellKnownOpenIdConfigurationFactory { 036 037 private final static String ISSUER = "issuer"; 038 private final static String JWKS_URI = "jwks_uri"; 039 040 private final SimpleHTTPClient simpleHTTPClient; 041 042 /** 043 * Generates a factory that will use the given timeouts when attempting to 044 * read the data form a remote location. 045 * 046 * @param readTimeout set the read timeout of HTTP operations in this factory 047 * @param connTimeout set the connection timeout of HTTP operations in this factory 048 */ 049 public WellKnownOpenIdConfigurationFactory(final int readTimeout, final int connTimeout) { 050 this.simpleHTTPClient = new SimpleHTTPClient(readTimeout, connTimeout); 051 } 052 053 /** 054 * For tests. 055 * @param simpleHTTPClient A passed-in simple client implementation 056 */ 057 WellKnownOpenIdConfigurationFactory(SimpleHTTPClient simpleHTTPClient) { 058 this.simpleHTTPClient = simpleHTTPClient; 059 } 060 061 /** 062 * Returns a JWKOpenIdResolverImpl representing the contents of the supplied URL. 063 * 064 * @param configUrl URL from which to read the JWKSet 065 * @return a usable JWKOpenIdResolverIMpl 066 * @throws FailedToLoadJWKException if there are issues reading or parsing the configUrl 067 */ 068 public JWKOpenIdResolverImpl build(final URL configUrl) throws FailedToLoadJWKException { 069 final String configurationContents; 070 071 try { 072 configurationContents = simpleHTTPClient.get(configUrl); 073 } catch (IOException e) { 074 LOG.debug("Unable to load the Configuration at " + configUrl + " over HTTP", e); 075 throw new FailedToLoadJWKException("Unable to load the Configuration over HTTP", e); 076 } 077 078 Map<String, Object> parsedJson = Utils.parseJson(configurationContents); 079 final JsonValue configuration = new JsonValue(parsedJson); 080 081 final String issuer = configuration.get(ISSUER).asString(); 082 final String jwkUri = configuration.get(JWKS_URI).asString(); 083 084 if (issuer == null || issuer.isEmpty()) { 085 LOG.debug("Invalid configuration - must include an issuer key"); 086 throw new FailedToLoadJWKException("Invalid configuration - must include an issuer key"); 087 } 088 089 if (jwkUri == null || jwkUri.isEmpty()) { 090 LOG.debug("No JWK URI in the supplied configuration"); 091 throw new FailedToLoadJWKException("No JWK URI in the supplied configuration"); 092 } 093 094 URL jwkUrl; 095 096 try { 097 jwkUrl = new URL(jwkUri); 098 } catch (MalformedURLException e) { 099 LOG.debug("Invalid URL supplied to generate JWKs"); 100 throw new FailedToLoadJWKException("Invalid URL supplied to generate JWKs", e); 101 } 102 103 return new JWKOpenIdResolverImpl(issuer, jwkUrl, simpleHTTPClient); 104 } 105 106}