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
Wednesday
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.
