- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 09-24-2024 09:30 PM
OAuth 2.0 is a widely adopted authentication method across numerous ServiceNow integrations. Whether it’s for a custom scripted integration or an Integration Hub spoke, OAuth token flows are frequently utilised. Two of the most commonly employed flows are the ‘Client Credentials’ and ‘Authorization Code’ token flows.
- Client Credentials Flow: Used for server-to-server integrations, where the application directly obtains an access token using its client ID and secret.
- Authorization Code Flow: Involves user consent; the application exchanges an authorization code for an access token after user authentication.
The Client Credentials flow is the simplest to implement, as it allows fetching a token from the third-party application without extra authentication. However, with the Authorization Code flow, issues can arise when the token expires, requiring explicit user authentication from the third-party application to obtain a new token.
Issue
Recently, during an integration with SharePoint Online, we observed that access tokens generated through the Authorization Code flow frequently expired, requiring us to repeatedly contact a SharePoint admin to re-authorize and refresh the token.
Observation
When examining the tokens in the OAuth Credentials [oauth_credential] table, we found that only the ‘refresh token’ was present, with no ‘access token’ available. Surprisingly, even the out-of-the-box spoke actions failed to automatically generate a new access token using the existing refresh token.
Wondering
This led me to wonder how ServiceNow’s outbound email functionality operates, as it also uses Microsoft Graph to send emails via Microsoft Exchange, relying on tokens generated through the Authorization Code flow. During my research, I found a support article – KB0823190 – which mentions the below as its 9th point.
- ‘As long there is a valid refresh token is available, the scheduled job named “Refresh email access token” will run every 3 minutes to check and get the new Access token.’
Further research
Upon investigating the instance further, I discovered the scheduled job in question, which runs a check every 3 minutes and calls a script include to reissue both the access and refresh tokens. This approach ensures that the tokens in ServiceNow never truly expire, keeping the email functionality active year-round, unless the credentials expire on the Microsoft side.
My initial plan was to leverage the out-of-the-box script include, EmailOAuthHelper, in a separate scheduled job to refresh the SharePoint tokens. However, ServiceNow has restricted it to handle token refreshes solely for email accounts. Nonetheless, it served as valuable inspiration for our approach.
Implementation
Create a modified version of the EmailOAuthHelper script include, removing any email account references and generalizing its functions, allowing it to refresh tokens from any table within ServiceNow.
The script is about 100 lines, so the copyable version can be found here.
Next steps
After creating the aforementioned script include in the instance, scheduled jobs can be set up to utilize it, automatically retrieving both access and refresh tokens from third-party applications. This approach significantly extends the longevity of integrations without requiring manual token refreshes.
- Example of the scheduled job for refreshing the SharePoint tokens.
- The GlideRecord used in the above script.
Conclusion
The scheduled script, combined with the script include, effectively replicate the functionality of the ‘Get OAuth Token’ related link available across various records in ServiceNow.
- Additional reference – KB0791131
- 14,746 Views

- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Thanks for the write up and sample script! This might just be what I'm looking for.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Steps:
Create a new script include:
Next steps
After creating the aforementioned script include in the instance, scheduled jobs can be set up to utilize it, automatically retrieving both access and refresh tokens from third-party applications. This approach significantly extends the longevity of integrations without requiring manual token refreshes.
- Example of the scheduled job for refreshing the SharePoint tokens.
- The GlideRecord used in the above script.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
We don't have a script include called EmailOAuthHelper in our instance and the link to the copyable version is blocked. Can you attach the script to this thread?
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
@NBeheydt Here you go.
var OauthRefreshTokenHandler = Class.create();
OauthRefreshTokenHandler.prototype = {
initialize: function() {},
isExpired: function(expiresIn, withinSeconds) {
if (expiresIn > withinSeconds)
return false;
return true;
},
getToken: function(requestorId, oauthProfileId) {
if (!requestorId || !oauthProfileId)
return null;
var client = new sn_auth.GlideOAuthClient();
return client.getToken(requestorId, oauthProfileId);
},
refreshAccessToken: function(requestorId, oauthProfileId, token) {
if (!(token && requestorId && oauthProfileId))
return;
var tokenRequest = new sn_auth.GlideOAuthClientRequest();
tokenRequest.setGrantType("refresh_token");
tokenRequest.setRefreshToken(token.getRefreshToken());
tokenRequest.setParameter('oauth_requestor_context', 'email');
tokenRequest.setParameter('oauth_requestor', requestorId);
tokenRequest.setParameter('oauth_provider_profile', oauthProfileId);
var oAuthClient = new sn_auth.GlideOAuthClient();
var tokenResponse = oAuthClient.requestTokenByRequest(null, tokenRequest);
var error = tokenResponse.getErrorMessage();
if (error)
gs.warn("Error:" + tokenResponse.getErrorMessage());
},
invokeClientCredentials: function(requestor, oauth_provider_profile) {
var requestor_context = "email";
var tokenRequest = new sn_auth.GlideOAuthClientRequest();
tokenRequest.setParameter('oauth_requestor_context', requestor_context);
tokenRequest.setParameter('oauth_requestor', requestor);
tokenRequest.setParameter('oauth_provider_profile', oauth_provider_profile);
var oAuthClient = new sn_auth.GlideOAuthClient();
var tokenResponse = oAuthClient.requestTokenByRequest(null, tokenRequest);
var errorMsg = tokenResponse.getErrorMessage();
if (errorMsg) {
gs.log('OAuth authentication failed for ' + requestor);
}
},
checkAndRefreshAccessToken: function(requestorGr, oauth_profile) {
var accountMsg = requestorGr.getValue("name");
if (!accountMsg)
accountMsg = requestorGr.getUniqueValue();
var token = this.getToken(requestorGr.getUniqueValue(), oauth_profile);
if (!token) {
gs.error("Access token is not fetched =" + requestorGr.getUniqueValue());
return;
}
var accessToken = token.getAccessToken();
if (accessToken) {
if (!this.isExpired(token.getExpiresIn(), 300))
return;
}
var oauthEntityGr = new GlideRecord("oauth_entity_profile");
if (oauthEntityGr.get(oauth_profile) && oauthEntityGr.getValue('grant_type') == 'client_credentials') {
this.invokeClientCredentials(requestorGr.getUniqueValue(), oauth_profile);
return;
}
if (!token.getRefreshToken()) {
gs.error("No OAuth refresh token. Manual reauthorization required. " + accountMsg);
return;
}
if (this.isExpired(token.getRefreshTokenExpiresIn(), 0)) {
gs.error("OAuth refresh token is expired. Manual reauthorization required. " + accountMsg);
return;
}
gs.info("Refreshing oauth access token. " + accountMsg);
this.refreshAccessToken(requestorGr.getUniqueValue(), oauth_profile, token);
},
type: 'OauthRefreshTokenHandler'
};
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Getting Access Token from the available Refresh token is very much clear from your Article , Thanks for that.
Please provide some insight what needs to be done for Refresh Token as it will expire in 100 days
Just an additional question : If i am using OOB Okta Spoke do i need to still use you article to generate new Access Tokens ??
Thanks in advance