Server-side
var APIAuthenticationManager = Class.create();
APIAuthenticationManager.prototype = {
initialize: function() {
this.strategies = {
'oauth': new OAuthStrategy(),
'basic': new BasicAuthStrategy(),
'apikey': new APIKeyStrategy()
};
},
authenticate: function(integrationName, authType) {
var strategy = this.strategies[authType];
if (!strategy) {
gs.error('APIAuthManager: Unsupported auth type: ' + authType);
return null;
}
var credentials = this._getCredentials(integrationName);
if (!credentials) {
gs.error('APIAuthManager: No credentials found for: ' + integrationName);
return null;
}
var authResult = strategy.authenticate(credentials);
this._logAuthEvent(integrationName, authType, authResult.success);
return authResult;
},
_getCredentials: function(integrationName) {
var gr = new GlideRecord('u_api_credentials');
gr.addQuery('u_integration_name', integrationName);
gr.query();
if (gr.next()) {
return {
clientId: gr.getDecryptedValue('u_client_id'),
clientSecret: gr.getDecryptedValue('u_client_secret'),
tokenUrl: gr.getDisplayValue('u_token_url'),
apiKey: gr.getDecryptedValue('u_api_key'),
username: gr.getDecryptedValue('u_username'),
password: gr.getDecryptedValue('u_password'),
scope: gr.getDisplayValue('u_scope'),
accessToken: gr.getDecryptedValue('u_access_token'),
tokenExpiry: gr.getDisplayValue('u_token_expiry'),
refreshToken: gr.getDecryptedValue('u_refresh_token')
};
}
return null;
},
_logAuthEvent: function(integration, authType, success) {
var gr = new GlideRecord('u_api_auth_log');
gr.initialize();
gr.u_integration_name = integration;
gr.u_auth_type = authType;
gr.u_success = success;
gr.u_timestamp = new GlideDateTime();
gr.u_user = gs.getUserID();
gr.u_session = gs.getSessionID();
gr.insert();
},
type: 'APIAuthenticationManager'
};
var OAuthStrategy = Class.create();
OAuthStrategy.prototype = {
authenticate: function(credentials) {
// Check if token is valid and not expired
if (this._isTokenValid(credentials)) {
return { success: true, token: credentials.accessToken, type: 'bearer' };
}
// Try refresh token first
if (credentials.refreshToken) {
var refreshResult = this._refreshToken(credentials);
if (refreshResult.success) {
return refreshResult;
}
}
// Fall back to client credentials flow
return this._getNewToken(credentials);
},
_isTokenValid: function(credentials) {
if (!credentials.accessToken || !credentials.tokenExpiry) {
return false;
}
var expiry = new GlideDateTime(credentials.tokenExpiry);
var now = new GlideDateTime();
var bufferMinutes = 5; // Refresh 5 minutes before expiry
expiry.addSeconds(-bufferMinutes * 60);
return now.before(expiry);
},
_refreshToken: function(credentials) {
try {
var request = new sn_ws.RESTMessageV2();
request.setEndpoint(credentials.tokenUrl);
request.setHttpMethod('POST');
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var body = 'grant_type=refresh_token&refresh_token=' + encodeURIComponent(credentials.refreshToken) +
'&client_id=' + encodeURIComponent(credentials.clientId) +
'&client_secret=' + encodeURIComponent(credentials.clientSecret);
request.setRequestBody(body);
var response = request.execute();
var responseBody = response.getBody();
var tokenData = JSON.parse(responseBody);
if (response.getStatusCode() == 200 && tokenData.access_token) {
this._updateStoredToken(credentials, tokenData);
return { success: true, token: tokenData.access_token, type: 'bearer' };
}
gs.warn('OAuth refresh failed: ' + responseBody);
return { success: false, error: 'Token refresh failed' };
} catch (ex) {
gs.error('OAuth refresh error: ' + ex.message);
return { success: false, error: ex.message };
}
},
_getNewToken: function(credentials) {
try {
var request = new sn_ws.RESTMessageV2();
request.setEndpoint(credentials.tokenUrl);
request.setHttpMethod('POST');
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var body = 'grant_type=client_credentials&client_id=' + encodeURIComponent(credentials.clientId) +
'&client_secret=' + encodeURIComponent(credentials.clientSecret);
if (credentials.scope) {
body += '&scope=' + encodeURIComponent(credentials.scope);
}
request.setRequestBody(body);
var response = request.execute();
var responseBody = response.getBody();
var tokenData = JSON.parse(responseBody);
if (response.getStatusCode() == 200 && tokenData.access_token) {
this._updateStoredToken(credentials, tokenData);
return { success: true, token: tokenData.access_token, type: 'bearer' };
}
gs.error('OAuth token request failed: ' + responseBody);
return { success: false, error: 'Token request failed' };
} catch (ex) {
gs.error('OAuth token error: ' + ex.message);
return { success: false, error: ex.message };
}
},
_updateStoredToken: function(credentials, tokenData) {
var gr = new GlideRecord('u_api_credentials');
gr.addQuery('u_integration_name', credentials.integrationName);
gr.query();
if (gr.next()) {
gr.setValue('u_access_token', tokenData.access_token);
if (tokenData.refresh_token) {
gr.setValue('u_refresh_token', tokenData.refresh_token);
}
var expiry = new GlideDateTime();
expiry.addSeconds(tokenData.expires_in || 3600);
gr.setValue('u_token_expiry', expiry);
gr.update();
}
},
type: 'OAuthStrategy'
};
var BasicAuthStrategy = Class.create();
BasicAuthStrategy.prototype = {
authenticate: function(credentials) {
if (!credentials.username || !credentials.password) {
return { success: false, error: 'Missing username or password' };
}
var encoded = GlideStringUtil.base64Encode(credentials.username + ':' + credentials.password);
return { success: true, token: encoded, type: 'basic' };
},
type: 'BasicAuthStrategy'
};
var APIKeyStrategy = Class.create();
APIKeyStrategy.prototype = {
authenticate: function(credentials) {
if (!credentials.apiKey) {
return { success: false, error: 'Missing API key' };
}
return { success: true, token: credentials.apiKey, type: 'apikey' };
},
type: 'APIKeyStrategy'
};
The code implements a strategy pattern with a central APIAuthenticationManager that coordinates different authentication strategies (OAuth, Basic Auth, API Key). Each strategy handles its specific authentication logic while the manager provides unified credential storage, automatic token refresh for OAuth, and comprehensive audit logging. The OAuth strategy includes intelligent token lifecycle management with automatic refresh before expiration and fallback to new token acquisition.