OAuth Bearer Token Near Expiration

techpriest001
Kilo Explorer

I've been working with my developer instance and exploring the API.   I am successful at making API calls using the OAuth integration with the "password" grant type.   But I've noticed something that seems less-than-optimal in regards to the access token and refresh token.   In my organization with our current ticket/incident system (not ServiceNow), we frequently make calls into that system to generate tickets, and we also drive automated workflows by polling our system for tickets/requests that can be serviced through automation. I expect we'll be doing the same with ServiceNow.   What I am concerned about is failed API calls due to calls being made near the expiration threshold of the access token's lifetime.

Please consider this example:

  1. One of our internal process begins polling ServiceNow for requests that can be automated.
  2. We initiate our first call into ServiceNow and we get our authorization token with a lifetime of 1799 seconds.
  3. Due to unfortunate timing, another process polls ServiceNow 1798'ish seconds later.
  4. We try to make a call into ServiceNow, but by the time the call launches and connects, our token is expired and our API call is rejected.

I have spent nearly 14 years doing API integrations, and the last 3 or so years working intimately with OAuth-protected APIs.   We could have any number of processes making calls into ServiceNow, and any number of them could land right at the access token's expiration threshold.   With other APIs in other systems, we eliminate this issue by looking at the remaining lifetime of the access token.   If the access token is within X seconds of expiration, we will preemptively refresh the token if the API allows it and the refresh token itself isn't expired, or we will initiate a new "login" to obtain a new access token and refresh token pair.

Unfortunately, ServiceNow's OAuth implementation seems like it makes it impossible to prevent this type of service interruption blip.   The issued access token does not include the absolute expiration date/time of the token.   The expiration time cannot be inferred because the creation time of the access token is not returned either.   Internally we can track when we requested the token and estimate the expiration time, but making the "refresh token" call simply returns the existing access token when we're too early.   Similarly, if we try to obtain a completely new access token early, we also get back the exact same existing access token we started with.

I guess I'm not sure what value the refresh token even provides, because we can't avoid "threshold expiration" by preemptively refreshing the token early.   Instead, I think we're going to have to create some kind of wasteful retry loop to ensure that if a call is made with a token about to expire, then we'll retry the operation after doing a new login attempt to obtaining an entirely new token. For this retry workaround, I wouldn't even bother with the refresh process because the calling code on our side won't know if the refresh token is also expired.

Has anyone else run into this issue or see something that I'm missing?   My solution would be for ServiceNow to issue tokens that include the expiration of the access token in UTC and also the expiration of the refresh token in UTC.   And ideally, the refresh process should always return a new token.

9 REPLIES 9

Pardhu Guduru
Tera Guru

HI Jason,



When you do the first call to get the access token , the response body will hold the expiration time of the access token as "expires_in". Please try that so that when ever the expires time breaches you can again do the call and get new Access token. Please see the below code as reference



var restbody = "grant_type=password&client_id=XXXXXXXXXXXX&client_secret=XXXXX&username=admin&password=welcome";




var request1 = new sn_ws.RESTMessageV2();


        request1.setEndpoint("https://yourinstance.service-now.com/oauth_token.do");


        request1.setHttpMethod("POST");


        request1.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded ');


        request1.setRequestBody(restbody);


var response1 = request1.execute();


        var responseBody1 = response1.getBody();


responseBody1 = new JSON.parse(responseBody1);


//var accesstoken = responseBody1.access_token;




gs.print(responseBody1.expires_in);


gs.print(responseBody1.access_token );


gs.print(responseBody1.refresh_token);



if in case any queries please feel free to get back.



regards,


Pardhu.


I understand that, but the expiration time is not precise and cannot be estimated accurately.   I don't know the exact time the token gets created, and I don't know how long the token takes to reach my API call.   I also don't know if a previous API call was made using the same credentials, thereby creating an access token much older than it appears.   Consider:



Process A makes an API call and gets a token with "expires_in" = 1800 seconds.


Process B makes an API call 1790 seconds later and gets the exact same access token with the exact same "expires_in" = 1800 seconds.



*Process B has 10 seconds to finish its work before its access token has to be completely regenerated, and nothing in the token makes that clear.



In every other OAuth implementation I have dealt with, the token can be refreshed prior to expiration or a completely new token can be issued, and the expiration is embedded as an absolute date/time stamp.   With this implementation, I can neither refresh the token nor can I obtain a fresh token that is getting near to expiration.   I have to wait for an authentication error to know that the token has expired.  


Were you able to find a solution to your scenario? We have the same issue.

No, sorry.  I'm hoping they improve their implementation and allow refreshing the token to actually generate a new token rather than returning the existing token with its approximate expiration.