@kwiz/common
Version:
KWIZ common utilities and helpers for M365 platform
392 lines • 19.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.GetTokenAudiencePrefix = GetTokenAudiencePrefix;
exports.GetDefaultScope = GetDefaultScope;
exports.GetMSALSiteScope = GetMSALSiteScope;
exports.GetGraphEndpointUrl = GetGraphEndpointUrl;
exports.GetMSALAdminConsentUrl = GetMSALAdminConsentUrl;
exports.GetSPFxClientAuthToken = GetSPFxClientAuthToken;
exports.GetSPFxClientAuthTokenSync = GetSPFxClientAuthTokenSync;
const collections_base_1 = require("../../helpers/collections.base");
const promises_1 = require("../../helpers/promises");
const random_1 = require("../../helpers/random");
const sharepoint_1 = require("../../helpers/sharepoint");
const typecheckers_1 = require("../../helpers/typecheckers");
const url_1 = require("../../helpers/url");
const auth_1 = require("../../types/auth");
const consolelogger_1 = require("../consolelogger");
const localstoragecache_1 = require("../localstoragecache");
const rest_1 = require("../rest");
const common_1 = require("../sharepoint.rest/common");
const discovery_1 = require("./discovery");
const logger = consolelogger_1.ConsoleLogger.get("utils/auth/common");
function GetTokenAudiencePrefix(appId) {
return `api://${appId}`;
}
function GetDefaultScope(appId) {
return `${GetTokenAudiencePrefix(appId)}/access_as_user`;
}
function GetMSALSiteScope(hostName) {
return `https://${hostName}`;
}
function _getGraphUrlFromHost(loginHostOrsharePointHost) {
loginHostOrsharePointHost = loginHostOrsharePointHost.toLowerCase();
if (loginHostOrsharePointHost.endsWith('/')) {
return loginHostOrsharePointHost.slice(0, -1);
}
if (loginHostOrsharePointHost.endsWith(".us")) {
return "https://graph.microsoft.us";
}
else if (loginHostOrsharePointHost.endsWith(".sharepoint.com") || loginHostOrsharePointHost.endsWith("login.microsoftonline.com")) {
return "https://graph.microsoft.com";
}
else if (loginHostOrsharePointHost.endsWith(".de")) {
return "https://graph.microsoft.de";
}
else if (loginHostOrsharePointHost.endsWith(".cn")) {
return "https://microsoftgraph.chinacloudapi.cn";
}
return null;
}
function GetGraphEndpointUrl() {
let url = "";
if ("location" in globalThis && !(0, typecheckers_1.isNullOrEmptyString)(globalThis.location.host)) {
url = _getGraphUrlFromHost(globalThis.location.host);
}
if ((0, typecheckers_1.isNullOrEmptyString)(url) && (0, common_1.hasGlobalContext)() === true) {
if (!(0, typecheckers_1.isNullOrEmptyString)(_spPageContextInfo["msGraphEndpointUrl"])) {
url = _spPageContextInfo["msGraphEndpointUrl"];
}
if ((0, typecheckers_1.isNullOrEmptyString)(url) && !(0, typecheckers_1.isNullOrEmptyString)(_spPageContextInfo["aadInstanceUrl"])) {
url = _getGraphUrlFromHost(_spPageContextInfo["aadInstanceUrl"]);
}
if ((0, typecheckers_1.isNullOrEmptyString)(url) && !(0, typecheckers_1.isNullOrEmptyString)(_spPageContextInfo["aadTenantId"])) {
let tenantInfo = (0, discovery_1.DiscoverTenantInfo)(_spPageContextInfo["aadTenantId"], true);
if (!(0, typecheckers_1.isNullOrUndefined)(tenantInfo) && !(0, typecheckers_1.isNullOrEmptyString)(tenantInfo.msGraphHost)) {
url = `https://${tenantInfo.msGraphHost}`;
}
}
}
if ((0, typecheckers_1.isNullOrEmptyString)(url)) {
let tenantInfo = (0, discovery_1.AutoDiscoverTenantInfo)(true);
if (!(0, typecheckers_1.isNullOrUndefined)(tenantInfo) && !(0, typecheckers_1.isNullOrEmptyString)(tenantInfo.msGraphHost)) {
url = `https://${tenantInfo.msGraphHost}`;
}
}
return !(0, typecheckers_1.isNullOrEmptyString)(url) ? url : "https://graph.microsoft.com";
}
function GetMSALAdminConsentUrl(params) {
const stateParam = (0, typecheckers_1.isNullOrEmptyString)(params.state) ? '' : `&state=${encodeURIComponent(params.state)}`;
const url = `https://login.microsoftonline.com/${params.tenantId || "common"}/adminconsent?client_id=${encodeURIComponent(params.clientId)}&redirect_uri=${encodeURIComponent(params.redirectUri)}${stateParam}`;
return url;
}
function _getGetSPFxClientAuthTokenParams(siteUrl, spfxTokenType = auth_1.SPFxAuthTokenType.Graph) {
let acquireURL = `${(0, common_1.GetRestBaseUrl)(siteUrl)}/SP.OAuth.Token/Acquire`;
//todo: add all the resource end points (ie. OneNote, Yammer, Stream)
let resource = "";
let isSPOToken = false;
switch (spfxTokenType) {
case auth_1.SPFxAuthTokenType.Outlook:
resource = "https://outlook.office365.com/search";
break;
case auth_1.SPFxAuthTokenType.SharePoint:
case auth_1.SPFxAuthTokenType.MySite:
isSPOToken = true;
let absUrl = (0, url_1.makeFullUrl)(acquireURL);
resource = new URL(absUrl).origin;
if (spfxTokenType === auth_1.SPFxAuthTokenType.MySite) {
let split = resource.split(".");
split[0] += "-my";
resource = split.join(".");
}
break;
default:
resource = GetGraphEndpointUrl();
break;
}
let data = {
resource: resource,
tokenType: isSPOToken ? "SPO" : undefined
};
let params = {
url: acquireURL,
body: JSON.stringify(data),
options: {
allowCache: true,
localStorageExpiration: {
minutes: 1
},
includeDigestInPost: true,
headers: {
"Accept": "application/json;odata.metadata=minimal",
"content-type": "application/json; charset=UTF-8",
"odata-version": "4.0",
}
}
};
return params;
}
function _parseAndCacheGetSPFxClientAuthTokenResult(result, spfxTokenType = auth_1.SPFxAuthTokenType.Graph) {
if ((0, common_1.hasGlobalContext)() && !(0, typecheckers_1.isNullOrUndefined)(result) && !(0, typecheckers_1.isNullOrEmptyString)(result.access_token)) {
let expiration = new Date();
if ((0, typecheckers_1.isNumeric)(result.expires_on)) {
expiration = new Date(parseInt(result.expires_on.toString()) * 1000);
}
else if ((0, typecheckers_1.isNumber)(result.expires_in)) {
expiration.setSeconds(expiration.getSeconds() + result.expires_in);
}
else {
expiration.setSeconds(expiration.getSeconds() + 60 * 30);
}
(0, localstoragecache_1.setCacheItem)(`spfx_access_token_${spfxTokenType}`, result.access_token, expiration);
return result.access_token;
}
return null;
}
function _getSPFxClientAuthTokenFromCache(spfxTokenType = auth_1.SPFxAuthTokenType.Graph) {
if ((0, common_1.hasGlobalContext)()) {
let cachedToken = (0, localstoragecache_1.getCacheItem)(`spfx_access_token_${spfxTokenType}`);
if (!(0, typecheckers_1.isNullOrEmptyString)(cachedToken)) {
return cachedToken;
}
}
return null;
}
function _getSPFxClientAuthTokenFromMSALCache(resource, spfxTokenType = auth_1.SPFxAuthTokenType.Graph) {
try {
let cachedToken;
for (let key in localStorage) {
if (key.startsWith(`Identity.OAuth.${_spPageContextInfo.systemUserKey}`)
&& key.indexOf(resource) !== -1) {
cachedToken = JSON.parse(localStorage.getItem(key));
break;
}
}
if (!(0, typecheckers_1.isNullOrUndefined)(cachedToken)
&& !(0, typecheckers_1.isNullOrEmptyString)(cachedToken.value)
&& (0, typecheckers_1.isNumber)(cachedToken.expiration)
&& cachedToken.expiration > new Date().getTime()) {
return _parseAndCacheGetSPFxClientAuthTokenResult({
access_token: cachedToken.value,
// convert milliseconds to seconds
expires_on: Math.floor(new Date(cachedToken.expiration).getTime() / 1000),
resource: resource,
scope: null,
token_type: "Bearer"
}, spfxTokenType);
}
}
catch {
}
return null;
}
/** Acquire an authorization token for a Outlook, Graph, or SharePoint the same way SPFx clients do */
async function GetSPFxClientAuthToken(siteUrl, spfxTokenType = auth_1.SPFxAuthTokenType.Graph) {
await (0, sharepoint_1.isSPPageContextInfoReady)();
if ((0, typecheckers_1.isTypeofFullNameNullOrUndefined)("_spPageContextInfo")
|| _spPageContextInfo.isSPO !== true
|| _spPageContextInfo.isAppWeb === true
|| _spPageContextInfo.isAnonymousGuestUser === true
|| _spPageContextInfo["isEmailAuthenticationGuestUser"] === true) {
return null;
}
let cachedToken = _getSPFxClientAuthTokenFromCache(spfxTokenType);
if (!(0, typecheckers_1.isNullOrEmptyString)(cachedToken)) {
return cachedToken;
}
let key = `GetSPFxClientAuthToken_${_spPageContextInfo.userLoginName}_${_spPageContextInfo.siteId}_${spfxTokenType}`;
return await (0, promises_1.promiseLock)(key, async () => {
if (spfxTokenType === auth_1.SPFxAuthTokenType.Graph) {
let graphResource = GetGraphEndpointUrl();
let token = _getSPFxClientAuthTokenFromMSALCache(graphResource, spfxTokenType);
if (!(0, typecheckers_1.isNullOrEmptyString)(token)) {
return token;
}
try {
let _spComponentLoader = window["_spComponentLoader"];
let manifests = _spComponentLoader.getManifests();
let manifest = (0, collections_base_1.firstOrNull)(manifests, (manifest) => {
return manifest.alias === "@microsoft/sp-http-base";
});
let module = await _spComponentLoader.loadComponentById(manifest.id);
let factory = new module.AadTokenProviderFactory();
let provider = await factory.getTokenProvider();
let token = await provider.getToken(graphResource, true);
if (!(0, typecheckers_1.isNullOrEmptyString)(token)) {
return _parseAndCacheGetSPFxClientAuthTokenResult({
access_token: token,
expires_on: null,
resource: graphResource,
scope: null,
token_type: "Bearer"
}, spfxTokenType);
}
}
catch (ex) {
logger.error(ex);
}
try {
let bufferToString = (buffer) => {
let result = Array.from(buffer, (c) => { return String.fromCodePoint(c); }).join("");
return window.btoa(result);
};
let ni = new Uint32Array(1);
let ci = () => {
let b = window.crypto.getRandomValues(ni);
return b[0];
};
let generateNonce = () => {
let ti = "0123456789abcdef";
const e = Date.now(), t = 1024 * ci() + (1023 & ci()), n = new Uint8Array(16), a = Math.trunc(t / 2 ** 30), i = t & 2 ** 30 - 1, r = ci();
n[0] = e / 2 ** 40,
n[1] = e / 2 ** 32,
n[2] = e / 2 ** 24,
n[3] = e / 65536,
n[4] = e / 256,
n[5] = e,
n[6] = 112 | a >>> 8,
n[7] = a,
n[8] = 128 | i >>> 24,
n[9] = i >>> 16,
n[10] = i >>> 8,
n[11] = i,
n[12] = r >>> 24,
n[13] = r >>> 16,
n[14] = r >>> 8,
n[15] = r;
let o = "";
for (let e = 0; e < n.length; e++)
o += ti.charAt(n[e] >>> 4),
o += ti.charAt(15 & n[e]),
3 !== e && 5 !== e && 7 !== e && 9 !== e || (o += "-");
return o;
};
let requestId = (0, random_1.getUniqueId)();
let stateBuffer = new TextEncoder().encode(JSON.stringify({
id: (0, random_1.getUniqueId)(),
meta: {
interactionType: "silent"
}
}));
let state = bufferToString(stateBuffer);
let redirectUri = `https://${window.location.host}/_forms/spfxsinglesignon.aspx`;
let sid = _spPageContextInfo["aadSessionId"];
let codeVerifierBuffer = window.crypto.getRandomValues(new Uint8Array(32));
let codeVerifier = bufferToString(codeVerifierBuffer).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
let codeChallengeBuffer = await window.crypto.subtle.digest("SHA-256", new TextEncoder().encode(codeVerifier));
let codeChallenge = bufferToString(new Uint8Array(codeChallengeBuffer)).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
let nonce = generateNonce();
let url = `${_spPageContextInfo["aadInstanceUrl"]}/${_spPageContextInfo.aadTenantId}/oauth2/v2.0/authorize?`;
url += `client_id=08e18876-6177-487e-b8b5-cf950c1e598c`;
url += `&scope=${encodeURIComponent(`${graphResource}/.default openid profile offline_access`)}`;
url += `&redirect_uri=${encodeURIComponent(redirectUri)}`;
url += `&client-request-id=${encodeURIComponent(requestId)}`;
url += `&response_mode=fragment`;
url += `&response_type=code`;
url += `&code_challenge=${codeChallenge}&code_challenge_method=S256&prompt=none`;
url += `&sid=${encodeURIComponent(sid)}&nonce=${nonce}`;
url += `&state=${encodeURIComponent(state)}`;
let getCodeFromIframe = async () => {
return new Promise((resolve, reject) => {
try {
let iframe = document.createElement("iframe");
iframe.style.visibility = "hidden";
iframe.style.position = "absolute";
iframe.style.width = iframe.style.height = "0";
iframe.style.border = "0";
iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
iframe.src = url;
iframe.onload = () => {
window.setTimeout(() => {
let params = new URLSearchParams(iframe.contentWindow.location.hash.replace("#", "?"));
let pCode = params.get("code");
let pState = params.get("state");
let pSid = params.get("session_state");
if (!(0, typecheckers_1.isNullOrEmptyString)(pCode) && pState === state && pSid === sid) {
resolve(pCode);
}
else {
reject();
}
document.body.removeChild(iframe);
}, 100);
};
document.body.appendChild(iframe);
}
catch {
reject();
}
});
};
let authCode = await getCodeFromIframe();
if (!(0, typecheckers_1.isNullOrEmptyString)(authCode)) {
let url = `${_spPageContextInfo["aadInstanceUrl"]}/${_spPageContextInfo.aadTenantId}/oauth2/v2.0/token?`;
url += `client-request-id=${encodeURIComponent(requestId)}`;
let fd = new FormData();
fd.append("client_id", "08e18876-6177-487e-b8b5-cf950c1e598c");
fd.append("scope", `https://${graphResource}/.default openid profile offline_access`);
fd.append("redirect_uri", redirectUri);
fd.append("code", authCode);
fd.append("grant_type", "authorization_code");
fd.append("code_verifier", codeVerifier);
let response = await fetch(url, {
method: "POST",
body: fd
});
if (response.ok) {
let authToken = await response.json();
return _parseAndCacheGetSPFxClientAuthTokenResult(authToken, spfxTokenType);
}
}
}
catch (ex) {
logger.error(ex);
}
}
else {
try {
let { url, body, options } = _getGetSPFxClientAuthTokenParams(siteUrl, spfxTokenType);
let result = await (0, rest_1.GetJson)(url, body, options);
return _parseAndCacheGetSPFxClientAuthTokenResult(result, spfxTokenType);
}
catch {
}
}
return null;
}, 6000);
}
/** Acquire an authorization token for a Outlook, Graph, or SharePoint the same way SPFx clients do */
function GetSPFxClientAuthTokenSync(siteUrl, spfxTokenType = auth_1.SPFxAuthTokenType.Graph) {
(0, sharepoint_1.isSPPageContextInfoReadySync)();
if ((0, typecheckers_1.isTypeofFullNameNullOrUndefined)("_spPageContextInfo")
|| _spPageContextInfo.isSPO !== true
|| _spPageContextInfo.isAppWeb === true
|| _spPageContextInfo.isAnonymousGuestUser === true
|| _spPageContextInfo["isEmailAuthenticationGuestUser"] === true) {
return null;
}
let cachedToken = _getSPFxClientAuthTokenFromCache(spfxTokenType);
if (!(0, typecheckers_1.isNullOrEmptyString)(cachedToken)) {
return cachedToken;
}
if (spfxTokenType === auth_1.SPFxAuthTokenType.Graph) {
let resource = GetGraphEndpointUrl();
let token = _getSPFxClientAuthTokenFromMSALCache(resource, spfxTokenType);
if (!(0, typecheckers_1.isNullOrEmptyString)(token)) {
return token;
}
// Cache it for next time using the async method
GetSPFxClientAuthToken(siteUrl, spfxTokenType);
}
else {
try {
let { url, body, options } = _getGetSPFxClientAuthTokenParams(siteUrl, spfxTokenType);
let response = (0, rest_1.GetJsonSync)(url, body, options);
return _parseAndCacheGetSPFxClientAuthTokenResult(response.result, spfxTokenType);
}
catch {
}
}
return null;
}
//# sourceMappingURL=common.js.map