You can configure e-signature in Service Portal to require re-authentication from approving users.

Before you begin

The com.snc.integration.esig.window system property is supported to enable SSO login in a new window.

Role required: admin

Procedure

  1. Activate the Approvals with e-Signature [com.glide.e_signature_approvals] plugin.
  2. Navigate to System Definition > e-Signature Registry.
  3. Add any tables you want to require an e-signature for to the list.

Result

With e-signature enabled, approvers are required to provide a password to approve or reject any requests. Authentication with touch ID works in the mobile app. On the mobile web, approvers are still required to enter a password. For more information on configuring e-signature, see Approval with e-signature.

Enable SSO login in a new window

Open a new window for SSO authentication when using e-Signature for approvals.

Before you begin

Security Assertion Markup Language (SAML) login is only supported on the desktop Service Portal.

For additional information, see the e-signature SSO login KB article.

Role required: admin

Procedure

  1. Enter the spEsignatureCustom script.
    1. Enter sys_ui_script.list in the Filter navigator.
    2. Select New, and create a new UI script.
    3. On the UI Script form, enter spEsignatureCustom in the API Name field.
    4. In the UI Type field, select Mobile/Service Portal.
    5. Paste the spEsignatureCustom script in the Script field.
    6. Select Submit.
  2. Enter the spAuthCustom script.
    1. In the same sys_ui_script.list table, create another new UI script by selecting New.
    2. On the UI Script form, enter spAuthCustom in the API Name field.
    3. In the UI Type field, select Mobile/Service Portal.
    4. Paste the spAuthCustom script in the Script field.
    5. Select Submit.
  3. Associate the UI scripts to the JS Include record.
    1. Navigate to Service Portal > Portals.
    2. Identify the Service Portal you want to apply the esignature support to and select the Theme.
    3. In the related lists, select JS Includes.
    4. Select New.
    5. On the form, fill in the fields.
    6. Select Submit.

Result

A new window opens when you use E-Signature for approvals.

spEsignatureCustom script

Paste the spEsignatureCustom script in the sys_ui_script.list script field.

angular.module('sn.$sp').provider('spEsignature', function() {
	'use strict';

	this.$get = function spEsignature($q, $http, $window, urlTools, xmlUtil) {
		var w = window;
		var esignOW;
		var windowWidth;
		var windowHeight;
		var redirectURL;

		function isWindowChosen() {
			var postParams = {
				sysparm_scope: 'global',
				sysparm_processor: 'ESignatureAuthUtils',
				sysparm_name: 'isWindowChosen'
			};
			return $http({
				method: 'POST',
				url: '/xmlhttp.do',
				data: urlTools.encodeURIParameters(postParams),
				headers: {
					'Content-Type': 'application/x-www-form-urlencoded'
				},
				transformResponse: function(response) {
					return xmlUtil.getDataFromXml(response);
				}
			}).then(
				function(response) {
					var data = response.data ? response.data[0] : {};
					return data.answer ===  'true'
				},
				function(response) {
					// Handling failure case.
					switch (response.status) {
						default:
						case 404:
							return false;
					}
				});
		}

		function initiateEsignature() {
			var postParams = {
				sysparm_scope: 'global',
				sysparm_processor: 'ESignatureAuthUtils',
				sysparm_name: 'fetchAuthDetails'
			}
			return $http({
				method: 'POST',
				url: '/xmlhttp.do',
				data: urlTools.encodeURIParameters(postParams),
				headers: {
					'Content-Type': 'application/x-www-form-urlencoded'
				},
				transformResponse: function(response) {
					return xmlUtil.getDataFromXml(response, 'result');
				}
			}).then(
				function(response) {
					var data = response.data ? response.data[0] : {};
					process(data);
				});
		}

		function process(data) {
			var shouldLogoutFirst = data.logoutFirst === 'true';
			w['windowHeight'] = data.popup_window_height;
			w['windowWidth'] = data.popup_window_width;
			w['redirectURL'] = data.loginURL;
			if (shouldLogoutFirst)
				openEsignatureWindow(data.logoutURL);
			else
				authenticate();
		}

		function openEsignatureWindow(url) {
			w['esignOW'] = window.open(url, 'esignatureAuthentication',
				'height='+w['windowHeight']+',width='+w['windowWidth']+',top=100,left=100,toolbar=0,location=0,menubar=0');
		}

		w['authenticate'] = function(){
			if(w['redirectURL']){
				if(w['esignOW']) //when we signed out previously, a window is already opened. reuse it.
					w['esignOW'].location.href = w['redirectURL'];
				else
					openEsignatureWindow(w['redirectURL']);
			}
		};

		w['evaluateRedirect'] = function(msg) {
			w['esignOW'].close();
			if (msg == "saml2 login complete") {
				$window.onReauthenticationComplete(msg);
			}
		};

		return {
			isWindowChosen: isWindowChosen,
			initiateEsignature: initiateEsignature
		};

	};
});

spAuthCustom script

Paste the spAuthCustom script in the sys_ui_script.list script field.

angular.module('sn.$sp').factory('spAuthModal', function($q, spModal, i18n, $http, spAuthentication, glideUserSession, cabrillo, $cookies, $window, spUtil, $uibModal, spEsignature) {
	"use strict";

	function _showAuthenticationModal(requestParams, username, userSysId) {
		var currentUser;
		var deferred = $q.defer();
		glideUserSession.loadCurrentUser({reload: true}).then(function(user) {
			if (!user) {
				deferred.reject({
					error: {
						status: 'ANONYMOUS',
						message: i18n.getMessage('Not logged in')
					}
				});
				return;
			}
			currentUser = user;
			var serializedUser = {
				sysId: currentUser.userID,
				userName: currentUser.userName,
				firstName: currentUser.firstName,
				lastName: currentUser.lastName
			};

			// hand off to native clients
			if (cabrillo.isNative()) {
				cabrillo.auth.reauthenticate(currentUser).then(function() {
					deferred.resolve(serializedUser);
				}, function(error) {
					if (error && error.status) {
						deferred.reject({
							error: error
						});
					}
					//TODO: Handle rejection a little more gracefully
					deferred.reject();
				});
				return;
			}

			var loginMethod = currentUser.$private.loginMethod;
			if (!loginMethod) {
				// As we were unable to determine the login method via the HTTP session, trying to get glide_sso_id cookie which is set in case of multisso login
				var providerSysId = $cookies.get('glide_sso_id');
				loginMethod = providerSysId ? 'multisso' : 'db';
			}

			if (loginMethod === 'saml' || loginMethod === 'oidc' || loginMethod === 'multisso') {
				spEsignature.isWindowChosen().then(function(result) {
					var modal;
					if (!spUtil.isMobile() && loginMethod !== 'oidc' && result === true) {
						// If enabled, Opening a new window for desktop saml login
						spEsignature.initiateEsignature();
					} else {
						glideUserSession.getSsoReauthenticationUrl().then(function(url) {
							requestParams.externalLoginURL = url;
							openExternalAuthModal(requestParams).then(function(m) {
								modal = m;
							});
						});
					}
					$window.onReauthenticationComplete = function(result) {
						deferred.resolve(serializedUser);
						if (modal)
							modal.close();
					};
				});
			} else {
				spModal.open({
					title:i18n.getMessage("Approver authentication"),
					message:i18n.getMessage("Additional authentication is required, enter your usename and password to continue."),
					footerStyle: {border: 'none', 'padding-top': 0},
					widget: 'simpleloginui',
					widgetInput: {},
					shared: requestParams,
					onSubmit: function() {
						return onLoginModalSubmit(requestParams, username);
					}
				}).then(function(confirm) {
					if (confirm.label == i18n.getMessage("OK")) {
						deferred.resolve(serializedUser);
					} else {
						deferred.reject();
					}
				});
			}
		});

		return deferred.promise;
	}

	function onLoginModalSubmit(requestParams, username) {
		//(1) call login service to verify auth
		//(2) verify same user
		return $q(function(resolve, reject) {
			var errorMessage = null;

			if(!requestParams.username || requestParams.username.trim() === "" ||
					!requestParams.password || requestParams.password.trim() === "") {
				errorMessage = i18n.getMessage("User name or password invalid");
			} else if(requestParams.username !== username) {
				errorMessage = i18n.getMessage("Attempted to authenticate as a different user");
			}

			if(!errorMessage || errorMessage === "") {
				spAuthentication.validateCreds(requestParams.username, requestParams.password).then(function(res) {
					resolve({status: res.success, errorMessage: res.message});
				});
			} else {
				resolve({status: !errorMessage || errorMessage === "", errorMessage: errorMessage});
			}
		});

	}

	function openExternalAuthModal(requestParams) {
		var deferred = $q.defer();

		var options = {
			title: i18n.getMessage("Approver authentication"),
			message: '',
			messageOnly: false,
			errorMessage: '',
			input: false,
			label: '',
			size: 'lg',
			value: '',
			required: false,
			footerStyle: {border: 'none', 'padding-top': 0},
			values: false,
			onSubmit: null,
			widget: 'simpleloginui',
			widgetInput: {},
			shared: requestParams,
			buttons: [{label: i18n.getMessage('Cancel'), cancel:true}]
		};

		var widgetURL = spUtil.getWidgetURL(options.widget);
		$http.post(widgetURL, options.widgetInput).success(function(response) {
			options.widget = response.result;
			options.widget.options.shared = options.shared;
			var modal = $uibModal.open({
				templateUrl:'sp-modal.html',
				controller: spModalCtrl,
				size: options.size,
				resolve: {
					options: function() {
						return options;
					}
				}
			});

			deferred.resolve(modal);
		});
		
		return deferred.promise;
	}

	function spModalCtrl($scope, options) {
		$scope.options = options;
		$scope.form = {};

		$scope.buttonClicked = function(button){
			if (button.cancel) {
				$scope.$dismiss();
				return;
			}
		}
	}

	return {
		prompt: _showAuthenticationModal
	}
}).decorator("spAuthModal", function($delegate) { return( $delegate );});