From a0d3b57f6fea8465c6316b3dc23aa34e21eaef5d Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Tue, 23 Apr 2024 12:44:16 -0500 Subject: [PATCH] [SDK-4763] - add support for JAR and PAR with JAR to Authentication API --- .../java/com/auth0/client/auth/AuthAPI.java | 46 ++++++++++++ .../com/auth0/client/auth/AuthAPITest.java | 73 +++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/src/main/java/com/auth0/client/auth/AuthAPI.java b/src/main/java/com/auth0/client/auth/AuthAPI.java index 3ce9dcc1..db75aaa2 100644 --- a/src/main/java/com/auth0/client/auth/AuthAPI.java +++ b/src/main/java/com/auth0/client/auth/AuthAPI.java @@ -238,6 +238,25 @@ public String authorizeUrlWithPAR(String requestUri) { .toString(); } + /** + * Builds an authorization URL for JWT-Secured Authorization Request (JAR) + * @param request the {@code request} parameter value. As specified, it must be a signed JWT and contain claims representing the authorization parameters. + * @see AuthAPI#pushedAuthorizationRequestWithJAR(String) + * @see Authorization Code Flow with JWT-Secured Authorization Requests (JAR) + * @see RFC 9101 + * @return the authorization URL to redirect users to for authentication. + */ + public String authorizeUrlWithJAR(String request) { + Asserts.assertNotNull(request, "request"); + return baseUrl + .newBuilder() + .addPathSegment("authorize") + .addQueryParameter("client_id", clientId) + .addQueryParameter("request", request) + .build() + .toString(); + } + /** * Builds a request to make a Pushed Authorization Request (PAR) to receive a {@code request_uri} to send to the {@code /authorize} endpoint. * @param redirectUri the URL to redirect to after authorization has been granted by the user. Your Auth0 application @@ -270,6 +289,33 @@ public Request pushedAuthorizationRequest(String re return request; } + /** + * Builds a request to make a Pushed Authorization Request (PAR) with JWT-Secured Authorization Requests (JAR), to receive a {@code request_uri} to send to the {@code /authorize} endpoint. + * @param request The signed JWT containing the authorization parameters as claims. + * @see Authorization Code Flow with PAR and JAR + * @see RFC 9101 + * @see RFC 9126 + * @return a request to execute. + */ + public Request pushedAuthorizationRequestWithJAR(String request) { + Asserts.assertNotNull(request, "request"); + + String url = baseUrl + .newBuilder() + .addPathSegments("oauth/par") + .build() + .toString(); + + FormBodyRequest req = new FormBodyRequest<>(client, null, url, HttpMethod.POST, new TypeReference() {}); + req.addParameter("client_id", clientId); + req.addParameter("request", request); + if (Objects.nonNull(this.clientSecret)) { + req.addParameter("client_secret", clientSecret); + } + + return req; + } + /** * Creates an instance of the {@link LogoutUrlBuilder} with the given return-to url. * i.e.: diff --git a/src/test/java/com/auth0/client/auth/AuthAPITest.java b/src/test/java/com/auth0/client/auth/AuthAPITest.java index fa6cee49..2a23a27a 100644 --- a/src/test/java/com/auth0/client/auth/AuthAPITest.java +++ b/src/test/java/com/auth0/client/auth/AuthAPITest.java @@ -1769,6 +1769,79 @@ public void shouldCreatePushedAuthorizationRequestWithoutSecret() throws Excepti assertThat(response.getExpiresIn(), notNullValue()); } + @Test + public void authorizeUrlWithJARShouldThrowWhenRequestNull() { + verifyThrows(IllegalArgumentException.class, + () -> api.authorizeUrlWithJAR(null), + "'request' cannot be null!"); + } + + @Test + public void shouldBuildAuthorizeUrlWithJAR() { + String requestJwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiIxMjM0NTYiLCJyZWRpcmVjdF91cmkiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJyZXNwb25zZV90eXBlIjoiY29kZSIsIm5vbmNlIjoiMTIzNCIsInN0YXRlIjoiNzhkeXVma2poZGYifQ.UQDz8hBIabaqatY75BvqGyiPoOqNYJQIsimUKg4_VrU"; + AuthAPI api = AuthAPI.newBuilder("domain.auth0.com", CLIENT_ID, CLIENT_SECRET).build(); + String url = api.authorizeUrlWithJAR("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiIxMjM0NTYiLCJyZWRpcmVjdF91cmkiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJyZXNwb25zZV90eXBlIjoiY29kZSIsIm5vbmNlIjoiMTIzNCIsInN0YXRlIjoiNzhkeXVma2poZGYifQ.UQDz8hBIabaqatY75BvqGyiPoOqNYJQIsimUKg4_VrU"); + assertThat(url, is(notNullValue())); + assertThat(url, isUrl("https", "domain.auth0.com", "/authorize")); + + assertThat(url, hasQueryParameter("request", requestJwt)); + assertThat(url, hasQueryParameter("client_id", CLIENT_ID)); + } + + @Test + public void pushedAuthorizationRequestShouldThrowWhenRequestIsNull() { + verifyThrows(IllegalArgumentException.class, + () -> api.pushedAuthorizationRequestWithJAR(null), + "'request' cannot be null!"); + } + + @Test + public void shouldCreatePushedAuthorizationJarRequest() throws Exception { + String requestJwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiIxMjM0NTYiLCJyZWRpcmVjdF91cmkiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJyZXNwb25zZV90eXBlIjoiY29kZSIsIm5vbmNlIjoiMTIzNCIsInN0YXRlIjoiNzhkeXVma2poZGYifQ.UQDz8hBIabaqatY75BvqGyiPoOqNYJQIsimUKg4_VrU"; + Request request = api.pushedAuthorizationRequestWithJAR(requestJwt); + assertThat(request, is(notNullValue())); + + server.jsonResponse(PUSHED_AUTHORIZATION_RESPONSE, 200); + PushedAuthorizationResponse response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/oauth/par")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/x-www-form-urlencoded")); + + String body = readFromRequest(recordedRequest); + assertThat(body, containsString("client_id=" + CLIENT_ID)); + assertThat(body, containsString("request=" + requestJwt)); + assertThat(body, containsString("client_secret=" + CLIENT_SECRET)); + + assertThat(response, is(notNullValue())); + assertThat(response.getRequestURI(), not(emptyOrNullString())); + assertThat(response.getExpiresIn(), notNullValue()); + } + + @Test + public void shouldCreatePushedAuthorizationJarRequestWithoutSecret() throws Exception { + String requestJwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRfaWQiOiIxMjM0NTYiLCJyZWRpcmVjdF91cmkiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJyZXNwb25zZV90eXBlIjoiY29kZSIsIm5vbmNlIjoiMTIzNCIsInN0YXRlIjoiNzhkeXVma2poZGYifQ.UQDz8hBIabaqatY75BvqGyiPoOqNYJQIsimUKg4_VrU"; + AuthAPI api = AuthAPI.newBuilder(server.getBaseUrl(), CLIENT_ID).build(); + Request request = api.pushedAuthorizationRequestWithJAR(requestJwt); + assertThat(request, is(notNullValue())); + + server.jsonResponse(PUSHED_AUTHORIZATION_RESPONSE, 200); + PushedAuthorizationResponse response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/oauth/par")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/x-www-form-urlencoded")); + + String body = readFromRequest(recordedRequest); + assertThat(body, containsString("client_id=" + CLIENT_ID)); + assertThat(body, containsString("request=" + requestJwt)); + assertThat(body, not(containsString("client_secret"))); + + assertThat(response, is(notNullValue())); + assertThat(response.getRequestURI(), not(emptyOrNullString())); + assertThat(response.getExpiresIn(), notNullValue()); + } + static class TestAssertionSigner implements ClientAssertionSigner { private final String token;