Tutorial: Signing up

Serverless Authentication Signing up

The sign-up operation creates a new identity, that represents the end user against the chosen authentication method, and associate it with a Webcom account. The Webcom SDK manages the association with a Webcom account contextually:

  • If no user is signed in when the sign-up operation is performed, then a new Webcom account is created, which to associate the signed up identity with,
  • If a user is signed in when the sign-up operation is performed, then the signed up identity is associated with the Webcom account of the already signed user.

In other words, in order to add a new identity (e.g. a phone-based login) to an existing user account, this user must first sign in to that application using an already existing identity (e.g. her/his facebook login). On the contrary, in order to create a new account for a given user (e.g. an email-based login), even if s/he has already one on the target application (e.g. a facebook login), s/he must first be signed out before signing her/his new identity up.

Signing up using guest login

Guest login is a special operation as it associates an end user with a temporary unique Webcom account. That means that each sign in operation results in a new Webcom account (until the corresponding authentication token is forgotten). In other words, for the guest authentication method, sign in and sign up operations are identical.

Signing in using guest login is forbidden if a user is already signed in! Indeed, allowing such an operation would mean signing a new guest identity up in the context of an existing Webcom account, that is, adding a guest identity to a user account, which is meaningless as nobody can sign an already guest identity in.

Signing up using third-party login

All authentication methods based on third-party login (including any OAuth2-based login, custom login and implicit mobile network-based login) have no explicit sign up operation. Actually their sign in operation acts as a sign-up one as soon as the authenticated third-party identity is not yet associated with an existing Webcom account for a given application. Of course, once a third-party identity is associated with a Webcom account of a given application, then it is guaranteed that all further operation that sign this identity in will always authenticate this Webcom account.

graph LR SignIn["Sign id In"] --> TestUid{"∃ uid
iduid?"} TestUid --yes--> AuthUid["Authenticate uid"] TestUid --no--> Create["Create a new account uid
Associate id with uid"] Create --> AuthUid

Signing up using internal login

Signing a Webcom internally managed identity up is an explicit operation with a dedicated API within the Webcom SDK. Its prerequisite is that the identity to sign up is not already bound to an existing Webcom account of the considered application. If so, the operation fails. Otherwise, the signed up identity is bound to either a new Webcom account for the application (created with a unique identifier) or an already existing Webcom account for the application.

Although not yet completely activated on the Webcom back end, each kind of internally managed identity (i.e. email- and phone-based identities) may be created following one of the following two authentication methods:

  • Permanent password authentication: in this case, the end user sets up a personal password (expected to be strong) s/he must provide for each further sign in operation.
    This option is currently activated on the Webcom back end only for email-based identities.
  • One time password authentication: in this case, the Webcom back end generates a new one time password at authentication operation (sign up and sign in) and sends it to the end user (by email or SMS).
    This option is currently activated on the Webcom back end only for phone-based identities.

In both cases, a newly created identity by a sign-up operation must be verified before being allowed to sign in. The delay for verifying identities may be set up within the "authentication" tab of the Webcom developer console. The verification process is specific to each kind of internally managed identity:

  • For email-based identities, it consists in providing the Webcom back end with a token sent to the user by email at sign-up. To ease the procedure from the user point of view, the actually sent email includes a link containing this token, which fires the verification request to the back end.
  • For phone-based identities, it consists in providing the Webcom back end with a one time password generated and sent to the user by SMS at sign up.

Email-based identities

The identifiers of email-based identities must be valid email addresses. After creation, the specified email address is sent a link with a token to validate the identity (see next section). To create a new email-based identity for a given application, use the following snippet (replace “<your-app>” with your actual application identifier):

// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
// Get an instance of the authentication service
const auth = app.authentication;
// uncomment the following line to bind the identity to the currently signed-in Webcom account
//auth.useCurrentContextForNextAuthOperation();
auth.addIdentity("password", {
        id: "macadamia@webcom.com",
        password: "nutbrittle"
    })
    .then((identity) => console.log("User identity created and bound to the following account uid:", identity.uid))
    .catch((error) => console.log("User identity not created:", error));

The identity parameter returned by the promise is a JSON object representing the created identity, with the following properties:

Property Type Description
provider string The authentication method associated to the newly created identity, here: "password".
providerUid string The identity ID, here the specified email address.
uid string The 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.
It can be specified using the profile property within the identity details object passed as second argument of the addIdentity() function.
import com.orange.webcom.sdk.authentication.AuthenticationService.Context

val myApp = WebcomApplication.default // the app defined by the 'webcom.properties' asset file
val authenticator = myApp.authenticationService

val personalPasswordMethod = authenticator.getPersonalPasswordMethod(Provider.Email, "macadamia@webcom.com")
personalPasswordMethod.attachIdentity(password = "nutbrittle", context = Context.New) { // it: WebcomResult<Unit>
  when (it) {
    is WebcomResult.Success -> print("User identity successfully created")
    is WebcomResult.Failure -> print("Failed creating user identity: ${it.error.message}")
  }
}

If the context parameter of the PersonalPasswordMethod.attachIdentity() method is set to Context.New, the newly created email-based identity is attached to a new Webcom account (hence, a new account is also created). If it is set to Context.Current, the newly created email-based identity is attached to the currently authenticated Webcom account. In this case, a new identity is added to an existing account.

The attachIdentity() method accepts additional arguments to customize the user profile attached to the newly created identity (displayName and profile) and the email sent to the user to verify this identity (messageDetails).

let authenticationService = Webcom.defaultApplication.authenticationService
let eMailMethod = AuthenticationMethodInternal(eMail: "macadamia@webcom.com")
authenticationService.initializeAuthentication(with: eMailMethod)
eMailMethod.setStaticPassword("nutbrittle")
eMailMethod.attachIdentity { result in
    switch result {
    case .success:
        print("User identity created!")
    case let .failure(error):
        print("Error:", error)
    }
}

Phone-based identities

The identifiers of phone-based identities must be valid MSISDN without the "+" prefix, which are capable of receiving SMS. After creation, the specified phone number is sent an SMS with a one time temporary code that will be required to validate the identity (see next section). To create a new phone-based identity for a given application, use the following snippet (replace “<your-app>” with your actual application identifier):

// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
// Get an instance of the authentication service
const auth = app.authentication;
// uncomment the following line to bind the identity to create to the currently signed in Webcom account
//auth.useCurrentContextForNextAuthOperation();
auth.addIdentity("phone", {
        id: "33612345678"
    })
    .then((identity) => console.log("User identity created and bound to the following account uid:", identity.uid))
    .catch((error) => console.log("User identity not created:", error));

The identity parameter returned by the promise is a JSON object representing the created identity, with the following properties:

Property Type Description
provider string The authentication method associated to the newly created identity, here: "phone".
providerUid string The identity ID, here the specified MSISDN (without the "+" prefix).
uid string The 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.
It can be specified using the profile property within the identity details object passed as second argument of the addIdentity() function.
import com.orange.webcom.sdk.authentication.AuthenticationService.Context

val myApp = WebcomApplication.default // the app defined by the 'webcom.properties' asset file
val authenticator = myApp.authenticationService

val oneTimePasswordMethod = authenticator.getOneTimePasswordMethod(Provider.Phone, "33612345678")
oneTimePasswordMethod.attachIdentity(context = Context.New) { // it: WebcomResult<Unit>
  when (it) {
    is WebcomResult.Success -> print("User identity successfully created")
    is WebcomResult.Failure -> print("Failed creating user identity: ${it.error.message}")
  }
}

Like with email-based identities, if the context parameter of the OneTimePasswordMethod.attachIdentity() method is set to Context.New, the newly created phone-based identity is attached to a new Webcom account (hence, a new account is also created). If it is set to Context.Current, the newly created phone-based identity is attached to the currently authenticated Webcom account. In this case, a new identity is added to an existing account.

The attachIdentity() method accepts additional arguments to customize the user profile attached to the newly created identity (displayName and profile) and the SMS sent to the user to verify this identity (messageDetails).

let authenticationService = Webcom.defaultApplication.authenticationService
let smsMethod = AuthenticationMethodInternal(msisdn: "33612345678")
authenticationService.initializeAuthentication(with: smsMethod)
phoneMethod.attachIdentity { result in
    switch result {
    case .success:
        print("User identity created!")
    case let .failure(error):
        print("Error:", error)
    }
}

Verifying internal 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 verify 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 again explicitly.

Email-based identities

To verify an email-based identity, simply use the following snippet. This code may typically be on the verification page that the verification email sent to the user links to (replace “<your-app>” with your actual application identifier):

// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
const auth = app.authentication;
auth.verifyIdentity("password", "macadamia@webcom.com", {
        token: "...the.verification.token.received.by.the.user.at.her/his.email.address..."
    })
    .then(authDetails => console.log("User identity verified, now authenticated with uid:", authDetails.uid))
    .catch(error => console.log("User identity verification failed:", error));

The authDetails parameter returned by the promise is a JSON object representing the verified (and signed in) identity, actually the same as the one got when signing in with an email-based identity.

The email sent to the user contains a link to a web page that is expected to perform the relevant request to the Webcom back end to actually verify her/his identity. This web page is usually implemented in JavaScript/HTML and so is based on the Webcom SDK for JavaScript.

However, if, for some reason, you need to implement this verification using the Webcom SDK for Android, here is the Kotlin snippet to do so. It follows the previous one to create a new email-based identity:

personalPasswordMethod.verifyAndAuthenticate("...the.verification.token.received.by.the.user.in.her/his.mailbox...") {
    // it: WebcomResult<AuthenticationDetails>
    when (it) {
      is WebcomResult.Success -> print("User identity successfully verified and signed-in: ${it.result.uid}")
      is WebcomResult.Failure -> print("Failed verifying the user identity: ${it.error.message}")
    }
}

The email sent to the user contains a link to a web page that is expected to perform the relevant request to the Webcom back end to actually verify her/his identity. This web page is usually implemented in JavaScript/HTML and so is based on the Webcom SDK for JavaScript.

However, if, for some reason, you need to implement this verification using the Webcom SDK for iOS, here is the Swift snippet to do so. It follows the previous one to create a new email-based identity:

eMailMethod.setVerificationToken("...the.verification.token.received.by.the.user.at.her/his.email.address...")
authenticationService.continueAuthentication { result in
    switch result {
    case let .success(details):
        print("User identity verified and user authenticated:", details.uid)
    case let .failure(error):
        print("User identity not verified:", error)
    }
}

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

curl -X PUT "https://io.datasync.orange.com/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 Webcom developer 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 Webcom developer 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 Webcom developer console.

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

In some (seldom) circumstances, the final user may not receive the verification token in her/his mailbox (indeed, email dispatching infrastructures are not guaranteed 100% reliable).
You can then ask the Webcom back end to send it again so that the user has a chance to finalize her/his email address verification, using the following snippet (replace “<your-app>” with your actual application identifier):

// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
const auth = app.authentication;
auth.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));
// following the previous snippet to create a new email-based identity:
emailPasswordMethod.resendVerificationCode {
  when (it) { // it: WebcomResult<Unit>
    is WebcomResult.Success -> print("Token sent successfully")
    is WebcomResult.Failure -> print("Failed to send the token: ${it.error.message}")
  }
}
let authenticationService = Webcom.defaultApplication.authenticationService
let eMailMethod = AuthenticationMethodInternal(eMail: "macadamia@webcom.com")
authenticationService.initializeAuthentication(with: eMailMethod)
eMailMethod.requestVerificationTokenAgain { result in
    switch result {
    case .success:
        print("The verification token was sent successfully")
    case let .failure(error):
        print("The verification token was not sent:", error)
    }
}
curl -X PUT "https://io.datasync.orange.com/auth/v2/<your-app>/password/identities/<id>/sendVerification"

Replace <id> with the url-encoded email for which verification token must be resent.

Phone-based identities

To verify a phone-based identity, simply use the following snippet. This code may typically follow the one at the previous section to create the identity (replace “<your-app>” with your actual application identifier):

// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
const auth = app.authentication;
auth.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(authDetails => console.log("User identity verified, now authenticated with uid:", authDetails.uid))
    .catch(error => console.log("User identity verification failed:", error));

The authDetails parameter returned by the promise is a JSON object representing the verified (and signed in) identity, actually the same as the one got when signing in with a phone-based identity.

fun askTheUserForTheReceivedCode(callback: (String) -> Unit): Unit {
  // ...
}

askTheUserForTheReceivedCode { code: String ->
  phoneOtpMethod.verifyAndAuthenticate(code) { auth: WebcomResult<AuthenticationDetails> ->
    when (auth) {
      is WebcomResult.Success -> print("New phone identity verified and signed-in: ${auth.result.uid}")
      is WebcomResult.Failure -> print("Phone identity verification failed: ${auth.error.message}")
    }
  }
}
phoneMethod.setVerificationOneTimePassword("...the.one.time.password.received.by.the.user.on.her/his.phone...")
phoneMethod.continueAuthentication { result in
    switch result {
    case let .success(details):
        print("User identity verified and user authenticated:", details.uid)
    case let .failure(error):
        print("User identity not verified:", error)
    }
}

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 currently no mean to send again the same verification code.

Updating internal identities

Updating password

In order to update the password associated with an email-based identity, it is necessary to provide the current one together with the new one to set.

Simply use the changePassword() method (replace “<your-app>” with your actual application identifier):

// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
const auth = app.authentication;
auth.changePassword(
        "chocolate@webcom.com", // email
        "pralinesandcaramel", // current password
        "midnightcookies" // new password
    ) 
    .then(() => console.log("Password changed successfully"))
    .catch(error => console.log("Error changing password:", error));
val myApp = WebcomApplication.default // the app defined by the 'webcom.properties' asset file
val authenticator = myApp.authenticationService

authenticator
  .getPersonalPasswordMethod(Provider.Email, "chocolate@webcom.com")
  .updatePassword("currentPralinesandcaramel", "newMidnightcookies") { // it: WebcomResult<Unit>
    when (it) {
      is WebcomResult.Success -> print("Password successfully updated")
      is WebcomResult.Failure -> print("Failed updating password: ${it.error.message}")
    }
  }
let authenticationService = Webcom.defaultApplication.authenticationService
let eMailMethod = AuthenticationMethodInternal(eMail: "chocolate@webcom.com")
authenticationService.initializeAuthentication(with: eMailMethod)
eMailMethod.setStaticPassword("pralinesandcaramel") // current password
eMailMethod.updatePassword(to: "midnightcookies") { result in // new password
    switch result {
    case .success:
        print("Password changed successfully")
    case let .failure(error):
        print("Error changing password:", error)
    }
}

Resetting password

In cases where the user has forgotten her/his password, the Webcom back end provides a request to send back to the user's email address an email containing a link to safely change her/his password without the need of the current one.

Use the sendPasswordResetEmail() method (replace “<your-app>” with your actual application identifier):

// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
const auth = app.authentication;
auth.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));

Use the sendPasswordResetEmail() method (replace “<your-app>” with your actual application identifier):

val myApp = WebcomApplication.default // the app defined by the 'webcom.properties' asset file
val authenticator = myApp.authenticationService

authenticator
  .getPersonalPasswordMethod(Provider.Email, "chocolate@webcom.com")
  .sendPasswordResetCode { // it: WebcomResult<Unit>
    when (it) {
      is WebcomResult.Success -> print("Recovery email successfully sent")
      is WebcomResult.Failure -> print("Failed sending the recovery email: ${it.error.message}")
    }
  }
let authenticationService = Webcom.defaultApplication.authenticationService
let eMailMethod = AuthenticationMethodInternal(eMail: "chocolate@webcom.com")
authenticationService.initializeAuthentication(with: eMailMethod)
eMailMethod.requestPasswordResetLink { result in
    switch result {
    case .success:
        print("The Reset password email was sent successfully")
    case let .failure(error):
        print("The Reset password email was not sent:", error)
    }
}

The reset password message links by default to a generic page served by the Webcom back end, which prompts the user with a form where s/he can enter and confirm a new password.

In order to customize the user experience, you can edit the message template in the "authentication" tab of the Webcom developer console. You can then include a link to a customized reset page from your application. In turn, this page must end in either calling the API shown above or requesting the following HTTP REST Webcom API:

curl -X PUT "https://io.datasync.orange.com/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 Webcom developer console.
  • <reset-token> must be replaced with the token generated by the Webcom 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 Webcom developer 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 a 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, a dedicated method is available. It requires that you are already authenticated with an email- or phone-based identity, and updates the profile associated with this currently authenticated identity. 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 fails.

// const app = Webcom.App("<your-app>"); // UNCOMMENT if you haven't yet an instance of your app!
const auth = app.authentication;
// Authenticate with an email-based identity...
auth.authInternally("password", {email: "macadamia@webcom.com", password: "my-password"})
    .then(() => {
        // ...and then update its profile
        auth.updateIdentityProfile({age: 42, zipcode: 22300})
            .then(() => console.log("The profile has been successfully updated"));
    })
    .catch(error => console.log("The update failed:", error));
val myApp = WebcomApplication.default // the app defined by the 'webcom.properties' asset file
val authenticator = myApp.authenticationService

authenticator
  .getPersonalPasswordMethod(Provider.Email, "macadamia@webcom.com")
  .updateProfile("age", 42) { // it: WebcomResult<Unit>
    when (it) {
      is WebcomResult.Success -> print("Profile successfully updated")
      is WebcomResult.Failure -> print("Failed updating profile: ${it.error.message}")
    }
  }
eMailMethod.processProfileOperation(.update(path: "age", value: 42)) { result in
    switch result {
    case .success:
        print("Profile updated!")
    case let .failure(error):
        print("Profile update error:", error)
    }
}