- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
2 hours ago
Introduction
In some integrations, the authentication flow requires generating a JSON Web Token (JWT) before requesting the actual access token. Instead of directly exchanging the client_id and client_secret with the authentication endpoint, the system expects a signed JWT that includes specific claims such as issuer, subject, timestamps, and other identifiers. In these scenarios, it may be necessary to implement a custom script in ServiceNow that constructs the JWT according to the provider’s requirements using values like clientId, clientSecret, and additional claims. Below is an outline of how this can be implemented.
1: Generate the JWT
In ServiceNow we create a Script Include which accepts inputs like client_id, client_secret, subject, and other claims. It then builds a JWT with the correct claims (iss, sub, iat etc.), signs it (HS256 in our case), and returns the token.
var JWTGenerator = Class.create();
JWTGenerator.prototype = {
initialize: function () {},
/**
* params = {
* clientId: "", // iss
* subject: "", // sub
* issuedAt: <GlideDateTime>,
* secret: "<CLIENT_SECRET>",
* user: ""
* }
*/
generate: function (params) {
var now = new GlideDateTime();
var issuedAt = params.issuedAt || now;
// ----- HEADER (order: typ, alg) -----
var headerObj = {};
headerObj.typ = "JWT";
headerObj.alg = "HS256";
var headerJson = JSON.stringify(headerObj);
var encodedHeader = this._base64UrlSafe(this._b64EncodeString(headerJson));
// ----- PAYLOAD (order: sub, iss, iat, user) -----
var payloadObj = {};
payloadObj.sub = params.subject;
payloadObj.iss = params.clientId;
payloadObj.iat = this._toEpochSeconds(issuedAt);
if (params.user)
payloadObj.user = params.user;
var payloadJson = JSON.stringify(payloadObj);
var encodedPayload = this._base64UrlSafe(this._b64EncodeString(payloadJson));
var unsignedToken = encodedHeader + "." + encodedPayload;
// ----- SIGNATURE (HS256) -----
var mac = new global.CertificateEncryption();
// Key must be base64 so HMAC uses the same raw bytes as the Java/Auth0 code
var keyBase64 = this._b64EncodeString(params.secret);
var signatureBase64 = mac.generateMac(
keyBase64,
"HmacSHA256",
unsignedToken
);
var encodedSignature = this._base64UrlSafe(signatureBase64);
return unsignedToken + "." + encodedSignature;
},
// ---------- helpers ----------
_b64EncodeString: function (str) {
try {
if (typeof GlideStringUtil !== 'undefined' &&
GlideStringUtil.base64Encode) {
return GlideStringUtil.base64Encode(str);
}
} catch (e) {
// ignore and fall back to gs
}
return gs.base64Encode(str);
},
_toEpochSeconds: function (gdt) {
return Math.floor(gdt.getNumericValue() / 1000);
},
_base64UrlSafe: function (b64) {
b64 = b64.split("=").join(""); // remove padding
b64 = b64.split("+").join("-"); // + -> -
b64 = b64.split("/").join("_"); // / -> _
return b64;
},
type: "JWTGenerator"
};
Important notes:
-
The CertificateEncryption() API is used to ensure the solution works in the custom scope.
-
The claims must match exactly what the authentication API expects.
Now the script include above can be called from the Action script in the Workflow Studio/ Flow Designer, expecting the userId as the action input:
(function execute(inputs, outputs) {
var now = new GlideDateTime();
var params = {
clientId: gs.getProperty({sysPropertyNameClientId}), // sys_property of "Password2" type
subject: {custom_value} + inputs.user + '@' + inputs.user,
issuedAt: now,
secret: gs.getProperty({sysPropertyNameClientSecret}), // sys_property of "Password2" type
user: inputs.user
};
var jwt = new x_{custom_app_name}.JWTGenerator.generate(params);
// Return the JWT to the Action outputs
outputs.jwt_token = jwt;
})(inputs, outputs);2: Send the JWT to Obtain the Access Token
Once you have the JWT, the next step is to call the authentication endpoint and submit the token in the required header or body parameter. The API then returns your access token, which you use for subsequent calls.
Placeholder – REST call + headers:
POST https://example.com/oauth2/token
Headers:
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer <your_jwt_token>
Once the response is received from the token API, you'll need to parse the response body JSON to get the required access token.
At the end, the Flow Designer action would contain the following steps:
-
Call the JWT Script Include and generate the JWT.
-
Call the REST message to retrieve the access token.
-
Parse the REST message response and get the access token.
Validate the JWT Externally
During development, you might need to use tools like jwt.io to validate the generated JWT. This helps ensure that things like the proper algorithm (HS256/RS256) are used, claims are correct, and a valid signature is present.
Summary
This pattern of generating a JWT, exchanging it for an access token, then using that token in your integration works reliably and can be packaged as a reusable Flow Designer Action within ServiceNow. It’s a valuable approach when dealing with APIs that expect JWT bearer authentication.
- 31 Views
