Skip to content

Commit

Permalink
Merge pull request #33283 from sberyozkin/oidc_token_introspection_im…
Browse files Browse the repository at this point in the history
…provement

Make it easier to get well known properties from TokenIntrospection
  • Loading branch information
sberyozkin authored May 11, 2023
2 parents d4cac96 + 9a0744e commit f0af1bd
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ public final class OidcConstants {
public static final String INTROSPECTION_TOKEN_TYPE_HINT = "token_type_hint";
public static final String INTROSPECTION_TOKEN = "token";
public static final String INTROSPECTION_TOKEN_ACTIVE = "active";
public static final String INTROSPECTION_TOKEN_CLIENT_ID = "client_id";
public static final String INTROSPECTION_TOKEN_EXP = "exp";
public static final String INTROSPECTION_TOKEN_IAT = "iat";
public static final String INTROSPECTION_TOKEN_USERNAME = "username";
public static final String INTROSPECTION_TOKEN_SUB = "sub";
public static final String INTROSPECTION_TOKEN_AUD = "aud";
public static final String INTROSPECTION_TOKEN_ISS = "iss";

public static final String REVOCATION_TOKEN = "token";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package io.quarkus.oidc;

import java.util.HashSet;
import java.util.Set;

import jakarta.json.JsonObject;

import io.quarkus.oidc.common.runtime.OidcConstants;
import io.quarkus.oidc.runtime.AbstractJsonObjectResponse;

/**
Expand All @@ -21,6 +25,44 @@ public TokenIntrospection(JsonObject json) {
super(json);
}

public boolean isActive() {
return getBoolean(OidcConstants.INTROSPECTION_TOKEN_ACTIVE);
}

public String getUsername() {
return getString(OidcConstants.INTROSPECTION_TOKEN_USERNAME);
}

public String getSubject() {
return getString(OidcConstants.INTROSPECTION_TOKEN_SUB);
}

public String getAudience() {
return getString(OidcConstants.INTROSPECTION_TOKEN_AUD);
}

public String getIssuer() {
return getString(OidcConstants.INTROSPECTION_TOKEN_ISS);
}

public Set<String> getScopes() {
if (this.contains(OidcConstants.TOKEN_SCOPE)) {
String[] scopesArray = getString(OidcConstants.TOKEN_SCOPE).split(" ");
Set<String> scopes = new HashSet<>(scopesArray.length);
for (String scope : scopesArray) {
scopes.add(scope.trim());
}
return scopes;
} else {
return null;
}

}

public String getClientId() {
return getString(OidcConstants.INTROSPECTION_TOKEN_CLIENT_ID);
}

public String getIntrospectionString() {
return getNonNullJsonString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static io.quarkus.oidc.runtime.OidcUtils.validateAndCreateIdentity;

import java.security.Principal;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
Expand Down Expand Up @@ -282,20 +283,15 @@ && tokenAutoRefreshPrepared(result, vertxContext, resolvedContext.oidcConfig)) {
}
} else {
OidcUtils.setSecurityIdentityIntrospection(builder, result.introspectionResult);
String principalMember = "";
if (result.introspectionResult.contains(OidcConstants.INTROSPECTION_TOKEN_USERNAME)) {
principalMember = OidcConstants.INTROSPECTION_TOKEN_USERNAME;
} else if (result.introspectionResult.contains(OidcConstants.INTROSPECTION_TOKEN_SUB)) {
// fallback to "sub", if "username" is not present
principalMember = OidcConstants.INTROSPECTION_TOKEN_SUB;
String principalName = result.introspectionResult.getUsername();
if (principalName == null) {
principalName = result.introspectionResult.getSubject();
}
userName = principalMember.isEmpty() ? ""
: result.introspectionResult.getString(principalMember);
if (result.introspectionResult.contains(OidcConstants.TOKEN_SCOPE)) {
for (String role : result.introspectionResult.getString(OidcConstants.TOKEN_SCOPE)
.split(" ")) {
builder.addRole(role.trim());
}
userName = principalName != null ? principalName : "";

Set<String> scopes = result.introspectionResult.getScopes();
if (scopes != null) {
builder.addRoles(scopes);
}
}
builder.setPrincipal(new Principal() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public TokenIntrospection apply(TokenIntrospection introspectionResult, Throwabl
if (t != null) {
throw new AuthenticationFailedException(t);
}
if (!Boolean.TRUE.equals(introspectionResult.getBoolean(OidcConstants.INTROSPECTION_TOKEN_ACTIVE))) {
if (!introspectionResult.isActive()) {
LOG.debugf("Token issued to client %s is not active", oidcConfig.clientId.get());
verifyTokenExpiry(introspectionResult.getLong(OidcConstants.INTROSPECTION_TOKEN_EXP));
throw new AuthenticationFailedException();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package io.quarkus.oidc.runtime;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Set;

import jakarta.json.JsonArray;
import jakarta.json.JsonObject;

import org.junit.jupiter.api.Test;

import io.quarkus.oidc.TokenIntrospection;

public class TokenIntrospectionTest {
TokenIntrospection introspection = new TokenIntrospection(
"{"
+ "\"active\": true,"
+ "\"username\": \"alice\","
+ "\"sub\": \"1234567\","
+ "\"aud\": \"http://localhost:8080\","
+ "\"iss\": \"http://keycloak/realm\","
+ "\"client_id\": \"quarkus\","
+ "\"custom\": null,"
+ "\"id\": 1234,"
+ "\"permissions\": [\"read\", \"write\"],"
+ "\"scope\": \"add divide\","
+ "\"scopes\": {\"scope\": \"see\"}"
+ "}");

@Test
public void testActive() {
assertTrue(introspection.isActive());
}

@Test
public void testGetUsername() {
assertEquals("alice", introspection.getUsername());
}

@Test
public void testGetSubject() {
assertEquals("1234567", introspection.getSubject());
}

@Test
public void testGetAudience() {
assertEquals("http://localhost:8080", introspection.getAudience());
}

@Test
public void testGetIssuer() {
assertEquals("http://keycloak/realm", introspection.getIssuer());
}

@Test
public void testGetScopes() {
assertEquals(Set.of("add", "divide"), introspection.getScopes());
}

@Test
public void testGetClientId() {
assertEquals("quarkus", introspection.getClientId());
}

@Test
public void testGetString() {
assertEquals("alice", introspection.getString("username"));
assertNull(introspection.getString("usernames"));
}

@Test
public void testGetBoolean() {
assertTrue(introspection.getBoolean("active"));
assertNull(introspection.getBoolean("activate"));
}

@Test
public void testGetLong() {
assertEquals(1234, introspection.getLong("id"));
assertNull(introspection.getLong("ids"));
}

@Test
public void testGetArray() {
JsonArray array = introspection.getArray("permissions");
assertNotNull(array);
assertEquals(2, array.size());
assertEquals("read", array.getString(0));
assertEquals("write", array.getString(1));
assertNull(introspection.getArray("permit"));
}

@Test
public void testGetObject() {
JsonObject map = introspection.getObject("scopes");
assertNotNull(map);
assertEquals(1, map.size());
assertEquals("see", map.getString("scope"));
}

@Test
public void testGetNullProperty() {
assertNull(introspection.getString("custom"));
}
}

0 comments on commit f0af1bd

Please sign in to comment.