Skip to content

Commit

Permalink
feat(jans-auth-server): added token status list endpoint and status c…
Browse files Browse the repository at this point in the history
…laim with index.

#8562
Signed-off-by: YuriyZ <yzabrovarniy@gmail.com>
  • Loading branch information
yuriyz committed Jun 5, 2024
1 parent d32bca8 commit d5b3ba9
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public static void parse(String json, OpenIdConfigurationResponse response) {
response.setIssuer(jsonObj.optString(ISSUER, null));
response.setAuthorizationEndpoint(jsonObj.optString(AUTHORIZATION_ENDPOINT, null));
response.setAuthorizationChallengeEndpoint(jsonObj.optString(AUTHORIZATION_CHALLENGE_ENDPOINT, null));
response.setTokenStatusListEndpoint(jsonObj.optString(TOKEN_STATUS_LIST_ENDPOINT, null));
response.setTokenEndpoint(jsonObj.optString(TOKEN_ENDPOINT, null));
response.setRevocationEndpoint(jsonObj.optString(REVOCATION_ENDPOINT, null));
response.setSessionRevocationEndpoint(jsonObj.optString(SESSION_REVOCATION_ENDPOINT, null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class OpenIdConfigurationResponse extends BaseResponse implements Seriali
private String issuer;
private String authorizationEndpoint;
private String authorizationChallengeEndpoint;
private String tokenStatusListEndpoint;
private String tokenEndpoint;
private String revocationEndpoint;
private String sessionRevocationEndpoint;
Expand Down Expand Up @@ -239,6 +240,24 @@ public void setAuthorizationChallengeEndpoint(String authorizationChallengeEndpo
this.authorizationChallengeEndpoint = authorizationChallengeEndpoint;
}

/**
* Gets token status list
*
* @return token status list
*/
public String getTokenStatusListEndpoint() {
return tokenStatusListEndpoint;
}

/**
* Sets token status list
*
* @param tokenStatusListEndpoint token status list
*/
public void setTokenStatusListEndpoint(String tokenStatusListEndpoint) {
this.tokenStatusListEndpoint = tokenStatusListEndpoint;
}

/**
* Returns the URL of the Token endpoint.
*
Expand Down Expand Up @@ -1300,6 +1319,7 @@ public String toString() {
"issuer='" + issuer + '\'' +
", authorizationEndpoint='" + authorizationEndpoint + '\'' +
", authorizationChallengeEndpoint='" + authorizationChallengeEndpoint + '\'' +
", tokenStatusListEndpoint='" + tokenStatusListEndpoint + '\'' +
", tokenEndpoint='" + tokenEndpoint + '\'' +
", revocationEndpoint='" + revocationEndpoint + '\'' +
", userInfoEndpoint='" + userInfoEndpoint + '\'' +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public abstract class BaseTest {
protected String gluuConfigurationEndpoint;
protected String tokenEndpoint;
protected String tokenRevocationEndpoint;
protected String tokenStatusListEndpoint;
protected String userInfoEndpoint;
protected String clientInfoEndpoint;
protected String checkSessionIFrame;
Expand Down Expand Up @@ -304,6 +305,14 @@ public void setAuthorizationChallengeEndpoint(String authorizationChallengeEndpo
this.authorizationChallengeEndpoint = authorizationChallengeEndpoint;
}

public String getTokenStatusListEndpoint() {
return tokenStatusListEndpoint;
}

public void setTokenStatusListEndpoint(String tokenStatusListEndpoint) {
this.tokenStatusListEndpoint = tokenStatusListEndpoint;
}

public String getTokenEndpoint() {
return tokenEndpoint;
}
Expand Down Expand Up @@ -1000,6 +1009,7 @@ public void discovery(ITestContext context) throws Exception {

authorizationEndpoint = response.getAuthorizationEndpoint();
authorizationChallengeEndpoint = response.getAuthorizationChallengeEndpoint();
tokenStatusListEndpoint = response.getTokenStatusListEndpoint();
tokenEndpoint = response.getTokenEndpoint();
tokenRevocationEndpoint = response.getRevocationEndpoint();
userInfoEndpoint = response.getUserInfoEndpoint();
Expand All @@ -1024,6 +1034,7 @@ public void discovery(ITestContext context) throws Exception {

authorizationEndpoint = context.getCurrentXmlTest().getParameter("authorizationEndpoint");
authorizationChallengeEndpoint = context.getCurrentXmlTest().getParameter("authorizationChallengeEndpoint");
tokenStatusListEndpoint = context.getCurrentXmlTest().getParameter("tokenStatusListEndpoint");
tokenEndpoint = context.getCurrentXmlTest().getParameter("tokenEndpoint");
tokenRevocationEndpoint = context.getCurrentXmlTest().getParameter("tokenRevocationEndpoint");
userInfoEndpoint = context.getCurrentXmlTest().getParameter("userInfoEndpoint");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ public enum FeatureFlagType {
@DocFeatureFlag(description = "Enable/Disable global token revocation endpoint",
defaultValue = "Enabled")
GLOBAL_TOKEN_REVOCATION("global_token_revocation"),
@DocFeatureFlag(description = "Enable/Disable token status list endpoint",
defaultValue = "Enabled")
TOKEN_STATUS_LIST("token_status_list"),
@DocFeatureFlag(description = "Enable/Disable active session endpoint",
defaultValue = "Enabled")
ACTIVE_SESSION("active_session"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ private Constants() {

public static final String CONTENT_TYPE_APPLICATION_JSON_UTF_8 = "application/json;charset=UTF-8";

public static final String CONTENT_TYPE_STATUSLIST_JSON = "application/statuslist+json";
public static final String CONTENT_TYPE_STATUSLIST_JWT = "application/statuslist+jwt";

public static final String AUTHORIZATION = "Authorization";
public static final String AUTHORIZATION_BEARER = "Authorization: Bearer ";
public static final String AUTHORIZATION_BASIC = "Authorization: Basic ";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ private ConfigurationResponseClaim() {
public static final String ISSUER = "issuer";
public static final String AUTHORIZATION_ENDPOINT = "authorization_endpoint";
public static final String AUTHORIZATION_CHALLENGE_ENDPOINT = "authorization_challenge_endpoint";
public static final String TOKEN_STATUS_LIST_ENDPOINT = "token_status_list_endpoint";
public static final String TOKEN_ENDPOINT = "token_endpoint";
public static final String REVOCATION_ENDPOINT = "revocation_endpoint";
public static final String SESSION_REVOCATION_ENDPOINT = "session_revocation_endpoint";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,15 @@ public String errorAsJson(IErrorType type, String reason) {
return error.toJSonString();
}

public boolean isFeatureFlagEnabled(FeatureFlagType flagType) {
final Set<FeatureFlagType> enabledFlags = FeatureFlagType.from(appConfiguration);
if (enabledFlags.isEmpty()) { // no restrictions
return true;
}

return enabledFlags.contains(flagType);
}

public void validateFeatureEnabled(FeatureFlagType flagType) {
final Set<FeatureFlagType> enabledFlags = FeatureFlagType.from(appConfiguration);
if (enabledFlags.isEmpty()) { // no restrictions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import io.jans.as.server.service.external.context.ExternalIntrospectionContext;
import io.jans.as.server.service.external.context.ExternalUpdateTokenContext;
import io.jans.as.server.service.stat.StatService;
import io.jans.as.server.service.token.StatusListService;
import io.jans.as.server.util.ServerUtil;
import io.jans.as.server.util.TokenHashUtil;
import io.jans.model.metric.MetricType;
Expand Down Expand Up @@ -102,6 +103,9 @@ public abstract class AuthorizationGrant extends AbstractAuthorizationGrant {
@Inject
private ErrorResponseFactory errorResponseFactory;

@Inject
private StatusListService statusListService;

private boolean isCachedWithNoPersistence = false;

protected AuthorizationGrant() {
Expand Down Expand Up @@ -301,6 +305,7 @@ public JwtSigner createAccessTokenAsJwt(AccessToken accessToken, ExecutionContex
}

Audience.setAudience(jwt.getClaims(), getClient());
statusListService.addStatusClaimWithIndex(jwt);

if (isTrue(client.getAttributes().getRunIntrospectionScriptBeforeJwtCreation())) {
runIntrospectionScriptAndInjectValuesIntoJwt(jwt, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import io.jans.as.server.service.external.ExternalUpdateTokenService;
import io.jans.as.server.service.external.context.DynamicScopeExternalContext;
import io.jans.as.server.service.external.context.ExternalUpdateTokenContext;
import io.jans.as.server.service.token.StatusListService;
import io.jans.model.JansAttribute;
import io.jans.model.custom.script.conf.CustomScriptConfiguration;
import io.jans.model.custom.script.type.auth.PersonAuthenticationType;
Expand Down Expand Up @@ -98,6 +99,9 @@ public class IdTokenFactory {
@Inject
private DateFormatterService dateFormatterService;

@Inject
private StatusListService statusListService;

private void setAmrClaim(JsonWebResponse jwt, String acrValues) {
List<String> amrList = Lists.newArrayList();

Expand Down Expand Up @@ -158,6 +162,8 @@ private void fillClaims(JsonWebResponse jwr,
jwr.setClaim("sid", session.getOutsideSid());
}

statusListService.addStatusClaimWithIndex(jwr);

addTokenExchangeClaims(jwr, executionContext, session);

String acrValues = authorizationGrant.getAcrValues();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public JSONObject process() {
jsonObj.put(ARCHIVED_JWKS_URI, appConfiguration.getArchivedJwksUri());
jsonObj.put(CHECK_SESSION_IFRAME, appConfiguration.getCheckSessionIFrame());

if (appConfiguration.isFeatureEnabled(FeatureFlagType.TOKEN_STATUS_LIST))
jsonObj.put(TOKEN_STATUS_LIST_ENDPOINT, getTokenStatusListEndpoint());
if (appConfiguration.isFeatureEnabled(FeatureFlagType.REVOKE_TOKEN))
jsonObj.put(REVOCATION_ENDPOINT, appConfiguration.getTokenRevocationEndpoint());
if (appConfiguration.isFeatureEnabled(FeatureFlagType.REVOKE_SESSION))
Expand Down Expand Up @@ -223,9 +225,16 @@ public JSONObject process() {
return jsonObj;
}

public String endpointUrl(String path) {
return endpointUrl(appConfiguration.getEndSessionEndpoint(), path);
}

public static String endpointUrl(String endSessionEndpoint, String path) {
return StringUtils.replace(endSessionEndpoint, "/end_session", path);
}

private String endpointUrl(String path) {
return StringUtils.replace(appConfiguration.getEndSessionEndpoint(), "/end_session", path);
public String getTokenStatusListEndpoint() {
return endpointUrl("/token_status_list");
}


Expand Down Expand Up @@ -279,16 +288,18 @@ private void addMtlsAliases(JSONObject jsonObj) {
aliases.put(AUTHORIZATION_CHALLENGE_ENDPOINT, appConfiguration.getMtlsAuthorizationChallengeEndpoint());
if (StringUtils.isNotBlank(appConfiguration.getMtlsTokenEndpoint()))
aliases.put(TOKEN_ENDPOINT, appConfiguration.getMtlsTokenEndpoint());
if (appConfiguration.isFeatureEnabled(FeatureFlagType.TOKEN_STATUS_LIST) && StringUtils.isNotBlank(appConfiguration.getMtlsEndSessionEndpoint()))
aliases.put(TOKEN_STATUS_LIST_ENDPOINT, endpointUrl(appConfiguration.getMtlsEndSessionEndpoint(), "/token_status_list"));
if (StringUtils.isNotBlank(appConfiguration.getMtlsJwksUri()))
aliases.put(JWKS_URI, appConfiguration.getMtlsJwksUri());
if (StringUtils.isNotBlank(appConfiguration.getMtlsCheckSessionIFrame()))
aliases.put(CHECK_SESSION_IFRAME, appConfiguration.getMtlsCheckSessionIFrame());
if (appConfiguration.isFeatureEnabled(FeatureFlagType.REVOKE_TOKEN) && StringUtils.isNotBlank(appConfiguration.getMtlsTokenRevocationEndpoint()))
aliases.put(REVOCATION_ENDPOINT, appConfiguration.getMtlsTokenRevocationEndpoint());
if (appConfiguration.isFeatureEnabled(FeatureFlagType.REVOKE_SESSION) && StringUtils.isNotBlank(appConfiguration.getMtlsEndSessionEndpoint()))
aliases.put(SESSION_REVOCATION_ENDPOINT, StringUtils.replace(appConfiguration.getMtlsEndSessionEndpoint(), "/end_session", "/revoke_session"));
aliases.put(SESSION_REVOCATION_ENDPOINT, endpointUrl(appConfiguration.getMtlsEndSessionEndpoint(), "/revoke_session"));
if (appConfiguration.isFeatureEnabled(FeatureFlagType.GLOBAL_TOKEN_REVOCATION) && StringUtils.isNotBlank(appConfiguration.getMtlsEndSessionEndpoint()))
aliases.put(GLOBAL_TOKEN_REVOCATION_ENDPOINT, StringUtils.replace(appConfiguration.getMtlsEndSessionEndpoint(), "/end_session", "/global-token-revocation"));
aliases.put(GLOBAL_TOKEN_REVOCATION_ENDPOINT, endpointUrl(appConfiguration.getMtlsEndSessionEndpoint(), "/global-token-revocation"));
if (appConfiguration.isFeatureEnabled(FeatureFlagType.USERINFO) && StringUtils.isNotBlank(appConfiguration.getMtlsUserInfoEndpoint()))
aliases.put(USER_INFO_ENDPOINT, appConfiguration.getMtlsUserInfoEndpoint());
if (appConfiguration.isFeatureEnabled(FeatureFlagType.CLIENTINFO) && StringUtils.isNotBlank(appConfiguration.getMtlsClientInfoEndpoint()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.jans.as.server.session.ws.rs.EndSessionRestWebServiceImpl;
import io.jans.as.server.session.ws.rs.SessionRestWebService;
import io.jans.as.server.ssa.ws.rs.SsaRestWebServiceImpl;
import io.jans.as.server.status.ws.rs.StatusListRestWebService;
import io.jans.as.server.token.ws.rs.TokenRestWebServiceImpl;
import io.jans.as.server.uma.ws.rs.*;
import io.jans.as.server.userinfo.ws.rs.UserInfoRestWebServiceImpl;
Expand Down Expand Up @@ -56,6 +57,7 @@ public Set<Class<?>> getClasses() {
classes.add(RevokeRestWebServiceImpl.class);
classes.add(RevokeSessionRestWebService.class);
classes.add(GlobalTokenRevocationRestWebService.class);
classes.add(StatusListRestWebService.class);
classes.add(JwkRestWebServiceImpl.class);
classes.add(ArchivedJwksWebServiceImpl.class);
classes.add(IntrospectionWebService.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.jans.as.server.service.token;

import io.jans.as.model.common.FeatureFlagType;
import io.jans.as.model.error.ErrorResponseFactory;
import io.jans.as.model.token.JsonWebResponse;
import io.jans.as.server.service.DiscoveryService;
import io.jans.orm.PersistenceEntryManager;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.ws.rs.core.Response;
import org.json.JSONObject;
import org.slf4j.Logger;

import java.util.concurrent.atomic.AtomicInteger;

/**
* @author Yuriy Z
*/
@Named
public class StatusListService {

@Inject
private Logger log;

@Inject
private ErrorResponseFactory errorResponseFactory;

@Inject
private DiscoveryService discoveryService;

@Inject
private PersistenceEntryManager persistenceEntryManager;

private final AtomicInteger localIndex = new AtomicInteger(0);

public Response requestStatusList(String acceptHeader) {
log.debug("Attempting to request token_status_list, acceptHeader: {} ...", acceptHeader);

errorResponseFactory.validateFeatureEnabled(FeatureFlagType.TOKEN_STATUS_LIST);

// todo WIP
throw new UnsupportedOperationException("WIP");
}

public void addStatusClaimWithIndex(JsonWebResponse jwr) {
if (!errorResponseFactory.isFeatureFlagEnabled(FeatureFlagType.TOKEN_STATUS_LIST)) {
log.trace("Skipped status claim addition because {} feature flag is disabled.", FeatureFlagType.TOKEN_STATUS_LIST.getValue());
return;
}

final JSONObject indexAndUri = new JSONObject();
indexAndUri.put("idx", nextIndex());
indexAndUri.put("uri", discoveryService.getTokenStatusListEndpoint());

final JSONObject statusList = new JSONObject();
statusList.put("status_list", indexAndUri);

jwr.getClaims().setClaim("status", statusList);
}

private int nextIndex() {
final int nextIndex = localIndex.incrementAndGet();
// todo
return nextIndex;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.jans.as.server.status.ws.rs;

import io.jans.as.server.service.token.StatusListService;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Response;
import org.slf4j.Logger;

import static io.jans.as.model.config.Constants.CONTENT_TYPE_STATUSLIST_JSON;
import static io.jans.as.model.config.Constants.CONTENT_TYPE_STATUSLIST_JWT;

/**
* @author Yuriy Z
*/
@Path("/")
public class StatusListRestWebService {

@Inject
private Logger log;

@Inject
private StatusListService statusService;

@GET
@Path("/token_status_list")
@Consumes({CONTENT_TYPE_STATUSLIST_JSON, CONTENT_TYPE_STATUSLIST_JWT})
@Produces({CONTENT_TYPE_STATUSLIST_JSON, CONTENT_TYPE_STATUSLIST_JWT})
public Response requestStatusList(@HeaderParam("Accept") String acceptHeader) {
try {
return statusService.requestStatusList(acceptHeader);
} catch (WebApplicationException e) {
throw e;
} catch (Exception e) {
log.error(e.getMessage(), e);
return Response.status(500).build();
}
}
}

0 comments on commit d5b3ba9

Please sign in to comment.