This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch 3.2.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/3.2.x-fixes by this push: new 0e0a899 CXF-7788 - Support POST method for OAuth authorization service 0e0a899 is described below commit 0e0a899435260cf4400a9a4d954cea481615107a Author: Colm O hEigeartaigh <cohei...@apache.org> AuthorDate: Thu Jul 12 11:02:10 2018 +0100 CXF-7788 - Support POST method for OAuth authorization service (cherry picked from commit ebfb3a364c496f76c8b27aacc9bdd7b8aa804602) --- .../oauth2/services/AuthorizationService.java | 13 +++ .../services/RedirectionBasedGrantService.java | 17 +++- .../security/oauth2/common/OAuth2TestUtils.java | 10 +- .../oauth2/grants/AuthorizationGrantTest.java | 42 ++++++++ .../systest/jaxrs/security/oidc/OIDCFlowTest.java | 113 +++++++++++++++++++++ 5 files changed, 190 insertions(+), 5 deletions(-) diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationService.java index 2674194..263bc36 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AuthorizationService.java @@ -50,6 +50,7 @@ public class AuthorizationService { service.setMessageContext(context); } } + @GET @Produces({"application/xhtml+xml", "text/html", "application/xml", "application/json" }) public Response authorize(@QueryParam(OAuthConstants.RESPONSE_TYPE) String responseType) { @@ -60,6 +61,18 @@ public class AuthorizationService { return reportInvalidResponseType(); } + @POST + @Consumes("application/x-www-form-urlencoded") + @Produces({"application/xhtml+xml", "text/html", "application/xml", "application/json" }) + public Response authorizePost(MultivaluedMap<String, String> params) { + String responseType = params.getFirst(OAuthConstants.RESPONSE_TYPE); + RedirectionBasedGrantService service = getService(responseType); + if (service != null) { + return service.authorize(); + } + return reportInvalidResponseType(); + } + @GET @Path("/decision") public Response authorizeDecision(@QueryParam(OAuthConstants.RESPONSE_TYPE) String responseType) { diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java index b016df3..90c3285 100644 --- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java +++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java @@ -99,6 +99,19 @@ public abstract class RedirectionBasedGrantService extends AbstractOAuthService } /** + * Handles the initial authorization request by preparing + * the authorization challenge data and returning it to the user. + * Typically the data are expected to be presented in the HTML form + * @return the authorization data + */ + @POST + @Consumes("application/x-www-form-urlencoded") + @Produces({"application/xhtml+xml", "text/html", "application/xml", "application/json" }) + public Response authorizePost(MultivaluedMap<String, String> params) { + return startAuthorization(params); + } + + /** * Processes the end user decision * @return The grant value, authorization code or the token */ @@ -389,7 +402,7 @@ public abstract class RedirectionBasedGrantService extends AbstractOAuthService return createErrorResponse(params, redirectUri, OAuthConstants.INVALID_SCOPE); } getMessageContext().put(AUTHORIZATION_REQUEST_PARAMETERS, params); - + String preAuthorizedTokenKey = params.getFirst(PREAUTHORIZED_TOKEN_KEY); if (preAuthorizedTokenKey != null && isRevokePreauthorizedTokenOnApproval()) { getDataProvider().revokeToken(client, preAuthorizedTokenKey, OAuthConstants.ACCESS_TOKEN); @@ -410,7 +423,7 @@ public abstract class RedirectionBasedGrantService extends AbstractOAuthService public void setRevokePreauthorizedTokenOnApproval(boolean revoke) { this.revokePreauthorizedTokenOnApproval = revoke; } - + public void setSessionAuthenticityTokenProvider(SessionAuthenticityTokenProvider sessionAuthenticityTokenProvider) { this.sessionAuthenticityTokenProvider = sessionAuthenticityTokenProvider; } diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/common/OAuth2TestUtils.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/common/OAuth2TestUtils.java index a19a75f..1db41b7 100644 --- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/common/OAuth2TestUtils.java +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/common/OAuth2TestUtils.java @@ -105,6 +105,10 @@ public final class OAuth2TestUtils { Response response = client.get(); OAuthAuthorizationData authzData = response.readEntity(OAuthAuthorizationData.class); + return getLocation(client, authzData, parameters.getState()); + } + + public static String getLocation(WebClient client, OAuthAuthorizationData authzData, String state) { // Now call "decision" to get the authorization code grant client.path("decision"); @@ -126,10 +130,10 @@ public final class OAuth2TestUtils { form.param("response_type", authzData.getResponseType()); form.param("oauthDecision", "allow"); - response = client.post(form); + Response response = client.post(form); String location = response.getHeaderString("Location"); - if (parameters.getState() != null) { - Assert.assertTrue(location.contains("state=" + parameters.getState())); + if (state != null) { + Assert.assertTrue(location.contains("state=" + state)); } return location; diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/AuthorizationGrantTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/AuthorizationGrantTest.java index ae22085..2fdf4a7 100644 --- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/AuthorizationGrantTest.java +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oauth2/grants/AuthorizationGrantTest.java @@ -74,6 +74,48 @@ public class AuthorizationGrantTest extends AbstractBusClientServerTestBase { assertNotNull(accessToken.getTokenKey()); } + // The authorization server MUST support the use of the HTTP "GET" + // method [RFC2616] for the authorization endpoint and MAY support the + // use of the "POST" method as well. + @org.junit.Test + public void testAuthorizationCodeGrantPOST() throws Exception { + URL busFile = AuthorizationGrantTest.class.getResource("client.xml"); + + String address = "https://localhost:" + PORT + "/services/"; + WebClient client = WebClient.create(address, OAuth2TestUtils.setupProviders(), + "alice", "security", busFile.toString()); + // Save the Cookie for the second request... + WebClient.getConfig(client).getRequestContext().put( + org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE); + + // Make initial authorization request + client.type("application/x-www-form-urlencoded"); + + client.path("authorize/"); + + Form form = new Form(); + form.param("client_id", "consumer-id"); + form.param("redirect_uri", "http://www.blah.apache.org"); + form.param("response_type", "code"); + Response response = client.post(form); + + OAuthAuthorizationData authzData = response.readEntity(OAuthAuthorizationData.class); + String location = OAuth2TestUtils.getLocation(client, authzData, null); + String code = OAuth2TestUtils.getSubstring(location, "code"); + assertNotNull(code); + + // Now get the access token + client = WebClient.create(address, OAuth2TestUtils.setupProviders(), + "consumer-id", "this-is-a-secret", busFile.toString()); + // Save the Cookie for the second request... + WebClient.getConfig(client).getRequestContext().put( + org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE); + + ClientAccessToken accessToken = + OAuth2TestUtils.getAccessTokenWithAuthorizationCode(client, code); + assertNotNull(accessToken.getTokenKey()); + } + @org.junit.Test public void testAuthorizationCodeGrantRefresh() throws Exception { URL busFile = AuthorizationGrantTest.class.getResource("client.xml"); diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oidc/OIDCFlowTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oidc/OIDCFlowTest.java index 1e15986..010d483 100644 --- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oidc/OIDCFlowTest.java +++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/oidc/OIDCFlowTest.java @@ -104,6 +104,54 @@ public class OIDCFlowTest extends AbstractBusClientServerTestBase { validateIdToken(idToken, null); } + // Authorization Servers MUST support the use of the HTTP GET and POST methods defined in RFC 2616 + // [RFC2616] at the Authorization Endpoint. + @org.junit.Test + public void testAuthorizationCodeFlowPOST() throws Exception { + URL busFile = OIDCFlowTest.class.getResource("client.xml"); + + String address = "https://localhost:" + PORT + "/services/"; + WebClient client = WebClient.create(address, OAuth2TestUtils.setupProviders(), + "alice", "security", busFile.toString()); + + // Save the Cookie for the second request... + WebClient.getConfig(client).getRequestContext().put( + org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE); + + // Make initial authorization request + client.type("application/x-www-form-urlencoded"); + + client.path("authorize/"); + + Form form = new Form(); + form.param("client_id", "consumer-id"); + form.param("scope", "openid"); + form.param("redirect_uri", "http://www.blah.apache.org"); + form.param("response_type", "code"); + Response response = client.post(form); + + OAuthAuthorizationData authzData = response.readEntity(OAuthAuthorizationData.class); + String location = OAuth2TestUtils.getLocation(client, authzData, null); + String code = OAuth2TestUtils.getSubstring(location, "code"); + assertNotNull(code); + + // Now get the access token + client = WebClient.create(address, OAuth2TestUtils.setupProviders(), + "consumer-id", "this-is-a-secret", busFile.toString()); + // Save the Cookie for the second request... + WebClient.getConfig(client).getRequestContext().put( + org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE); + + ClientAccessToken accessToken = + OAuth2TestUtils.getAccessTokenWithAuthorizationCode(client, code); + assertNotNull(accessToken.getTokenKey()); + assertTrue(accessToken.getApprovedScope().contains("openid")); + + String idToken = accessToken.getParameters().get("id_token"); + assertNotNull(idToken); + validateIdToken(idToken, null); + } + // Just a normal OAuth invocation, check it all works ok @org.junit.Test public void testAuthorizationCodeOAuth() throws Exception { @@ -373,6 +421,71 @@ public class OIDCFlowTest extends AbstractBusClientServerTestBase { OidcUtils.validateAccessTokenHash(accessToken, jwt, true); } + // Authorization Servers MUST support the use of the HTTP GET and POST methods defined in RFC 2616 + // [RFC2616] at the Authorization Endpoint. + @org.junit.Test + public void testImplicitFlowPOST() throws Exception { + URL busFile = OIDCFlowTest.class.getResource("client.xml"); + + String address = "https://localhost:" + PORT + "/services/"; + WebClient client = WebClient.create(address, OAuth2TestUtils.setupProviders(), + "alice", "security", busFile.toString()); + // Save the Cookie for the second request... + WebClient.getConfig(client).getRequestContext().put( + org.apache.cxf.message.Message.MAINTAIN_SESSION, Boolean.TRUE); + + // Get Access Token + client.type("application/x-www-form-urlencoded"); + + client.path("authorize-implicit/"); + + Form form = new Form(); + form.param("client_id", "consumer-id"); + form.param("scope", "openid"); + form.param("redirect_uri", "http://www.blah.apache.org"); + form.param("response_type", "id_token token"); + form.param("nonce", "123456789"); + Response response = client.post(form); + + OAuthAuthorizationData authzData = response.readEntity(OAuthAuthorizationData.class); + + // Now call "decision" to get the access token + client.path("decision"); + client.type("application/x-www-form-urlencoded"); + + form = new Form(); + form.param("session_authenticity_token", authzData.getAuthenticityToken()); + form.param("client_id", authzData.getClientId()); + form.param("redirect_uri", authzData.getRedirectUri()); + form.param("scope", authzData.getProposedScope()); + if (authzData.getResponseType() != null) { + form.param("response_type", authzData.getResponseType()); + } + if (authzData.getNonce() != null) { + form.param("nonce", authzData.getNonce()); + } + form.param("oauthDecision", "allow"); + + response = client.post(form); + + String location = response.getHeaderString("Location"); + + // Check Access Token + String accessToken = OAuth2TestUtils.getSubstring(location, "access_token"); + assertNotNull(accessToken); + + // Check IdToken + String idToken = OAuth2TestUtils.getSubstring(location, "id_token"); + assertNotNull(idToken); + validateIdToken(idToken, null); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(idToken); + JwtToken jwt = jwtConsumer.getJwtToken(); + Assert.assertNotNull(jwt.getClaims().getClaim(IdToken.ACCESS_TOKEN_HASH_CLAIM)); + Assert.assertNotNull(jwt.getClaims().getClaim(IdToken.NONCE_CLAIM)); + OidcUtils.validateAccessTokenHash(accessToken, jwt, true); + } + @org.junit.Test public void testImplicitFlowNoAccessToken() throws Exception { URL busFile = OIDCFlowTest.class.getResource("client.xml");