User Registration

As a complement to the article on User Management, this article describes the tools that are provided by Apstrata in order to simplify user registration. Indeed, along the “SaveUser” API, Apstrata also ships with server-side scripts and a JavaScript/HTML 5 registration widget that facilitate the work of the developers (more on server-side scripting with Apstrata).

The scripts implement a registration process in three steps:

  1. Create a user in the user store and tag it as suspended. A suspended user cannot log-in to an application that delegates Identity management to Apstrata.
  2. Send a verification e-mail to the user who has registered, with a link he needs to click on in order to validate his registration
  3. Un-suspend the user once the verification is done (the user is now able to use the application) and send a confirmation email.

As for the JavaScript/HTML 5 registration widget, it can be easily dropped into an HTML (or PHP) page to display a registration form containing the mandatory users fields (login, name, password, email) as well as some other fields (job title, web site, company and phone). It is of course possible to modify the form in order to add and/or remove some fields, as it will be explained in the following paragraphs.

Please note that the scripts and the widgets are complementary to each others and to the SaveUser API, therefore, they can be used separately or in conjunction. These scripts and the widget are part of the Apstrata SDK application that can be downloaded from here

The server-side registration scripts.

The registration process is implemented by four scripts that you will need to deploy on your Apstrata account. In order to deploy a script on your Apstrata account, just copy the script content from your favorite IDE, then open the “Scripts” section of the apstrata workbench and paste it there as a new script (keep the same names).

1. widgets.common

This is a configuration placeholder, where you will notably specify the URL of your Apstrata cluster, your authentication Key, as well as the template of the e-mail that will be sent to the users that have submitted their registration. This script contains many configuration parameters that you will have to fill, among which:

  • projectName = This is the name of your application. It will be sent in the verification e-mail
  • defaultUsersGroup = Users can belong to zero or many groups in Apstrata. Filling this variable specifies the group to which all subscribed users will automatically be assigned. Please note that this group has to be created before using the registration scripts.
  • apstrataHomeKey = This is the authentication key of the account owner of the application.
  • apstrataHomeSecret = This is the secret of the account owner of the application.
  • sendEmailOnceRegistrationConfirmed = Set this parameter to true if an email should be sent to the user upon successful registration. The e-mail address to use is specified using the configuration.templatesConfirmed.adminEmail parameter in the script.
  • registrationRedirectUrl = This parameter is optional. It defines a URL to which the “registerUser” script (see below) will redirect once the user submits his registration or once he clicks on the verification link received by e-mail.

2. widgets.Registration.registerUser

The “registerUser” script implements the first two steps of the registration process described above. It is the script that you need to call in order to submit a user’s registration, whether you used the provided widgets to capture the registration information or if you provided your own HTML form. You should normally not modify this script, unless you have additional requirements.

When ran, the script will first verify if the registration data it received matches and existing user. If not, it will create a new user and tag it as suspended, then send a verification e-mail if configured to do so (default behavior in the configuration script).

Note that the “registerUser” script will create a user with all the data sent by the registration form (you are not limited to the aforementioned user fields), as long as the field names are prefixed with ”user.”. Also note the form should send the type of all fields that are not of type string. This is done simply by adding a hidden field in the form such as fieldName.apsdb.fieldType = type, for every field. Types should be one of: date, numeric, text. For more on this topic, please refer to the “SaveUser” API documentation.

Invoking the registerUser script can be done in two different ways:

  1. By invoking the script through a REST service call
  2. By invoking the service from JavaScript, using the Apstrata JS SDK.

1. Invoking the registerUser script through a REST service call

You need to build the following request:

https://varick.apstrata.com/apsdb/rest/[auth key]/RunScript

with the following parameters:

"apsdb.scriptName": "widgets.Registration.registerUser", 
"apsws.time": [timestamp],
"apsws.authSig": [the hash generated as a result of signing the request with the authentication key and secret],
"apsws.authMode": "simple",
"user.name": [first name last name],
"user.email": [user's email],
"user.login": [user's login],
"user.password": [user's password],
//... any other user field,

2. Invoke the registerUser script from the Apstrata JS SDK

// Create a new Apstrata connection using your account owner's credentials
var connection = new apstrata.sdk.Connection({
 credentials: {
 key: "[authentication key]",
 secret: "[secret]"
 },
 loginType: "master"
})

// Instantiate an Apstrata JS Client object passing the connection
var client = new apstrata.sdk.Client(connection)

// Prepare the parameters of the call (notice that the signing will be handled by the Apstrata JS client)
var attr = {
 "action": "RunScript", 
 "request": {
 "apsdb.scriptName": "widgets.Registration.registerUser", 
 "user.name": "[last name, first name]", 
 "user.login": "[user's login]", 
 "user.email": "[user's emails]", 
 "user.password": "[user's password]"
 }
}

// Executes the call to apstrata
client.call(attr.action, attr.request).then(
 // The event handler that gets called on success
 function(response) {
 // Replace with appropriate code
 console.dir(response)
 },
 // The event handler that gets called on call failure
 function(operation) {
 // Replace with appropriate code
 console.dir(operation.response)
 }
)

3. widgets.Registration.userExists

This script is used by the preceding one. It verifies if the registration data matches an existing user. You normally should not modify this script.

4. widgets.Registration.verifyAccount

This script implements the third step of the registration process. It is triggered by clicking on the link sent in the verification e-mail. The script verifies that the parameters received match a validation code and an existing login. When it is the case, the script un-suspends the user who is now able to log-in to the application. Last, if configured to do so (as per the values of the parameters defined in “widgets.common”), a confirmation e-mail will be sent to the user who will also optionally be redirected to the specified “registrationRedirectUrl”.

The Registration widget

The “RegistrationWidget” is a JavaScript/HTML 5 component that ships with the Apstrata JS SDK. If you are a JavaScript developer, then you should probably use this widget by simply dropping it into your HTML/JS pages. The “RegistrationWidget” provides many useful out of the box features, such as:

  • Display of a registration form
  • Verification of required fields (login, password and e-mail)
  • Validation of the password and e-mail fields (users will have to re-enter their password in the registration form and the widget will compare the two values. As for the e-mail, the widget makes sure that the entered value matches the e-mail standard format)
  • Verification of e-mail existence (the widget calls the widgets.Registration.userExist script to verify that a user with the same e-mail does not already exist)
  • Call of the widgets.Registration.registerUser script upon submission of the form.

How to use the “RegistrationWidget” ?

To use the “RegistrationWidget”, first make sure that you have the Apstrata JS SDK available as a library in your client application (the SDK also requires you to download the dojo 1.6 toolkit).

In your HTML, simply import the “RegistrationWidget” and place the component into a DOM node as demonstrated in the following example:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
 <title>apstrata.ui.widgets.RegistrationWidget test page</title>

 // Include the dojo library and enable parsing of the widgets while loading the document
 <script type="text/javascript" src="../../../../lib/dojo/dojo/dojo/dojo.js" djConfig="parseOnLoad: true, isDesbug: true"></script>

 // Include the apstrata library
 <script type="text/javascript" src="../../../sdk/apstrata.js"></script>

 // Import the required dojo and apstrata style sheets
 <style>
 @import "../../../../lib/dojo/dojo/dijit/themes/claro/claro.css";
 @import "../../themes/apstrata/apstrata.css";
 @import "../../themes/apstrata/RegistrationWidget.css";
 </style>

     <script type="text/javascript">

 	// Import the Apstrata JS Client class
	dojo.require("apstrata.sdk.Client");

 	// Import the dojo widget parser
 	dojo.require("dojo.parser");

 	// Import the RegistrationWidget class
 	dojo.require("apstrata.ui.widgets.RegistrationWidget");

 	// Import the Apstrata ApConfig class that contains the applications's configuration
 	// notably the Apstrata cluster URL and the credentials to use
 	dojo.require("apstrata.sdk.ApConfig");

	// When the DOM tree is ready and all required classes have been resolved
 	dojo.ready(function() {

 		// Create an instance of the RegistrationWidget component
 		var rw = new apstrata.ui.widgets.RegistrationWidget();

 		// Place the widget dom node into the document body... and that's it !
 		dojo.place(rw.domNode, dojo.body());
 	})
 </script>
 </head>
 <body class='claro apstrata'>
 </body>
</html>

How to extend the RegistrationWidget?

Some developers will probably need to extend the existing “RegistrationWidget” by adding supplementary fields or removing existing ones, or by adding additional actions to the registration form (currently the form only offers the “save” action that submits the form). The good news is that this is actually very easy to do.

Form definitions

The Apstrata JS SDK ships with a “FormGenerator” component, which is used to generate HTML forms. This component is used by all form-related widgets in the Apstrata JS SDK, such as the “RegistrationWidget”. Although the complete description of the “FormGenerator” capabilities is beyond the scope of this article, it suffices to know that the “FormGenerator” requires a “form definition” to be defined in order to generate a form. A “form definition” is a simple JavaScript object that defines the field used in the form as well as the actions that will be exposed by this latter.

If you look inside the “RegistrationWidget” class, this is the form definition that you will find:

definition: {
 label: "User Registration",
 cssClass: "newClass",
 fieldset: [
 	{name: "required", label: "", type: "subform", style: "form", cssClass:"column",
 	fieldset: [
 		{name: "name", label: "Name", type: "string", required: true},
		{name: "email", label: "Email", type: "string", required: true},
 		{name: "password", label: "Password", type: "password", required: true},
 		{name: "confirmPassword", label: "Confirm Password", type: "password", required: true, attrs: {invalidMessage: "Passwords don't match"}},
 		]
 	},
 	{name: "optional", label: "", type: "subform", style: "form", cssClass:"column", 
 	fieldset: [
 		{name: "jobTitle", label: "Job title", type: "string"},
		{name: "webSite", label: "Website", type: "string"},
 		{name: "company", label: "Company", type: "string"},
 		{name: "phone", label: "Phone", type: "string"}
 		]
 	},
 ],
 actions: ['save']
 },

And if you look a bit further in the code of that class, you will find the definition of the “save” action (a function), which will be bound to the form:

save: function(values) {
 var self = this

 var request = {
 	"apsdb.scriptName": "widgets.Registration.registerUser",
 	"user.login": values.email,
 	"user.groups": "users"
 }

 delete values.confirmPassword

 for (k in values) {
 	request["user."+k] = values[k]
 } 

 this.client.call("RunScript", request, null, {method: "get"}).then(
 function(response) {
 	if (response.result.metadata.errorCode == "DUPLICATE_USER") {
 		self.message(self.nls.EMAIL_ALREADY_REGISTERED)
 		self.form.getField("email").invalidMessage = self.nls.EMAIL_ALREADY_REGISTERED +" <a href=''>" + self.nls.LOGIN + "</a>"
 		self.form.getField("email").validator = function(value, constraints) {
 			return false // true if email not found, false otherwise
 		}
 	self.form.validate()
 	}

 	window.location = response.result.url;
 }, 
 function(response) {
 	console.dir(response)
 })

 if (values.password != values.confirmPassword) { } else { }
 }
Adding fields and actions to the existing “RegistrationWidget”

Now do not dive too much into the details of the above code and let us get back to our initial concern, which is adding new fields and actions to the initial form definition. All you have to do is to:

  • Create a new registration widget class that will extend the former,
  • Override the existing definition with a new one.
  • Provide a new function for every new action in your form and connect these function to the “onAction” event (this event is fired when a button matching an action is clicked on the form).

Let us demonstrate this with an example. Assume that we need the following fields: “first name”, “last name”, “email”, “password”, and “country” (a combo box), as well as a “cancel” function that cleans up the form.

This is how your new registration widget would look like:

// this is the demo/ExtendedRegistrationWidget.js file
dojo.provide("demo.ExtendedRegistrationWidget");
dojo.require("apstrata.ui.FlashAlert")

dojo.require("apstrata.sdk.Client")
dojo.require("apstrata.sdk.Connection")
dojo.require("apstrata.ui.widgets.RegistrationWidget");

dojo.requireLocalization("apstrata.ui.widgets", "registration-widget")

/**
 * Extended version of User registration widgets. 
 * 
 * @param {Object} attrs
 */
dojo.declare("demo.ExtendedRegistrationWidget", 
[apstrata.ui.widgets.RegistrationWidget], 
{
  // This is the new definition
  definition: {
    label: "User",
    cssClass: "newClass",
    fieldset: [
      {name: "required", label: "", type: "subform", style: "form", cssClass:"column",
        fieldset: [
          {name: "first_name", label: "First Name", type: "string", required: true},
          {name: "last_name", label: "Last Name", type: "string", required: true},
          {name: "email", label: "Email", type: "string", required: true},          
   	  {name: "country", label: "Country", type: "string", widget: "dijit.form.ComboBox", "formGenerator-options": ["USA", "Canada", "China", "France", "Lebanon"]},
          {name: "password", label: "Password", type: "password", required: true},
          {name: "confirmPassword", label: "Confirm Password", type: "password", required: true, attrs: {invalidMessage: "Passwords don't match"}},
        ]
      },
    ],
    actions: ['save', 'cancel']
  },

  // override the postCreate function
  postCreate: function() {
    // Let the parent class handle all the creation work
    this.inherited(arguments);

    // FormGenerator class has an "onAction" method that is called when an action is clicked. We connect to it
    // in order to re-route the event to the cancel function we need to add to the parent class
    dojo.connect(this.form, "onAction", this, "_onAction");        
  },

  // this function is called every time an action (button) is clicked on the widget
  _onAction: function(action, values) {
    if (action == "cancel") {
      this.cancel(values);
    } else if(action == "save") {
       this.save(values); // call the inherited save function
    }
  },

  // cancellation logic.  
  cancel: function(values) {
    this.form.frmMain.reset();    
  }

})

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>