Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it easier to get well known properties from TokenIntrospection #33283

Merged
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 @@ -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"));
}
}