Skip to content

Commit a3b6219

Browse files
Joaosoumoreira/add method to enroll email mfa (#744)
Co-authored-by: Joao Moreira <joaomoreira@curative.com>
1 parent 8902e15 commit a3b6219

File tree

2 files changed

+286
-18
lines changed

2 files changed

+286
-18
lines changed

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

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,27 @@ public Request<CreatedOtpResponse> addOtpAuthenticator(String mfaToken) {
13951395
return request;
13961396
}
13971397

1398+
1399+
private BaseRequest<CreatedOobResponse> createBaseOobRequest(String mfaToken, List<String> oobChannels) {
1400+
String url = baseUrl
1401+
.newBuilder()
1402+
.addPathSegment("mfa")
1403+
.addPathSegment("associate")
1404+
.build()
1405+
.toString();
1406+
1407+
BaseRequest<CreatedOobResponse> request = new BaseRequest<>(client, null, url, HttpMethod.POST, new TypeReference<CreatedOobResponse>() {
1408+
});
1409+
1410+
request.addParameter("authenticator_types", Collections.singletonList("oob"));
1411+
request.addParameter("oob_channels", oobChannels);
1412+
request.addParameter(KEY_CLIENT_ID, clientId);
1413+
addClientAuthentication(request, false);
1414+
request.addHeader("Authorization", "Bearer " + mfaToken);
1415+
1416+
return request;
1417+
}
1418+
13981419
/**
13991420
* Associates or adds a new OOB authenticator for multi-factor authentication (MFA).
14001421
* Confidential clients (Regular Web Apps) <strong>must</strong> have a client secret configured on this {@code AuthAPI} instance.
@@ -1415,32 +1436,67 @@ public Request<CreatedOtpResponse> addOtpAuthenticator(String mfaToken) {
14151436
* @param phoneNumber The phone number for "sms" or "voice" channels. May be null if not using "sms" or "voice".
14161437
* @return a Request to execute.
14171438
* @see <a href="https://auth0.com/docs/api/authentication#add-an-authenticator">Add an Authenticator API documentation</a>
1439+
* @deprecated Use {@linkplain #addOobAuthenticator(String, List, String, String)} instead.
14181440
*/
1441+
@Deprecated
14191442
public Request<CreatedOobResponse> addOobAuthenticator(String mfaToken, List<String> oobChannels, String phoneNumber) {
14201443
Asserts.assertNotNull(mfaToken, "mfa token");
14211444
Asserts.assertNotNull(oobChannels, "OOB channels");
1445+
if (oobChannels.contains("sms") || oobChannels.contains("voice")) {
1446+
Asserts.assertNotNull(phoneNumber, "phone number");
1447+
}
14221448

1423-
String url = baseUrl
1424-
.newBuilder()
1425-
.addPathSegment("mfa")
1426-
.addPathSegment("associate")
1427-
.build()
1428-
.toString();
1429-
1430-
BaseRequest<CreatedOobResponse> request = new BaseRequest<>(client, null, url, HttpMethod.POST, new TypeReference<CreatedOobResponse>() {
1431-
});
1449+
BaseRequest<CreatedOobResponse> request = createBaseOobRequest(mfaToken, oobChannels);
14321450

1433-
request.addParameter("authenticator_types", Collections.singletonList("oob"));
1434-
request.addParameter("oob_channels", oobChannels);
1435-
request.addParameter(KEY_CLIENT_ID, clientId);
14361451
if (phoneNumber != null) {
14371452
request.addParameter("phone_number", phoneNumber);
14381453
}
1439-
addClientAuthentication(request, false);
1440-
request.addHeader("Authorization", "Bearer " + mfaToken);
1454+
14411455
return request;
14421456
}
14431457

1458+
/**
1459+
* Associates or adds a new OOB authenticator for multi-factor authentication (MFA).
1460+
* Confidential clients (Regular Web Apps) <strong>must</strong> have a client secret configured on this {@code AuthAPI} instance.
1461+
* <pre>
1462+
* {@code
1463+
* try {
1464+
* CreatedOobResponse result = authAPI.addOobAuthenticator("the-mfa-token", Arrays.asList("sms", "email"), "phone-number", "email-address")
1465+
* .execute()
1466+
* .getBody();
1467+
* } catch (Auth0Exception e) {
1468+
* //Something happened
1469+
* }
1470+
* }
1471+
* </pre>
1472+
*
1473+
* @param mfaToken The token received from mfa_required error. Must not be null.
1474+
* @param oobChannels The type of OOB channels supported by the client. Must not be null.
1475+
* @param phoneNumber The phone number for "sms" or "voice" channels. May be null if not using "sms" or "voice".
1476+
* @param emailAddress The email address for "email" channel. May be null if not using "email".
1477+
* @return a Request to execute.
1478+
* @see <a href="https://auth0.com/docs/secure/multi-factor-authentication/authenticate-using-ropg-flow-with-mfa/enroll-challenge-sms-voice-authenticators#enroll-with-sms-or-voice">Enroll with SMS or voice</a>
1479+
*/
1480+
public Request<CreatedOobResponse> addOobAuthenticator(String mfaToken, List<String> oobChannels, String phoneNumber, String emailAddress) {
1481+
Asserts.assertNotNull(mfaToken, "mfa token");
1482+
Asserts.assertNotNull(oobChannels, "OOB channels");
1483+
if (oobChannels.contains("sms") || oobChannels.contains("voice")) {
1484+
Asserts.assertNotNull(phoneNumber, "phone number");
1485+
}
1486+
if (oobChannels.contains("email")) {
1487+
Asserts.assertNotNull(emailAddress, "email address");
1488+
}
1489+
1490+
BaseRequest<CreatedOobResponse> request = createBaseOobRequest(mfaToken, oobChannels);
1491+
if (phoneNumber != null) {
1492+
request.addParameter("phone_number", phoneNumber);
1493+
}
1494+
if (emailAddress != null) {
1495+
request.addParameter("email", emailAddress);
1496+
}
1497+
1498+
return request;
1499+
}
14441500

14451501
/**
14461502
* Returns a list of authenticators associated with your application.

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

Lines changed: 216 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,22 +1541,41 @@ public void addOtpAuthenticatorRequest() throws Exception {
15411541
assertThat(response.getRecoveryCodes(), notNullValue());
15421542
}
15431543

1544+
@SuppressWarnings("deprecation")
15441545
@Test
1545-
public void addOobAuthenticatorThrowsWhenTokenNull() {
1546+
public void addOobAuthenticatorDeprecatedThrowsWhenTokenNull() {
15461547
verifyThrows(IllegalArgumentException.class,
1547-
() -> api.addOobAuthenticator(null, Collections.singletonList("otp"), null),
1548+
() -> api.addOobAuthenticator(null, Collections.singletonList("auth0"), null),
15481549
"'mfa token' cannot be null!");
15491550
}
15501551

1552+
@SuppressWarnings("deprecation")
15511553
@Test
1552-
public void addOobAuthenticatorThrowsWhenChannelsNull() {
1554+
public void addOobAuthenticatorDeprecatedThrowsWhenChannelsNull() {
15531555
verifyThrows(IllegalArgumentException.class,
15541556
() -> api.addOobAuthenticator("mfaToken", null, null),
15551557
"'OOB channels' cannot be null!");
15561558
}
15571559

1560+
@SuppressWarnings("deprecation")
1561+
@Test
1562+
public void addOobAuthenticatorDeprecatedThrowsWhenPhoneNumberNull() {
1563+
verifyThrows(IllegalArgumentException.class,
1564+
() -> api.addOobAuthenticator("mfaToken", Collections.singletonList("sms"), null),
1565+
"'phone number' cannot be null!");
1566+
}
1567+
1568+
@SuppressWarnings("deprecation")
15581569
@Test
1559-
public void addOobAuthenticatorRequest() throws Exception {
1570+
public void addOobAuthenticatorDeprecatedThrowsWhenPhoneNumberNullVoiceChannel() {
1571+
verifyThrows(IllegalArgumentException.class,
1572+
() -> api.addOobAuthenticator("mfaToken", Collections.singletonList("voice"), null),
1573+
"'phone number' cannot be null!");
1574+
}
1575+
1576+
@SuppressWarnings("deprecation")
1577+
@Test
1578+
public void addOobAuthenticatorDeprecatedRequest() throws Exception {
15601579
Request<CreatedOobResponse> request = api.addOobAuthenticator("mfaToken", Collections.singletonList("sms"), "phone-number");
15611580

15621581
server.jsonResponse(AUTH_OOB_AUTHENTICATOR_RESPONSE, 200);
@@ -1579,6 +1598,199 @@ public void addOobAuthenticatorRequest() throws Exception {
15791598
assertThat(response.getRecoveryCodes(), notNullValue());
15801599
}
15811600

1601+
@SuppressWarnings("deprecation")
1602+
@Test
1603+
public void addOobAuthenticatorDeprecatedRequestWithNoPhoneNumber() throws Exception {
1604+
Request<CreatedOobResponse> request = api.addOobAuthenticator("mfaToken", Collections.singletonList("auth0"), null);
1605+
1606+
server.jsonResponse(AUTH_OOB_AUTHENTICATOR_RESPONSE, 200);
1607+
CreatedOobResponse response = request.execute().getBody();
1608+
RecordedRequest recordedRequest = server.takeRequest();
1609+
1610+
assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/mfa/associate"));
1611+
assertThat(recordedRequest, hasHeader("Content-Type", "application/json"));
1612+
1613+
Map<String, Object> body = bodyFromRequest(recordedRequest);
1614+
assertThat(body, hasEntry("authenticator_types", Collections.singletonList("oob")));
1615+
assertThat(body, hasEntry("oob_channels", Collections.singletonList("auth0")));
1616+
1617+
assertThat(response, is(notNullValue()));
1618+
assertThat(response.getAuthenticatorType(), not(emptyOrNullString()));
1619+
assertThat(response.getOobChannel(), not(emptyOrNullString()));
1620+
assertThat(response.getOobCode(), not(emptyOrNullString()));
1621+
assertThat(response.getBarcodeUri(), not(emptyOrNullString()));
1622+
assertThat(response.getRecoveryCodes(), notNullValue());
1623+
}
1624+
1625+
@Test
1626+
public void addOobAuthenticatorThrowsWhenTokenNull() {
1627+
verifyThrows(IllegalArgumentException.class,
1628+
() -> api.addOobAuthenticator(null, Collections.singletonList("auth0"), null, null),
1629+
"'mfa token' cannot be null!");
1630+
}
1631+
1632+
1633+
@Test
1634+
public void addOobAuthenticatorThrowsWhenChannelsNull() {
1635+
verifyThrows(IllegalArgumentException.class,
1636+
() -> api.addOobAuthenticator("mfaToken", null, null, null),
1637+
"'OOB channels' cannot be null!");
1638+
}
1639+
1640+
@Test
1641+
public void addOobAuthenticatorThrowsWhenChannelsNullWithPhoneNumber() {
1642+
verifyThrows(IllegalArgumentException.class,
1643+
() -> api.addOobAuthenticator("mfaToken", null, "phone-number", null),
1644+
"'OOB channels' cannot be null!");
1645+
}
1646+
1647+
@Test
1648+
public void addOobAuthenticatorThrowsWhenChannelsNullWithEmail() {
1649+
verifyThrows(IllegalArgumentException.class,
1650+
() -> api.addOobAuthenticator("mfaToken", null, null, "email-address"),
1651+
"'OOB channels' cannot be null!");
1652+
}
1653+
1654+
@Test
1655+
public void addOobAuthenticatorThrowsWhenPhoneNumberNull() {
1656+
verifyThrows(IllegalArgumentException.class,
1657+
() -> api.addOobAuthenticator("mfaToken", Collections.singletonList("sms"), null, null),
1658+
"'phone number' cannot be null!");
1659+
}
1660+
1661+
@Test
1662+
public void addOobAuthenticatorThrowsWhenPhoneNumberNullWithVoiceChannel() {
1663+
verifyThrows(IllegalArgumentException.class,
1664+
() -> api.addOobAuthenticator("mfaToken", Collections.singletonList("voice"), null, null),
1665+
"'phone number' cannot be null!");
1666+
}
1667+
1668+
@Test
1669+
public void addOobAuthenticatorThrowsWhenEmailNull() {
1670+
verifyThrows(IllegalArgumentException.class,
1671+
() -> api.addOobAuthenticator("mfaToken", Collections.singletonList("email"), null, null),
1672+
"'email address' cannot be null!");
1673+
}
1674+
1675+
@Test
1676+
public void addOobAuthenticatorRequestWithPhoneNumber() throws Exception {
1677+
Request<CreatedOobResponse> request = api.addOobAuthenticator("mfaToken", Collections.singletonList("sms"), "phone-number", null);
1678+
1679+
server.jsonResponse(AUTH_OOB_AUTHENTICATOR_RESPONSE, 200);
1680+
CreatedOobResponse response = request.execute().getBody();
1681+
RecordedRequest recordedRequest = server.takeRequest();
1682+
1683+
assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/mfa/associate"));
1684+
assertThat(recordedRequest, hasHeader("Content-Type", "application/json"));
1685+
1686+
Map<String, Object> body = bodyFromRequest(recordedRequest);
1687+
assertThat(body, hasEntry("authenticator_types", Collections.singletonList("oob")));
1688+
assertThat(body, hasEntry("oob_channels", Collections.singletonList("sms")));
1689+
assertThat(body, hasEntry("phone_number", "phone-number"));
1690+
1691+
assertThat(response, is(notNullValue()));
1692+
assertThat(response.getAuthenticatorType(), not(emptyOrNullString()));
1693+
assertThat(response.getOobChannel(), not(emptyOrNullString()));
1694+
assertThat(response.getOobCode(), not(emptyOrNullString()));
1695+
assertThat(response.getBarcodeUri(), not(emptyOrNullString()));
1696+
assertThat(response.getRecoveryCodes(), notNullValue());
1697+
}
1698+
1699+
@Test
1700+
public void addOobAuthenticatorRequestWithEmail() throws Exception {
1701+
Request<CreatedOobResponse> request = api.addOobAuthenticator("mfaToken", Collections.singletonList("email"), null, "email-address");
1702+
1703+
server.jsonResponse(AUTH_OOB_AUTHENTICATOR_RESPONSE, 200);
1704+
CreatedOobResponse response = request.execute().getBody();
1705+
RecordedRequest recordedRequest = server.takeRequest();
1706+
1707+
assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/mfa/associate"));
1708+
assertThat(recordedRequest, hasHeader("Content-Type", "application/json"));
1709+
1710+
Map<String, Object> body = bodyFromRequest(recordedRequest);
1711+
assertThat(body, hasEntry("authenticator_types", Collections.singletonList("oob")));
1712+
assertThat(body, hasEntry("oob_channels", Collections.singletonList("email")));
1713+
assertThat(body, hasEntry("email", "email-address"));
1714+
1715+
assertThat(response, is(notNullValue()));
1716+
assertThat(response.getAuthenticatorType(), not(emptyOrNullString()));
1717+
assertThat(response.getOobChannel(), not(emptyOrNullString()));
1718+
assertThat(response.getOobCode(), not(emptyOrNullString()));
1719+
assertThat(response.getBarcodeUri(), not(emptyOrNullString()));
1720+
assertThat(response.getRecoveryCodes(), notNullValue());
1721+
}
1722+
1723+
@Test
1724+
public void addOobAuthenticatorRequestWithNoContactInfo() throws Exception {
1725+
Request<CreatedOobResponse> request = api.addOobAuthenticator("mfaToken", Collections.singletonList("auth0"), null, null);
1726+
1727+
server.jsonResponse(AUTH_OOB_AUTHENTICATOR_RESPONSE, 200);
1728+
CreatedOobResponse response = request.execute().getBody();
1729+
RecordedRequest recordedRequest = server.takeRequest();
1730+
1731+
assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/mfa/associate"));
1732+
assertThat(recordedRequest, hasHeader("Content-Type", "application/json"));
1733+
1734+
Map<String, Object> body = bodyFromRequest(recordedRequest);
1735+
assertThat(body, hasEntry("authenticator_types", Collections.singletonList("oob")));
1736+
assertThat(body, hasEntry("oob_channels", Collections.singletonList("auth0")));
1737+
1738+
assertThat(response, is(notNullValue()));
1739+
assertThat(response.getAuthenticatorType(), not(emptyOrNullString()));
1740+
assertThat(response.getOobChannel(), not(emptyOrNullString()));
1741+
assertThat(response.getOobCode(), not(emptyOrNullString()));
1742+
assertThat(response.getBarcodeUri(), not(emptyOrNullString()));
1743+
assertThat(response.getRecoveryCodes(), notNullValue());
1744+
}
1745+
1746+
@Test
1747+
public void addOobAuthenticatorRequestWithMultipleChannels() throws Exception {
1748+
Request<CreatedOobResponse> request = api.addOobAuthenticator("mfaToken", Arrays.asList("sms", "email", "auth0"), "phone-number", "email-address");
1749+
1750+
server.jsonResponse(AUTH_OOB_AUTHENTICATOR_RESPONSE, 200);
1751+
CreatedOobResponse response = request.execute().getBody();
1752+
RecordedRequest recordedRequest = server.takeRequest();
1753+
1754+
assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/mfa/associate"));
1755+
assertThat(recordedRequest, hasHeader("Content-Type", "application/json"));
1756+
1757+
Map<String, Object> body = bodyFromRequest(recordedRequest);
1758+
assertThat(body, hasEntry("authenticator_types", Collections.singletonList("oob")));
1759+
assertThat(body, hasEntry("oob_channels", Arrays.asList("sms", "email", "auth0")));
1760+
assertThat(body, hasEntry("phone_number", "phone-number"));
1761+
assertThat(body, hasEntry("email", "email-address"));
1762+
1763+
assertThat(response, is(notNullValue()));
1764+
assertThat(response.getAuthenticatorType(), not(emptyOrNullString()));
1765+
assertThat(response.getOobChannel(), not(emptyOrNullString()));
1766+
assertThat(response.getOobCode(), not(emptyOrNullString()));
1767+
assertThat(response.getBarcodeUri(), not(emptyOrNullString()));
1768+
assertThat(response.getRecoveryCodes(), notNullValue());
1769+
}
1770+
1771+
@Test
1772+
public void addOobAuthenticatorRequestWithAuth0Channel() throws Exception {
1773+
Request<CreatedOobResponse> request = api.addOobAuthenticator("mfaToken", Collections.singletonList("auth0"), null, "email-address");
1774+
1775+
server.jsonResponse(AUTH_OOB_AUTHENTICATOR_RESPONSE, 200);
1776+
CreatedOobResponse response = request.execute().getBody();
1777+
RecordedRequest recordedRequest = server.takeRequest();
1778+
1779+
assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/mfa/associate"));
1780+
assertThat(recordedRequest, hasHeader("Content-Type", "application/json"));
1781+
1782+
Map<String, Object> body = bodyFromRequest(recordedRequest);
1783+
assertThat(body, hasEntry("authenticator_types", Collections.singletonList("oob")));
1784+
assertThat(body, hasEntry("oob_channels", Collections.singletonList("auth0")));
1785+
1786+
assertThat(response, is(notNullValue()));
1787+
assertThat(response.getAuthenticatorType(), not(emptyOrNullString()));
1788+
assertThat(response.getOobChannel(), not(emptyOrNullString()));
1789+
assertThat(response.getOobCode(), not(emptyOrNullString()));
1790+
assertThat(response.getBarcodeUri(), not(emptyOrNullString()));
1791+
assertThat(response.getRecoveryCodes(), notNullValue());
1792+
}
1793+
15821794
@Test
15831795
public void listAuthenticatorsThrowsWhenTokenNull() {
15841796
verifyThrows(IllegalArgumentException.class,

0 commit comments

Comments
 (0)