Pre-populate Email Address for SSO with Microsoft / Azure
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
06-23-2022 04:26 PM
Hi there,
I hope someone can help me out. We have a request to streamline our current login process for internal staff. In our environment we use Microsoft Azure for SSO on our ServiceNow instance so staff can use their existing MS credentials. Currently when staff go to our instance landing page, they will enter their email (someone@somewhere.org), click "submit", and then be taken to a Microsoft login screen that looks like this...
...the request is to pre-populate the sign in field with the user's email address from the landing page so it looks like this...
I tried following this article but have not been able to get it working (the sign in field does not get pre-populated with user's email submitted on landing page).
Below is the code from the modified "MultiSSOv2_SAML2_custom" SSO login script and the current "MultiSSOv2_SAML2_internal" SSO login script. The MultiSSOv2_SAML2_custom script is currently referenced on the "Single Sign-on Script" field on Advanced tab for our Microsoft Azure identify provider record.
The modified "MultiSSOv2_SAML2_custom" script is appending a "&login_hint=" parameter based on info from this Microsoft article.
Note: I did an "inspect" on the field the user enter's email address on the landing page (before being taken to Microsoft login screen) and the field name is "username".
//MultiSSOv2_SAML2_custom
//This is a modified version of MultiSSOv2_SAML2_custom with '&login_hint=' + request.getParameter("username"); appended to url
gs.include("PrototypeServer");
var MultiSSOv2_SAML2_custom = Class.create();
MultiSSOv2_SAML2_custom.prototype = Object.extend(new MultiSSOv2_SAML2_internal(), {
initialize: function() {
MultiSSOv2_SAML2_internal.prototype.initialize.call(this);
},
setActionRedirectURL: function() {
var url = null;
if (this.SAML2.isTestSAMLConnection()) {
//url = SNC.GlideSAML2Tester.getActionRedirectURL(this.requestType);
url = SNC.GlideSAML2Tester.getActionRedirectURL(this.requestType) + '&login_hint=' + request.getParameter("username");
} else if (this.redirectURL) {
//url = this.redirectURL;
url = this.redirectURL + '&login_hint=' + request.getParameter("username");
}
if (url) {
this.logDebug("We will be redirecting user to the URL: " + url);
action.setRedirect(url);
}
},
type: 'MultiSSOv2_SAML2_custom'
});
//MultiSSOv2_SAML2_internal
gs.include("PrototypeServer");
gs.include("SAML2_custom");
var MultiSSOv2_SAML2_internal = Class.create();
MultiSSOv2_SAML2_internal.prototype = Object.extend(new MultiSSO_Abstract_Core(), {
initialize: function() {
this.propertiesGR = null;
this.SAML2 = new SAML2_custom();
},
/*
return user_name or error code
*/
process: function() {
try {
this.redirectURL = null;
this.requestType = "request";
return this.processSAMLMessage();
} finally {
this.setActionRedirectURL();
}
},
setActionRedirectURL: function() {
var url = null;
if (this.SAML2.isTestSAMLConnection()) {
url = SNC.GlideSAML2Tester.getActionRedirectURL(this.requestType);
} else if (this.redirectURL) {
url = this.redirectURL;
}
if (url) {
this.logDebug("We will be redirecting user to the URL: " + url);
action.setRedirect(url);
}
},
getSAML2: function() {
return this.SAML2;
},
//override parent
setSSORecord: function(gr) {
this.propertiesGR = gr;
this.SAML2.setSSORecord(this.propertiesGR);
// set it to session so the response can find the right idp
if (!this.SAML2.isTestSAMLConnection()) {
var request = GlideTransaction.get().getRequest();
request.getSession().setAttribute(SNC.SSOUtils.SSOID(), this.propertiesGR.getUniqueValue());
}
},
//return user_name or errcode including 'failed_authentication', 'logout_success'
processSAMLMessage: function() {
if (this.SAML2.isSAMLLoginResponse()) {
var error = this.SAML2.doValidateLoginResponse();
var errorUrl = this.SAML2.getErrorURL();
var subject = this.SAML2.getSubjectName();
if (subject && !errorUrl) {
return this.loginProcess(subject);
}
if (errorUrl) {
return this.SAML2.getErrorURL(); //value of ssorecord.failed_requirement_redirect
}
return error;
} else if (this.SAML2.isSAMLLogoutResponse()) {
this.requestType = "logoutResponse";
return this.SAML2.doValidateLogoutResponse();
} else {
return this.SAML2.doAuthnRequest();
}
},
loginProcess: function(subjectUserName) {
return this.loginUser(subjectUserName);
},
getResponseElement: function() {
return this.SAML2.getResponseElement();
},
//return user_name or 'failed_authentication'
loginUser: function(subjectUserName) {
var eventLogParm1 = "user_name=" + subjectUserName;
var respType = this.SAML2.isIdPInitiated() ? "IdP" : "SP";
var eventLogParm2 = "initiator=" + respType + ",multisso=true,idpsysid=" + this.propGR.getUniqueValue();
var userField = this.propertiesGR.user_field;
if (subjectUserName == null) {
SNC.SecurityEventSender.sendSAMLLoginFailureEventData("", eventLogParm2);
return this.propertiesGR.failed_requirement_redirect;
}
if (!SNC.AuthenticationHelper.isUsernameValid(subjectUserName)) {
SNC.SecurityEventSender.sendSAMLLoginFailureEventData(eventLogParm1, eventLogParm2);
this.SAML2.logError(gs.getMessage("Subject Username validation failed"));
return "failed_authentication";
}
if (!userField || userField == '') {
var errorMessage = gs.getMessage("User Field validation failed");
SNC.SSOUtils.writeLogSummary(false, errorMessage, gs.getMessage("Ensure that the 'User Field' field is not null or blank"));
this.SAML2.logError(errorMessage);
SNC.SecurityEventSender.sendSAMLLoginFailureEventData(eventLogParm1, eventLogParm2);
return "failed_authentication";
} else if (!GlideTableDescriptor.fieldExists('sys_user', userField)) {
var errorMessage = gs.getMessage("Invalid User Field. {0} is not a field on the sys_user table.", userField);
SNC.SSOUtils.writeLogSummary(false, gs.getMessage("User Field validation failed"), errorMessage);
this.SAML2.logError(errorMessage);
SNC.SecurityEventSender.sendSAMLLoginFailureEventData(eventLogParm1, eventLogParm2);
return "failed_authentication";
}
var ugr = new GlideRecord("sys_user");
ugr.addQuery(this.propertiesGR.user_field, subjectUserName);
ugr.query();
var foundUser = ugr.next();
if (foundUser) {
if (!(this.SAML2.isTestSAMLConnection() || this.isAutoRedirectIDP(this.propertiesGR.sys_id))) {
var userSsoSource = ugr.getValue('sso_source');
var companySysId = ugr.getValue('company');
if (!GlideStringUtil.nil(userSsoSource)) {
var userSso = userSsoSource.split(':');
if (!GlideStringUtil.nil(userSso[1]) && userSso[1] != this.propertiesGR.sys_id) {
var errorMessage = gs.getMessage("Ensure that the user you are trying to login is from the correct source, as mentioned in user's sso source field in servicenow instance.");
this.SAML2.logError(errorMessage);
return;
}
} else if (!GlideStringUtil.nil(companySysId)) {
var cgr = new GlideRecord("core_company");
cgr.get(companySysId);
var companySsoSource = cgr.getValue('sso_source');
if (!GlideStringUtil.nil(companySsoSource)) {
var companySso = companySsoSource.split(':');
if (!GlideStringUtil.nil(companySso[1]) && companySso[1] != this.propertiesGR.sys_id) {
var errorMessage = gs.getMessage("Ensure that the user you are trying to login is from the correct source, as mentioned in company's sso source field for user in servicenow instance.");
this.SAML2.logError(errorMessage);
return;
}
}
}
}
this.logDebug("Test connection or auto-redirect IDP, Skipping SSO source field validation.");
this.importOrUpdateSAMLUser(true);
} else {
if (!this.SAML2.isTestSAMLConnection()) {
this.importOrUpdateSAMLUser(false);
ugr.query(); // query again to make sure import is successful
foundUser = ugr.next();
}
if (!foundUser) {
var errorMessage = gs.getMessage("User: {0} not found", subjectUserName);
SNC.SSOUtils.writeMultipleLogSummary(false, errorMessage, gs.getMessage("Ensure that the user you are trying the test connection with is present in the system."), 'userField');
this.SAML2.logError(errorMessage);
SNC.SecurityEventSender.sendSAMLLoginFailureEventData(eventLogParm1, eventLogParm2);
return "failed_authentication";
}
}
var userName = ugr.getValue("user_name");
if (GlideStringUtil.nil(userName)) {
SNC.SecurityEventSender.sendSAMLLoginFailureEventData(eventLogParm1, eventLogParm2);
this.SAML2.logError("user_name value is empty.");
return "failed_authentication";
}
if (this.SAML2.isTestSAMLConnection()) {
var result = SNC.GlideSAML2Tester.validateUserRecord(ugr);
if ("failed_authentication" == result) {
return result;
}
}
if (!this.SAML2.isTestSAMLConnection()) {
this.redirectURL = request.getSession().getAttribute("SAML_RelayState");
if (!this.redirectURL) {
this.logDebug("SAML_RelayState is not available in the session, try the RelayState in the request.");
this.redirectURL = request.getParameter("RelayState");
}
SNC.SecurityEventSender.sendSAMLLoginSuccessEventData(eventLogParm1, eventLogParm2);
request.getSession().setAttribute("SAML_RelayState", null);
// successfully logged in. we need set sso_id cookie
this.SAML2.saveSSOIdInCookie(this.propertiesGR.sys_id);
request.getSession().setAttribute("glide.authenticate.multisso.login.method", "saml");
}
return userName;
},
importOrUpdateSAMLUser: function(isUserUpdate) {
var samlapi = this.SAML2.getGlideSaml2Api();
samlapi.loadImportSet(this.getAttributesMap(), isUserUpdate);
},
getAttributesMap: function() {
return this.SAML2.getGlideSaml2Api().calculateResponseAttributes();
},
isAutoRedirectIDP: function(sysId) {
return new SNC.GlideMultiSSO().isAutoRedirectIdp(sysId);
},
logDebug: function(msg) {
this.SAML2.logDebug(msg);
},
type: 'MultiSSOv2_SAML2_internal'
});
Does anyone have any tips or working code to help me out? Basically we do not want user's to have to retype their email again (someone@somewhere.org) on the Microsoft login screen after having already entering it on landing page.
Thanks in advance for any help!
-Chris
- Labels:
-
Instance Configuration
- 1,144 Views
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-12-2024 11:20 AM
Having this issue now. Were you able to solve this problem?