Created
November 20, 2016 17:31
-
-
Save paulryan/8cc3ef5642f0d02f84362a9a7a9b93a2 to your computer and use it in GitHub Desktop.
Custom "adal" implementation for OAuth implict flow
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var CC = CC || {}; | |
CC.CORE = CC.CORE || {}; | |
CC.CORE.Log = function (errMsg) { | |
// console.log is undefined in IE10 and earlier unless in debug mode, so must check for it | |
if (typeof window.console === "object" && typeof console.log === "function") { | |
console.log(errMsg); | |
} | |
}; | |
CC.CORE.Adal = (function () { | |
"use strict"; | |
var appTokenFactory = function (aadAppClientId, resource) { | |
// redirectUrl is the URL which the iframe will redirect to once auth occurs. | |
// we use blank.gif as it is a very low payload | |
var redirectUrl = _spPageContextInfo.webAbsoluteUrl + "/_layouts/images/blank.gif"; | |
// NOTE on security: include the userId in the cache key to prevent the case where a user logs out but | |
// leaves the tab open and a new user logs in on the same tab. The first user's calender | |
// would be returned if we didn't associate the cache key with the current user. | |
var cacheKey = "candc_cache_adal_" + _spPageContextInfo.userId + "_" + aadAppClientId + "_" + resource; | |
this.params = { | |
clientId: aadAppClientId, | |
redirectUrl: redirectUrl, | |
resource: resource, | |
cacheKey: cacheKey | |
}; | |
var getAuthorizeUri = function (params, redirectUrl) { | |
var authUri = "https://login.microsoftonline.com/common/oauth2/authorize" + | |
"?client_id=" + params.clientId + | |
"&response_type=token" + | |
"&redirect_uri=" + encodeURIComponent(redirectUrl) + | |
"&resource=" + encodeURIComponent(params.resource); | |
return authUri; | |
}; | |
var getQueryStringParameterByName = function (name, url) { | |
name = name.replace(/[\[\]]/g, "\\$&"); | |
var regex = new RegExp("[?&#]" + name + "(=([^&#]*)|&|#|$)"); | |
var results = regex.exec(url); | |
if (!results) return null; | |
if (!results[2]) return ''; | |
return decodeURIComponent(results[2].replace(/\+/g, " ")); | |
}; | |
// create iframe, set its href, set listener for when loaded | |
// to parse the query string. Deferred returns upon parse of query string in iframe. | |
var acquirePassiveToken = function (params) { | |
var deferred = jQuery.Deferred(); | |
// create iframe and inject into dom | |
var iframe = jQuery("<iframe />").attr({ | |
width: 1, | |
height: 1, | |
src: getAuthorizeUri(params, params.redirectUrl) | |
}) | |
jQuery(document.body).append(iframe); | |
// bind event handler to iframe for parse query string on load | |
iframe.on("load", function (iframeData) { | |
parseAccessTokenFromIframe(iframeData, deferred); | |
}); | |
return deferred.promise(); | |
}; | |
// handle iframe once it has loaded | |
var parseAccessTokenFromIframe = function (iframeData, deferred) { | |
// read the iframe href | |
var frameHref = ""; | |
try { | |
// this will throw a cross-domain error for any issue other than success | |
// as the iframe will diplay the error on the login.microsoft domain | |
frameHref = iframeData.currentTarget.contentWindow.location.href; | |
} | |
catch (error) { | |
deferred.reject(error); | |
return; | |
} | |
// parse iframe query string parameters | |
var accessToken = getQueryStringParameterByName("access_token", frameHref); | |
var expiresInSeconds = getQueryStringParameterByName("expires_in", frameHref); | |
// delete the iframe, and event handler. | |
var iframe = jQuery(iframeData.currentTarget); | |
iframe.remove(); | |
// resolve promise | |
deferred.resolve({ | |
accessToken: accessToken, | |
expiresInSeconds: expiresInSeconds | |
}); | |
}; | |
// get the most recent token from the cache, or if not available, | |
// fetch a new token via iframe | |
var getToken = function (params) { | |
var deferred = jQuery.Deferred(); | |
// check for cached token | |
var tokenFromCache = CC.CORE.Cache.Get(params.cacheKey); | |
if (!tokenFromCache) { | |
// fetch token via iframe | |
acquirePassiveToken(params) | |
.done(function (tokenFromIframe) { | |
CC.CORE.Log("ADAL: Fetched token from iframe."); | |
// expire cache a minute before token expires to be safe | |
var cacheTimeout = (tokenFromIframe.expiresInSeconds - 60) * 1000; | |
CC.CORE.Cache.Set(params.cacheKey, tokenFromIframe, cacheTimeout); | |
// resolve the promise | |
deferred.resolve(tokenFromIframe); | |
}) | |
.fail(function (error) { | |
// Logs when rejection is caught | |
deferred.reject(error); | |
}); | |
} | |
else { | |
CC.CORE.Log("ADAL: Fetched token from cache."); | |
// resolve the promise | |
deferred.resolve(tokenFromCache); | |
} | |
return deferred.promise(); | |
}; | |
this.ExecuteQuery = function (query, additionalHeaders) { | |
var deferred = jQuery.Deferred(); | |
var params = this.params; | |
// get token from cache or via iframe | |
getToken(params) | |
.done(function (token) { | |
// submit request with token in header | |
var ajaxHeaders = { | |
'Authorization': 'Bearer ' + token.accessToken | |
}; | |
if (typeof additionalHeaders === "object") { | |
jQuery.extend(ajaxHeaders, additionalHeaders); | |
} | |
jQuery.ajax({ | |
type: "GET", | |
url: params.resource + query, | |
headers: ajaxHeaders | |
}).done(function (response) { | |
deferred.resolve(response); | |
}).fail(function (error) { | |
deferred.reject({ | |
error: error | |
}); | |
}); | |
}) | |
.fail(function (error) { | |
CC.CORE.Log('ADAL error occurred: ' + error); | |
deferred.reject({ | |
error: error, | |
authorizeUrl: getAuthorizeUri(params, window.location.href) | |
}); | |
}); | |
return deferred.promise(); | |
}; | |
}; | |
return { | |
AppTokenFactory: appTokenFactory | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment