ServiceNow

How to protect ServiceNow with Multi-factor authentication (MFA) using Integrity by Fortified ID

Scenario

In this scenario, we will setup ServiceNow as an OpenID Connect Relying Party and connect it to Integrity, acting as an OpenID Connect Provider.

Any authentication method provided by Integrity may be used for this scenario.

The example below describes authentication with Username and Password, using an LDAP/Active Directory account with the mail attribute set.

Prerequisite

There are some prerequisite for this use case. You will need the following environment:

  • LDAP directory. The users to authenticate reside in this user directory. The example code is configured using an Active Directory. We are using the mail attribute on the Active Directory user.

  • The scenario server must be published online.

    • Communication access (https) from the ServiceNow instance to the scenario server.

    • A front-end web server, terminating SSL, must be placed in front of the Integrity server. The front-end web server must proxy all traffic to integrity, using port 8080. An apache example config is present here.

  • The email address of the user must be present in the ServiceNow user store.

  • Before you start enabling external authentication providers in ServiceNow, make sure you enable Account Recovery Context on your administrator user to avoid lockouts.

Note. All configuration is carried out on the scenario server. Testing is carried out in any web browser.

Configuration

Install and prepare configuration

  1. Download and install Integrity Web

    1. To install Integrity Web, se documentation and installation.

  2. All data in the server-config.json will be replaced with data from this use case.

    1. At the bottom of this page you have the entire configuration to paste to your server-config.file. In the steps below we will explain the part of the configuration you need to change to map to your environment.

  3. Go to bottom of the page or click the link right, Complete server-config.json file

  4. Copy the information and paste it to your server-config.json file.

Update configuration to map your environment

In this section we will look at parts of the configuration and add/replace data for your environment. In this use case we are using the globals concept which is using variables to easily replace data specific to an environment or if a value is used in many places just update it in one place.

  1. file_paths The file paths below is created where a folder called /customer is root and a subfolder is called config which stored the server-config.file. Then there are a number of subfolders under customer depending in the use case. The file paths might be different depending if you install in Windows/Linux or Docker. Below is an example of a folder structure.

    1. Find in module globals section: file-paths

    2. base_dir is the top folder where data is located that you do not want to be overwritten by an upgrade. Update the base_dir folder to map your installation.

      1. For Windows the value is correct

      2. If you use Docker, the change the value to ".", result should look like: "base_dir": ".",

  2. ldap Update the ldap information to map your environment.

    1. Find in module globals section: ldap

      "ldap": {
          "base_dn": "dc=company,dc=local",
          "host": "127.0.0.1",
          "port": 636,
          "bind_dn": "CN=admin,CN=Users,DC=company,DC=local",
          "bind_password": "password_for_the_ldap_account" }
    2. Create a test ldap user Make sure you have a test user to test with. Make sure the user has a value in the mail attribut since it is used as username to login with.

  3. http

    Update the http information to map your environment. This is the port that Integrity Web will use to host the http service.

    1. Find in module globals section: http.

      "http": {
          "http_port": 8080 }
  4. keystore

    Either you download and use the test certificate in this scenario provided by us, if so you do not need to change anything. If you have a certificate then update the values below to map your certificate.

    1. Find in module globals section: keystore

      "keystore": {
          "alias": "fortifiedid",
          "key_password": "fortifiedid",
          "password": "fortifiedid",
          "path": "/config/resources/certificates/fortifiedid.p12" }
  5. Public adress

    1. Set the server domain. This is the public address that integrity will respond to.

       "oidc_op_address": "https://demo.fortifiedid.se",
  6. OIDC relying party This defines service now as a OIDC relying party. Change client_secret to any value of your choice. This will be set in the ServiceNow configuration. Change the ServiceNow domain to match your environment.

    1.  "relyingpartys": [{
      			"client_id": "servicenow",
      			"client_secret": "set_your_own_password",
      			"redirect_uri": ["https://crwpublicdemo.service-now.com/navpage.do"],
      			"post_logout_redirect_uris": ["https://crwpublicdemo.service-now.com/external_logout_complete.do"],
      			"pipe_id": "Retrieve_data_for_token_population"
      		}]

Get OpenID Connect Discovery URL

  1. Start Integrity Web. Verify logs to make sure it was started correctly.

  2. Open a web browser.

  3. Open the Discovery URL: https://<your_domain>/oidc/tenant1/.well-known/openid-configuration

  4. A JSON object should be displayed, showing properties of the OpenID Connect Provider.

Configure ServiceNow

Trust needs to be established between the RP (ServiceNow) and the OP (Integrity Web).

Add Integrity Web as a trusted identity provider

  1. Login to your ServiceNow domain as an administrator.

  2. Activate the Multi-provider SSO plugin using this guide.

  3. In the left menu, search for SSO

  4. Select Properties and Enable SSO

  5. In the left menu, search for Identity Providers.

  6. Click New

  7. Select OpenID Connect

  8. Enter the OP Discovery URL (fetched in previous step above)

  9. Enter a name

  10. Enter the client_id and client_secret (must match the configuration above)

  11. Enter the SSO label

  12. Enable Show as login option

  13. Save

Test the configuration

Login to ServiceNow using Integrity WEB as the authentication provider

  1. Open a browser

  2. Browse to https://<your_servicenow_domain>/

  3. Click on the provider

  4. You should be redirected to Integrity Web.

  5. You should be prompted for authentication.

  6. After authentication, you should be redirected back to your ServiceNow domain.

  7. You should now be logged in.

Troubleshooting

  1. If the error message is displayed by ServiceNow, go to 3. If the error message is displayed by Integrity, go to 2.

  2. Consult the server.log file to find the error. Fix accordingly.

  3. Login to your servicenow domain as an administrator.

  4. In the left menu, search for log.

  5. Open System logs

  6. View the log to find the error. Fix accordingly.

Complete server-config.json file

{
	"globals": {
		"file_paths": {
			"base_dir": "../customer"
		},
		"ldap": {
			"base_dn": "dc=company,dc=local",
			"host": "127.0.0.1",
			"port": 636,
			"bind_dn": "CN=admin,CN=Users,DC=company,DC=local",
			"bind_password": "password_for_the_ldap_account"
		},
		"http": {
			"http_port": 8080
		},
		"keystore": {
			"alias": "fortifiedid",
			"key_password": "fortifiedid",
			"password": "fortifiedid",
			"path": "/config/resources/certificates/fortifiedid.p12"
		},
		"oidc_op_address": "https://demo.fortifiedid.se",
		"relyingpartys": [{
			"client_id": "servicenow",
			"client_secret": "set_your_own_password",
			"redirect_uri": ["https://crwpublicdemo.service-now.com/navpage.do"],
			"post_logout_redirect_uris": ["https://crwpublicdemo.service-now.com/external_logout_complete.do"],
			"pipe_id": "Retrieve_data_for_token_population"
		}]
	},

	"modules": [{
			"name": "CefEventModule",
			"config": {}
		},
		{
			"name": "HttpClient",
			"config": {
				"name": "default",
				"idle_timeout_ms": 5000,
				"connect_timeout_ms": 5000
			}
		},
		{
			"name": "LdapClient",
			"enabled": true,
			"instances": 1,
			"config": {
				"name": "default",
				"connection": {
					"host": "${globals.ldap.host}",
					"port": "${globals.ldap.port}",
					"bind_dn": "${globals.ldap.bind_dn}",
					"bind_password": "${globals.ldap.bind_password}",
					"use_ssl": true,
					"ssl_trust_all": true
				}
			}
		},
		{
			"name": "OIDCModule",
			"enabled": true,
			"config": {
				"providers": [{
					"http_context": "/oidc",
					"http_port": ${globals.http.http_port},
					"discovery_meta": {
						"issuer": "${globals.oidc_op_address}/oidc/tenant1",
						"authorization_endpoint": "${globals.oidc_op_address}/test/authn/oidc_start",
						"token_endpoint": "${globals.oidc_op_address}/oidc/tenant1/token-endpoint",
						"userinfo_endpoint": "${globals.oidc_op_address}/oidc/tenant1/userinfo-endpoint",
						"jwks_uri": "${globals.oidc_op_address}/oidc/tenant1/.well-known/openid-configuration/jwks",
						"end_session_endpoint": "${globals.oidc_op_address}/test/authn/oidc/logout",
						"scopes_supported": ["openid"],
						"response_types_supported": ["code"],
						"grant_types_supported": ["authorization_code"],
						"subject_types_supported": ["public"],
						"id_token_signing_alg_values_supported": ["RS256"],
						"token_endpoint_auth_methods_supported": ["client_secret_post", "client_secret_basic"],
						"claims_supported": ["iss", "ver", "sub", "email", "email_verified", "family_name", "given_name", "name"],
						"request_parameter_supported": true
					},
					"tenant_op_path": "/tenant1",
					"keystore": "${globals.keystore}",
					"sign_jwt_keystore_password": "${globals.keystore.password}",
					"sign_jwt_keystore_alias": "${globals.keystore.alias}",
					"rps": "${globals.relyingpartys}"
				}]
			}
		},
		{
			"name": "AuthN",
			"enabled": true,
			"config": {
				"context_path": "/authn",
				"webroot_dir": "web",
				"authenticators": [{
						"id": "oidc",
						"type": "OIDCAuthCodeFlow",
						"config": {
							"base_path": "/test/authn",
							"failure_location": "/authn/failure.html",
							"simple_logout": true,
							"required_request_parameters": ["response_type", "client_id", "redirect_uri", "scope"],
							"required_authenticators": [
								"uid_pwd"
							],
							"jwt_headers": {
								"kid": ""
							},
							"jwt_claims": {
								"aud": "servicenow",
								"sub": "{{{item.subject}}}",
								"nonce": "{{{request.nonce}}}",
								"family_name": "{{{item.sn}}}",
								"given_name": "{{{item.givenName}}}",
								"name": "{{{item.displayName}}}",
								"iss": "${globals.oidc_op_address}/oidc/tenant1",
								"email": "{{{item.mail}}}",
								"email_verified": true
							},
							"userinfo_claims": {

								"email": "{{{item.mail}}}",
								"email_verified": true,
								"subject": "{{{item.subject}}}"
							},
							"token_endpoint_ttl": 90000,
							"userinfo_endpoint_ttl": 90000,
							"keystore": "${globals.keystore}",
							"sign_jwt_keystore_password": "${globals.keystore.password}",
							"sign_jwt_keystore_alias": "${globals.keystore.alias}",
							"rps": "${globals.relyingpartys}"
						}
					},

					{
						"id": "uid_pwd",
						"type": "UserNameAndPassword",
						"config": {
							"base_path": "/test/authn",
							"context_path": "/test/authn/uid_pwd",
							"pipe_id": "Validate_Username_Password_for_LDAP_user",
							"webroot_dir": "web/authenticator/username_password"

						}
					}
				]
			}
		},
		{
			"name": "Pipes",
			"config": {
				"pipes": [{
						"id": "Validate_Username_Password_for_LDAP_user",
						"config": {
							"valves": [{
									"name": "LDAPSearch",
									"enabled": true,
									"config": {
										"destination": "default",
										"base_dn": "${globals.ldap.base_dn}",
										"scope": "SUB",
										"filter": "mail={{{request.identifier}}}",
										"attributes": []
									}
								},
								{
									"name": "LDAPBind",
									"enabled": true,
									"config": {
										"destination": "default",
										"dn": "{{{item.id}}}",
										"password": "{{{request.password}}}"
									}
								},
								{
									"name": "DumpSession",
									"config": {}
								},
								{
									"name": "DumpState",
									"config": {}
								}
							]
						}
					},
					{
						"id": "Retrieve_data_for_token_population",
						"config": {
							"valves": [{
									"name": "DumpRequest",
									"config": {}
								},
								{
									"name": "LDAPSearch",

									"enabled": true,
									"config": {
										"destination": "default",
										"base_dn": "${globals.ldap.base_dn}",
										"scope": "SUB",
										"filter": "mail={{{request.username}}}",
										"attributes": [{
												"name": "givenName",
												"multivalue": false
											},
											{
												"name": "sn",
												"multivalue": false
											},
											{
												"name": "mail",
												"multivalue": false
											},
											{
												"name": "displayName",
												"multivalue": false
											}
										]
									}
								}
							]
						}
					}
				]
			}
		}
	]
}

Additional information

Integrity Web can be setup with multiple logical OpenID Connect Providers. This is applicable in use cases such as if you would like ServiceNow to present a list of different authentication methods.

Would you like to learn more about OpenID Connect, MFA or identity management for ServiceNow? Please contact Fortified ID for more information!

Last updated