001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2022, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.jwk.source; 019 020 021import java.io.IOException; 022import java.net.URL; 023import java.util.Objects; 024 025import net.jcip.annotations.ThreadSafe; 026 027import com.nimbusds.jose.KeySourceException; 028import com.nimbusds.jose.jwk.JWKSet; 029import com.nimbusds.jose.proc.SecurityContext; 030import com.nimbusds.jose.util.Resource; 031import com.nimbusds.jose.util.ResourceRetriever; 032 033 034/** 035 * JWK set source that loads the keys from a {@link URL}, without health status 036 * reporting. 037 * 038 * @author Thomas Rørvik Skjølberg 039 * @author Vladimir Dzhuvinov 040 * @version 2025-04-03 041 */ 042@ThreadSafe 043public class URLBasedJWKSetSource<C extends SecurityContext> implements JWKSetSource<C> { 044 045 private final URL url; 046 private final ResourceRetriever resourceRetriever; 047 048 049 /** 050 * Creates a new URL based JWK set source. 051 * 052 * @param url The JWK set URL. Must not be {@code null}. 053 * @param resourceRetriever The resource retriever to use. Must not 054 * be {@code null}. 055 */ 056 public URLBasedJWKSetSource(final URL url, final ResourceRetriever resourceRetriever) { 057 Objects.requireNonNull(url, "The URL must not be null"); 058 this.url = url; 059 Objects.requireNonNull(resourceRetriever, "The resource retriever must not be null"); 060 this.resourceRetriever = resourceRetriever; 061 } 062 063 064 /** 065 * Returns the JWK set URL. 066 * 067 * @return The JWK set URL. 068 */ 069 public URL getJWKSetURL() { 070 return url; 071 } 072 073 074 /** 075 * Returns the HTTP resource retriever. 076 * 077 * @return The HTTP resource retriever. 078 */ 079 public ResourceRetriever getResourceRetriever() { 080 081 return resourceRetriever; 082 } 083 084 085 @Override 086 public JWKSet getJWKSet(final JWKSetCacheRefreshEvaluator refreshEvaluator, final long currentTime, final C context) throws KeySourceException { 087 088 Resource resource; 089 try { 090 resource = getResourceRetriever().retrieveResource(getJWKSetURL()); 091 } catch (IOException e) { 092 throw new JWKSetRetrievalException("Couldn't retrieve JWK set from URL: " + e.getMessage(), e); 093 } 094 095 try { 096 // Note on error handling: We want to avoid any generic HTML document 097 // (i.e. default HTTP error pages) and other invalid responses being accepted 098 // as an empty list of JWKs. This is handled by the underlying parser; 099 // it checks that the transferred document is in fact a JSON document, 100 // and that the "keys" field is present. 101 return JWKSet.parse(resource.getContent()); 102 103 } catch (Exception e) { 104 // Guard against unexpected exceptions 105 throw new JWKSetParseException("Unable to parse JWK set", e); 106 } 107 } 108 109 110 @Override 111 public void close() throws IOException { 112 // do nothing 113 } 114}