diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractAuthorizationGrant.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractAuthorizationGrant.java
index a5527a51f9d..553bde0efef 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractAuthorizationGrant.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractAuthorizationGrant.java
@@ -72,6 +72,7 @@ public abstract class AbstractAuthorizationGrant implements IAuthorizationGrant
private String codeChallengeMethod;
private String claims;
private String dpopJkt;
+ private String referenceId;
private String acrValues;
private String sessionDn;
@@ -98,6 +99,14 @@ protected void init(User user, AuthorizationGrantType authorizationGrantType, Cl
this.grantId = UUID.randomUUID().toString();
}
+ public String getReferenceId() {
+ return referenceId;
+ }
+
+ public void setReferenceId(String referenceId) {
+ this.referenceId = referenceId;
+ }
+
public String getDpopJkt() {
return dpopJkt;
}
@@ -340,6 +349,7 @@ public AccessToken createAccessToken(ExecutionContext executionContext) {
accessToken.setSessionDn(getSessionDn());
accessToken.setX5ts256(CertUtils.confirmationMethodHashS256(executionContext.getCertAsPem()));
+ accessToken.setReferenceId(executionContext.getTokenReferenceId());
final String dpop = executionContext.getDpop();
if (StringUtils.isNoneBlank(dpop)) {
@@ -352,6 +362,8 @@ public AccessToken createAccessToken(ExecutionContext executionContext) {
@Override
public RefreshToken createRefreshToken(ExecutionContext context) {
+ context.generateRandomTokenReferenceId();
+
int lifetime = appConfiguration.getRefreshTokenLifetime();
if (client.getRefreshTokenLifetime() != null && client.getRefreshTokenLifetime() > 0) {
lifetime = client.getRefreshTokenLifetime();
@@ -361,15 +373,20 @@ public RefreshToken createRefreshToken(ExecutionContext context) {
refreshToken.setSessionDn(getSessionDn());
refreshToken.setDpop(context.getDpop());
+ refreshToken.setReferenceId(context.getTokenReferenceId());
+
return refreshToken;
}
@Override
public RefreshToken createRefreshToken(ExecutionContext context, int lifetime) {
+ context.generateRandomTokenReferenceId();
+
RefreshToken refreshToken = new RefreshToken(lifetime);
refreshToken.setSessionDn(getSessionDn());
refreshToken.setDpop(context.getDpop());
+ refreshToken.setReferenceId(context.getTokenReferenceId());
return refreshToken;
}
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractToken.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractToken.java
index 8d00a61930c..376231b0c51 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractToken.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AbstractToken.java
@@ -8,7 +8,6 @@
import io.jans.as.model.crypto.signature.SignatureAlgorithm;
import io.jans.as.model.util.HashUtil;
-import io.jans.as.model.util.Util;
import io.jans.as.server.model.token.HandleTokenFactory;
import io.jans.as.server.util.ServerUtil;
import io.jans.orm.annotation.AttributeName;
@@ -56,6 +55,9 @@ public abstract class AbstractToken implements Serializable, Deletable {
@AttributeName(name = "dpop")
private String dpop;
+ @AttributeName(name = "jansId")
+ private String referenceId;
+
@Expiration
private int ttl;
@@ -212,6 +214,24 @@ public synchronized void setRevoked(boolean revoked) {
this.revoked = revoked;
}
+ /**
+ * Gets reference id
+ *
+ * @return reference id
+ */
+ public String getReferenceId() {
+ return referenceId;
+ }
+
+ /**
+ * Sets reference id
+ *
+ * @param referenceId reference id
+ */
+ public void setReferenceId(String referenceId) {
+ this.referenceId = referenceId;
+ }
+
/**
* Return true
if the token has expired.
*
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrant.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrant.java
index ac382ca845a..96f1738fb67 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrant.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrant.java
@@ -122,6 +122,7 @@ private IdToken createIdTokenInternal(AuthorizationCode authorizationCode, Acces
JsonWebResponse jwr = idTokenFactory.createJwr(this, authorizationCode, accessToken, refreshToken, executionContext);
final IdToken idToken = new IdToken(jwr.toString(), jwr.getClaims().getClaimAsDate(JwtClaimName.ISSUED_AT),
jwr.getClaims().getClaimAsDate(JwtClaimName.EXPIRATION_TIME));
+ idToken.setReferenceId(executionContext.getTokenReferenceId());
if (log.isTraceEnabled())
log.trace("Created id_token: {}", idToken.getCode());
return idToken;
@@ -202,6 +203,7 @@ private void initTokenFromGrant(TokenEntity token) {
public AccessToken createAccessToken(ExecutionContext context) {
try {
context.initFromGrantIfNeeded(this);
+ context.generateRandomTokenReferenceId();
final AccessToken accessToken = super.createAccessToken(context);
if (accessToken.getExpiresIn() < 0) {
@@ -282,6 +284,7 @@ public JwtSigner createAccessTokenAsJwt(AccessToken accessToken, ExecutionContex
jwt.getClaims().setIssuedAt(accessToken.getCreationDate());
jwt.getClaims().setSubjectIdentifier(getSub());
jwt.getClaims().setClaim("x5t#S256", accessToken.getX5ts256());
+ jwt.getClaims().setClaim("jti", context.getTokenReferenceId());
final AuthzDetails authzDetails = getAuthzDetails();
if (!AuthzDetails.isEmpty(authzDetails)) {
@@ -401,6 +404,7 @@ public IdToken createIdToken(
executionContext.setClaimsAsString(getClaims());
executionContext.setNonce(nonce);
executionContext.setState(state);
+ executionContext.generateRandomTokenReferenceId();
final IdToken idToken = createIdTokenInternal(authorizationCode, accessToken, refreshToken, executionContext);
final AuthorizationGrant grant = executionContext.getGrant();
@@ -488,6 +492,7 @@ public TokenEntity asTokenEntity(AbstractToken token) {
result.setUserId(getUserId());
result.setUserDn(getUserDn());
result.setClientId(getClientId());
+ result.setReferenceId(token.getReferenceId());
result.getAttributes().setX5cs256(token.getX5ts256());
result.getAttributes().setDpopJkt(getDpopJkt());
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java
index 8a3158713cf..7c854b097c6 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/AuthorizationGrantList.java
@@ -275,6 +275,18 @@ public AuthorizationGrant getAuthorizationGrantByIdToken(String idToken) {
return null;
}
+ @Override
+ public AuthorizationGrant getAuthorizationGrantByReferenceId(String referenceId) {
+ if (StringUtils.isBlank(referenceId)) {
+ return null;
+ }
+ final TokenEntity tokenEntity = grantService.getGrantByReferenceId(referenceId);
+ if (tokenEntity != null) {
+ return asGrant(tokenEntity);
+ }
+ return null;
+ }
+
public AuthorizationGrant asGrant(TokenEntity tokenEntity) {
if (tokenEntity != null) {
final AuthorizationGrantType grantType = AuthorizationGrantType.fromString(tokenEntity.getGrantType());
@@ -353,6 +365,7 @@ public AuthorizationGrant asGrant(TokenEntity tokenEntity) {
result.setX5ts256(tokenEntity.getAttributes().getX5cs256());
result.setDpopJkt(tokenEntity.getAttributes().getDpopJkt());
result.setTokenEntity(tokenEntity);
+ result.setReferenceId(tokenEntity.getReferenceId());
if (StringUtils.isNotBlank(grantId)) {
result.setGrantId(grantId);
}
@@ -381,34 +394,40 @@ public AuthorizationGrant asGrant(TokenEntity tokenEntity) {
final AuthorizationCode code = new AuthorizationCode(tokenEntity.getTokenCode(), tokenEntity.getCreationDate(), tokenEntity.getExpirationDate());
final AuthorizationCodeGrant g = (AuthorizationCodeGrant) result;
code.setX5ts256(g.getX5ts256());
+ code.setReferenceId(tokenEntity.getReferenceId());
g.setAuthorizationCode(code);
}
break;
case REFRESH_TOKEN:
final RefreshToken refreshToken = new RefreshToken(tokenEntity.getTokenCode(), tokenEntity.getCreationDate(), tokenEntity.getExpirationDate());
refreshToken.setX5ts256(result.getX5ts256());
+ refreshToken.setReferenceId(tokenEntity.getReferenceId());
result.setRefreshTokens(Collections.singletonList(refreshToken));
break;
case ACCESS_TOKEN:
final AccessToken accessToken = new AccessToken(tokenEntity.getTokenCode(), tokenEntity.getCreationDate(), tokenEntity.getExpirationDate());
accessToken.setDpop(tokenEntity.getDpop());
accessToken.setX5ts256(result.getX5ts256());
+ accessToken.setReferenceId(tokenEntity.getReferenceId());
result.setAccessTokens(Collections.singletonList(accessToken));
break;
case TX_TOKEN:
final TxToken txToken = new TxToken(tokenEntity.getTokenCode(), tokenEntity.getCreationDate(), tokenEntity.getExpirationDate());
txToken.setDpop(tokenEntity.getDpop());
txToken.setX5ts256(result.getX5ts256());
+ txToken.setReferenceId(tokenEntity.getReferenceId());
result.setTxTokens(Collections.singletonList(txToken));
break;
case ID_TOKEN:
final IdToken idToken = new IdToken(tokenEntity.getTokenCode(), tokenEntity.getCreationDate(), tokenEntity.getExpirationDate());
idToken.setX5ts256(result.getX5ts256());
+ idToken.setReferenceId(tokenEntity.getReferenceId());
result.setIdToken(idToken);
break;
case LONG_LIVED_ACCESS_TOKEN:
final AccessToken longLivedAccessToken = new AccessToken(tokenEntity.getTokenCode(), tokenEntity.getCreationDate(), tokenEntity.getExpirationDate());
longLivedAccessToken.setX5ts256(result.getX5ts256());
+ longLivedAccessToken.setReferenceId(tokenEntity.getReferenceId());
result.setLongLivedAccessToken(longLivedAccessToken);
break;
}
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/ExecutionContext.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/ExecutionContext.java
index 93e7e3f7c16..94c4b272fe7 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/ExecutionContext.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/ExecutionContext.java
@@ -18,6 +18,7 @@
import io.jans.as.server.model.audit.OAuth2AuditLog;
import io.jans.model.custom.script.conf.CustomScriptConfiguration;
import io.jans.model.token.TokenEntity;
+import io.jans.util.IdUtil;
import jakarta.faces.context.ExternalContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -65,6 +66,7 @@ public class ExecutionContext {
private String nonce;
private String state;
+ private String tokenReferenceId = IdUtil.randomShortUUID();
private boolean includeIdTokenClaims;
@@ -158,6 +160,19 @@ public static ExecutionContext of(ExecutionContext context) {
return executionContext;
}
+ public String generateRandomTokenReferenceId() {
+ tokenReferenceId = IdUtil.randomShortUUID();
+ return tokenReferenceId;
+ }
+
+ public String getTokenReferenceId() {
+ return tokenReferenceId;
+ }
+
+ public void setTokenReferenceId(String tokenReferenceId) {
+ this.tokenReferenceId = tokenReferenceId;
+ }
+
public ExecutionContext copy() {
return of(this);
}
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/IAuthorizationGrantList.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/IAuthorizationGrantList.java
index 0e3589655cf..32ed6363cd2 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/IAuthorizationGrantList.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/common/IAuthorizationGrantList.java
@@ -47,6 +47,8 @@ public interface IAuthorizationGrantList {
AuthorizationGrant getAuthorizationGrantByIdToken(String idToken);
+ AuthorizationGrant getAuthorizationGrantByReferenceId(String idToken);
+
CIBAGrant getCIBAGrant(String authReqId);
DeviceCodeGrant createDeviceGrant(DeviceAuthorizationCacheControl data, User user);
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/IdTokenFactory.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/IdTokenFactory.java
index b665a6608b0..ac16c678af9 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/IdTokenFactory.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/token/IdTokenFactory.java
@@ -148,7 +148,7 @@ private void fillClaims(JsonWebResponse jwr,
jwr.getClaims().setExpirationTime(expiration);
jwr.getClaims().setIssuedAt(issuedAt);
- jwr.setClaim("random", UUID.randomUUID().toString()); // provided uniqueness of id_token for same RP requests, oxauth: 1493
+ jwr.setClaim("jti", executionContext.getTokenReferenceId()); // provided uniqueness of id_token for same RP requests, oxauth: 1493
if (executionContext.getPreProcessing() != null) {
executionContext.getPreProcessing().apply(jwr);
diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/GrantService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/GrantService.java
index 40d32f0e667..2e0565dda24 100644
--- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/GrantService.java
+++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/GrantService.java
@@ -207,6 +207,22 @@ public TokenEntity getGrantByCode(String code) {
}
}
+ public TokenEntity getGrantByReferenceId(String referenceId) {
+ try {
+ final List grants = persistenceEntryManager.findEntries(tokenBaseDn(), TokenEntity.class, Filter.createEqualityFilter("jansId", referenceId));
+ if (grants.size() > 1) {
+ log.error("Found more then one tokens by referenceId {}", referenceId);
+ return null;
+ }
+ if (grants.size() == 1) {
+ return grants.get(0);
+ }
+ } catch (Exception e) {
+ logException(e);
+ }
+ return null;
+ }
+
private void logException(Exception e) {
if (isTrue(appConfiguration.getLogNotFoundEntityAsError())) {
log.error(e.getMessage(), e);
diff --git a/jans-core/service/src/main/java/io/jans/model/token/TokenEntity.java b/jans-core/service/src/main/java/io/jans/model/token/TokenEntity.java
index d7374065eed..5b8cc5d2469 100644
--- a/jans-core/service/src/main/java/io/jans/model/token/TokenEntity.java
+++ b/jans-core/service/src/main/java/io/jans/model/token/TokenEntity.java
@@ -6,16 +6,11 @@
package io.jans.model.token;
+import io.jans.orm.annotation.*;
+
import java.io.Serializable;
import java.util.Date;
-import io.jans.orm.annotation.AttributeName;
-import io.jans.orm.annotation.DN;
-import io.jans.orm.annotation.DataEntry;
-import io.jans.orm.annotation.Expiration;
-import io.jans.orm.annotation.JsonObject;
-import io.jans.orm.annotation.ObjectClass;
-
/**
* @author Yuriy Zabrovarnyy
* @author Javier Rojas Blum
@@ -68,6 +63,8 @@ public class TokenEntity implements Serializable {
private String claims;
@AttributeName(name = "tknBndCnf")
private String tokenBindingHash;
+ @AttributeName(name = "jansId")
+ private String referenceId;
@AttributeName(name = "acr")
private String authMode;
@@ -84,6 +81,14 @@ public class TokenEntity implements Serializable {
@AttributeName(name = "dpop")
private String dpop;
+ public String getReferenceId() {
+ return referenceId;
+ }
+
+ public void setReferenceId(String referenceId) {
+ this.referenceId = referenceId;
+ }
+
public TokenAttributes getAttributes() {
if (attributes == null) {
attributes = new TokenAttributes();
diff --git a/jans-core/util/src/main/java/io/jans/util/IdUtil.java b/jans-core/util/src/main/java/io/jans/util/IdUtil.java
new file mode 100644
index 00000000000..035a2b82f0b
--- /dev/null
+++ b/jans-core/util/src/main/java/io/jans/util/IdUtil.java
@@ -0,0 +1,34 @@
+package io.jans.util;
+
+import java.nio.ByteBuffer;
+import java.util.Base64;
+import java.util.Random;
+import java.util.UUID;
+
+/**
+ * @author Yuriy Z
+ */
+public class IdUtil {
+
+ // we are ok to have not secured random
+ private static final Random RANDOM = new Random();
+
+ private IdUtil() {
+ }
+
+ public static String randomShortUUID() {
+ return toShortUUID(UUID.randomUUID());
+ }
+
+ public static String toShortUUID(UUID uuid) {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(16);
+ byteBuffer.putLong(uuid.getMostSignificantBits());
+ byteBuffer.putLong(uuid.getLeastSignificantBits());
+ return Base64.getEncoder().withoutPadding().encodeToString(byteBuffer.array())
+ .replace("/", randomChar()).replace("+", randomChar());
+ }
+
+ private static String randomChar() {
+ return (char) (RANDOM.nextInt(26) + 'a') + "";
+ }
+}
diff --git a/jans-core/util/src/test/java/io/jans/util/IdUtilTest.java b/jans-core/util/src/test/java/io/jans/util/IdUtilTest.java
new file mode 100644
index 00000000000..101293fd752
--- /dev/null
+++ b/jans-core/util/src/test/java/io/jans/util/IdUtilTest.java
@@ -0,0 +1,24 @@
+package io.jans.util;
+
+import org.testng.annotations.Test;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+/**
+ * @author Yuriy Z
+ */
+public class IdUtilTest {
+
+ @Test
+ public void shortUuid_lenthMustBe22() {
+ assertEquals(22, IdUtil.randomShortUUID().length());
+ }
+
+ @Test(enabled = false)
+ public void shortUuid_generateALotIdsAndPrintThem() {
+ for (int i = 0; i < 100000; i++) {
+ final String shortUUID = IdUtil.randomShortUUID();
+ System.out.println(shortUUID + " length: " + shortUUID.length());
+ };
+ }
+}
diff --git a/jans-linux-setup/jans_setup/schema/jans_schema.json b/jans-linux-setup/jans_setup/schema/jans_schema.json
index 79c8f7f54d8..ef1c7677229 100644
--- a/jans-linux-setup/jans_setup/schema/jans_schema.json
+++ b/jans-linux-setup/jans_setup/schema/jans_schema.json
@@ -4498,7 +4498,8 @@
"ssnId",
"attr",
"tknBndCnf",
- "dpop"
+ "dpop",
+ "jansId"
],
"must": [
"objectclass"