Custom HTTP REST API Discovery with Two-Stage Authentication
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
12-27-2023 12:15 PM - edited 12-29-2023 12:02 PM
Hello,
I am attempting to produce a custom discovery process which obtains data through a REST API. The documentation via the link below is fairly helpful, but it assumed a single set of authentication credentials are provided with each API call. In my use case, the REST API first requires the client to authenticate via a Login endpoint and obtain an access token, which is then inserted into the Authorization header of subsequent calls. Is it possible for ServiceNow's discovery module to accommodate these requirements?
Thanks,
Matt
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
03-02-2026 08:54 AM - edited 03-13-2026 03:57 PM
After reading through a handful of OOTB custom HTTP patterns and mid server script includes, I have something that's working enough for me. I wish the Java packages leveraged had documentation and there was a documented method that reliably knew which credentials to use instead of looping through all of them for a match.
/**
* Discovery Probe - API Endpoint Query
*
* Authenticates against a management API using basic auth credentials,
* obtains a short-lived API key, then queries a target resource endpoint.
*
* Context variables:
* $username - Username to match in credential store
* $resourcePath - API resource path to query
* $outputAttribute - CTX attribute name for storing the response body
*/
var LOG_PREFIX = 'API Endpoint Probe';
var managementIp = CTX.getManagementIP();
// ── Packages & credential setup ────────────────────────────────────────
var credFactory = Packages.com.snc.commons.credentials.CredentialsProviderFactory;
var provider = credFactory.getCredentialsProvider();
var dbString = Packages.com.snc.automation_common.integration.creds.CredentialType;
var dbStringConv = [dbString.fromDbString("basic_auth")];
var base64 = new Packages.org.apache.commons.codec.binary.Base64();
var ioutils = new Packages.org.apache.commons.io.IOUtils();
var credentials = provider.iterator(dbStringConv, null, null, null);
// ── Step 1: Find matching credential ───────────────────────────────────
var username = '';
var password = '';
var credDetails = '';
while (credentials.hasNext()) {
credDetails = credentials.next();
username = credDetails.getAttribute("user_name");
if (username == $username) {
password = credDetails.getAttribute("password");
break;
}
}
if (!username || !password) {
ms.log(LOG_PREFIX + ' - No matching credential found for user: ' + $username);
CTX.setAttribute($outputAttribute, '');
} else {
// ── Step 2: Authenticate and obtain API key ────────────────────────
var apiKeyAuthValue = "Basic " + base64.encodeBase64String(ioutils.toByteArray(username + ':' + password));
var authUrl = 'https://' + managementIp + '/api/v1/realm_auth';
var authBody = JSON.stringify({"realm": "some-realm"});
var apiKey = '';
var authReq = new Packages.com.glide.communications.HTTPRequest(authUrl);
authReq.addHeader("Content-Type", "application/json");
authReq.addHeader("Authorization", apiKeyAuthValue);
var authResponse = authReq.post(authBody);
var authStatus = authResponse.getStatusCode();
var authResponseBody = authResponse.getBody();
if (authStatus < 200 || authStatus >= 300) {
ms.log(LOG_PREFIX + ' - Auth failed on ' + managementIp +
' - HTTP ' + authStatus);
} else if (!authResponseBody) {
ms.log(LOG_PREFIX + ' - Auth returned empty body on ' + managementIp);
} else {
try {
var apiKeyJson = JSON.parse(authResponseBody);
if ("api_key" in apiKeyJson) {
apiKey = apiKeyJson.api_key;
} else {
ms.log(LOG_PREFIX + ' - Auth response missing api_key field on ' + managementIp);
}
} catch (e) {
ms.log(LOG_PREFIX + ' - Failed to parse auth response on ' + managementIp +
': ' + e.message);
}
}
// ── Step 3: Query the target endpoint ──────────────────────────────
var respBody = '';
if (!apiKey) {
ms.log(LOG_PREFIX + ' - Skipping endpoint query, no API key obtained for ' + managementIp);
} else {
var endpointUrl = 'https://' + managementIp + $resourcePath;
var authValue = "Basic " + base64.encodeBase64String(ioutils.toByteArray(apiKey + ':'));
var endpointReq = new Packages.com.glide.communications.HTTPRequest(endpointUrl);
endpointReq.addHeader("Content-Type", "application/json");
endpointReq.addHeader("Authorization", authValue);
var endpointResp = endpointReq.get();
var endpointStatus = endpointResp.getStatusCode();
if (endpointStatus < 200 || endpointStatus >= 300) {
ms.log(LOG_PREFIX + ' - Endpoint query failed on ' + managementIp +
$resourcePath + ' - HTTP ' + endpointStatus);
} else {
respBody = endpointResp.getBody() || '';
}
}
CTX.setAttribute($outputAttribute, respBody);
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
a month ago
I'm trying to do something similar for a Discovery Pattern.
I have a Basic Auth credential in the credentials table that I need to retrieve and generate a base64encode of the username:password for the http call.
The problem is I can't seem to find the libraries to pull in the password, generate it as plain text for it to use in a base64encode operation.
Not sure if any one has done this but any help would be appreciated.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 weeks ago
I believe the packages you need are listed in the "Packages & credential setup" section of my example; steps 1 & 2 essentially accomplish what you're looking for.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
4 weeks ago
Thanks @hcallen
I couldn't isolate how to get the basic auth credential I needed because I think it was authenticating with the host login credential and not the basic auth credential i needed.
What I did instead was made an 'applicative' credential which is still a password2 username password anyway. the used the following code form my CI Type.
var rtrn = '';
var token = '';
var base64 = new Packages.org.apache.commons.codec.binary.Base64();
var ioutils = new Packages.org.apache.commons.io.IOUtils();
var gr = new GlideRecord('sa_applicative_credentials');
gr.addEncodedQuery('type=u_ibm_ace_message_flows');
gr.query();
if (gr.next()) {
var credID = gr.sys_id.toString();
}
var ciType = 'u_ibm_ace_message_flows';
var credList = CTX.getApplicativeCredentials(ciType).iterator();
if(credList.hasNext())
{
creds = credList.next();
var user = creds.getUserName();
var pass = creds.getPassword();
var string_to_encode = user + ":" + pass;
var token = "Basic " + base64.encodeBase64String(ioutils.toByteArray(string_to_encode));
}
rtrn = token;
Appreciate your input, I would have had a very difficult time doing this without your help.
darren
