Skip to content

Commit 9a32eef

Browse files
committed
sso updates
1 parent 6723f7c commit 9a32eef

File tree

3 files changed

+360
-4
lines changed

3 files changed

+360
-4
lines changed

src/js/api.library.js

Lines changed: 248 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,14 @@ api.ajax.jsonrpc = {};
396396
* @param {*} pItemSpinner
397397
* @param {*} pHideSuccessErrorModal
398398
*/
399+
399400
api.ajax.jsonrpc.request = function (pAPI_URL, pAPI_Method, pAPI_Params, callbackFunctionName_onSuccess, callbackParams_onSuccess, callbackFunctionName_onError, callbackParams_onError, pAJAX_Params, pItemSpinner = null, pHideSuccessErrorModal = false) {
401+
//call the async function to handle catch block of acquiretokensilently
402+
api.ajax.jsonrpc.processRequestAsync(pAPI_URL, pAPI_Method, pAPI_Params, callbackFunctionName_onSuccess, callbackParams_onSuccess, callbackFunctionName_onError, callbackParams_onError, pAJAX_Params, pItemSpinner, pHideSuccessErrorModal)
403+
}
404+
405+
406+
api.ajax.jsonrpc.processRequestAsync = async function (pAPI_URL, pAPI_Method, pAPI_Params, callbackFunctionName_onSuccess, callbackParams_onSuccess, callbackFunctionName_onError, callbackParams_onError, pAJAX_Params, pItemSpinner = null, pHideSuccessErrorModal = false) {
400407
// Default API parameters
401408
pAPI_Params = pAPI_Params || {};
402409

@@ -430,6 +437,7 @@ api.ajax.jsonrpc.request = function (pAPI_URL, pAPI_Method, pAPI_Params, callbac
430437
"id": callID
431438
};
432439

440+
433441
// Extend AJAX Parameters
434442
var extendedAJAXParams = {
435443
url: pAPI_URL,
@@ -511,10 +519,24 @@ api.ajax.jsonrpc.request = function (pAPI_URL, pAPI_Method, pAPI_Params, callbac
511519
}
512520
}
513521

514-
// Merge pAJAX_Params into extendedAJAXParams
515-
$.extend(extendedAJAXParams, pAJAX_Params);
522+
523+
let headers = pAJAX_Params.headers || {};
524+
516525

517526
try {
527+
//force to wait for return
528+
const token = await api.sso.acquireAccessTokenSilently(pAPI_Method);
529+
530+
if (token) {
531+
headers["MSAL"] = `Bearer ${token}`;
532+
}
533+
534+
// Inject headers into AJAX params
535+
extendedAJAXParams.headers = headers;
536+
537+
// Merge pAJAX_Params after injecting headers to avoid overwrite
538+
$.extend(true, extendedAJAXParams, pAJAX_Params);
539+
518540
// Start the nav loader
519541
$("#nav-loader").removeClass('invisible');
520542
$("body").css("cursor", "progress");
@@ -528,7 +550,7 @@ api.ajax.jsonrpc.request = function (pAPI_URL, pAPI_Method, pAPI_Params, callbac
528550
// Make the Ajax call
529551
return $.ajax(extendedAJAXParams);
530552
} catch (error) {
531-
console.log(error)
553+
console.log(error);
532554
// Pop the exception in the Bootstrap Modal
533555
api.modal.exception("An unhandled Ajax exception has occurred. Please try again.");
534556
return false;
@@ -1058,4 +1080,226 @@ api.cookie.session.intervalRoutine = function () {
10581080
} else {
10591081
// The session is valid, do nothing
10601082
}
1061-
};
1083+
};
1084+
1085+
1086+
/*******************************************************************************
1087+
API - Library - Single Sign On
1088+
*******************************************************************************/
1089+
api.sso = {};
1090+
api.sso.msalConfigObj = {};
1091+
api.sso.msalInstance = null;
1092+
api.sso.tokenPromise = null;
1093+
1094+
//temporary, to be removed
1095+
api.sso.response = null;
1096+
1097+
api.sso.authenticateUserSilently = function (callbackFunctionName_onSuccess, callbackFunctionName_onError, hideLoginPopup) {
1098+
api.spinner.start();
1099+
1100+
hideLoginPopup = hideLoginPopup || false;
1101+
1102+
let loginRequest = {
1103+
scopes: ["api://" + api.sso.msalConfigObj.auth.clientId + "/access_application"]
1104+
};
1105+
1106+
api.sso.msalInstance.ssoSilent(loginRequest).then((response) => {
1107+
api.spinner.stop();
1108+
//User logged in successfully
1109+
1110+
console.log(response.accessToken)
1111+
/* console.log("expiresOn : " + response.expiresOn);
1112+
console.log("extExpiresOn : " + response.extExpiresOn);
1113+
console.log("FIRST");
1114+
*/
1115+
1116+
api.sso.response = response;
1117+
1118+
1119+
api.ajax.callback(callbackFunctionName_onSuccess, response);
1120+
}).catch(error => {
1121+
api.spinner.stop();
1122+
console.log("Silent Error: " + error);
1123+
1124+
if (hideLoginPopup) {
1125+
//Error during login
1126+
if (callbackFunctionName_onError != null) {
1127+
api.ajax.callback(callbackFunctionName_onError, error);
1128+
}
1129+
} else if (error instanceof msal.InteractionRequiredAuthError) {
1130+
//if the error indicates interaction is required, fallback to login
1131+
console.log("SSO Silent requires interation. prompting login....")
1132+
api.sso.authenticateUserPopup(callbackFunctionName_onSuccess, callbackFunctionName_onError);
1133+
} else {
1134+
//Error during login
1135+
if (callbackFunctionName_onError != null) {
1136+
api.ajax.callback(callbackFunctionName_onError, error);
1137+
}
1138+
}
1139+
});
1140+
};
1141+
1142+
1143+
/**
1144+
* call to entra ID to authenticate the user with popup
1145+
* @param {*} callbackFunctionName_onSuccess
1146+
* @param {*} callbackFunctionName_onError
1147+
*/
1148+
api.sso.authenticateUserPopup = function (callbackFunctionName_onSuccess, callbackFunctionName_onError) {
1149+
console.log("authenticateUserPopup");
1150+
let loginRequest = {
1151+
scopes: ["api://" + api.sso.msalConfigObj.auth.clientId + "/access_application"]
1152+
};
1153+
api.spinner.start();
1154+
api.sso.msalInstance.loginPopup(loginRequest)
1155+
.then((response) => {
1156+
//User logged in successfully
1157+
api.spinner.stop();
1158+
console.log(response.accessToken)
1159+
//// api.idle.startIdleTimer();
1160+
api.ajax.callback(callbackFunctionName_onSuccess, response);
1161+
})
1162+
.catch(error => {
1163+
api.spinner.stop();
1164+
console.log("login failed:", error);
1165+
//Error during login
1166+
api.ajax.callback(callbackFunctionName_onError, error);
1167+
});
1168+
}
1169+
1170+
/**
1171+
* call to entra ID to authenticate the user with popup
1172+
* @param {*} callbackFunctionName_onSuccess
1173+
* @param {*} callbackFunctionName_onError
1174+
*/
1175+
api.sso.authenticateUserPopupAsync = async function () {
1176+
console.log("authenticateUserPopup");
1177+
let loginRequest = {
1178+
scopes: ["api://" + api.sso.msalConfigObj.auth.clientId + "/access_application"]
1179+
};
1180+
1181+
try {
1182+
return await api.sso.msalInstance.loginPopup(loginRequest);
1183+
} catch (error) {
1184+
console.log("login failed:", error);
1185+
// Error during login
1186+
return null;
1187+
}
1188+
1189+
}
1190+
1191+
/**
1192+
* call to entra ID to logout the user with popup
1193+
* @param {*} callbackFunctionName_onSuccess
1194+
* @param {*} callbackFunctionName_onError
1195+
*/
1196+
api.sso.logout = function (callbackFunctionName_onSuccess, callbackFunctionName_onError) {
1197+
// Default callback functions
1198+
callbackFunctionName_onSuccess = callbackFunctionName_onSuccess || null;
1199+
callbackFunctionName_onError = callbackFunctionName_onError || null;
1200+
// you can select which account application should sign out
1201+
const logoutRequest = {
1202+
account: api.sso.getMsalAccount()
1203+
};
1204+
api.sso.msalInstance.logoutPopup(logoutRequest)
1205+
.then(() => {
1206+
if (callbackFunctionName_onSuccess) {
1207+
api.ajax.callback(callbackFunctionName_onSuccess);
1208+
}
1209+
1210+
})
1211+
.catch((error) => {
1212+
if (callbackFunctionName_onError) {
1213+
api.ajax.callback(callbackFunctionName_onError, error);
1214+
}
1215+
});
1216+
};
1217+
1218+
1219+
/**
1220+
* check if the user has an entra account and retrieve token if present
1221+
* @param {*} pAPI_Method
1222+
*/
1223+
api.sso.acquireAccessTokenSilently = function (pAPI_Method) {
1224+
1225+
console.log("get access token silently");
1226+
1227+
const validAccount = api.sso.getMsalAccount();
1228+
1229+
if (validAccount) {
1230+
// Set the first account as the active account
1231+
api.sso.msalInstance.setActiveAccount(validAccount);
1232+
} else {
1233+
// Handle the case where no account is signed in
1234+
return null; // Promise.reject("No account signed in");
1235+
}
1236+
1237+
console.log(api.sso.tokenPromise);
1238+
1239+
// If token request is already in progress, reuse it
1240+
if (api.sso.tokenPromise) return api.sso.tokenPromise;
1241+
1242+
let scope = ["api://" + api.sso.msalConfigObj.auth.clientId + "/access_application"];
1243+
1244+
/***
1245+
*
1246+
*
1247+
*
1248+
* to test the catch block
1249+
* api.sso.tokenPromise = Promise.reject(new msal.InteractionRequiredAuthError("Simulated error"))
1250+
*
1251+
*
1252+
*/
1253+
1254+
api.sso.tokenPromise = api.sso.msalInstance.acquireTokenSilent({
1255+
scopes: scope,
1256+
account: api.sso.msalInstance.getActiveAccount(),
1257+
forceRefresh: true
1258+
}).then((response) => {
1259+
// User is authenticated, return access token
1260+
api.sso.tokenPromise = null; // clear cache
1261+
console.log(response.expiresOn);
1262+
console.log("finished2 =>" + pAPI_Method);
1263+
1264+
return response.accessToken;
1265+
}).catch(async (error) => {
1266+
if (error instanceof msal.InteractionRequiredAuthError) {
1267+
const response = await api.sso.authenticateUserPopupAsync();
1268+
api.sso.tokenPromise = null;
1269+
if (response.accessToken) {
1270+
return response.accessToken;
1271+
} else {
1272+
return null
1273+
}
1274+
} else {
1275+
api.sso.tokenPromise = null;
1276+
return null
1277+
}
1278+
});
1279+
1280+
console.log("finished1 =>" + pAPI_Method);
1281+
return api.sso.tokenPromise;
1282+
}
1283+
1284+
/**
1285+
*
1286+
*
1287+
* gets msalAccount for the application
1288+
*/
1289+
api.sso.getMsalAccount = function () {
1290+
1291+
if (api.sso.msalInstance == null) {
1292+
return null;
1293+
}
1294+
1295+
// Get all accounts
1296+
const accounts = api.sso.msalInstance.getAllAccounts();
1297+
1298+
//get the valid account
1299+
const validAccount = accounts.find(account => {
1300+
const idTokenClaims = account.idTokenClaims;
1301+
return idTokenClaims && idTokenClaims.aud === api.sso.msalConfigObj.auth.clientId;
1302+
});
1303+
1304+
return validAccount;
1305+
}

src/js/api.msal.config.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Configuration object to be passed to MSAL instance on creation.
3+
* For a full list of MSAL.js configuration parameters, visit:
4+
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md
5+
*/
6+
7+
api.sso.msalConfig = function (pClientId, pAuthority, pRedirectUri) {
8+
return {
9+
auth: {
10+
clientId: pClientId, // This is the ONLY mandatory field that you need to supply.
11+
authority: pAuthority,//////'https://c2c05d52-cfdf-45bf-bc6d-dd64f9a5ad8.ciamlogin.com/', // Replace the placeholder with your tenant subdomain
12+
redirectUri: pRedirectUri, // You must register this URI on Microsoft Entra admin center/App Registration. Defaults to window.location.href e.g. http://localhost:3000/
13+
navigateToLoginRequestUrl: false, // If "true", will navigate back to the original request location before processing the auth code response.
14+
},//'https://login.microsoftonline.com/2be945b2-703e-45c4-bb9b-d456cffaf4a8',//////
15+
cache: {
16+
cacheLocation: 'localStorage', // Configures cache location. "sessionStorage" is more secure, but "localStorage" gives you SSO.
17+
storeAuthStateInCookie: false, // set this to true if you have to support IE
18+
},
19+
system: {
20+
loggerOptions: {
21+
loggerCallback: (level, message, containsPii) => {
22+
if (containsPii) {
23+
return;
24+
}
25+
switch (level) {
26+
case msal.LogLevel.Error:
27+
// console.error(message);
28+
return;
29+
case msal.LogLevel.Info:
30+
// console.info(message);
31+
return;
32+
case msal.LogLevel.Verbose:
33+
// console.debug(message);
34+
return;
35+
case msal.LogLevel.Warning:
36+
// console.warn(message);
37+
return;
38+
}
39+
},
40+
},
41+
},
42+
}
43+
};

0 commit comments

Comments
 (0)