Updated authorization error response.
Included error and error description in the client redirect URI except
for missing or invalid client id or redirect URI.
Change-Id: Ic35c5dcaf056f6ba761d80246c9ca64a7cf2016a
diff --git a/full/Changes b/full/Changes
index cad48b8..3bf608d 100644
--- a/full/Changes
+++ b/full/Changes
@@ -12,6 +12,11 @@
2022-04-13
- Updated OAuth2Client list API (added redirect_uri, registration_date,
permitted, source to OAuth2UserClientDto).
+2022-04-20
+ - Updated authorization error response. (Included error and error
+ description in the client redirect URI except for missing or
+ invalid client id or redirect URI.
+
# version 0.65.2
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/OAuth2AuthorizationRequest.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/OAuth2AuthorizationRequest.java
index 503a5c8..1819c11 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/OAuth2AuthorizationRequest.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/OAuth2AuthorizationRequest.java
@@ -3,15 +3,30 @@
import javax.servlet.http.HttpServletRequest;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
+import org.apache.oltu.oauth2.as.validator.CodeValidator;
import org.apache.oltu.oauth2.common.OAuth;
+import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
+import org.apache.oltu.oauth2.common.message.types.ResponseType;
+import org.apache.oltu.oauth2.common.utils.OAuthUtils;
+import org.apache.oltu.oauth2.common.validators.OAuthValidator;
/**
* Customization of {@link OAuthAuthzRequest} from Apache Oltu.
- * Limit extraction of client id from request's parameters since
+ * <ul>
+ * <li>Limit extraction of client id from request's parameters since
* Kustvakt requires user authentication via Basic authentication for
- * authorization code requests.
+ * authorization code requests. </li>
+ *
+ * <li>Exclude TokenValidator since it is not supported in
+ * Kustvakt.</li>
+ *
+ * <li>Minimize {{@link #validate()} to include missing response type
+ * response in client redirect URI when the client id and redirect URI
+ * are valid. </li>
+ *
+ * </ul>
*
* @author margaretha
*
@@ -27,4 +42,39 @@
public String getClientId () {
return getParam(OAuth.OAUTH_CLIENT_ID);
}
+
+ @Override
+ protected OAuthValidator<HttpServletRequest> initValidator ()
+ throws OAuthProblemException, OAuthSystemException {
+ validators.put(ResponseType.CODE.toString(), CodeValidator.class);
+ // validators.put(ResponseType.TOKEN.toString(),
+ // TokenValidator.class);
+ final String requestTypeValue = getParam(OAuth.OAUTH_RESPONSE_TYPE);
+ if (!requestTypeValue.isEmpty()) {
+ if (requestTypeValue.equals(ResponseType.CODE.toString())) {
+
+ }
+ else if (requestTypeValue.equals(ResponseType.TOKEN.toString())) {
+ throw OAuthProblemException.error(
+ OAuthError.CodeResponse.UNSUPPORTED_RESPONSE_TYPE)
+ .description("response_type token is not supported");
+ }
+ else {
+ throw OAuthUtils.handleOAuthProblemException(
+ "Invalid response_type parameter value");
+ }
+ }
+
+ return OAuthUtils.instantiateClass(validators.get("code"));
+ }
+
+ @Override
+ protected void validate ()
+ throws OAuthSystemException, OAuthProblemException {
+ validator = initValidator();
+ validator.validateMethod(request);
+ validator.validateContentType(request);
+ validator.validateRequiredParameters(request);
+ validator.validateClientAuthenticationCredentials(request);
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
index cbd638d..a3b9b01 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/oltu/service/OltuAuthorizationService.java
@@ -6,8 +6,10 @@
import javax.servlet.http.HttpServletRequest;
+import org.apache.http.HttpStatus;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
+import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.springframework.beans.factory.annotation.Autowired;
@@ -21,6 +23,7 @@
import de.ids_mannheim.korap.oauth2.constant.OAuth2Error;
import de.ids_mannheim.korap.oauth2.entity.OAuth2Client;
import de.ids_mannheim.korap.oauth2.service.OAuth2AuthorizationService;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ClientService;
/**
* OAuth2 authorization service using Apache Oltu
@@ -33,8 +36,10 @@
@Autowired
private RandomCodeGenerator codeGenerator;
+ @Autowired
+ private OAuth2ClientService clientService;
- /**
+ /**e.description("Redirect URI is required");
* Authorization code request does not require client
* authentication, but only checks if the client id exists.
*
@@ -70,7 +75,7 @@
String scope, code;
try {
- checkResponseType(authzRequest.getResponseType());
+ //checkResponseType(authzRequest.getResponseType(), redirectURI);
code = codeGenerator.createRandomCode();
scope = createAuthorization(username, authzRequest.getClientId(),
redirectUriStr, authzRequest.getScopes(), code,
@@ -86,7 +91,8 @@
oAuthResponse = OAuthASResponse
.authorizationResponse(request,
Status.FOUND.getStatusCode())
- .setCode(code).setScope(scope).location(verifiedRedirectUri)
+ .setCode(code).setScope(scope)
+ .location(verifiedRedirectUri)
.buildQueryMessage();
}
catch (OAuthSystemException e) {
@@ -99,4 +105,85 @@
}
return oAuthResponse.getLocationUri();
}
+
+ public OAuthProblemException checkRedirectUri (OAuthProblemException e,
+ String clientId, String redirectUri) {
+ if (!clientId.isEmpty()) {
+ String registeredUri = null;
+ try {
+ OAuth2Client client = clientService.retrieveClient(clientId);
+ registeredUri = client.getRedirectURI();
+ }
+ catch (KustvaktException e1) {}
+
+ if (redirectUri != null && !redirectUri.isEmpty()) {
+ if (registeredUri != null && !registeredUri.isEmpty()
+ && !redirectUri.equals(registeredUri)) {
+ e.description("Invalid redirect URI");
+ }
+ else {
+ e.setRedirectUri(redirectUri);
+ e.responseStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
+ }
+ }
+ else if (registeredUri != null && !registeredUri.isEmpty()) {
+ e.setRedirectUri(registeredUri);
+ e.responseStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
+ }
+ else {
+ e.description("Redirect URI is required");
+ }
+ }
+
+ return e;
+ }
+
+ public KustvaktException checkRedirectUri (KustvaktException e,
+ String clientId, String redirectUri){
+ int statusCode = e.getStatusCode();
+ if (!clientId.isEmpty()
+ && statusCode != StatusCodes.CLIENT_NOT_FOUND
+ && statusCode != StatusCodes.AUTHORIZATION_FAILED) {
+ String registeredUri = null;
+ try {
+ OAuth2Client client = clientService.retrieveClient(clientId);
+ registeredUri = client.getRedirectURI();
+ }
+ catch (KustvaktException e1) {}
+
+ if (redirectUri != null && !redirectUri.isEmpty()) {
+ if (registeredUri != null && !registeredUri.isEmpty()
+ && !redirectUri.equals(registeredUri)) {
+ return new KustvaktException(StatusCodes.INVALID_REDIRECT_URI,
+ "Invalid redirect URI", OAuth2Error.INVALID_REQUEST);
+ }
+ else {
+ try {
+ e.setRedirectUri(new URI(redirectUri));
+ }
+ catch (URISyntaxException e1) {
+ return new KustvaktException(StatusCodes.INVALID_REDIRECT_URI,
+ "Invalid redirect URI", OAuth2Error.INVALID_REQUEST);
+ }
+ e.setResponseStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
+ }
+ }
+ else if (registeredUri != null && !registeredUri.isEmpty()) {
+ try {
+ e.setRedirectUri(new URI(registeredUri));
+ }
+ catch (URISyntaxException e1) {
+ return new KustvaktException(StatusCodes.INVALID_REDIRECT_URI,
+ "Invalid redirect URI", OAuth2Error.INVALID_REQUEST);
+ }
+ e.setResponseStatus(HttpStatus.SC_TEMPORARY_REDIRECT);
+ }
+ else {
+ return new KustvaktException(StatusCodes.MISSING_REDIRECT_URI,
+ "Redirect URI is required", OAuth2Error.INVALID_REQUEST);
+ }
+ }
+
+ return e;
+ }
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java
index 4a50c8a..69713f8 100644
--- a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2AuthorizationService.java
@@ -72,6 +72,7 @@
return String.join(" ", scopeSet);
}
+ @Deprecated
protected void checkResponseType (String responseType)
throws KustvaktException {
if (responseType == null || responseType.isEmpty()) {
@@ -124,7 +125,7 @@
}
else {
throw new KustvaktException(StatusCodes.MISSING_REDIRECT_URI,
- "redirect_uri is required",
+ "Redirect URI is required",
OAuth2Error.INVALID_REQUEST);
}
}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/OAuth2ResponseHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/OAuth2ResponseHandler.java
index 0b21a14..3c86fd3 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/OAuth2ResponseHandler.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/OAuth2ResponseHandler.java
@@ -10,6 +10,7 @@
import javax.ws.rs.core.Response.Status;
import org.apache.http.HttpHeaders;
+import org.apache.http.HttpStatus;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
@@ -56,20 +57,21 @@
}
public WebApplicationException throwit (OAuthProblemException e) {
- return throwit(e, null);
- }
-
- public WebApplicationException throwit (OAuthProblemException e,
- String state) {
OAuthResponse oAuthResponse = null;
+ String state = e.getState();
try {
- OAuthErrorResponseBuilder builder = OAuthResponse.errorResponse(e.getResponseStatus())
- .error(e);
-
+ OAuthErrorResponseBuilder builder =
+ OAuthResponse.errorResponse(e.getResponseStatus()).error(e);
if (state != null && !state.isEmpty()) {
builder.setState(state);
}
- oAuthResponse = builder.buildJSONMessage();
+ if (e.getRedirectUri()!= null && !e.getRedirectUri().isEmpty()) {
+ builder.location(e.getRedirectUri());
+ oAuthResponse = builder.buildQueryMessage();
+ }
+ else {
+ oAuthResponse = builder.buildJSONMessage();
+ }
}
catch (OAuthSystemException e1) {
throwit(e1, state);
@@ -84,17 +86,20 @@
}
public WebApplicationException throwit (KustvaktException e, String state) {
- OAuthResponse oAuthResponse = null;
String errorCode = e.getEntity();
+ int responseStatus = e.getResponseStatus();
try {
- if (errorCode == null){
+ if(responseStatus>0) {
+ return throwit(createOAuthProblemException(e, responseStatus, state));
+ }
+ else if (errorCode == null){
return super.throwit(e);
}
else if (errorCode.equals(OAuth2Error.INVALID_CLIENT)
|| errorCode.equals(OAuth2Error.UNAUTHORIZED_CLIENT)
|| errorCode.equals(OAuth2Error.INVALID_TOKEN)) {
- oAuthResponse = createOAuthResponse(e,
- Status.UNAUTHORIZED.getStatusCode(), state);
+ return throwit(createOAuthProblemException(e,
+ Status.UNAUTHORIZED.getStatusCode(), state));
}
else if (errorCode.equals(OAuth2Error.INVALID_GRANT)
|| errorCode.equals(OAuth2Error.INVALID_REQUEST)
@@ -102,20 +107,20 @@
|| errorCode.equals(OAuth2Error.UNSUPPORTED_GRANT_TYPE)
|| errorCode.equals(OAuth2Error.UNSUPPORTED_RESPONSE_TYPE)
|| errorCode.equals(OAuth2Error.ACCESS_DENIED)) {
- oAuthResponse = createOAuthResponse(e,
- Status.BAD_REQUEST.getStatusCode(), state);
+ return throwit(createOAuthProblemException(e,
+ Status.BAD_REQUEST.getStatusCode(), state));
}
else if (errorCode.equals(OAuth2Error.INSUFFICIENT_SCOPE)) {
- oAuthResponse = createOAuthResponse(e,
- Status.FORBIDDEN.getStatusCode(), state);
+ return throwit(createOAuthProblemException(e,
+ Status.FORBIDDEN.getStatusCode(), state));
}
else if (errorCode.equals(OAuth2Error.SERVER_ERROR)) {
- oAuthResponse = createOAuthResponse(e,
- Status.INTERNAL_SERVER_ERROR.getStatusCode(), state);
+ return throwit(createOAuthProblemException(e,
+ Status.INTERNAL_SERVER_ERROR.getStatusCode(), state));
}
else if (errorCode.equals(OAuth2Error.TEMPORARILY_UNAVAILABLE)) {
- oAuthResponse = createOAuthResponse(e,
- Status.SERVICE_UNAVAILABLE.getStatusCode(), state);
+ return throwit(createOAuthProblemException(e,
+ Status.SERVICE_UNAVAILABLE.getStatusCode(), state));
}
else {
return super.throwit(e);
@@ -124,29 +129,18 @@
catch (OAuthSystemException e1) {
return throwit(e1, state);
}
-
- Response r = createResponse(oAuthResponse);
- return new WebApplicationException(r);
}
- private OAuthResponse createOAuthResponse (KustvaktException e,
- int statusCode, String state) throws OAuthSystemException {
- OAuthProblemException oAuthProblemException = OAuthProblemException
- .error(e.getEntity()).state(state).description(e.getMessage());
-
- OAuthErrorResponseBuilder responseBuilder = OAuthResponse
- .errorResponse(statusCode).error(oAuthProblemException);
- if (state!=null && !state.isEmpty()){
- responseBuilder.setState(state);
+ private OAuthProblemException createOAuthProblemException (
+ KustvaktException e, int statusCode, String state)
+ throws OAuthSystemException {
+ OAuthProblemException ex = OAuthProblemException.error(e.getEntity())
+ .responseStatus(statusCode).state(state)
+ .description(e.getMessage());
+ if (e.getRedirectUri()!= null) {
+ ex.setRedirectUri(e.getRedirectUri().toString());
}
-
- URI redirectUri = e.getRedirectUri();
- if (redirectUri != null) {
- responseBuilder.location(redirectUri.toString());
- return responseBuilder.buildQueryMessage();
- }
-
- return responseBuilder.buildJSONMessage();
+ return ex;
}
/**
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
index e29ad15..8ea0f37 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/OAuth2Controller.java
@@ -100,7 +100,10 @@
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response requestAuthorizationCode (
@Context HttpServletRequest request,
- @Context SecurityContext context, @FormParam("state") String state,
+ @Context SecurityContext context,
+ @FormParam("state") String state,
+ @FormParam("client_id") String clientId,
+ @FormParam("redirect_uri") String redirectUri,
MultivaluedMap<String, String> form) {
TokenContext tokenContext = (TokenContext) context.getUserPrincipal();
@@ -122,9 +125,12 @@
throw responseHandler.throwit(e, state);
}
catch (OAuthProblemException e) {
- throw responseHandler.throwit(e, state);
+ e.state(state);
+ e = authorizationService.checkRedirectUri(e, clientId, redirectUri);
+ throw responseHandler.throwit(e);
}
catch (KustvaktException e) {
+ e = authorizationService.checkRedirectUri(e, clientId, redirectUri);
throw responseHandler.throwit(e, state);
}
}
@@ -134,6 +140,8 @@
public Response requestAuthorizationCode (
@Context HttpServletRequest request,
@Context SecurityContext context,
+ @QueryParam("client_id") String clientId,
+ @QueryParam("redirect_uri") String redirectUri,
@QueryParam("state") String state
) {
@@ -151,13 +159,16 @@
return responseHandler.sendRedirect(uri);
}
catch (OAuthSystemException e) {
- throw responseHandler.throwit(e, state);
+ throw responseHandler.throwit(e,state);
}
catch (OAuthProblemException e) {
- throw responseHandler.throwit(e, state);
+ e.state(state);
+ e = authorizationService.checkRedirectUri(e,clientId,redirectUri);
+ throw responseHandler.throwit(e);
}
catch (KustvaktException e) {
- throw responseHandler.throwit(e, state);
+ e = authorizationService.checkRedirectUri(e, clientId, redirectUri);
+ throw responseHandler.throwit(e,state);
}
}
diff --git a/full/src/main/resources/db/test/V3.5__insert_oauth2_clients.sql b/full/src/main/resources/db/test/V3.5__insert_oauth2_clients.sql
index b2d6949..159c713 100644
--- a/full/src/main/resources/db/test/V3.5__insert_oauth2_clients.sql
+++ b/full/src/main/resources/db/test/V3.5__insert_oauth2_clients.sql
@@ -43,13 +43,12 @@
INSERT INTO oauth2_client(id,name,secret,type,super,
- redirect_uri, registered_by, description, url, registration_date,
+ registered_by, description, url, registration_date,
is_permitted)
VALUES ("nW5qM63Rb2a7KdT9L","test public client",null,
- "PUBLIC", 0,
- "https://korap.ids-mannheim.de/public/redirect","system",
- "This is a test public client.",
- "http://korap.ids-mannheim.de/public", CURRENT_TIMESTAMP, 1);
+ "PUBLIC", 0, "https://korap.ids-mannheim.de/public/redirect",
+ "system", "http://korap.ids-mannheim.de/public",
+ CURRENT_TIMESTAMP, 1);
INSERT INTO oauth2_access_token(token,user_id,created_date,
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
index b7911b3..a7d108c 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2ControllerTest.java
@@ -15,13 +15,10 @@
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.TokenType;
import org.junit.Test;
-import org.springframework.util.MultiValueMap;
-import org.springframework.web.util.UriComponentsBuilder;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.net.HttpHeaders;
import com.sun.jersey.api.client.ClientResponse;
-import com.sun.jersey.api.uri.UriComponent;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
@@ -47,25 +44,114 @@
@Test
public void testAuthorizeConfidentialClient () throws KustvaktException {
- ClientResponse response = requestAuthorizationCode("code",
- confidentialClientId, "", "", state, userAuthHeader);
+ // with registered redirect URI
+ ClientResponse response =
+ requestAuthorizationCode("code", confidentialClientId, "",
+ "match_info search client_info", state, userAuthHeader);
assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
response.getStatus());
URI redirectUri = response.getLocation();
- MultiValueMap<String, String> params = UriComponentsBuilder
- .fromUri(redirectUri).build().getQueryParams();
+ MultivaluedMap<String, String> params =
+ getQueryParamsFromURI(redirectUri);
assertNotNull(params.getFirst("code"));
assertEquals("thisIsMyState", params.getFirst("state"));
+ assertEquals("match_info search client_info", params.getFirst("scope"));
}
@Test
public void testAuthorizePublicClient () throws KustvaktException {
+ // with registered redirect URI
String code = requestAuthorizationCode(publicClientId, userAuthHeader);
assertNotNull(code);
}
@Test
+ public void testAuthorizePublicClientWithRedirectUri () throws KustvaktException {
+ ClientResponse response =
+ requestAuthorizationCode("code", publicClientId2,
+ "https://public.com/redirect", "", "", userAuthHeader);
+ assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+ response.getStatus());
+
+ URI redirectUri = response.getLocation();
+ assertEquals(redirectUri.getScheme(), "https");
+ assertEquals(redirectUri.getHost(), "public.com");
+ assertEquals(redirectUri.getPath(), "/redirect");
+
+ String[] queryParts = redirectUri.getQuery().split("&");
+ assertTrue(queryParts[0].startsWith("code="));
+ assertEquals(queryParts[1], "scope=match_info+search");
+ }
+
+ @Test
+ public void testAuthorizeWithoutScope () throws KustvaktException {
+ ClientResponse response = requestAuthorizationCode("code",
+ confidentialClientId, "", "", "", userAuthHeader);
+ assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+ response.getStatus());
+
+ URI redirectUri = response.getLocation();
+ assertEquals(redirectUri.getScheme(), "https");
+ assertEquals(redirectUri.getHost(), "third.party.com");
+ assertEquals(redirectUri.getPath(), "/confidential/redirect");
+
+ String[] queryParts = redirectUri.getQuery().split("&");
+ assertTrue(queryParts[0].startsWith("code="));
+ assertEquals(queryParts[1], "scope=match_info+search");
+ }
+
+ @Test
+ public void testAuthorizeMissingClientId () throws KustvaktException {
+ ClientResponse response = requestAuthorizationCode("code", "", "", "",
+ "", userAuthHeader);
+ assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals("Missing parameters: client_id",
+ node.at("/error_description").asText());
+ }
+
+ @Test
+ public void testAuthorizeMissingRedirectUri () throws KustvaktException {
+ ClientResponse response = requestAuthorizationCode("code",
+ publicClientId2, "", "", state, userAuthHeader);
+ assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+
+ String entity = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
+ node.at("/error").asText());
+ assertEquals("Redirect URI is required",
+ node.at("/error_description").asText());
+ assertEquals("thisIsMyState", node.at("/state").asText());
+ }
+
+ @Test
+ public void testAuthorizeMissingResponseType () throws KustvaktException {
+ ClientResponse response = requestAuthorizationCode("",
+ confidentialClientId, "", "", "", userAuthHeader);
+ assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+ response.getStatus());
+
+ assertEquals("https://third.party.com/confidential/redirect?"
+ + "error_description=Missing+parameters%3A+response_type&"
+ + "error=invalid_request", response.getLocation().toString());
+ }
+
+ @Test
+ public void testAuthorizeInvalidClientId () throws KustvaktException {
+ ClientResponse response = requestAuthorizationCode("code",
+ "unknown-client-id", "", "", "", userAuthHeader);
+// assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+ String entity = response.getEntity(String.class);
+ System.out.println(entity);
+ JsonNode node = JsonUtils.readTree(entity);
+ assertEquals("Unknown client with unknown-client-id.",
+ node.at("/error_description").asText());
+ }
+
+ @Test
public void testAuthorizeInvalidRedirectUri () throws KustvaktException {
String redirectUri = "https://different.uri/redirect";
ClientResponse response = requestAuthorizationCode("code",
@@ -81,57 +167,54 @@
node.at("/error_description").asText());
assertEquals("thisIsMyState", node.at("/state").asText());
}
-
-// @Test
-// public void testAuthorizeRedirectUriLocalhost () throws KustvaktException {
-// String redirectUri = "http://localhost:1410/";
-// ClientResponse response =
-// requestAuthorizationCode("code", confidentialClientId2,
-// redirectUri, null, "myState", userAuthHeader);
-// System.out.println(response.getStatus());
-// System.out.println(response.getEntity(String.class));
-// }
-
- @Test
- public void testAuthorizeMissingRequiredParameters ()
- throws KustvaktException {
- // missing response_type
- ClientResponse response = requestAuthorizationCode("",
- confidentialClientId, "", "", state, userAuthHeader);
-
- assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
-
- String entity = response.getEntity(String.class);
- JsonNode node = JsonUtils.readTree(entity);
- assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
- node.at("/error").asText());
- assertEquals("Missing response_type parameter value",
- node.at("/error_description").asText());
- assertEquals("thisIsMyState", node.at("/state").asText());
-
- // missing client_id
- response = requestAuthorizationCode("code","", "", "", "", userAuthHeader);
- entity = response.getEntity(String.class);
- node = JsonUtils.readTree(entity);
- assertEquals("Missing parameters: client_id",
- node.at("/error_description").asText());
- }
@Test
public void testAuthorizeInvalidResponseType () throws KustvaktException {
- MultivaluedMap<String, String> form = new MultivaluedMapImpl();
- form.add("response_type", "string");
- form.add("state", "thisIsMyState");
-
+ // without redirect URI in the request
ClientResponse response = requestAuthorizationCode("string",
confidentialClientId, "", "", state, userAuthHeader);
+ assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+ response.getStatus());
+
+ assertEquals("https://third.party.com/confidential/redirect?"
+ + "error_description=Invalid+response_type+parameter+"
+ + "value&state=thisIsMyState&" + "error=invalid_request",
+ response.getLocation().toString());
+
+ // with redirect URI, and no registered redirect URI
+ response = requestAuthorizationCode("string", publicClientId2,
+ "https://public.client.com/redirect", "", state,
+ userAuthHeader);
+ assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+ response.getStatus());
+
+ assertEquals("https://public.client.com/redirect?error_description="
+ + "Invalid+response_type+parameter+value&state=thisIsMyState&"
+ + "error=invalid_request", response.getLocation().toString());
+
+ // with different redirect URI
+ String redirectUri = "https://different.uri/redirect";
+ response = requestAuthorizationCode("string", confidentialClientId,
+ redirectUri, "", state, userAuthHeader);
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
- String entity = response.getEntity(String.class);
- JsonNode node = JsonUtils.readTree(entity);
+ JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
node.at("/error").asText());
- assertEquals("Invalid response_type parameter value",
+ assertEquals("Invalid redirect URI",
+ node.at("/error_description").asText());
+ assertEquals("thisIsMyState", node.at("/state").asText());
+
+ // without redirect URI in the request and no registered
+ // redirect URI
+ response = requestAuthorizationCode("string", publicClientId2, "", "",
+ state, userAuthHeader);
+ assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+
+ node = JsonUtils.readTree(response.getEntity(String.class));
+ assertEquals(OAuthError.CodeResponse.INVALID_REQUEST,
+ node.at("/error").asText());
+ assertEquals("Redirect URI is required",
node.at("/error_description").asText());
assertEquals("thisIsMyState", node.at("/state").asText());
}
@@ -139,18 +222,30 @@
@Test
public void testAuthorizeInvalidScope () throws KustvaktException {
String scope = "read_address";
-
ClientResponse response = requestAuthorizationCode("code",
confidentialClientId, "", scope, state, userAuthHeader);
- assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+ assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+ response.getStatus());
- URI location = response.getLocation();
- MultiValueMap<String, String> params =
- UriComponentsBuilder.fromUri(location).build().getQueryParams();
- assertEquals(OAuth2Error.INVALID_SCOPE, params.getFirst("error"));
- assertEquals("read_address+is+an+invalid+scope",
- params.getFirst("error_description"));
- assertEquals("thisIsMyState", params.getFirst("state"));
+ assertEquals(
+ "https://third.party.com/confidential/redirect?"
+ + "error_description=read_address+is+an+invalid+scope&"
+ + "state=thisIsMyState&error=invalid_scope",
+ response.getLocation().toString());
+ }
+
+ @Test
+ public void testAuthorizeUnsupportedTokenResponseType ()
+ throws KustvaktException {
+ ClientResponse response = requestAuthorizationCode("token",
+ confidentialClientId, "", "", state, userAuthHeader);
+ assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
+ response.getStatus());
+
+ assertEquals("https://third.party.com/confidential/redirect?"
+ + "error_description=response_type+token+is+not+"
+ + "supported&state=thisIsMyState&error=unsupported_"
+ + "response_type", response.getLocation().toString());
}
@Test
@@ -182,9 +277,8 @@
String scope = "search";
ClientResponse response = requestAuthorizationCode("code",
confidentialClientId, "", scope, state, userAuthHeader);
- URI redirectUri = response.getLocation();
MultivaluedMap<String, String> params =
- UriComponent.decodeQuery(redirectUri, true);
+ getQueryParamsFromURI(response.getLocation());
String code = params.get("code").get(0);
String scopes = params.get("scope").get(0);
@@ -249,9 +343,8 @@
ClientResponse response =
requestAuthorizationCode("code", confidentialClientId,
redirect_uri, scope, state, userAuthHeader);
- URI redirectUri = response.getLocation();
MultivaluedMap<String, String> params =
- UriComponent.decodeQuery(redirectUri, true);
+ getQueryParamsFromURI(response.getLocation());
String code = params.get("code").get(0);
testRequestTokenAuthorizationInvalidClient(code);
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
index bcdcee7..1a043d7 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2TestBase.java
@@ -20,6 +20,7 @@
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.uri.UriComponent;
import com.sun.jersey.core.util.MultivaluedMapImpl;
import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
@@ -45,6 +46,8 @@
protected RefreshTokenDao refreshTokenDao;
protected String publicClientId = "8bIDtZnH6NvRkW2Fq";
+ // without registered redirect URI
+ protected String publicClientId2 = "nW5qM63Rb2a7KdT9L";
protected String confidentialClientId = "9aHsGW6QflV13ixNpez";
protected String confidentialClientId2 = "52atrL0ajex_3_5imd9Mgw";
protected String superClientId = "fCBbQkAyYzI4NzUxMg";
@@ -56,6 +59,10 @@
protected String clientURL = "http://example.client.com";
protected String clientRedirectUri = "https://example.client.com/redirect";
+
+ protected MultivaluedMap<String, String> getQueryParamsFromURI (URI uri) {
+ return UriComponent.decodeQuery(uri, true);
+ };
protected ClientResponse requestAuthorizationCode (String responseType,
String clientId, String redirectUri, String scope, String state,