Skip to content

Commit

Permalink
Merge pull request #34883 from sberyozkin/oidc_credentials_jwt_extra_…
Browse files Browse the repository at this point in the history
…claims

Allow adding more claims to OIDC JWT authentication token
  • Loading branch information
sberyozkin authored Jul 20, 2023
2 parents b229350 + 38dc01f commit 14d455e
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.nio.file.Path;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;

Expand Down Expand Up @@ -299,6 +301,12 @@ public static class Jwt {
@ConfigItem
public Optional<String> subject = Optional.empty();

/**
* Additional claims.
*/
@ConfigItem
public Map<String, String> claims = new HashMap<>();

/**
* Signature algorithm, also used for the {@link #keyFile} property.
* Supported values: RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, HS256, HS384, HS512.
Expand Down Expand Up @@ -368,6 +376,14 @@ public void setKeyFile(String keyFile) {
this.keyFile = Optional.of(keyFile);
}

public Map<String, String> getClaims() {
return claims;
}

public void setClaims(Map<String, String> claims) {
this.claims = claims;
}

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ public static Key clientJwtKey(Credentials creds) {
public static String signJwtWithKey(OidcCommonConfig oidcConfig, String tokenRequestUri, Key key) {
// 'jti' and 'iat' claims are created by default, 'iat' - is set to the current time
JwtSignatureBuilder builder = Jwt
.claims(additionalClaims(oidcConfig.credentials.jwt.getClaims()))
.issuer(oidcConfig.credentials.jwt.issuer.orElse(oidcConfig.clientId.get()))
.subject(oidcConfig.credentials.jwt.subject.orElse(oidcConfig.clientId.get()))
.audience(oidcConfig.credentials.jwt.getAudience().isPresent()
Expand All @@ -367,6 +368,11 @@ public static String signJwtWithKey(OidcCommonConfig oidcConfig, String tokenReq
}
}

@SuppressWarnings({ "unchecked", "rawtypes" })
private static Map<String, Object> additionalClaims(Map<String, String> claims) {
return (Map) claims;
}

private static SignatureAlgorithm getSignatureAlgorithm(Credentials credentials, SignatureAlgorithm defaultAlgorithm) {
if (credentials.jwt.getSignatureAlgorithm().isPresent()) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.util.Base64;
import java.util.Optional;
import java.util.StringTokenizer;

import org.junit.jupiter.api.Test;

import io.vertx.core.json.JsonObject;
import io.vertx.core.net.ProxyOptions;

public class OidcCommonUtilsTest {
Expand Down Expand Up @@ -42,4 +48,53 @@ public void testProxyOptionsWithHostWithScheme() throws Exception {
assertEquals("user", options.getUsername());
assertEquals("password", options.getPassword());
}

@Test
public void testJwtTokenWithScope() throws Exception {
OidcCommonConfig cfg = new OidcCommonConfig();
cfg.setClientId("client");
cfg.credentials.jwt.claims.put("scope", "read,write");
PrivateKey key = KeyPairGenerator.getInstance("RSA").generateKeyPair().getPrivate();
String jwt = OidcCommonUtils.signJwtWithKey(cfg, "http://localhost", key);
JsonObject json = decodeJwtContent(jwt);
String scope = json.getString("scope");
assertEquals("read,write", scope);
}

public static JsonObject decodeJwtContent(String jwt) {
String encodedContent = getJwtContentPart(jwt);
if (encodedContent == null) {
return null;
}
return decodeAsJsonObject(encodedContent);
}

public static String getJwtContentPart(String jwt) {
StringTokenizer tokens = new StringTokenizer(jwt, ".");
// part 1: skip the token headers
tokens.nextToken();
if (!tokens.hasMoreTokens()) {
return null;
}
// part 2: token content
String encodedContent = tokens.nextToken();

// let's check only 1 more signature part is available
if (tokens.countTokens() != 1) {
return null;
}
return encodedContent;
}

private static JsonObject decodeAsJsonObject(String encodedContent) {
try {
return new JsonObject(base64UrlDecode(encodedContent));
} catch (IllegalArgumentException ex) {
return null;
}
}

private static String base64UrlDecode(String encodedContent) {
return new String(Base64.getUrlDecoder().decode(encodedContent), StandardCharsets.UTF_8);
}
}

0 comments on commit 14d455e

Please sign in to comment.