Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public DefaultPublicKeyLocator(
@Override
public List<Verifier> findVerifier(String issuer, String keyId) {
URI serverDescriptor = descriptorProvider.getServerDescriptor(issuer);
if (serverDescriptor == null) {
return null;
}
Verifier rsaVerifier =
new RsaSHA256Verifier(
descriptorResolver.resolve(serverDescriptor).getVerificationKey(keyId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,52 @@
*/
package net.oauth.jsontoken.discovery;

import com.google.common.collect.ImmutableSet;
import java.net.URI;
import java.util.Collection;

/**
* A {@link ServerDescriptorProvider} that returns the issuer id as the server descriptor. If a JSON
* Token issuer uses their own server descriptor as their issuer id, then the JSON Token verifier
* would use this implementation of {@link ServerDescriptorProvider} with the {@link
* DefaultPublicKeyLocator}.
* A {@link ServerDescriptorProvider} that returns the issuer id as the server descriptor for
* explicitly allowlisted issuers. If a JSON Token issuer uses their own server descriptor as their
* issuer id, then the JSON Token verifier would use this implementation of {@link
* ServerDescriptorProvider} with the {@link DefaultPublicKeyLocator}.
*
* <p>For example, some OAuth Servers might use their Client's server descriptors as client_ids, and
* then use this implementation of {@link ServerDescriptorProvider} with the {@link
* DefaultPublicKeyLocator}.
*/
public class IdentityServerDescriptorProvider implements ServerDescriptorProvider {

private final ImmutableSet<String> allowedIssuers;

/**
* Public constructor.
*
* @param allowedIssuers A collection of trusted issuer IDs whose server descriptors may be
* resolved.
*/
public IdentityServerDescriptorProvider(Collection<String> allowedIssuers) {
this.allowedIssuers = ImmutableSet.copyOf(allowedIssuers);
}

/**
* Public constructor.
*
* @param allowedIssuers One or more trusted issuer IDs whose server descriptors may be resolved.
*/
public IdentityServerDescriptorProvider(String... allowedIssuers) {
this.allowedIssuers = ImmutableSet.copyOf(allowedIssuers);
}

/*
* (non-Javadoc)
* @see net.oauth.jsontoken.discovery.ServerDescriptorProvider#getServerDescriptor(java.lang.String)
*/
@Override
public URI getServerDescriptor(String issuer) {
if (issuer == null || !allowedIssuers.contains(issuer)) {
Comment thread
dujinhui marked this conversation as resolved.
return null;
}
return URI.create(issuer);
}
}
20 changes: 18 additions & 2 deletions src/test/java/net/oauth/jsontoken/JsonTokenParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public void testVerifyAndDeserialize_tokenFromRuby() throws Exception {
}

public void testPublicKey() throws Exception {
RsaSHA256Signer signer = new RsaSHA256Signer("google.com", "key1", privateKey);
RsaSHA256Signer signer = new RsaSHA256Signer("example.com", "key1", privateKey);

JsonToken token = new JsonToken(signer, clock);
token.setParam("bar", 15);
Expand All @@ -111,7 +111,7 @@ public void testPublicKey() throws Exception {

JsonTokenParser parser = getJsonTokenParser();
token = parser.verifyAndDeserialize(tokenString);
assertEquals("google.com", token.getIssuer());
assertEquals("example.com", token.getIssuer());
assertEquals(15, token.getParamAsPrimitive("bar").getAsLong());
assertEquals("some value", token.getParamAsPrimitive("foo").getAsString());

Expand All @@ -133,6 +133,22 @@ public void testPublicKey() throws Exception {
assertThrows(SignatureException.class, () -> parser.verifyAndDeserialize(tamperedToken));
}

public void testPublicKey_untrustedIssuer() throws Exception {
RsaSHA256Signer signer = new RsaSHA256Signer("attacker.com", "key1", privateKey);

JsonToken token = new JsonToken(signer, clock);
token.setParam("bar", 15);
token.setExpiration(clock.now().plus(Duration.ofMillis(60)));

String tokenString = token.serializeAndSign();

JsonTokenParser parser = getJsonTokenParser();
assertThrowsWithErrorCode(
IllegalStateException.class,
ErrorCode.NO_VERIFIER,
() -> parser.verifyAndDeserialize(tokenString));
}

private JsonTokenParser getJsonTokenParser() {
return new JsonTokenParser(clock, locators, new AlwaysPassChecker());
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/net/oauth/jsontoken/JsonTokenTestBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ protected void setUp() throws Exception {

VerifierProvider rsaLocator =
new DefaultPublicKeyLocator(
new IdentityServerDescriptorProvider(),
new IdentityServerDescriptorProvider("example.com"),
uri -> JsonServerInfo.getDocument(SERVER_INFO_DOCUMENT));

locators = new VerifierProviders();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.oauth.jsontoken.discovery;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import com.google.common.collect.ImmutableList;
import java.net.URI;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class IdentityServerDescriptorProviderTest {

@Test
public void testAllowedIssuer_varargs() {
IdentityServerDescriptorProvider provider =
new IdentityServerDescriptorProvider("https://accounts.google.com", "https://example.com");

assertEquals(
URI.create("https://accounts.google.com"),
provider.getServerDescriptor("https://accounts.google.com"));
assertEquals(
URI.create("https://example.com"),
provider.getServerDescriptor("https://example.com"));
}

@Test
public void testAllowedIssuer_collection() {
IdentityServerDescriptorProvider provider =
new IdentityServerDescriptorProvider(
ImmutableList.of("https://accounts.google.com", "https://example.com"));

assertEquals(
URI.create("https://accounts.google.com"),
provider.getServerDescriptor("https://accounts.google.com"));
}

@Test
public void testDisallowedIssuer_returnsNull() {
IdentityServerDescriptorProvider provider =
new IdentityServerDescriptorProvider("https://accounts.google.com");

assertNull(provider.getServerDescriptor("https://attacker.com"));
assertNull(provider.getServerDescriptor("http://169.254.169.254/latest/meta-data/"));
}

@Test
public void testNullOrEmptyIssuer_returnsNull() {
IdentityServerDescriptorProvider provider =
new IdentityServerDescriptorProvider("https://accounts.google.com");

assertNull(provider.getServerDescriptor(null));
assertNull(provider.getServerDescriptor(""));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class SignedTokenBuilderTest extends JsonTokenTestBase {

public void testSignature() throws Exception {

Signer signer = new RsaSHA256Signer("google.com", "key1", privateKey);
Signer signer = new RsaSHA256Signer("example.com", "key1", privateKey);

SignedOAuthToken token = new SignedOAuthToken(signer);
token.setMethod("GET");
Expand Down
Loading