Tutorial: Serverless Authentication

Serverless Authentication

The authentication component makes it possible to reliably identify application users, in order to finely control their access to data. It additionally retrieves user identity details, which can then be used by applications to customize their behavior (user experience, specific configuration...).

Three classes of authentication methods are currently available:

  • anonymous login, which associates a temporary (virtual) user to each connection,
  • own authentication methods, based on identities directly managed by the [[service]] backend. Among them, 2 methods are currently available:
    • email-based login, based on the user email address and a password set by the user or a one time password sent to the user by email,
    • phone-based login, based on the user phone number (MSISDN) and a one time password sent to the user by SMS,
  • delegated authentication methods, based on a 3rd-party identity provider. Among them, we find:
    • implicit login of mobile devices (only for Orange customers), based on the mobile data network and the SIM card of the device,
    • standard authentication delegation, based on the OAuth2.0 standard for authorisation (including OIDC and Mobile Connect),
    • custom authentication delegation for platforms that have their own specific authentication mechanism.

Each method returns:

  • an identity (or providerUid), which is a user identifier unique within each authentication method,
  • a [[service]] account (or uid), which is a user identifier unique across all authentication methods for a given application.

Several identities may be bound to the same [[service]] account. For example, the same end user can log in using either her/his email (with the email-based login) or her/his facebook login (with the standard authentication delegation method). Applications should therefore rely on accounts/uid rather than identities/providerUid.

Use the best suited authentication method

Here are the detailed step by step guides for each available authentication method. Choose the one that best fits your needs:

Method Description
Anonymous login Authentication without creating an actual account. This method allows to protect users' data for the duration of a "guest" session only.
Email login [[service]] own authentication, based on email & password.
Phone login [[service]] own authentication, based on phone number & one time password
Implicit mobile login (for Orange customers only) Authentication based on the mobile device and the mobile data network used.
OAuth2 login Authentication delegation to an OAuth2.0 compliant third-party identity provider.
Custom login Authentication delegation to an existing specific third-pary authentication system. It allows to integrate with any other authentication component.

Each authentication method can be enabled (or disabled) and setup in the "authentication" tab of the [[console]].

Authentication information

In a [[service]] application, you can register an authentication callback, and later unregister it, using the following snippet [[snippet]]:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Define an authentication callback
var authCb = 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/password 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);
  }
};
// Register it
ref.registerAuthCallback(authCb);
// { ...
// Within this fragment, 'authCb' is called each time the authentication state changes
// ... }
// Unregister it
ref.unregisterAuthCallback(authCb)
// Create a connection to the back-end
try {
    Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
    OnAuth listener = new OnAuth() {
        @Override
        public void onComplete(@Nullable AuthResponse response) {
            if(response == null){ // Successfull Logout
                System.out.println("Logout succeeded");
            } else{ // Successfull login
                System.out.println("Authentication succeeded");
            }
        }
        @Override
        public void onError(WebcomError error) {
            System.out.println(error.getMessage());
        }
    };
    ref.registerAuthCallback(listener);
    // { ...
    // Within this fragment, 'listener' is called each time the authentication state changes
    // ... }
} catch (WebcomException e) {
    e.printStackTrace();
}

The authentication callback is called each time the authentication state changes (typically when a user is logged in or out or the user session expires).

When a login operation fails, the error parameter receives a JSON structure describing the encountered failure with two mandatory attributes: code and message (the table at the end of this page gives the most usual error codes):

{
  "code": "INVALID_TOKEN",
  "message": "The requested operation cannot be fulfilled because the content or privilege of the provided token is invalid."
}

When a user is successfully logged in, the auth parameter receives a JSON structure representing the identity used to log in with the following fields:

Field Type Description
webcomAuthToken String The [[service]]-signed authentication token for this session.
expires Number Expiration date of the session token (in seconds since the Unix epoch).
provider String The authentication method used to log in (e.g. password, facebook...).
providerUid String The ID of the identity used to log in. It is unique within a given authentication method.
createdAt Number The date (expressed in seconds since the Unix epoch) of creation of the identity used to log in (warning: this is not the creation date of the user account).
uid String The user account ID. It is unique across all authentication methods.
displayName String (optional) a human-readable description of the logged in user.

Depending on the authentication method, some additional fields may be provided, see the documentation of each available authentication method for more details.

The generated authentication token contains itself a subset of the above JSON structure, with at least the provider, providerUid and uid fields. It may contains additional fields specific to the authentication method used. This structure is passed to security rules within the auth variable (which is different from the auth parameter of the authentication callback) when a user is logged in (or null if no user is logged in). Security rules can therefore be easily adapted to the authentication method used or the [[service]] account uid associated to the logged in end user, as shown in this complete example.

When a login operation fails, the onError method is called. The WebcomError instance contains a code and a message describing the root cause of the error.

When a user is successfully logged in, the onComplete method is called with a non null response. The passed AuthResponse object contains the used authentication method, the [[service]] account uid associated to the logged in end user, the providerUid identifier, as well as possibly additional information specific to the used authentication method.

When a user is successfully logged out, the onComplete method is called with a null response.

Resume

When starting an application or an activity, it is often useful to resume the authentication state, that is, refresh it from the last authentication token stored within the local (or app) storage. Typically, if it is still valid, it can be reused for authentication without requiring the user to re-authenticate explicitly.

If some authentication callbacks are registered before resuming, then they are invoked with the resumed state.

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Register an authentication callback
ref.registerAuthCallback(myAuthCb);
// Resume
ref.resume();
// 'myAuthCb' will be called with the resumed authentication state
try {
    // Create a connection to the back-end
    Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
    // Register an authentication callback
    ref.registerAuthCallback(myOnAuthListener);
    // Resume
    ref.resume();
    // 'myOnAuthListener' will be called with the resumed authentication state
} catch (WebcomException e) {
    e.printStackTrace();
}

Login & logout

The login procedure directly depends on the authentication method. For example, with email-based authentication method, use 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/password 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);
    }
  });
try{
    // Create a connection to the back-end
    final Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
    final OnAuth listener = new OnAuth(){
        @Override
        public void onComplete(@Nullable AuthResponse authResponse) {
            Log.v(TAG,"onComplete");
            Log.v(TAG,"uid :" + authResponse.getUid());
        }
        @Override
        public void onError(WebcomError webcomError) {
            Log.v(TAG,"onError");
            Log.v(TAG,"Error : " + webcomError.getMessage());
        }
    };    
    ref.authWithPassword("macadamia@webcom.com", "my-password", listener);
} catch (Exception e) {
    Log.e(TAG, e.getMessage(), e);
}
// Create a connection to the back-end
let ref = WCWebcom(url: "file://io./base/<your-app>")
ref?.auth(withMail: "macadamia@webcom.com", andPassword: "my-password", andRememberMe: false, onComplete: { (error, auth) in
            if (error != nil){
                print(error.debugDescription)
            }else {
               print("Authentication succeeded  Token  : , \(auth!.authToken)");
               print("Authentication succeeded  Email  : , \(auth!.email)");
            }

        })

The logout procedure is common to all authentication methods, simply use the following snippet [[snippet]]:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Log out
ref.logout(function (error) {
   if (error) {
     console.log("The log-out failed: " + error.message);
   } else {
     console.log("Logged out successfully!");
   }
});
try{
    // Create a connection to the back-end
    final Webcom ref = new Webcom("[[baseUrl]]/base/<your-app>");
    ref.logout(new OnComplete(){
       @Override
       public void onComplete() {
            Log.v(TAG,"logout complete ");
       }
       @Override
       public void onError(WebcomError error){
            Log.v(TAG,"onError : " + error.getMessage());
       }       
    });
} catch (Exception e) {
    Log.e(TAG, e.getMessage(), e);
}
let ref = WCWebcom(url: "file://io./base/<your-app>")
ref?.logout(callback: { (error) in
            if (error != nil){
                print("The log-out failed: \(error.debugDescription) ")
            }else {
                print("Logged out successfully!")
            }
        })

Automatic reconnection

When the Internet connection is lost and returns, the [[service]] clients will automatically re-authenticate to the server.

Multiple authentications

Several authentications on the same [[service]] database with different credentials simultaneously is not possible, even on different [[service]] references. The authentication state is global and applies to all references to a given [[service]] database. However, references to different [[service]] databases can authenticate independently.

Deletion of accounts and identities

The [[service]] API provides with two commands to delete user's accounts. Both these commands act equally for all authentication methods on the currently logged in user. Thus, if no user is currently logged in, both these commands fail.

As these operations are particularly sensible, they can also fail if the current user has logged in for a too long time (the error code TOKEN_TOO_LONG is then received). In other words, one can delete an identity or an account of only freshly logged in users. When deleting an identity or an account, one must therefore plan to force the user to explicitly re-authenticate if s/he were logged in for a too long time.

The first command makes it possible to delete the identity used by the user to authenticate from her/his account, whithout deleting the account. It can be used to remove an identity from an account associated to several identities. If the deleted identity is the last one associated to the user's account, then the account is also deleted. Otherwise, it would result in a "ghost" [[service]] account with no authentication means.

In order to delete the [[service]] account of the currently logged in user, simply use the following snippet [[snippet]]:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Resume the authentication state
ref.resume();
// Delete the user account
ref.removeAccount()
    .then(() => console.log("Account deleted!"))
    .catch(error => {
        if (error.code === "TOKEN_TOO_OLD") {
            console.log("The user must re-authenticate before deleting her/his account");
        } else {
            console.log("Failed deleting the user account!");
        }
    });

If you need to delete only the current identity of the user without deleting her/his whole [[service]] account, use the following snippet [[snippet]]:

// Create a connection to the back-end
var ref = new Webcom("<your-app>");
// Resume the authentication state
ref.resume();
// Delete the user identity
ref.removeIdentity()
    .then(() => console.log("Identity deleted!"))
    .catch(error => {
        if (error.code === "TOKEN_TOO_OLD") {
            console.log("The user must re-authenticate before deleting her/his identity");
        } else {
            console.log("Failed deleting the user identity!");
        }
    });

Authentication errors

This table gives the most common error codes passed to the error parameter of the authentication callback (the list is not exhaustive):

Error Code Authentication
Methods
Description
UNKNOWN_APPLICATION all The authentication request was on an nonexistent [[service]] application.
PROVIDER_DISABLED all The authentication method is disabled for the application. You must enable it in the "authentication" tab of the [[console]].
INVALID_PROVIDER_SETTINGS all The settings of the authentication method to update are invalid.
TOKEN_TOO_OLD all The provided authentication token must be fresher for the requested operation to complete, please re-authenticate before trying it again
INVALID_CREDENTIALS
INVALID_PASSWORD (sdk≤1.3.2)
email The given email or password are incorrect.
IDENTITY_TEMPORARILY_LOCKED email There have been too much signin failure with this identity. It will be locked down in a while.
MISSING_EMAIL_OR_PASSWORD email The authentication request is missing email or password.
EXISTING_IDENTITY
EMAIL_TAKEN (sdk≤1.3.2)
email The identity to create cannot be created because an identity with the same email already exists.
UNVERIFIED_IDENTITY
UNVERIFIED_ACCOUNT (sdk≤1.3.2)
email The identity used to log in has not been verified.
TOO_WEAK_PASSWORD email The provided password is not strong enough (when creating an email/password identity or updating a password).
UNSUPPORTED_PROVIDER OAuth2 The OAuth2.0 identity provider is not supported by [[service]].
MISSING_PARAMETER OAuth2 The authentication request is missing some parameter.
UNAUTHORIZED_REQUEST_ORIGIN OAuth2 The authentication request was emitted from a non approved web origin. Authorized origins can be set in the "authentication" tab of the [[console]].
INVALID_STATE OAuth2 The state parameter of the authentication request is invalid (maybe the request was intercepted by some unwanted people).
INVALID_TOKEN custom The token used to authenticate is invalid (may be bad-signed, expired or with wrong information).