I recently had a customer ask me a question dealing with validating a username entered at login in ServiceNow. This question prompted me to write this post. The ServiceNow login mechanism works exactly how you would expect it to. If a valid username and password are provided, the user is allowed into the system. If you want to perform additional validation on a username or check for a role, then you’ve got to add that logic somehow. This post explains how you can override the out-of-box login routine to perform some custom validation before allowing login to your ServiceNow instance. I’ll give you a couple of common installation exits that I’ve created and used before.


The default login behavior in ServiceNow is handled by the ‘Login’ installation exit. Installation exits are found by navigating in the left nav to ‘System Definition -> Installation Exits’. The examples given here are designed to be used as overrides to the ‘Login’ installation exit.

Restrict Login by Role in Non-Production Instances
One request I see commonly is for users to be able to log on to a production environment, but only certain users should be able to log on to the other development and test environments. Activating the installation exit below (which overrides the ‘Login’ installation exit) allows you to do this. It checks any incoming login attempt and sees if the user provided has the ‘admin’ role. If they don’t, they are alerted accordingly. If you decide to let all users into the system, you can simply de-activate the ‘AdminOnlyLogin’ installation exit record included below and everyone will be able to log in again.

AdminOnlyLogin Installation Exit
Name: AdminOnlyLogin (Note: It is critical that this name matches the class name in the script below)
Overrides: Login (Note: The value here must match the name of the ‘Login’ Installation Exit exactly)
Active: True
Script:

gs.include("PrototypeServer");

var AdminOnlyLogin = Class.create();
AdminOnlyLogin.prototype = {
   initialize : function() {
   },
   
   process : function() {
      // the request is passed in as a global
      var userName = request.getParameter("user_name");
      var userPassword = request.getParameter("user_password");
      if (typeof GlideUser != 'undefined')
         var user = GlideUser;
      else
         var user = Packages.com.glide.sys.User;
     
      //Query to see if the user has the 'admin' role
      var isAdmin = false;
      var rec = new GlideRecord('sys_user');
      rec.addQuery('user_name', userName);
      rec.query();
      if(rec.next()){
         //Query the roles table for this user
         var rec1 = new GlideRecord('sys_user_has_role');
         rec1.addQuery('user', rec.sys_id);
         rec1.query();
         while(rec1.next()){
            if(rec1.role.getDisplayValue() == 'admin'){
               isAdmin = true;
               break;
            }
         }
      }
     
      var authed = user.authenticate(userName, userPassword);
     
      //Allow access if the user is an admin
      if((authed && isAdmin) || (authed && userName.indexOf('@snc') > -1)){
         return user.getUser(userName);
      }
     
      this.loginFailed();
     
      //Alert if the user is not an admin
      if(!isAdmin){
         gs.addErrorMessage('You must be a ServiceNow admin to access this system.');
      }
     
      return "login.failed";
   },
   
   loginFailed : function() {
      if (typeof GlideSysMessage != 'undefined')
         var sysMessage = GlideSysMessage;
      else
         var sysMessage = Packages.com.glide.ui.SysMessage;
      var message = sysMessage.format("login_invalid");

      if (typeof GlideSession != 'undefined')
          var session = GlideSession.get();
      else
         var session = Packages.com.glide.sys.GlideSession.get();
      session.addErrorMessage(message);
     
      var userName = request.getParameter("user_name");

      if (typeof GlideEventManager != 'undefined')
         var EventManager = GlideEventManager;
      else
         var EventManager = Packages.com.glide.policy.EventManager;

      if (typeof GlideTransaction != 'undefined')
         var t = GlideTransaction.get();
      else
         var t = Packages.com.glide.sys.Transaction.get();
      EventManager.queue("login.failed", "", userName, "");
   }
}

Alert User on UNC (domain\username) Login Attempt
Another situation I’ve come across before deals with the way that users should log into a ServiceNow instance. If you’ve integrated your instance with LDAP then you’ll have users using their domain credentials to authenticate. Some users get in the habit of specifying the domain along with their username in a Universal Naming Convention (UNC) format (Domain\User). ServiceNow just needs a username and password so authentication will fail if UNC format is used to authenticate.

You can use an installation exit to help manage this scenario…either to parse out the domain portion of the username if it is found, or to alert the user if a backslash is included in their username as shown in the installation exit below.

DomainAlertLogin Installation Exit
Name: DomainAlertLogin (Note: It is critical that this name matches the class name in the script below)
Overrides: Login (Note: The value here must match the name of the ‘Login’ Installation Exit exactly)
Active: True
Script:

gs.include("PrototypeServer");

var DomainAlertLogin = Class.create();
DomainAlertLogin.prototype = {
   initialize : function() {
   },
   
   process : function() {
      // the request is passed in as a global
      var userName = request.getParameter("user_name");
      var userPassword = request.getParameter("user_password");
     
      if (typeof GlideUser != 'undefined')
         var user = GlideUser;
      else
         var user = Packages.com.glide.sys.User;
      var authed = user.authenticate(userName, userPassword);
      if (authed)
         return user.getUser(userName);
     
      this.loginFailed();
     
      //See if the userName given has any backslash characters included
      if(userName.indexOf('\') > -1){
         gs.addErrorMessage('
Please remove any backslashes in username for login.');
      }
     
      return "login.failed";
   },
   
   loginFailed : function() {
      var sysMessage = Packages.com.glide.ui.SysMessage;
      var message = sysMessage.format("login_invalid");
      var session = Packages.com.glide.sys.GlideSession.get();
      session.addErrorMessage(message);
     
      var userName = request.getParameter("user_name");
      var EventManager = Packages.com.glide.policy.EventManager;
      var t = Packages.com.glide.sys.Transaction.get();
     
      EventManager.queue("login.failed", "", userName, t == null ? null : t.getRemoteAddr());
   }
   loginFailed : function() {
      if (typeof GlideSysMessage != '
undefined')
         var sysMessage = GlideSysMessage;
      else
         var sysMessage = Packages.com.glide.ui.SysMessage;
      var message = sysMessage.format("login_invalid");

      if (typeof GlideSession != '
undefined')
          var session = GlideSession.get();
      else
         var session = Packages.com.glide.sys.GlideSession.get();
      session.addErrorMessage(message);
     
      var userName = request.getParameter("user_name");

      if (typeof GlideEventManager != '
undefined')
         var EventManager = GlideEventManager;
      else
         var EventManager = Packages.com.glide.policy.EventManager;

      if (typeof GlideTransaction != '
undefined')
         var t = GlideTransaction.get();
      else
         var t = Packages.com.glide.sys.Transaction.get();

      EventManager.queue("login.failed", "", userName, t == null ? null : t.getRemoteAddr());
   }
}