Skip to content

Commit

Permalink
[SDK-4763] - add support for JAR and PAR with JAR to Authentication A…
Browse files Browse the repository at this point in the history
…PI (#636)
  • Loading branch information
jimmyjames authored Apr 24, 2024
2 parents 0549138 + a0d3b57 commit c050bf2
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 0 deletions.
46 changes: 46 additions & 0 deletions src/main/java/com/auth0/client/auth/AuthAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 <a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow/authorization-code-flow-with-jar">Authorization Code Flow with JWT-Secured Authorization Requests (JAR)</a>
* @see <a href="https://datatracker.ietf.org/doc/html/rfc9101">RFC 9101</a>
* @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
Expand Down Expand Up @@ -270,6 +289,33 @@ public Request<PushedAuthorizationResponse> 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 <a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow/authorization-code-flow-with-par-and-jar">Authorization Code Flow with PAR and JAR</a>
* @see <a href="https://datatracker.ietf.org/doc/html/rfc9101">RFC 9101</a>
* @see <a href="https://www.rfc-editor.org/rfc/rfc9126.html">RFC 9126</a>
* @return a request to execute.
*/
public Request<PushedAuthorizationResponse> pushedAuthorizationRequestWithJAR(String request) {
Asserts.assertNotNull(request, "request");

String url = baseUrl
.newBuilder()
.addPathSegments("oauth/par")
.build()
.toString();

FormBodyRequest<PushedAuthorizationResponse> req = new FormBodyRequest<>(client, null, url, HttpMethod.POST, new TypeReference<PushedAuthorizationResponse>() {});
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.:
Expand Down
73 changes: 73 additions & 0 deletions src/test/java/com/auth0/client/auth/AuthAPITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<PushedAuthorizationResponse> 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<PushedAuthorizationResponse> 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;
Expand Down

0 comments on commit c050bf2

Please sign in to comment.