Creating iMIS Logins

Creating User Logins and Passwords in iMIS 15+

iMIS 15 introduced a new concept called Unified Login. Under unified login, all iMIS modules and applications use a single authentication store to allow users access. Currently, this store is based on the Microsoft Membership Provider model. There are several methods of interacting with the provider, from the database level all the way up to .NET APIs and Web Services. The various options are presented below.

Using the Provider from SQL

By default, iMIS uses the SQL Membership Provider (with some enhancements layered on top, extending to the use of a custom provider in iMIS 15.1). Other providers can be configured and used; interacting with them is left as an exercise for the reader. :)

The SQL Provider exists as a set of SQL tables and stored procedures; both the tables and the stored procedures use the prefix aspnet_. They can be called and used as normal from SQL, although there are some actions (such as creating/encrypting passwords) that may be more complex than in the other options given below. This method is generally not recommended, in part for this reason (and in part because tools written to use this layer cannot later be modified to use other providers).

Using the Provider from .NET code

If your application is a .NET Web Site or Web Application, using the iMIS provider to authenticate users is easy; just copy the membership provider configuration section from your iMIS application server's web.config file into your own web.config file. In fact, if you place your application within the iMIS application server folder, you automatically get all of iMIS' own configuration for free. In either case you can access the provider directly, using Microsoft's documented methods and properties. Note that starting in in iMIS 15.1.0, ASI provides a custom membership provider (which still uses standard providers under the covers, so you can continue using whatever provider you prefer) which encapsulates iMIS-specific authentication and user management logic so your application doesn't need to.

An example of creating a user in C#, from a website. Note that in iMIS 15.1.0 and higher, the providerUserKey should be the ContactKey of the user's row in ContactMain (this is because the 15.1.0 provider will automatically create/update the rows in Name_Security and UserMain for the user; pre-15.1.0, you must create/update the Name_Security and other iMIS rows yourself). The password will automatically be encrypted (using the method specified in web.config) before storing in the aspnet_Users table.

string username = "emeans";
string
password = "password";
string
email = "emeans@advsol.com";
string
passwordQuestion = "What is your favorite color?";
string
passwordAnswer = "Blue";
object
providerUserKey = null;
System.Web.Security.MembershipCreateStatus status;
System.Web.Security.MembershipUser newUser = System.Web.Security.Membership.CreateUser(username, password, email, passwordQuestion, passwordAnswer, true, providerUserKey, out status);

Currently there is no way to use the provider directly from a non-website .NET project; you must use an indirect method, two of which are provided below.

Using Asi.Membership

.Net applications, as well as any application which can consume a COM object, can reference the Asi.Membership.dll library included in all iMIS 15 application server installs to access the iMIS membership provider. This library provides a friendly interface over the ASI Membership Web Service (and has the same requirements -- notably, access to an iMIS application server via HTTP or HTTPS). Asi.Membership calls look very similar to using the provider directly:

string username = "emeans";
string
password = "password";
string
email = "emeans@advsol.com";
string
passwordQuestion = "What is your favorite color?";
string
passwordAnswer = "Blue";
object
providerUserKey = null;
Asi.Membership.MembershipWebService.WebServiceProxyableMembershipUser newUser = Asi.Membership.MembershipProvider.CreateUser(username, password, email, passwordQuestion, passwordAnswer, true, providerUserKey); 

Using the Membership Web Service

Finally, if your application cannot use one of the prior methods, each iMIS application server install includes the Membership Web Service, which can be consumed by virtually any modern language (including Java, ColdFusion, Ruby, Python, and many others). Note that the Membership web service uses cookies to provide authentication information, so your application must pass the authentication cookies returned by one of the Login methods back to the service with each call, or you will receive errors.

The methods available on the Membership Web Service are a superset of those available from Asi.Membership. The web service lives at http://[server]/[imis15]/AsiCommon/Services/Membership/MembershipWebService.asmx, where [server] is the name of your iMIS application server and [imis15] is the name of the virtual directory where you chose to install the application server. Visiting that URL from a browser local to the server (i.e. log on to the server and use the local browser) will display a test page you can use to view the methods available, call various methods, etc.

Encrypting Passwords

If you are using one of the methods above that does not encrypt the password for you (such as direct SQL), you'll need to encrypt the password yourself. There are a couple of options supported by the provider; note that unless you use the default (one-way hashing with SHA1) you will need to modify the web.config file of all application and public servers or they will not be able to verify passwords.

Generating a Random Salt

C# code to generate a random salt for use with the methods below:

private static string GetSalt()

{

    byte[] buffer = new byte[16];

    (new System.Security.Cryptography.RNGCryptoServiceProvider()).GetBytes(buffer);

    return Convert.ToBase64String(buffer);

}

Option 1: Hashing

This is the default; passwords are hashed using the SHA1 algorithm and a random salt. The salt and the encrypted password are stored in the aspnet_Users table (or the provider's equivalent storage). When a user authenticates, the password they provide is hashed using the salt in the database for that user and the result compared to the stored hash value; if they match, the login is successful.

Sample C# code for hashing a password:

 

private static string HashPassword(string password, string salt)

{

    byte[] bIn = System.Text.Encoding.Unicode.GetBytes(password);

    byte[] bSalt = Convert.FromBase64String(salt);

    byte[] bAll = new byte[bSalt.Length + bIn.Length];

    byte[] bRet;

 

    Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length);

    Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length);

    System.Security.Cryptography.HashAlgorithm s = System.Security.Cryptography.HashAlgorithm.Create("SHA1");

    bRet = s.ComputeHash(bAll);

 

    return Convert.ToBase64String(bRet);

}

Option 2: Encrypting

Instead of using a one-way hash, passwords can be encrypted; the default implementation uses the Encryption method specified in the <machineKey> element of web.config (which defaults to AES in the standard ASP.Net install). Passwords which have been encrypted, unlike hashed passwords, can be decrypted (for example, to allow a user's password to be retrieved if it is lost, instead of having to reset the password). I don't have example code for manually encrypting passwords, although if you were to do so note that this method requires that the decryptionKey attribute of the machineKey element be set in the web.config, as this key is used for encryption and decryption of passwords.

Option 3: Clear Text

The third option is to not encrypt passwords, instead storing them in clear text. This method is strongly discouraged, as it will lead to large-scale passwords theft should someone get access to the aspnet_Users table.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Perl code to create the encrypted passwords

 

perl -e 'use MIME::Base64(); use Digest::SHA1 qw(sha1); $password="helloworld"; $salt=MIME::Base64::decode("WnEtrtMCoIOmm62WDO7hiA=="); $password=~s/(.)/$1\000/g; print MIME::Base64::encode(sha1($salt . $password));'

(the above will output:  mAZkIVQJNe0+vteCt1EjdkCINtg= )

the \000 step converts to unicode, FYI.