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;