From 1caf702f7d4a6d5f7848f0a9f704e20e4ea89eec Mon Sep 17 00:00:00 2001 From: Javier Rojas Blum Date: Thu, 29 Nov 2018 01:02:58 -0400 Subject: [PATCH] =?UTF-8?q?oxAuth=20#830=E2=80=A9=20Client-specific=20acce?= =?UTF-8?q?ss=20token=20expiration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/xdi/oxauth/client/RegisterClient.java | 6 +- .../xdi/oxauth/client/RegisterRequest.java | 28 +++- .../ClientSpecificAccessTokenExpiration.java | 154 ++++++++++++++++++ Client/src/test/resources/testng.xml | 7 + .../model/register/RegisterRequestParam.java | 9 +- .../common/AbstractAuthorizationGrant.java | 24 ++- .../xdi/oxauth/model/common/CacheGrant.java | 25 ++- .../xdi/oxauth/model/registration/Client.java | 32 ++-- .../ws/rs/RegisterRestWebServiceImpl.java | 11 +- .../org/xdi/oxauth/service/GrantService.java | 65 ++++---- 10 files changed, 283 insertions(+), 78 deletions(-) create mode 100644 Client/src/test/java/org/xdi/oxauth/ws/rs/ClientSpecificAccessTokenExpiration.java diff --git a/Client/src/main/java/org/xdi/oxauth/client/RegisterClient.java b/Client/src/main/java/org/xdi/oxauth/client/RegisterClient.java index 17f856e2b3..390c2e565f 100644 --- a/Client/src/main/java/org/xdi/oxauth/client/RegisterClient.java +++ b/Client/src/main/java/org/xdi/oxauth/client/RegisterClient.java @@ -29,7 +29,7 @@ * @author Javier Rojas Blum * @author Yuriy Zabrovarnyy * @author Yuriy Movchan - * @version May 30, 2018 + * @version November 28, 2018 */ public class RegisterClient extends BaseClient { @@ -207,7 +207,6 @@ private RegisterResponse _exec() { if (getRequest().getClientSecretExpiresAt() != null) { requestBody.put(CLIENT_SECRET_EXPIRES_AT_.toString(), getRequest().getClientSecretExpiresAt().getTime()); } - if (getRequest().getFrontChannelLogoutSessionRequired() != null) { requestBody.put(FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED.getName(), getRequest().getFrontChannelLogoutSessionRequired()); } @@ -217,6 +216,9 @@ private RegisterResponse _exec() { if (getRequest().getAuthorizedOrigins() != null && !getRequest().getAuthorizedOrigins().isEmpty()) { requestBody.put(AUTHORIZED_ORIGINS.toString(), new JSONArray(getRequest().getAuthorizedOrigins())); } + if (getRequest().getAccessTokenLifetime() != null) { + requestBody.put(ACCESS_TOKEN_LIFETIME.toString(), getRequest().getAccessTokenLifetime()); + } if (getRequest().getScopes() != null && !getRequest().getScopes().isEmpty()) { requestBody.put(SCOPES.toString(), new JSONArray(getRequest().getScopes())); diff --git a/Client/src/main/java/org/xdi/oxauth/client/RegisterRequest.java b/Client/src/main/java/org/xdi/oxauth/client/RegisterRequest.java index 654d0bd838..1d6a5d6326 100644 --- a/Client/src/main/java/org/xdi/oxauth/client/RegisterRequest.java +++ b/Client/src/main/java/org/xdi/oxauth/client/RegisterRequest.java @@ -32,7 +32,7 @@ * * @author Javier Rojas Blum * @author Yuriy Zabrovarnyy - * @version May 30, 2018 + * @version November 28, 2018 */ public class RegisterRequest extends BaseRequest { @@ -75,6 +75,7 @@ public class RegisterRequest extends BaseRequest { private List postLogoutRedirectUris; private List requestUris; private List authorizedOrigins; + private Integer accessTokenLifetime; /** * @deprecated This param will be removed in a future version because the correct is 'scope' not 'scopes', see (rfc7591). @@ -904,6 +905,23 @@ public void setClaims(List claims) { this.claims = claims; } + /** + * Returns the Client-specific access token expiration. + * + * @return The Client-specific access token expiration. + */ + public Integer getAccessTokenLifetime() { + return accessTokenLifetime; + } + /** + * Sets the Client-specific access token expiration (in seconds). Set it to Null or Zero to use the system default value. + * + * @param accessTokenLifetime The Client-specific access token expiration. + */ + public void setAccessTokenLifetime(Integer accessTokenLifetime) { + this.accessTokenLifetime = accessTokenLifetime; + } + public String getHttpMethod() { return httpMethod; } @@ -1064,6 +1082,9 @@ public Map getParameters() { if (clientSecretExpiresAt != null) { parameters.put(CLIENT_SECRET_EXPIRES_AT_.toString(), Long.toString(clientSecretExpiresAt.getTime())); } + if (accessTokenLifetime != null) { + parameters.put(ACCESS_TOKEN_LIFETIME.toString(), accessTokenLifetime.toString()); + } // Custom params if (customAttributes != null && !customAttributes.isEmpty()) { @@ -1222,6 +1243,8 @@ public static RegisterRequest fromJson(String p_json, boolean authorizationReque result.setRequireAuthTime(requestObject.has(REQUIRE_AUTH_TIME.toString()) && requestObject.getBoolean(REQUIRE_AUTH_TIME.toString())); result.setFrontChannelLogoutUris(frontChannelLogoutUris); result.setFrontChannelLogoutSessionRequired(requestObject.optBoolean(FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED.toString())); + result.setAccessTokenLifetime(requestObject.has(ACCESS_TOKEN_LIFETIME.toString()) ? + requestObject.getInt(ACCESS_TOKEN_LIFETIME.toString()) : null); result.setDefaultMaxAge(requestObject.has(DEFAULT_MAX_AGE.toString()) ? requestObject.getInt(DEFAULT_MAX_AGE.toString()) : null); result.setAccessTokenAsJwt(requestObject.optBoolean(ACCESS_TOKEN_AS_JWT.toString())); @@ -1401,6 +1424,9 @@ public JSONObject getJSONParameters() throws JSONException { if (clientSecretExpiresAt != null) { parameters.put(CLIENT_SECRET_EXPIRES_AT_.toString(), clientSecretExpiresAt.getTime()); } + if (accessTokenLifetime != null) { + parameters.put(ACCESS_TOKEN_LIFETIME.toString(), accessTokenLifetime); + } // Custom params if (customAttributes != null && !customAttributes.isEmpty()) { for (Map.Entry entry : customAttributes.entrySet()) { diff --git a/Client/src/test/java/org/xdi/oxauth/ws/rs/ClientSpecificAccessTokenExpiration.java b/Client/src/test/java/org/xdi/oxauth/ws/rs/ClientSpecificAccessTokenExpiration.java new file mode 100644 index 0000000000..d560ead5e8 --- /dev/null +++ b/Client/src/test/java/org/xdi/oxauth/ws/rs/ClientSpecificAccessTokenExpiration.java @@ -0,0 +1,154 @@ +/* + * oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2014, Gluu + */ + +package org.xdi.oxauth.ws.rs; + +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; +import org.xdi.oxauth.BaseTest; +import org.xdi.oxauth.client.*; +import org.xdi.oxauth.model.common.AuthenticationMethod; +import org.xdi.oxauth.model.common.GrantType; +import org.xdi.oxauth.model.common.ResponseType; +import org.xdi.oxauth.model.common.SubjectType; +import org.xdi.oxauth.model.jwt.JwtClaimName; +import org.xdi.oxauth.model.register.ApplicationType; +import org.xdi.oxauth.model.util.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import static org.testng.Assert.*; + +/** + * @author Javier Rojas Blum + * @version November 28, 2018 + */ +public class ClientSpecificAccessTokenExpiration extends BaseTest { + + @Parameters({"userId", "userSecret", "redirectUris", "redirectUri", "sectorIdentifierUri"}) + @Test + public void authorizationCodeFlow( + final String userId, final String userSecret, final String redirectUris, final String redirectUri, + final String sectorIdentifierUri) throws Exception { + showTitle("authorizationCodeFlow"); + + List responseTypes = Arrays.asList( + ResponseType.CODE, + ResponseType.ID_TOKEN); + List scopes = Arrays.asList("openid", "profile", "address", "email", "phone", "user_name"); + + // 1. Register client + RegisterRequest registerRequest = new RegisterRequest(ApplicationType.WEB, "oxAuth test app", + StringUtils.spaceSeparatedToList(redirectUris)); + registerRequest.setResponseTypes(responseTypes); + registerRequest.setScope(scopes); + registerRequest.setSubjectType(SubjectType.PAIRWISE); + registerRequest.setSectorIdentifierUri(sectorIdentifierUri); + registerRequest.setAccessTokenLifetime(3); + + RegisterClient registerClient = new RegisterClient(registrationEndpoint); + registerClient.setRequest(registerRequest); + RegisterResponse registerResponse = registerClient.exec(); + + showClient(registerClient); + assertEquals(registerResponse.getStatus(), 200, "Unexpected response code: " + registerResponse.getEntity()); + assertNotNull(registerResponse.getClientId()); + assertNotNull(registerResponse.getClientSecret()); + assertNotNull(registerResponse.getRegistrationAccessToken()); + assertNotNull(registerResponse.getClientIdIssuedAt()); + assertNotNull(registerResponse.getClientSecretExpiresAt()); + + String clientId = registerResponse.getClientId(); + String clientSecret = registerResponse.getClientSecret(); + + // 2. Request authorization and receive the authorization code. + String nonce = UUID.randomUUID().toString(); + String state = UUID.randomUUID().toString(); + + AuthorizationRequest authorizationRequest = new AuthorizationRequest(responseTypes, clientId, scopes, redirectUri, nonce); + authorizationRequest.setState(state); + + AuthorizationResponse authorizationResponse = authenticateResourceOwnerAndGrantAccess( + authorizationEndpoint, authorizationRequest, userId, userSecret); + + assertNotNull(authorizationResponse.getLocation(), "The location is null"); + assertNotNull(authorizationResponse.getCode(), "The authorization code is null"); + assertNotNull(authorizationResponse.getState(), "The state is null"); + assertNotNull(authorizationResponse.getScope(), "The scope is null"); + + String scope = authorizationResponse.getScope(); + String authorizationCode = authorizationResponse.getCode(); + String idToken = authorizationResponse.getIdToken(); + + // 3. Request access token using the authorization code. + TokenRequest tokenRequest = new TokenRequest(GrantType.AUTHORIZATION_CODE); + tokenRequest.setCode(authorizationCode); + tokenRequest.setRedirectUri(redirectUri); + tokenRequest.setAuthUsername(clientId); + tokenRequest.setAuthPassword(clientSecret); + tokenRequest.setAuthenticationMethod(AuthenticationMethod.CLIENT_SECRET_BASIC); + + TokenClient tokenClient = new TokenClient(tokenEndpoint); + tokenClient.setRequest(tokenRequest); + TokenResponse tokenResponse = tokenClient.exec(); + + showClient(tokenClient); + assertEquals(tokenResponse.getStatus(), 200, "Unexpected response code: " + tokenResponse.getStatus()); + assertNotNull(tokenResponse.getEntity(), "The entity is null"); + assertNotNull(tokenResponse.getAccessToken(), "The access token is null"); + assertNotNull(tokenResponse.getExpiresIn(), "The expires in value is null"); + assertNotNull(tokenResponse.getTokenType(), "The token type is null"); + assertNotNull(tokenResponse.getRefreshToken(), "The refresh token is null"); + + String accessToken = tokenResponse.getAccessToken(); + + // 4. Request user info + { + UserInfoClient userInfoClient = new UserInfoClient(userInfoEndpoint); + UserInfoResponse userInfoResponse = userInfoClient.execUserInfo(accessToken); + + showClient(userInfoClient); + assertEquals(userInfoResponse.getStatus(), 200, "Unexpected response code: " + userInfoResponse.getStatus()); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.SUBJECT_IDENTIFIER)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.NAME)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.BIRTHDATE)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.FAMILY_NAME)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.GENDER)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.GIVEN_NAME)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.MIDDLE_NAME)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.NICKNAME)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.PICTURE)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.PREFERRED_USERNAME)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.PROFILE)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.WEBSITE)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.EMAIL)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.EMAIL_VERIFIED)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.PHONE_NUMBER)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.PHONE_NUMBER_VERIFIED)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.ADDRESS)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.LOCALE)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.ZONEINFO)); + assertNotNull(userInfoResponse.getClaim(JwtClaimName.USER_NAME)); + assertNull(userInfoResponse.getClaim("org_name")); + assertNull(userInfoResponse.getClaim("work_phone")); + } + + Thread.sleep(5000); + + // 5. Request user info + { + UserInfoClient userInfoClient = new UserInfoClient(userInfoEndpoint); + UserInfoResponse userInfoResponse = userInfoClient.execUserInfo(accessToken); + + showClient(userInfoClient); + assertEquals(userInfoResponse.getStatus(), 400, "Unexpected response code: " + userInfoResponse.getStatus()); + assertNotNull(userInfoResponse.getErrorType(), "Unexpected result: errorType not found"); + assertNotNull(userInfoResponse.getErrorDescription(), "Unexpected result: errorDescription not found"); + } + } +} \ No newline at end of file diff --git a/Client/src/test/resources/testng.xml b/Client/src/test/resources/testng.xml index a03020ea29..10bcc349d9 100644 --- a/Client/src/test/resources/testng.xml +++ b/Client/src/test/resources/testng.xml @@ -86,6 +86,13 @@ + + + + + + + diff --git a/Model/src/main/java/org/xdi/oxauth/model/register/RegisterRequestParam.java b/Model/src/main/java/org/xdi/oxauth/model/register/RegisterRequestParam.java index a4c3040c4d..e1e17c052e 100644 --- a/Model/src/main/java/org/xdi/oxauth/model/register/RegisterRequestParam.java +++ b/Model/src/main/java/org/xdi/oxauth/model/register/RegisterRequestParam.java @@ -13,7 +13,7 @@ * * @author Yuriy Zabrovarnyy * @author Javier Rojas Blum - * @version June 20, 2018 + * @version November 28, 2018 */ public enum RegisterRequestParam { @@ -259,7 +259,12 @@ public enum RegisterRequestParam { /** * Authorized JavaScript origins. */ - AUTHORIZED_ORIGINS("authorized_origins"); + AUTHORIZED_ORIGINS("authorized_origins"), + + /** + * Client-specific access token expiration. Set this value to null or zero to use the default value. + */ + ACCESS_TOKEN_LIFETIME("access_token_lifetime"); /** * Parameter name diff --git a/Server/src/main/java/org/xdi/oxauth/model/common/AbstractAuthorizationGrant.java b/Server/src/main/java/org/xdi/oxauth/model/common/AbstractAuthorizationGrant.java index f5482d401d..4949e22167 100644 --- a/Server/src/main/java/org/xdi/oxauth/model/common/AbstractAuthorizationGrant.java +++ b/Server/src/main/java/org/xdi/oxauth/model/common/AbstractAuthorizationGrant.java @@ -6,18 +6,6 @@ package org.xdi.oxauth.model.common; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.CopyOnWriteArraySet; - -import javax.inject.Inject; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xdi.oxauth.model.authorize.JwtAuthorizationRequest; @@ -27,11 +15,17 @@ import org.xdi.oxauth.model.registration.Client; import org.xdi.oxauth.util.TokenHashUtil; +import javax.inject.Inject; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CopyOnWriteArraySet; + /** * @author Yuriy Zabrovarnyy * @author Javier Rojas Blum * @author Yuriy Movchan - * @version September 6, 2017 + * @version November 28, 2018 */ public abstract class AbstractAuthorizationGrant implements IAuthorizationGrant { @@ -278,6 +272,10 @@ public String checkScopesPolicy(String requestedScopes) { @Override public AccessToken createAccessToken() { int lifetime = appConfiguration.getAccessTokenLifetime(); + // oxAuth #830 Client-specific access token expiration + if (client != null && client.getAccessTokenLifetime() != null && client.getAccessTokenLifetime() > 0) { + lifetime = client.getAccessTokenLifetime(); + } AccessToken accessToken = new AccessToken(lifetime); accessToken.setAuthMode(getAcrValues()); diff --git a/Server/src/main/java/org/xdi/oxauth/model/common/CacheGrant.java b/Server/src/main/java/org/xdi/oxauth/model/common/CacheGrant.java index 8b282bdcb5..d269c31939 100644 --- a/Server/src/main/java/org/xdi/oxauth/model/common/CacheGrant.java +++ b/Server/src/main/java/org/xdi/oxauth/model/common/CacheGrant.java @@ -1,18 +1,23 @@ -package org.xdi.oxauth.model.common; - -import java.io.Serializable; -import java.util.Date; -import java.util.Set; +/* + * oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2014, Gluu + */ -import javax.enterprise.inject.Instance; +package org.xdi.oxauth.model.common; import org.apache.commons.lang.StringUtils; import org.xdi.oxauth.model.configuration.AppConfiguration; import org.xdi.oxauth.model.registration.Client; +import javax.enterprise.inject.Instance; +import java.io.Serializable; +import java.util.Date; +import java.util.Set; + /** * @author yuriyz - * @version September 6, 2017 + * @version November 28, 2018 */ public class CacheGrant implements Serializable { @@ -65,6 +70,10 @@ private void initExpiresIn(AuthorizationGrant grant, AppConfiguration appConfigu expiresIn = grant.getAuthorizationCode().getExpiresIn(); } else { expiresIn = appConfiguration.getAccessTokenLifetime(); + // oxAuth #830 Client-specific access token expiration + if (client != null && client.getAccessTokenLifetime() != null && client.getAccessTokenLifetime() > 0) { + expiresIn = client.getAccessTokenLifetime(); + } } } @@ -193,7 +202,7 @@ public String cacheKey() { return cacheKey(client.getClientId(), authorizationCodeString, grantId); } - public static String cacheKey(String clientId, String code, String grantId ) { + public static String cacheKey(String clientId, String code, String grantId) { if (StringUtils.isBlank(code)) { return grantId; } diff --git a/Server/src/main/java/org/xdi/oxauth/model/registration/Client.java b/Server/src/main/java/org/xdi/oxauth/model/registration/Client.java index 8b1a0d663d..7283645ab3 100644 --- a/Server/src/main/java/org/xdi/oxauth/model/registration/Client.java +++ b/Server/src/main/java/org/xdi/oxauth/model/registration/Client.java @@ -7,30 +7,20 @@ package org.xdi.oxauth.model.registration; import org.apache.commons.lang.StringUtils; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; +import org.gluu.persist.model.base.CustomAttribute; +import org.gluu.site.ldap.persistence.annotation.*; +import org.xdi.oxauth.model.common.AuthenticationMethod; +import org.xdi.oxauth.model.common.GrantType; +import org.xdi.oxauth.model.common.ResponseType; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; -import org.gluu.persist.model.base.CustomAttribute; -import org.gluu.site.ldap.persistence.annotation.LdapAttribute; -import org.gluu.site.ldap.persistence.annotation.LdapAttributesList; -import org.gluu.site.ldap.persistence.annotation.LdapCustomObjectClass; -import org.gluu.site.ldap.persistence.annotation.LdapDN; -import org.gluu.site.ldap.persistence.annotation.LdapEntry; -import org.gluu.site.ldap.persistence.annotation.LdapObjectClass; -import org.xdi.oxauth.model.common.AuthenticationMethod; -import org.xdi.oxauth.model.common.GrantType; -import org.xdi.oxauth.model.common.ResponseType; - /** * @author Javier Rojas Blum - * @version May 30, 2018 + * @version November 28, 2018 */ @LdapEntry @LdapObjectClass(values = {"top", "oxAuthClient"}) @@ -185,6 +175,9 @@ public class Client implements Serializable { @LdapAttribute(name = "oxRefreshTokenLifetime") private Integer refreshTokenLifetime; + @LdapAttribute(name = "oxAccessTokenLifetime") + private Integer accessTokenLifetime; + @LdapAttributesList(name = "name", value = "values", sortByName = true) private List customAttributes = new ArrayList(); @@ -1095,6 +1088,13 @@ public void setRefreshTokenLifetime(Integer refreshTokenLifetime) { this.refreshTokenLifetime = refreshTokenLifetime; } + public Integer getAccessTokenLifetime() { + return accessTokenLifetime; + } + public void setAccessTokenLifetime(Integer accessTokenLifetime) { + this.accessTokenLifetime = accessTokenLifetime; + } + public List getCustomAttributes() { return customAttributes; } diff --git a/Server/src/main/java/org/xdi/oxauth/register/ws/rs/RegisterRestWebServiceImpl.java b/Server/src/main/java/org/xdi/oxauth/register/ws/rs/RegisterRestWebServiceImpl.java index 3f033f86fb..be2cc942ae 100644 --- a/Server/src/main/java/org/xdi/oxauth/register/ws/rs/RegisterRestWebServiceImpl.java +++ b/Server/src/main/java/org/xdi/oxauth/register/ws/rs/RegisterRestWebServiceImpl.java @@ -59,7 +59,7 @@ * @author Javier Rojas Blum * @author Yuriy Zabrovarnyy * @author Yuriy Movchan - * @version August 2, 2018 + * @version November 28, 2018 */ @Path("/") public class RegisterRestWebServiceImpl implements RegisterRestWebService { @@ -459,6 +459,10 @@ private void updateClientFromRequestObject(Client p_client, RegisterRequest requ // Custom params putCustomStuffIntoObject(p_client, requestObject.getJsonObject()); } + + if (requestObject.getAccessTokenLifetime() != null) { + p_client.setAccessTokenLifetime(requestObject.getAccessTokenLifetime()); + } } @Override @@ -496,10 +500,10 @@ public Response requestClientUpdate(String requestParams, String clientId, @Head if (externalDynamicClientRegistrationService.isEnabled()) { updateClient = externalDynamicClientRegistrationService.executeExternalUpdateClientMethods(request, client); } - + if (updateClient) { clientService.merge(client); - + oAuth2AuditLog.setScope(clientScopesToString(client)); oAuth2AuditLog.setSuccess(true); applicationAuditLogger.sendMessage(oAuth2AuditLog); @@ -633,6 +637,7 @@ private JSONObject getJSONObject(Client client, boolean authorizationRequestCust Util.addToJSONObjectIfNotNull(responseJsonObject, POST_LOGOUT_REDIRECT_URIS.toString(), client.getPostLogoutRedirectUris()); Util.addToJSONObjectIfNotNull(responseJsonObject, REQUEST_URIS.toString(), client.getRequestUris()); Util.addToJSONObjectIfNotNull(responseJsonObject, AUTHORIZED_ORIGINS.toString(), client.getAuthorizedOrigins()); + Util.addToJSONObjectIfNotNull(responseJsonObject, ACCESS_TOKEN_LIFETIME.toString(), client.getAccessTokenLifetime()); if (!Util.isNullOrEmpty(client.getJwks())) { Util.addToJSONObjectIfNotNull(responseJsonObject, JWKS.toString(), new JSONObject(client.getJwks())); } diff --git a/Server/src/main/java/org/xdi/oxauth/service/GrantService.java b/Server/src/main/java/org/xdi/oxauth/service/GrantService.java index af4cacdad5..0b1458cbf1 100644 --- a/Server/src/main/java/org/xdi/oxauth/service/GrantService.java +++ b/Server/src/main/java/org/xdi/oxauth/service/GrantService.java @@ -6,21 +6,6 @@ package org.xdi.oxauth.service; -import static org.xdi.oxauth.util.ServerUtil.isTrue; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Set; -import java.util.UUID; - -import javax.ejb.Stateless; -import javax.inject.Inject; -import javax.inject.Named; - import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.gluu.persist.PersistenceEntryManager; @@ -42,13 +27,21 @@ import org.xdi.oxauth.model.ldap.Grant; import org.xdi.oxauth.model.ldap.TokenLdap; import org.xdi.oxauth.model.ldap.TokenType; +import org.xdi.oxauth.model.registration.Client; import org.xdi.oxauth.util.TokenHashUtil; import org.xdi.service.CacheService; +import javax.ejb.Stateless; +import javax.inject.Inject; +import javax.inject.Named; +import java.util.*; + +import static org.xdi.oxauth.util.ServerUtil.isTrue; + /** * @author Yuriy Zabrovarnyy * @author Javier Rojas Blum - * @version November 11, 2016 + * @version November 28, 2018 */ @Stateless @Named @@ -137,7 +130,13 @@ public void persist(TokenLdap token) { expiration = Integer.toString(appConfiguration.getRefreshTokenLifetime()); break; case ACCESS_TOKEN: - expiration = Integer.toString(appConfiguration.getAccessTokenLifetime()); + int lifetime = appConfiguration.getAccessTokenLifetime(); + Client client = clientService.getClient(token.getClientId()); + // oxAuth #830 Client-specific access token expiration + if (client != null && client.getAccessTokenLifetime() != null && client.getAccessTokenLifetime() > 0) { + lifetime = client.getAccessTokenLifetime(); + } + expiration = Integer.toString(lifetime); break; } @@ -386,14 +385,14 @@ public void cleanUp() { private void cleanUpImpl() { // Cleaning oxAuthToken - BatchOperation tokenBatchService = new ProcessBatchOperation() { + BatchOperation tokenBatchService = new ProcessBatchOperation() { @Override - public void performAction(List entries) { + public void performAction(List entries) { auditLogging(entries); remove(entries); } }; - ldapEntryManager.findEntries(baseDn(), TokenLdap.class, getExpiredTokenFilter(), SearchScope.SUB, new String[] { "oxAuthTokenCode", "oxAuthClientId", "oxAuthScope", "oxAuthUserId" }, tokenBatchService, 0, 0, CleanerTimer.BATCH_SIZE); + ldapEntryManager.findEntries(baseDn(), TokenLdap.class, getExpiredTokenFilter(), SearchScope.SUB, new String[]{"oxAuthTokenCode", "oxAuthClientId", "oxAuthScope", "oxAuthUserId"}, tokenBatchService, 0, 0, CleanerTimer.BATCH_SIZE); // Cleaning oxAuthGrant BatchOperation grantBatchService = new ProcessBatchOperation() { @@ -403,7 +402,7 @@ public void performAction(List entries) { } }; - ldapEntryManager.findEntries(baseDn(), Grant.class, getExpiredGrantFilter(), SearchScope.SUB, new String[] { "" }, grantBatchService, 0, 0, CleanerTimer.BATCH_SIZE); + ldapEntryManager.findEntries(baseDn(), Grant.class, getExpiredGrantFilter(), SearchScope.SUB, new String[]{""}, grantBatchService, 0, 0, CleanerTimer.BATCH_SIZE); // Cleaning old oxAuthGrant // Note: This block should be removed, it is used only to delete old legacy data. @@ -413,7 +412,7 @@ public void performAction(List entries) { removeGrants(entries); } }; - ldapEntryManager.findEntries(baseDn(), Grant.class, getExpiredOldGrantFilter(), SearchScope.SUB, new String[] { "" }, oldGrantBatchService, 0, 0, CleanerTimer.BATCH_SIZE); + ldapEntryManager.findEntries(baseDn(), Grant.class, getExpiredOldGrantFilter(), SearchScope.SUB, new String[]{""}, oldGrantBatchService, 0, 0, CleanerTimer.BATCH_SIZE); } private Filter getExpiredTokenFilter() { @@ -425,20 +424,20 @@ private Filter getExpiredGrantFilter() { calendar.add(Calendar.SECOND, 60); Filter hasSubordinates = Filter.createORFilter(Filter.createEqualityFilter("numsubordinates", "0"), - Filter.createEqualityFilter("hasSubordinates", "FALSE")); - Filter creationDate = Filter.createLessOrEqualFilter("oxAuthCreation", ldapEntryManager.encodeTime(calendar.getTime())); - Filter filter = Filter.createANDFilter(creationDate, hasSubordinates); - - return filter; + Filter.createEqualityFilter("hasSubordinates", "FALSE")); + Filter creationDate = Filter.createLessOrEqualFilter("oxAuthCreation", ldapEntryManager.encodeTime(calendar.getTime())); + Filter filter = Filter.createANDFilter(creationDate, hasSubordinates); + + return filter; } private Filter getExpiredOldGrantFilter() { - Filter hasSubordinatesFilter = Filter.createORFilter(Filter.createEqualityFilter("numsubordinates", "0"), - Filter.createEqualityFilter("hasSubordinates", "FALSE")); - Filter noCreationDate = Filter.createNOTFilter(Filter.createPresenceFilter("oxAuthCreation")); - Filter filter = Filter.createANDFilter(noCreationDate, hasSubordinatesFilter); - - return filter; + Filter hasSubordinatesFilter = Filter.createORFilter(Filter.createEqualityFilter("numsubordinates", "0"), + Filter.createEqualityFilter("hasSubordinates", "FALSE")); + Filter noCreationDate = Filter.createNOTFilter(Filter.createPresenceFilter("oxAuthCreation")); + Filter filter = Filter.createANDFilter(noCreationDate, hasSubordinatesFilter); + + return filter; } private void addGrantBranch(final String p_grantId, final String p_clientId) {