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

Improvements - Pushed Authorization Request support for IS #2108

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 @@ -2451,7 +2451,11 @@ private void handleOIDCRequestObject(OAuthMessage oAuthMessage, OAuthAuthzReques

private void validateRequestObjectParams(OAuthAuthzRequest oauthRequest) throws RequestObjectException {

// With in the same request it can not be used both request parameter and request_uri parameter.
/*
With in the same request it can not be used both request parameter and request_uri parameter.
This validation is skipped when ALLOW_REQUEST_URI_AND_REQUEST_OBJECT_IN_REQUEST parameter is set to true,
where both request param and request_uri param will be present in the parameter map.
*/
if (Boolean.parseBoolean(oauthRequest.getParam(
OAuthConstants.ALLOW_REQUEST_URI_AND_REQUEST_OBJECT_IN_REQUEST))) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
* Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
* Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com).
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
Expand All @@ -18,6 +18,8 @@

package org.wso2.carbon.identity.oauth.endpoint.par;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.interceptor.InInterceptors;
import org.json.JSONObject;
import org.wso2.carbon.identity.oauth.client.authn.filter.OAuthClientAuthenticatorProxy;
Expand Down Expand Up @@ -56,6 +58,8 @@
@InInterceptors(classes = OAuthClientAuthenticatorProxy.class)
public class OAuth2ParEndpoint {

private static final Log log = LogFactory.getLog(OAuth2ParEndpoint.class);

@POST
@Path("/")
@Consumes("application/x-www-form-urlencoded")
Expand All @@ -67,7 +71,7 @@ public Response par(@Context HttpServletRequest request, @Context HttpServletRes
handleValidation(request, params);
Map<String, String> parameters = transformParams(params);
ParAuthResponseData parAuthResponseData =
EndpointUtil.getParAuthService().generateParAuthResponse(parameters);
EndpointUtil.getParAuthService().handleParAuthRequest(parameters);
return createAuthResponse(response, parAuthResponseData);
} catch (ParClientException e) {
return handleParClientException(e);
Expand All @@ -94,9 +98,9 @@ private Map<String, String> transformParams(MultivaluedMap<String, String> param
private Response createAuthResponse(HttpServletResponse response, ParAuthResponseData parAuthResponseData) {

response.setContentType(MediaType.APPLICATION_JSON);
net.minidev.json.JSONObject parAuthResponse = new net.minidev.json.JSONObject();
org.json.JSONObject parAuthResponse = new org.json.JSONObject();
parAuthResponse.put(OAuthConstants.OAuth20Params.REQUEST_URI,
ParConstants.REQUEST_URI_HEAD + parAuthResponseData.getReqUriUUID());
ParConstants.REQUEST_URI_PREFIX + parAuthResponseData.getrequestURIReference());
parAuthResponse.put(ParConstants.EXPIRES_IN, parAuthResponseData.getExpiryTime());
Response.ResponseBuilder responseBuilder = Response.status(HttpServletResponse.SC_CREATED);
return responseBuilder.entity(parAuthResponse.toString()).build();
Expand All @@ -115,6 +119,7 @@ private Response handleParClientException(ParClientException exception) {
} else {
responseBuilder = Response.status(HttpServletResponse.SC_BAD_REQUEST);
}
log.debug("PAR client Exception: ", exception);
return responseBuilder.entity(parErrorResponse.toString()).build();
}

Expand All @@ -125,6 +130,7 @@ private Response handleParCoreException(ParCoreException parCoreException) {
parErrorResponse.put(OAuthConstants.OAUTH_ERROR_DESCRIPTION, parCoreException.getMessage());

Response.ResponseBuilder respBuilder = Response.status(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
log.debug("PAR Server Exception: ", parCoreException);
return respBuilder.entity(parErrorResponse.toString()).build();
}

Expand All @@ -144,6 +150,7 @@ private void handleValidation(HttpServletRequest request, MultivaluedMap<String,

private boolean isRequestUriProvided(MultivaluedMap<String, String> params) {

return params.containsKey(OAuthConstants.OAuth20Params.REQUEST_URI);
return params.containsKey(OAuthConstants.OAuth20Params.REQUEST_URI) &&
!params.get(OAuthConstants.OAuth20Params.REQUEST_URI).isEmpty();
}
}
18 changes: 13 additions & 5 deletions components/org.wso2.carbon.identity.oauth.par/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.application.authentication.framework</artifactId>
</dependency>

<dependency>
<groupId>org.apache.ws.commons.axiom.wso2</groupId>
<artifactId>axiom</artifactId>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -104,23 +107,28 @@
org.wso2.carbon.identity.oauth.par.internal,
</Private-Package>
<Import-Package>
com.fasterxml.jackson.core; version="${com.fasterxml.jackson.version}"
com.fasterxml.jackson.core.type; version="${com.fasterxml.jackson.version}",
org.apache.axiom.om; version="${axiom.osgi.version.range}",
com.fasterxml.jackson.core.*; version="${com.fasterxml.jackson.version}"
com.fasterxml.jackson.databind; version="${com.fasterxml.jackson.databind.version.range}",
org.apache.commons.logging; version="${commons-logging.osgi.version.range}",
org.apache.commons.lang; version="${commons-lang.wso2.osgi.version.range}",
org.wso2.carbon.identity.oauth.common.*;version="${identity.inbound.auth.oauth.exp.pkg.version}"
org.apache.oltu.oauth2.common.*; version="${oltu.package.import.version.range}",
org.osgi.framework; version="${osgi.framework.imp.pkg.version.range}",
org.osgi.service.component; version="${osgi.service.component.imp.pkg.version.range}",
org.wso2.carbon.identity.application.authentication.framework.cache; version="${carbon.identity.framework.imp.pkg.version.range}",
org.wso2.carbon.identity.base; version="${carbon.identity.framework.imp.pkg.version.range}",
org.wso2.carbon.identity.core.*; version="${carbon.identity.framework.imp.pkg.version.range}",
javax.servlet; version="${imp.pkg.version.javax.servlet}",
javax.servlet.*; version="${imp.pkg.version.javax.servlet}",

com.fasterxml.jackson.databind; version="${com.fasterxml.jackson.databind.version.range}",
org.apache.oltu.oauth2.common.*; version="${oltu.package.import.version.range}",
</Import-Package>
<Export-Package>
!org.wso2.carbon.identity.oauth.par.internal,
org.wso2.carbon.identity.oauth.par.*;
version="${identity.inbound.auth.oauth.exp.pkg.version}",
</Export-Package>
<DynamicImport-Package>*</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,38 +38,39 @@ public class CacheBackedParDAO implements ParMgtDAO {
private final ParMgtDAOImpl parMgtDAO = new ParMgtDAOImpl();

@Override
public void persistParRequest(String reqUriUUID, String clientId, long scheduledExpiryTime,
Map<String, String> parameters) throws ParCoreException {
public void persistRequestData(String requestURIReference, String clientId, long expiresIn,
Map<String, String> parameters) throws ParCoreException {

ParRequestCacheEntry parRequestCacheEntry = new ParRequestCacheEntry(reqUriUUID, parameters,
scheduledExpiryTime);
parMgtDAO.persistParRequest(reqUriUUID, clientId, scheduledExpiryTime, parameters);
parCache.addToCache(reqUriUUID, parRequestCacheEntry);
ParRequestCacheEntry parRequestCacheEntry = new ParRequestCacheEntry(requestURIReference, parameters,
expiresIn, clientId);
parMgtDAO.persistRequestData(requestURIReference, clientId, expiresIn, parameters);
parCache.addToCache(requestURIReference, parRequestCacheEntry);
}

@Override
public ParRequestDO getParRequest(String reqUriUUID) throws ParCoreException {
public ParRequestDO getRequestData(String requestURIReference) throws ParCoreException {

ParRequestCacheEntry parCacheRequest = parCache.getValueFromCache(reqUriUUID);
ParRequestCacheEntry parCacheRequest = parCache.getValueFromCache(requestURIReference);
ParRequestDO parRequestDO;
if (parCacheRequest != null) {
if (log.isDebugEnabled()) {
log.debug("Cache miss for expiry time of local uuid: %s for tenant:%s " + reqUriUUID);
log.debug("Cache miss for expiry time of local uuid: %s for tenant:%s " + requestURIReference);
}
return new ParRequestDO(parCacheRequest);
parRequestDO = new ParRequestDO(parCacheRequest.getParams(), parCacheRequest.getExpiresIn(),
parCacheRequest.getClientId());
} else {
if (log.isDebugEnabled()) {
log.debug("Cache hit for expiry time of uuid:%s for tenant:%s " + reqUriUUID);
log.debug("Cache hit for expiry time of uuid:%s for tenant:%s " + requestURIReference);
}
parRequestDO = parMgtDAO.getParRequest(reqUriUUID);
parRequestDO = parMgtDAO.getRequestData(requestURIReference);
}
return parRequestDO;
}

@Override
public void removeParRequest(String reqUriUUID) throws ParCoreException {
public void removeRequestData(String requestURIReference) throws ParCoreException {

parCache.clearCacheEntry(reqUriUUID);
parMgtDAO.removeParRequest(reqUriUUID);
parCache.clearCacheEntry(requestURIReference);
parMgtDAO.removeRequestData(requestURIReference);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,20 @@
* Contains the required constants for PAR feature.
*/
public class ParConstants {

public static final long EXPIRES_IN_DEFAULT_VALUE_IN_SEC = 60;
public static final long EXPIRES_IN_DEFAULT_VALUE = 60;
public static final long SEC_TO_MILLISEC_FACTOR = 1000;
public static final String UTC = "UTC";
public static final String EXPIRES_IN = "expires_in";
public static final String REQUEST_URI_HEAD = "urn:ietf:params:wso2is:request_uri:";
public static final String REQUEST_URI_IN_REQUEST_BODY_ERROR = "request.with.request_uri.not.allowed";
public static final String REQUESTED_EXPIRY = "requested_expiry";
public static final String REQUEST_URI_PREFIX = "urn:ietf:params:oauth:request_uri:";
public static final String REQUEST_URI_IN_REQUEST_BODY_ERROR = "Request with request_uri not allowed.";
public static final String CACHE_NAME = "ParCache";
public static final String COL_LBL_JSON_PARAMS = "PARAMETERS";
public static final String COL_LBL_PARAMETERS = "PARAMETERS";
public static final String COL_LBL_SCHEDULED_EXPIRY = "SCHEDULED_EXPIRY";
public static final String COL_LBL_CLIENT_ID = "CLIENT_ID";

public static final String PAR = "PAR";
public static final String EXPIRY_TIME = "ExpiryTime";
public static final String CONFIG_ELEM_OAUTH = "OAuth";

private ParConstants() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@


/**
* Provides authentication services.
* Provides the PAR services.
*/
public interface ParAuthService {

/**
* Creates PAR AuthenticationResponse.
* Creates PAR AuthenticationResponse by setting the values for the response to be generated from PAR endpoint.
*
* @return parAuthResponse that contains response data for request.
*/
ParAuthResponseData generateParAuthResponse(Map<String, String> parameters) throws ParCoreException;
ParAuthResponseData handleParAuthRequest(Map<String, String> parameters) throws ParCoreException;

/**
* Retrieve parameters from store.
* Retrieves the parameter map relevant to the provided request_uri from store after validating.
*
* @return parameter map for request.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@

package org.wso2.carbon.identity.oauth.par.core;

import org.apache.axiom.om.OMElement;
import org.apache.commons.lang.StringUtils;
import org.wso2.carbon.identity.core.util.IdentityConfigParser;
import org.wso2.carbon.identity.core.util.IdentityCoreConstants;
import org.wso2.carbon.identity.oauth.common.OAuth2ErrorCodes;
import org.wso2.carbon.identity.oauth.common.OAuthConstants;
import org.wso2.carbon.identity.oauth.par.common.ParConstants;
Expand All @@ -33,66 +36,86 @@
import java.util.TimeZone;
import java.util.UUID;

import javax.xml.namespace.QName;


/**
* Provides authentication services.
* Provides PAR services.
*/
public class ParAuthServiceImpl implements ParAuthService {

ParMgtDAO parMgtDAO = ParDAOFactory.getInstance().getParAuthMgtDAO();

@Override
public ParAuthResponseData generateParAuthResponse(Map<String, String> parameters) throws ParCoreException {
public ParAuthResponseData handleParAuthRequest(Map<String, String> parameters) throws ParCoreException {

String uuid = UUID.randomUUID().toString();
long expiry = ParConstants.EXPIRES_IN_DEFAULT_VALUE_IN_SEC;

ParAuthResponseData parAuthResponse = new ParAuthResponseData();
parAuthResponse.setReqUriUUID(uuid);
parAuthResponse.setExpiryTime(expiry);
parAuthResponse.setrequestURIReference(uuid);
parAuthResponse.setExpiryTime(getExpiresInValue());

persistParRequest(uuid, parameters, getScheduledExpiry(System.currentTimeMillis()));

return parAuthResponse;
}

private void persistParRequest(String uuid, Map<String, String> params, long scheduledExpiryTime)
private void persistParRequest(String uuid, Map<String, String> params, long expiresIn)
throws ParCoreException {

parMgtDAO.persistParRequest(uuid, params.get(OAuthConstants.OAuth20Params.CLIENT_ID),
scheduledExpiryTime, params);
parMgtDAO.persistRequestData(uuid, params.get(OAuthConstants.OAuth20Params.CLIENT_ID),
expiresIn, params);
}

@Override
public Map<String, String> retrieveParams(String uuid, String clientId) throws ParCoreException {

ParRequestDO parRequestDO = parMgtDAO.getParRequest(uuid);
parMgtDAO.removeParRequest(uuid);
isRequestUriExpired(parRequestDO.getScheduledExpiryTime());
isClientIdValid(clientId, parRequestDO.getClientId());
ParRequestDO parRequestDO = parMgtDAO.getRequestData(uuid);
parMgtDAO.removeRequestData(uuid);
validateRequestURI(parRequestDO.getExpiresIn());
validateClientID(clientId, parRequestDO.getClientId());

return parRequestDO.getParams();
}

private void isRequestUriExpired(long scheduledExpiryTime) throws ParCoreException {
private void validateRequestURI(long expiresIn) throws ParCoreException {

long currentTimeInMillis = Calendar.getInstance(TimeZone.getTimeZone(ParConstants.UTC)).getTimeInMillis();

if (currentTimeInMillis > scheduledExpiryTime) {
if (currentTimeInMillis > expiresIn) {
throw new ParCoreException(OAuth2ErrorCodes.INVALID_REQUEST, "request_uri expired");
}
}

private void isClientIdValid(String clientId, String parClientId) throws
private void validateClientID(String clientId, String parClientId) throws
ParCoreException {

if (!StringUtils.equals(parClientId, clientId)) {
throw new ParCoreException(OAuth2ErrorCodes.INVALID_CLIENT, "client_ids does not match");
throw new ParCoreException(OAuth2ErrorCodes.INVALID_CLIENT, "client_ids do not match.");
}
}

private static long getExpiresInValue() throws ParCoreException {

try {
OMElement parConfig = IdentityConfigParser.getInstance().getConfigElement(ParConstants.CONFIG_ELEM_OAUTH)
.getFirstChildWithName(new QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE,
ParConstants.PAR));
String expiryTimeValue = parConfig.getFirstChildWithName(new QName(
IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, ParConstants.EXPIRY_TIME)).getText();
if (StringUtils.isNotBlank(expiryTimeValue)) {
return Long.parseLong(expiryTimeValue);
}
return ParConstants.EXPIRES_IN_DEFAULT_VALUE;
} catch (NumberFormatException e) {
throw new ParCoreException("Error while parsing the expiry time value.", e);
}

}

private long getScheduledExpiry(long requestedTime) {
private long getScheduledExpiry(long requestedTime) throws ParCoreException {

long defaultExpiryInSecs = ParConstants.EXPIRES_IN_DEFAULT_VALUE_IN_SEC * ParConstants.SEC_TO_MILLISEC_FACTOR;
long defaultExpiryInSecs = getExpiresInValue() * ParConstants.SEC_TO_MILLISEC_FACTOR;
return requestedTime + defaultExpiryInSecs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,10 @@
*/
public class ParDAOFactory {

// Implementation of DAO.
private final ParMgtDAO parMgtDAO;

private ParDAOFactory() {

// This factory creates instance of PAR DAOImplementation.
parMgtDAO = new CacheBackedParDAO();
}

Expand All @@ -47,6 +45,8 @@ public static ParDAOFactory getInstance() {
}

/**
* Returns instance of ParMgtDAO.
*
* @return ParMgtDAO.
*/
public ParMgtDAO getParAuthMgtDAO() {
Expand Down
Loading
Loading