Skip to content

Commit

Permalink
Merge pull request #300 from auth0/passwordless-support
Browse files Browse the repository at this point in the history
Add Passwordless support
  • Loading branch information
lbalmaceda authored Oct 21, 2020
2 parents d58667e + c64b3e6 commit ed07a5b
Show file tree
Hide file tree
Showing 11 changed files with 548 additions and 10 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,42 @@ try {
}
```

### Passwordless Authentication

This library supports [Passwordless Authentication](https://auth0.com/docs/connections/passwordless) to allow users to log in without the need to remember a password.

The email flow supports sending both a code or link to initiate login:

```java
try {
PasswordlessEmailResponse = auth.startPasswordlessEmailFlow("[email protected]", PasswordlessEmailType.CODE)
.execute();
} catch (Auth0Exception e) {
// handle request error
}
```

You can also initiate the passwordless flow by sending a code via SMS:

```java
try {
PasswordlessSmsResponse result = auth.startPasswordlessSmsFlow("+16511234567")
.execute();
} catch (Auth0Exception e) {
// handle request error
}
```

Using the verification code sent to the user, you can complete the passwordless authentication flow and obtain the tokens:

```java
AuthRequest request = auth.exchangePasswordlessOtp("emailOrPhone", PasswordlessRealmType.EMAIL, new char[]{'c','o','d','e'});
try {
TokenHolder tokens = request.execute();
} catch (Auth0Exception e) {
// handle request error
}
```

## Management API

Expand Down
139 changes: 138 additions & 1 deletion src/main/java/com/auth0/client/auth/AuthAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.auth0.client.HttpOptions;
import com.auth0.client.ProxyOptions;
import com.auth0.json.auth.PasswordlessEmailResponse;
import com.auth0.json.auth.PasswordlessSmsResponse;
import com.auth0.json.auth.UserInfo;
import com.auth0.net.Request;
import com.auth0.net.*;
Expand Down Expand Up @@ -34,11 +36,15 @@ public class AuthAPI {
private static final String KEY_CONNECTION = "connection";
private static final String KEY_TOKEN = "token";
private static final String KEY_REFRESH_TOKEN = "refresh_token";
private static final String KEY_OTP = "otp";
private static final String KEY_REALM = "realm";

private static final String PATH_OAUTH = "oauth";
private static final String PATH_TOKEN = "token";
private static final String PATH_DBCONNECTIONS = "dbconnections";
private static final String PATH_REVOKE = "revoke";
private static final String PATH_PASSWORDLESS = "passwordless";
private static final String PATH_START = "start";

private final OkHttpClient client;
private final String clientId;
Expand Down Expand Up @@ -553,7 +559,53 @@ public AuthRequest login(String emailOrUsername, char[] password, String realm)
request.addParameter(KEY_GRANT_TYPE, "http://auth0.com/oauth/grant-type/password-realm");
request.addParameter(KEY_USERNAME, emailOrUsername);
request.addParameter(KEY_PASSWORD, password);
request.addParameter("realm", realm);
request.addParameter(KEY_REALM, realm);
return request;
}

/**
* Creates a login request using the Passwordless grant type.
*
* <pre>
* {@code
* AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne");
* try {
* TokenHolder result = auth.exchangePasswordlessOtp("[email protected]", "email", new char[]{'c','o','d','e'})
* .execute();
* } catch (Auth0Exception e) {
* // Something happened
* }
* }
* </pre>
*
* @param emailOrPhone The email or phone number of the user. Must not be null.
* @param realm The realm to use. Typically "email" or "sms", unless using a custom Passwordless connection. Must not be null.
* @param otp The one-time password used to authenticate using Passwordless connections. Must not be null.
*
* @return A request to configure and execute
*
* @see <a href="https://auth0.com/docs/connections/passwordless/reference/relevant-api-endpoints">Using Passwordless APIs</a>
* @see com.auth0.client.auth.AuthAPI#startPasswordlessEmailFlow(String, PasswordlessEmailType)
* @see com.auth0.client.auth.AuthAPI#startPasswordlessSmsFlow(String)
*/
public AuthRequest exchangePasswordlessOtp(String emailOrPhone, String realm, char[] otp) {
Asserts.assertNotNull(emailOrPhone, "emailOrPhone");
Asserts.assertNotNull(realm, "realm");
Asserts.assertNotNull(otp, "otp");

String url = baseUrl
.newBuilder()
.addPathSegment(PATH_OAUTH)
.addPathSegment(PATH_TOKEN)
.build()
.toString();
TokenRequest request = new TokenRequest(client, url);
request.addParameter(KEY_CLIENT_ID, clientId);
request.addParameter(KEY_CLIENT_SECRET, clientSecret);
request.addParameter(KEY_GRANT_TYPE, "http://auth0.com/oauth/grant-type/passwordless/otp");
request.addParameter(KEY_USERNAME, emailOrPhone);
request.addParameter(KEY_REALM, realm);
request.addParameter(KEY_OTP, otp);
return request;
}

Expand Down Expand Up @@ -698,4 +750,89 @@ public AuthRequest exchangeCode(String code, String redirectUri) {
request.addParameter("redirect_uri", redirectUri);
return request;
}

/**
* Create a request to send an email containing a link or a code to begin authentication with Passwordless connections.
*
* <pre>
* {@code
* AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne");
* try {
* PasswordlessEmailResponse result = auth.startPasswordlessEmailFlow("[email protected]", PasswordlessEmailType.CODE)
* .execute();
* } catch (Auth0Exception e) {
* // Something happened
* }
* }
* </pre>
*
* @param email the email address to send the code or link to. Must not be null.
* @param type the type of the passwordless email request. Must not be null.
*
* @return a Request to configure and execute.
*
* @see <a href="https://auth0.com/docs/connections/passwordless/guides/email-otp">Passwordless Authentication with Email documentation</a>
* @see <a href="https://auth0.com/docs/api/authentication#get-code-or-link">Get code or link API reference documentation</a>
*/
public CustomRequest<PasswordlessEmailResponse> startPasswordlessEmailFlow(String email, PasswordlessEmailType type) {
Asserts.assertNotNull(email, "email");
Asserts.assertNotNull(type, "type");

String url = baseUrl
.newBuilder()
.addPathSegment(PATH_PASSWORDLESS)
.addPathSegment(PATH_START)
.build()
.toString();

CustomRequest<PasswordlessEmailResponse> request = new CustomRequest<>(client, url, "POST", new TypeReference<PasswordlessEmailResponse>() {
});
request.addParameter(KEY_CLIENT_ID, clientId);
request.addParameter(KEY_CLIENT_SECRET, clientSecret);
request.addParameter(KEY_CONNECTION, "email");
request.addParameter(KEY_EMAIL, email);
request.addParameter("send", type.getType());
return request;
}

/**
* Create a request to send a text message containing a code to begin authentication with Passwordless connections.
*
* <pre>
* {@code
* AuthAPI auth = new AuthAPI("me.auth0.com", "B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne");
* try {
* PasswordlessSmsResponse result = auth.startPasswordlessSmsFlow("+16511234567")
* .execute();
* } catch (Auth0Exception e) {
* // Something happened
* }
* }
* </pre>
*
* @param phoneNumber The phone number to send the code to. Must not be null.
*
* @return a Request to configure and execute.
*
* @see <a href="https://auth0.com/docs/connections/passwordless/guides/sms-otp">Passwordless Authentication with SMS documentation</a>
* @see <a href="https://auth0.com/docs/api/authentication#get-code-or-link">Get code or link API reference documentation</a>
*/
public CustomRequest<PasswordlessSmsResponse> startPasswordlessSmsFlow(String phoneNumber) {
Asserts.assertNotNull(phoneNumber, "phoneNumber");

String url = baseUrl
.newBuilder()
.addPathSegment(PATH_PASSWORDLESS)
.addPathSegment(PATH_START)
.build()
.toString();

CustomRequest<PasswordlessSmsResponse> request = new CustomRequest<>(client, url, "POST", new TypeReference<PasswordlessSmsResponse>() {
});
request.addParameter(KEY_CLIENT_ID, clientId);
request.addParameter(KEY_CLIENT_SECRET, clientSecret);
request.addParameter(KEY_CONNECTION, "sms");
request.addParameter("phone_number", phoneNumber);
return request;
}
}
32 changes: 32 additions & 0 deletions src/main/java/com/auth0/client/auth/PasswordlessEmailType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.auth0.client.auth;

/**
* Represents the type of the Passwordless email request.
*/
public enum PasswordlessEmailType {

/**
* Send a link.
*/
LINK("link"),

/**
* Send a code.
*/
CODE("code");

private final String type;

PasswordlessEmailType(String type) {
this.type = type;
}

/**
* Gets the type of Passwordless email request.
*
* @return the type of Passwordless email request.
*/
public String getType() {
return type;
}
}
50 changes: 50 additions & 0 deletions src/main/java/com/auth0/json/auth/PasswordlessEmailResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.auth0.json.auth;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* Represents the successful response when initiating the passwordless flow via email.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PasswordlessEmailResponse {

@JsonProperty("_id")
private String id;
@JsonProperty("email")
private String email;
@JsonProperty("email_verified")
private Boolean emailVerified;

/**
* The Identifier of this request.
*
* @return the identifier of this request.
*/
@JsonProperty("_id")
public String getId() {
return id;
}

/**
* Gets the email to which the code or link was sent to.
*
* @return the email to which the code or link was sent to.
*/
@JsonProperty("email")
public String getEmail() {
return email;
}

/**
* Whether the email address has been verified (true) or has not been verified (false).
*
* @return whether the email address has been verified (true) or has not been verified (false).
*/
@JsonProperty("email_verified")
public Boolean isEmailVerified() {
return emailVerified;
}
}
61 changes: 61 additions & 0 deletions src/main/java/com/auth0/json/auth/PasswordlessSmsResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.auth0.json.auth;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* Represents the successful response when initiating the passwordless flow via SMS.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class PasswordlessSmsResponse {
@JsonProperty("_id")
private String id;
@JsonProperty("phone_number")
private String phoneNumber;
@JsonProperty("phone_verified")
private Boolean phoneVerified;
@JsonProperty("request_language")
private String requestLanguage;

/**
* The Identifier of this request.
*
* @return the identifier of this request.
*/
@JsonProperty("_id")
public String getId() {
return id;
}

/**
* Gets the phone number the code was sent to.
*
* @return the phone number the code was sent to.
*/
@JsonProperty("phone_number")
public String getPhoneNumber() {
return phoneNumber;
}

/**
* Whether the phone number has been verified (true) or has not been verified (false).
*
* @return whether the phone number has been verified (true) or has not been verified (false).
*/
@JsonProperty("phone_verified")
public Boolean isPhoneVerified() {
return phoneVerified;
}

/**
* The language of the message sent, if set.
*
* @return The language of the message sent, or null if not changed from the default.
*/
@JsonProperty("request_language")
public String getRequestLanguage() {
return requestLanguage;
}
}
2 changes: 2 additions & 0 deletions src/test/java/com/auth0/client/MockServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ public class MockServer {
public static final String MGMT_JOB_POST_USERS_IMPORTS = "src/test/resources/mgmt/job_post_users_imports.json";
public static final String MGMT_JOB_POST_USERS_IMPORTS_INPUT = "src/test/resources/mgmt/job_post_users_imports_input.json";
public static final String MULTIPART_SAMPLE = "src/test/resources/mgmt/multipart_sample.json";
public static final String PASSWORDLESS_EMAIL_RESPONSE = "src/test/resources/auth/passwordless_email.json";
public static final String PASSWORDLESS_SMS_RESPONSE = "src/test/resources/auth/passwordless_sms.json";

private final MockWebServer server;

Expand Down
Loading

0 comments on commit ed07a5b

Please sign in to comment.