Tutorial: Login with email or phone

Serverless Authentication Login with email or phone

[[service]] is able to provide internally managed identities to authenticate users. Currently two kinds of identities are provided:

  • Email-based identities rely on the user's email address,
  • Phone-based identities rely on the user's mobile phone number (or at least a phone number which can receive SMS).

Credentials to authenticate these identities are either securely stored and encrypted in the [[service]] back-end (typically when users are prompted to enter a permanent password) or securely and randomly generated on demand (typically when the users are sent a one time password).

By default, only the email-based authentication method is enabled on new [[service]] applications. Email- and phone-based authentication methods may be freely enabled or disabled in the "authentication" tab of the [[console]].

[[service]] SDK exposes functions to create, authenticate and update internally managed (email- and phone-based) identities, letting the application developer have full control over the user interface.

In order to configure SMS sending for phone authentication, follow inscrutions at section Setting up SMS sending.

Creation of internally managed identities

Each created email- or phone-based identity also creates a [[service]] user account associated to this identity. In future versions, it will be possible to bind other identities (from any authentication method) to an existing account. Typically, the same end user will be allowed to authenticate either with one or more email- or phone-based credentials or with a google or facebook account. Consequently, applications should manage their users always using their account uid, but never using their identity id (i.e. their email address in case of email-based authentication method or their phone number in case of phone-based authentication method).

Email-based identities

In this case, the identifier of the identity to create must be a valid email address. After creation, the specified email address is sent a link with a token to validate it (see next section). To create a new email-based identity (and therefore a new [[service]] user account) for a given application, use the following snippet [[snippet]]:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Create a new email-based identity
ref.addAccount("password", {
  id: "macadamia@webcom.com",
  password: "nutbrittle"
}).then(function(identity) {
   console.log("User identity created and bound to the new account uid:", identity.uid);
 })
 .catch(function(error) {
   console.log("User identity not created:", error);
 });
Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
ref.addAccount("password", new AuthDetails("macadamia@webcom.com").setPassword("nutbrittle"), new OnAuth() {
  @Override
  public void onComplete(@Nullable AuthResponse response) {
    // User identity created and bound to a new account uid
    String uid = response.getUid();
  }
  @Override
  public void onError(WebcomError error) {
    // User identity not created
  }
});

The identity parameter received by the given callback is a JSON structure representing the created identity, with the following fields:

Field Type Description
provider String The authentication method associated to the newly created identity, here: "password".
providerUid String The identity ID, here the email address used to log in.
uid String The newly created user account ID, to which the identity is bound.
It is unique across all authentication methods.
createdAt Number The date (expressed in seconds since the Unix epoch) of creation of the identity.
providerProfile Object (optional) The profile data associated to the identity, which can be specified using the profile attribute within the identity details object passed as second argument of the addAccount() function.

In case of error, the onError method is called with an object describing the root cause of the error. Otherwise, the onComplete method is called with an authResponse representing the just created (email-based) identity.

Phone-based identities

In this case, the identifier of the identity to create must be a valid MSISDN without the "+" prefix, which is capable of receiving SMS. After creation, the specified phone number is sent a SMS with a one time temporary code that can validate it (see next section). To create a new phone-based identity (and therefore a new [[service]] user account) for a given application, use the following snippet [[snippet]]:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Create a new phone-based identity
ref.addAccount("phone", {
  id: "33612345678"
}).then(function(identity) {
   console.log("User identity created and bound to the new account uid:", identity.uid);
 })
 .catch(function(error) {
   console.log("User identity not created:", error);
 });
Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
ref.addAccount("phone", new AuthDetails("33612345678"), new OnAuth() {
  @Override
  public void onComplete(@Nullable AuthResponse response) {
    // User identity created and bound to a new account uid
    String uid = response.getUid();
  }
  @Override
  public void onError(WebcomError error) {
    // User identity not created
  }
});

The identity parameter received by the given callback is a JSON structure representing the created identity, with the following fields:

Field Type Description
provider String The authentication method associated to the newly created identity, here: "phone".
providerUid String The identity ID, here the MSISDN (without the "+" prefix) used to log in.
uid String The newly created user account ID, to which the identity is bound.
It is unique across all authentication methods.
createdAt Number The date (expressed in seconds since the Unix epoch) of creation of the identity.
providerProfile Object (optional) The profile data associated to the identity, which can be specified using the profile attribute within the identity details object passed as second argument of the addAccount() function.

In case of error, the onError method is called with an object describing the root cause of the error. Otherwise, the onComplete method is called with an authResponse representing the just created (phone-based) identity.

Verification of internally managed identities

Once created, an internally managed identity is set in the unverified state. It cannot be used to authenticate any user until it is verified. To verifiy it, one needs the identity identifier (email address or phone number), the identity descriptor returned by the previous identity creation method, and the verification token or code received by the user at her/his email address or phone number.

If the verification method succeeds, then the user is automatically authenticated with the verified identity, there is no need to authenticate explicitly.

Email-based identities

To verify an email-based identity, simply use the following snippet [[snippet]]. This code may typically be on the verification page that the verification email sent to the user links to:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Create a new phone-based identity
ref.verifyIdentity("password", "macadamia@webcom.com", {
  token: "...the.verification.token.received.by.the.user.at.her/his.email.address..."
}).then(function(identity) {
   console.log("User identity verified, now authenticated with uid:", identity.uid);
 })
 .catch(function(error) {
   console.log("User identity verification failed:", error);
 });
Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
ref.verifyIdentity("password", "macadamia@webcom.com",
    new VerificationData("...the.verification.token.received.by.the.user.at.her/his.email.address..."),
    new OnAuth() {
        @Override
        public void onComplete(@Nullable AuthResponse response) {
            // User identity verified and user authenticated
            String uid = response.getUid();
        }
        @Override
        public void onError(WebcomError error) {
            // User identity not verified
        }
    });

Alternatively, the identity may be verified using the following HTTP REST request:

curl -X PUT "[[baseUrl]]/auth/v2/<your-app>/password/identities/<id>/verify"
            -d '{"token":"<verification-token>"}' -H 'Content-type: application/json'

where:

  • <your-app> must be replaced with your application identifier. If your verification page is not application-specific, it can be passed to the verification page as a query-string using the %app% macro within the message template edited on the [[console]].
  • <id> must be replaced with the email address of the identity to verify. It can be passed to the verification page as a query-string using the %id% macro within the message template edited on the [[console]].
  • <verification-token> must be replaced with the token received by the user at her/his email address. It can be passed to the verification page as a query-string using the %token% macro within the message template edited on the [[console]].

In case of success, the HTTP request returns a [[service]] token that authenticates the user with the verified identity.

If the final user doesn't receive the verification token in her/his mailbox for some reason (although it is seldom it may occur as the email dispatching infrastructures are not guaranteed 100% reliable), you can ask the [[service]] back-end to send it again so that the user has a chance to finalize her/his email address verification:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Send a message with a verification token
ref.sendVerificationCode("password", "macadamia@webcom.com") // the email for which verification token must be resent
    .then(() => console.log("The verification token was sent successfully"))
    .catch(error => console.log("The verification token was not sent:", error));
// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Send a message with a verification token
ref.sendVerificationCode("password", "macadamia@webcom.com") // the email for which verification token must be resent
    .then(() => console.log("The verification token was sent successfully"))
    .catch(error => console.log("The verification token was not sent:", error));
Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
ref.sendVerificationCode("password", "macadamia@webcom.com", new OnAuth(){
  @Override
  public void onComplete(@Nullable AuthResponse response) {
    // The verification token was sent successfully
  }
  @Override
  public void onError(WebcomError error) {
    //The verification token was not sent
  }
});
Phone-based identities

To verify a phone-based identity, simply use the following snippet [[snippet]]. This code may typically follow the one at the previous section to create the identity:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Create a new phone-based identity
ref.verifyIdentity("phone", "33612345678", {
    auth: {/*the identity object resulting from the previous call to the addAccount(...) method*/},
    password: "<the.one.time.password.received.by.the.user.on.her/his.phone"
}).then(function(identity) {
    console.log("User identity verified, now authenticated with uid:", identity.uid);
}).catch(function(error) {
    console.log("User identity verification failed:", error);
});
Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
ref.verifyIdentity("phone", "33612345678",
    new VerificationData(/*AuthResponse object resulting from the previous call to the addAccount(...) method*/,
                         "...the.one.time.password.received.by.the.user.on.her/his.phone..."),
    new OnAuth() {
        @Override
        public void onComplete(@Nullable AuthResponse response) {
            // User identity verified and user authenticated
            String uid = response.getUid();
        }
        @Override
        public void onError(WebcomError error) {
            // User identity not verified
        }
    });

If the final user doesn't receive the SMS containing the verification code, as this code is valid for a very short time, then the whole process of creating the user and verifying her/his phone-based identity must be replayed. There is no mean to send again the same verification code.

Login with internally managed identities

Email-based identities

Once confirmed, an email-based identity can be used to log in, using the following snippet [[snippet]]:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Log in
ref.authWithPassword({
  email    : "macadamia@webcom.com",
  password : "my-password"},
  function(error, auth) {
   if (error) {
     switch (error.code) {
       case "INVALID_CREDENTIALS":
         console.log("The email or password is incorrect.");
         break;
       case "PROVIDER_DISABLED":
         console.log("The email-based authentication method is disabled in the application. It must be enabled in the [[console]].");
         break;
       default:
         console.log("An unexpected error occurs, please retry and contact your administrator.", error);
     }
   } else {
     console.log("Authentication succeeded", auth);
   }
 }
);
Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
ref.authWithPassword("macadamia@webcom.com", "my-password", new OnAuth(){
  @Override
  public void onComplete(@Nullable AuthResponse authResponse) {
    // User account authenticated
    String token = authResponse.getToken();
  }
  @Override
  public void onError(WebcomError error) {
    // User account not authenticated
  }
});

The auth parameter received by the authentication callback function is a JSON structure representing the identity used to log in.

In addition to the generic fields, it includes the following ones:

Field Type Description
provider String Equals "password".
providerUid String The email address of the logged in user.
providerProfile Object (optional) The profile of the logged in user, when specified at creation with the addAccount() method.

In case of error, the onError method is called with an object describing the root cause of the error. Otherwise, the onComplete method is called with an authResponse representing the identity used to log in.

Phone-based identities

Login with a phone identity is a 2-steps process:

  1. Send a SMS to the user with the phone number containing a one time password (OTP) dedicated to the login operation,
  2. Log the user in by providing, in addition to the user's phone number, the identifier of the OTP previously sent (called challenge) and the OTP itself received by the user.

To do so, use the following snippet [[snippet]]:

// Create a connection to the back-end
let ref = new Webcom("<your-app>");
let phoneId = "33612345678";
// Send the OTP
ref.sendOtp("phone", phoneId)
    .then(challenge => {
        askTheUserForTheReceivedOtp() // function expected to prompt the user with an input box to enter the OTP
            .then(otp => {
                // Actually log in
                ref.authWithPhone({id: phoneId, challenge: challenge, password: otp})
                    .then(auth => console.log(`Logged in with uid: ${auth.uid}`))
                    .catch(error => console.log(`Login failed: ${error}`));
            })
            .catch(() => console.log('The user gave up the login operation...'));
    })
    .catch(error => console.log(`Failed to send OTP: ${error}`));
// Create a connection to the back-end
Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
String phoneId = "33612345678";
// Send the OTP
ref.sendOtp("phone", phoneId, new OnValue() {
  @Override
  public void onValue(JSONValue challenge) {
    String otp = askTheUserForTheReceivedOtp(); // method expected to prompt the user with an input box to enter the OTP
    ref.authWithPhone(phoneId, challenge.stringify(), otp, new OnAuth() {
      @Override
      public void onComplete(@Nullable AuthResponse authResponse) {
        // User account authenticated
        String token = authResponse.getToken();
      }
      @Override
      public void onError(WebcomError error) {
        // User account not authenticated
      }
    });
  }
  @Override
  public void onError(WebcomError error) {
    // Failed to send OTP
  }
});

The auth parameter received by the authentication callback function is a JSON structure representing the identity used to log in.

In addition to the generic fields, it includes the following ones:

Field Type Description
provider String Equals "phone".
providerUid String The MSISDN of the logged in user (without the "+" prefix).
providerProfile Object (optional) The profile of the logged in user, when specified at creation with the addAccount() method.

In case of error (when sending the OTP or when authenticating), the onError method is called with an object describing the root cause of the error.

Otherwise, after successful sending of the OTP, the onValue method is called with the identifier of the sent OTP (or challenge) passed as a JSONValue. Then, after successful authentication, the onComplete method is called with an authResponse representing the identity used to log in.

Update of internally managed identities

Updating password

There are 2 cases to update the password of an email-based identity: either you know the currently associated password, or you don't know (or have forgotten) it. In the first case, simply use the changePassword() method [[snippet]]:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Update the password
ref.changePassword("chocolate@webcom.com", // email
  "pralinesandcaramel", // current password
  "midnightcookies", // new password
  function(error) {
    if (error === null) {
      console.log("Password changed successfully");
    } else {
      console.log("Error changing password:", error);
    }
  });
// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Update the password
ref.changePassword("chocolate@webcom.com", // email
  "pralinesandcaramel", // current password
  "midnightcookies" // new password
).then(function() {
  console.log("Password changed successfully");
}).catch(function(error) {
  console.log("Error changing password:", error);
});
Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
ref.changePassword("chocolate@webcom.com", "pralinesandcaramel", "midnightcookies", new OnAuth(){
  @Override
  public void onComplete(@Nullable AuthResponse response) {
    // Password changed successfully
  }
  @Override
  public void onError(WebcomError error) {
    // Error changing password
  }
});

In the second case, use the sendPasswordResetEmail() method [[snippet]]:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Send a message to reset the password
ref.sendPasswordResetCode("password", "macadamia@webcom.com") // the email for which the password must be reset
    .then(() => console.log("The Reset password email was sent successfully"))
    .catch(error => console.log("The Reset password email was sent successfully"));
// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Send a message to reset the password
ref.sendPasswordResetCode("password", "macadamia@webcom.com") // the email for which the password must be reset
    .then(() => console.log("The Reset password email was sent successfully"))
    .catch(error => console.log("The Reset password email was not sent:", error));
Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
ref.sendPasswordResetCode("password", "chocolate@webcom.com", new OnAuth(){
  @Override
  public void onComplete(@Nullable AuthResponse response) {
    // The Reset password email was sent successfully
  }
  @Override
  public void onError(WebcomError error) {
    //The Reset password email was not sent
  }
});

The [[service]] back-end then emails a message to the specified user, with a link to safely change her/his password without need of the current password. The default message links to a generic page served by the [[service]] back-end.

In order to customize the user experience for resetting an identity password, you can edit the message template in the "authentication" tab of the [[console]]. You can then include a link to a customized reset page from your application. In turn, this page must end in requesting the following HTTP REST [[service]] API:

curl -X PUT "[[baseUrl]]/auth/v2/<your-app>/password/update"
     -H "Authorization: Bearer <reset-token>"
     -H "Content-Type: application/x-www-form-urlencoded"
     -d "newPassword=<new-password>"

Where:

  • <your-app> must be replaced with your application identifier. If your password reset page is not application-specific, it can be passed to the password reset page as a query-string using the %app% macro within the message template edited on the [[console]].
  • <reset-token> must be replaced with the token generated by the [[service]] back-end when it sent the password reset message to the user. It can be passed to the reset page as a query-string using the %token% macro within the message template edited on the [[console]].
  • <new-password> must be replaced with the new password to associate to the user's email. It typically comes from a form displayed by the password reset page.
    Note: this parameter is part of an URL-encoded form data, therefore it has to be percent-encoded with UTF-8 encoding.
Updating profile

As mentioned above, when creating an email- or phone-based identity, a profile may be associated to it, which is specified as a JSON object. In order to update this profile, you can use the updateIdentityProfile method. It requires that you are already authenticated with an email- or phone-based identity, it will then update the profile associated to this currently authenticated identity.

// Authenticate with an email-based identity...
var ref = new Webcom("<your-app>");
ref.authWithPassword({email: "macadamia@webcom.com", password: "my-password"}, function(error) {
    if (!error) {
        // ...and then update its profile
        ref.updateIdentityProfile({age: 42, zipcode: 22300})
            .then(() => console.log("The profile has been successfully updated"))
            .catch(error => console.log("The update failed:", error));
    }
})
// Authenticate with an email-based identity...
let ref = new Webcom("<your-app>");
ref.authWithPassword({email: "macadamia@webcom.com", password: "my-password"})
    .then(() => {
        // ...and then update its profile
        ref.updateIdentityProfile({age: 42, zipcode: 22300})
            .then(() => console.log("The profile has been successfully updated"));
        })
    .catch(error => console.log("The update failed:", error));
// Authenticate with an email-based identity...
Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
ref.authWithPassword("macadamia@webcom.com", "my-password", new OnAuth() {
    @Override
    public void onComplete(@Nullable AuthResponse response) {
        // ...and then update its profile
        ref.updateIdentityProfile("age", 42, new OnAuth() {
            @Override
            public void onComplete(@Nullable AuthResponse response) {
                // profile updated!
            }
            @Override
            public void onError(WebcomError error) {
                ...
            }
        });
    }
    @Override
    public void onError(WebcomError error) {
        ...
    }
});

If no user is currently authenticated or if the currently authenticated user is not identified with an email- or phone-based identity, then the method will end in rejecting the returned promise with an error.