Skip to content

Commit e437647

Browse files
committed
Merge branch 'master' into sdk-5212-sso
2 parents 489399b + 15367e7 commit e437647

File tree

8 files changed

+269
-1
lines changed

8 files changed

+269
-1
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ dependencies {
134134
implementation "com.auth0:java-jwt:4.4.0"
135135
implementation "net.jodah:failsafe:2.4.4"
136136

137-
testImplementation "org.bouncycastle:bcprov-jdk15on:1.70"
137+
testImplementation "org.bouncycastle:bcprov-jdk18on:1.78"
138138
testImplementation "org.mockito:mockito-core:4.8.1"
139139
testImplementation "com.squareup.okhttp3:mockwebserver:${okhttpVersion}"
140140
testImplementation "org.hamcrest:hamcrest:${hamcrestVersion}"

src/main/java/com/auth0/client/auth/AuthAPI.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,61 @@ public AuthorizeUrlBuilder authorizeUrl(String redirectUri) {
225225
return AuthorizeUrlBuilder.newInstance(baseUrl, clientId, redirectUri);
226226
}
227227

228+
public Request<BackChannelAuthorizeResponse> authorizeBackChannel(String scope, String bindingMessage, Map<String, Object> loginHint) {
229+
return authorizeBackChannel(scope, bindingMessage, loginHint, null, null);
230+
}
231+
232+
public Request<BackChannelAuthorizeResponse> authorizeBackChannel(String scope, String bindingMessage, Map<String, Object> loginHint, String audience, Integer requestExpiry) {
233+
Asserts.assertNotNull(scope, "scope");
234+
Asserts.assertNotNull(bindingMessage, "binding message");
235+
Asserts.assertNotNull(loginHint, "login hint");
236+
237+
String url = baseUrl
238+
.newBuilder()
239+
.addPathSegment("bc-authorize")
240+
.build()
241+
.toString();
242+
243+
FormBodyRequest<BackChannelAuthorizeResponse> request = new FormBodyRequest<>(client, null, url, HttpMethod.POST, new TypeReference<BackChannelAuthorizeResponse>() {});
244+
245+
request.addParameter(KEY_CLIENT_ID, clientId);
246+
addClientAuthentication(request, false);
247+
request.addParameter("scope", scope);
248+
request.addParameter("binding_message", bindingMessage);
249+
250+
if(Objects.nonNull(audience)){
251+
request.addParameter(KEY_AUDIENCE, audience);
252+
}
253+
if(Objects.nonNull(requestExpiry)){
254+
request.addParameter("request_expiry", requestExpiry);
255+
}
256+
257+
try {
258+
String loginHintJson = getMapper().writeValueAsString(loginHint);
259+
request.addParameter("login_hint", loginHintJson);
260+
}
261+
catch (JsonProcessingException e) {
262+
throw new IllegalArgumentException("'loginHint' must be a map that can be serialized to JSON", e);
263+
}
264+
return request;
265+
}
266+
267+
public Request<BackChannelTokenResponse> getBackChannelLoginStatus(String authReqId, String grantType) {
268+
Asserts.assertNotNull(authReqId, "auth req id");
269+
Asserts.assertNotNull(grantType, "grant type");
270+
271+
String url = getTokenUrl();
272+
273+
FormBodyRequest<BackChannelTokenResponse> request = new FormBodyRequest<>(client, null, url, HttpMethod.POST, new TypeReference<BackChannelTokenResponse>() {});
274+
275+
request.addParameter(KEY_CLIENT_ID, clientId);
276+
addClientAuthentication(request, false);
277+
request.addParameter("auth_req_id", authReqId);
278+
request.addParameter(KEY_GRANT_TYPE, grantType);
279+
280+
return request;
281+
}
282+
228283
/**
229284
* Builds an authorization URL for Pushed Authorization Requests (PAR)
230285
* @param requestUri the {@code request_uri} parameter from a successful pushed authorization request.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.auth0.json.auth;
2+
3+
import com.fasterxml.jackson.annotation.*;
4+
5+
@JsonIgnoreProperties(ignoreUnknown = true)
6+
@JsonInclude(JsonInclude.Include.NON_NULL)
7+
public class BackChannelAuthorizeResponse {
8+
@JsonProperty("auth_req_id")
9+
private String authReqId;
10+
@JsonProperty("expires_in")
11+
private Long expiresIn;
12+
@JsonProperty("interval")
13+
private Integer interval;
14+
15+
@JsonCreator
16+
public BackChannelAuthorizeResponse(@JsonProperty("auth_req_id") String authReqId, @JsonProperty("expires_in") Long expiresIn, @JsonProperty("interval") Integer interval) {
17+
this.authReqId = authReqId;
18+
this.expiresIn = expiresIn;
19+
this.interval = interval;
20+
}
21+
22+
/**
23+
* Getter for the Auth Request ID.
24+
* @return the Auth Request ID.
25+
*/
26+
public String getAuthReqId() {
27+
return authReqId;
28+
}
29+
30+
/**
31+
* Getter for the Expires In value.
32+
* @return the Expires In value.
33+
*/
34+
public Long getExpiresIn() {
35+
return expiresIn;
36+
}
37+
38+
/**
39+
* Getter for the Interval value.
40+
* @return the Interval value.
41+
*/
42+
public Integer getInterval() {
43+
return interval;
44+
}
45+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.auth0.json.auth;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import com.fasterxml.jackson.annotation.JsonProperty;
6+
7+
@JsonIgnoreProperties(ignoreUnknown = true)
8+
@JsonInclude(JsonInclude.Include.NON_NULL)
9+
public class BackChannelTokenResponse {
10+
@JsonProperty("access_token")
11+
private String accessToken;
12+
@JsonProperty("id_token")
13+
private String idToken;
14+
@JsonProperty("expires_in")
15+
private long expiresIn;
16+
@JsonProperty("scope")
17+
private String scope;
18+
19+
public String getAccessToken() {
20+
return accessToken;
21+
}
22+
23+
public String getIdToken() {
24+
return idToken;
25+
}
26+
27+
public long getExpiresIn() {
28+
return expiresIn;
29+
}
30+
31+
public String getScope() {
32+
return scope;
33+
}
34+
}

src/test/java/com/auth0/client/MockServer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ public class MockServer {
116116
public static final String PASSWORDLESS_EMAIL_RESPONSE = "src/test/resources/auth/passwordless_email.json";
117117
public static final String PASSWORDLESS_SMS_RESPONSE = "src/test/resources/auth/passwordless_sms.json";
118118
public static final String PUSHED_AUTHORIZATION_RESPONSE = "src/test/resources/auth/pushed_authorization_response.json";
119+
public static final String BACK_CHANNEL_AUTHORIZE_RESPONSE = "src/test/resources/auth/back_channel_authorize_response.json";
120+
public static final String BACK_CHANNEL_LOGIN_STATUS_RESPONSE = "src/test/resources/auth/back_channel_login_status_response.json";
119121
public static final String AUTHENTICATOR_METHOD_BY_ID = "src/test/resources/mgmt/authenticator_method_by_id.json";
120122
public static final String AUTHENTICATOR_METHOD_CREATE = "src/test/resources/mgmt/authenticator_method_create.json";
121123
public static final String AUTHENTICATOR_METHOD_LIST = "src/test/resources/mgmt/authenticator_method_list.json";

src/test/java/com/auth0/client/auth/AuthAPITest.java

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,6 +2009,127 @@ public void shouldThrowWhenCreatePushedAuthorizationJarRequestWithInvalidAuthDet
20092009
assertThat(e.getCause(), instanceOf(JsonProcessingException.class));
20102010
}
20112011

2012+
@Test
2013+
public void authorizeBackChannelWhenScopeIsNull() {
2014+
verifyThrows(IllegalArgumentException.class,
2015+
() -> api.authorizeBackChannel(null, "This is binding message", getLoginHint()),
2016+
"'scope' cannot be null!");
2017+
}
2018+
2019+
@Test
2020+
public void authorizeBackChannelWhenBindingMessageIsNull() {
2021+
verifyThrows(IllegalArgumentException.class,
2022+
() -> api.authorizeBackChannel("openid", null, getLoginHint()),
2023+
"'binding message' cannot be null!");
2024+
}
2025+
2026+
@Test
2027+
public void authorizeBackChannelWhenLoginHintIsNull() {
2028+
verifyThrows(IllegalArgumentException.class,
2029+
() -> api.authorizeBackChannel("openid", "This is binding message", null),
2030+
"'login hint' cannot be null!");
2031+
}
2032+
2033+
@Test
2034+
public void authorizeBackChannel() throws Exception {
2035+
Request<BackChannelAuthorizeResponse> request = api.authorizeBackChannel("openid", "This is binding message", getLoginHint());
2036+
assertThat(request, is(notNullValue()));
2037+
2038+
server.jsonResponse(BACK_CHANNEL_AUTHORIZE_RESPONSE, 200);
2039+
BackChannelAuthorizeResponse response = request.execute().getBody();
2040+
RecordedRequest recordedRequest = server.takeRequest();
2041+
2042+
assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/bc-authorize"));
2043+
assertThat(recordedRequest, hasHeader("Content-Type", "application/x-www-form-urlencoded"));
2044+
2045+
String body = URLDecoder.decode(readFromRequest(recordedRequest), StandardCharsets.UTF_8.name());
2046+
assertThat(body, containsString("scope=" + "openid"));
2047+
assertThat(body, containsString("client_id=" + CLIENT_ID));
2048+
assertThat(body, containsString("client_secret=" + CLIENT_SECRET));
2049+
assertThat(body, containsString("binding_message=This is binding message"));
2050+
assertThat(body, containsString("login_hint={\"sub\":\"auth0|user1\",\"format\":\"format1\",\"iss\":\"https://auth0.com\"}"));
2051+
2052+
assertThat(response, is(notNullValue()));
2053+
assertThat(response.getAuthReqId(), not(emptyOrNullString()));
2054+
assertThat(response.getExpiresIn(), notNullValue());
2055+
assertThat(response.getInterval(), notNullValue());
2056+
}
2057+
2058+
@Test
2059+
public void authorizeBackChannelWithAudienceAndRequestExpiry() throws Exception {
2060+
Request<BackChannelAuthorizeResponse> request = api.authorizeBackChannel("openid", "This is binding message", getLoginHint(), "https://api.example.com", 300);
2061+
assertThat(request, is(notNullValue()));
2062+
2063+
server.jsonResponse(BACK_CHANNEL_AUTHORIZE_RESPONSE, 200);
2064+
BackChannelAuthorizeResponse response = request.execute().getBody();
2065+
RecordedRequest recordedRequest = server.takeRequest();
2066+
2067+
assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/bc-authorize"));
2068+
assertThat(recordedRequest, hasHeader("Content-Type", "application/x-www-form-urlencoded"));
2069+
2070+
String body = URLDecoder.decode(readFromRequest(recordedRequest), StandardCharsets.UTF_8.name());
2071+
assertThat(body, containsString("scope=" + "openid"));
2072+
assertThat(body, containsString("client_id=" + CLIENT_ID));
2073+
assertThat(body, containsString("client_secret=" + CLIENT_SECRET));
2074+
assertThat(body, containsString("binding_message=This is binding message"));
2075+
assertThat(body, containsString("login_hint={\"sub\":\"auth0|user1\",\"format\":\"format1\",\"iss\":\"https://auth0.com\"}"));
2076+
assertThat(body, containsString("request_expiry=" + 300));
2077+
assertThat(body, containsString("audience=" + "https://api.example.com"));
2078+
2079+
assertThat(response, is(notNullValue()));
2080+
assertThat(response.getAuthReqId(), not(emptyOrNullString()));
2081+
assertThat(response.getExpiresIn(), notNullValue());
2082+
assertThat(response.getInterval(), notNullValue());
2083+
}
2084+
2085+
private Map<String, Object> getLoginHint() {
2086+
Map<String, Object> loginHint = new HashMap<>();
2087+
loginHint.put("format", "format1");
2088+
loginHint.put("iss", "https://auth0.com");
2089+
loginHint.put("sub", "auth0|user1");
2090+
return loginHint;
2091+
}
2092+
2093+
@Test
2094+
public void getBackChannelLoginStatusWhenAuthReqIdIsNull() {
2095+
verifyThrows(IllegalArgumentException.class,
2096+
() -> api.getBackChannelLoginStatus(null, "ciba"),
2097+
"'auth req id' cannot be null!");
2098+
}
2099+
2100+
@Test
2101+
public void getBackChannelLoginStatusWhenGrantTypeIsNull() {
2102+
verifyThrows(IllegalArgumentException.class,
2103+
() -> api.getBackChannelLoginStatus("red_id_1", null),
2104+
"'grant type' cannot be null!");
2105+
}
2106+
2107+
@Test
2108+
public void getBackChannelLoginStatus() throws Exception {
2109+
Request<BackChannelTokenResponse> request = api.getBackChannelLoginStatus("red_id_1", "ciba");
2110+
assertThat(request, is(notNullValue()));
2111+
2112+
server.jsonResponse(BACK_CHANNEL_LOGIN_STATUS_RESPONSE, 200);
2113+
BackChannelTokenResponse response = request.execute().getBody();
2114+
RecordedRequest recordedRequest = server.takeRequest();
2115+
2116+
assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/oauth/token"));
2117+
assertThat(recordedRequest, hasHeader("Content-Type", "application/x-www-form-urlencoded"));
2118+
2119+
String body = URLDecoder.decode(readFromRequest(recordedRequest), StandardCharsets.UTF_8.name());
2120+
assertThat(body, containsString("client_id=" + CLIENT_ID));
2121+
assertThat(body, containsString("client_secret=" + CLIENT_SECRET));
2122+
assertThat(body, containsString("auth_req_id=red_id_1"));
2123+
assertThat(body, containsString("grant_type=ciba"));
2124+
2125+
assertThat(response, is(notNullValue()));
2126+
assertThat(response.getAccessToken(), not(emptyOrNullString()));
2127+
assertThat(response.getIdToken(), not(emptyOrNullString()));
2128+
assertThat(response.getExpiresIn(), notNullValue());
2129+
assertThat(response.getScope(), not(emptyOrNullString()));
2130+
}
2131+
2132+
20122133
private Map<String, String> getQueryMap(String input) {
20132134
String[] params = input.split("&");
20142135

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"auth_req_id": "red_id_1",
3+
"expires_in": 300,
4+
"interval": 5
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"access_token": "eyJhbGciOiJkaXIi.....",
3+
"id_token": "eyJhbGciOiJSUzI1NiIs.....",
4+
"expires_in": 86400,
5+
"scope": "openid"
6+
}

0 commit comments

Comments
 (0)