-
Notifications
You must be signed in to change notification settings - Fork 0
/
WrkEasyOpenId.js
252 lines (233 loc) · 15.7 KB
/
WrkEasyOpenId.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
////////////////////////////////////////////////////////////////////////////
// _____ ___ ___ ____ //
// | ____|__ _ ___ _ _ / _ \ _ __ ___ _ __ |_ _| _ \ //
// | _| / _` / __| | | | | | | | '_ \ / _ \ '_ \ | || | | | //
// | |__| (_| \__ \ |_| | | |_| | |_) | __/ | | || || |_| | //
// |_____\__,_|___/\__, | \___/| .__/ \___|_| |_|___|____/ //
// Written |___/ by |_| Paul Friedli / v1.0 //
// //
// -------------------------------------------------------------------- //
// //
// Released under the WTFPL //
// (https://fr.wikipedia.org/wiki/WTFPL) //
// //
// NO rights reserved and NO copyrights //
// (and NO responsabilities :-) //
// //
// Just don't forget who wrote it and ... //
// ...please mention it in your sources //
// //
////////////////////////////////////////////////////////////////////////////
var WrkEasyOpenId = function () {
const msalConfig = {
auth: {
// 'Application (client) ID' dans le portail Azure, c'est un GUID
clientId: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", // <= a remplacer par votre propre ID
// URL complet pour atteindre le 'tenant' dans la forme https://login.microsoftonline.com/<tenant-id>
authority: "https://login.microsoftonline.com/YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY", // <= a remplacer par votre propre URL avec tenant ID
// URL de redirection complet de votre application
redirectUri: "https://www.your-domain-name.com/your-app-redirection-url", // <= a remplacer par votre propre URL de redirection complet
},
cache: {
// Pour configurer où sera votre cache :
// - utilisez "sessionStorage" (cas normal) et chaque onglet du navigateur vous permettra d'utiliser un compte différent pour vous authentifier
// - utilisez "localStorage" et chaque onglet utilisera le même utilisateur
cacheLocation: "sessionStorage",
// Mettre 'true' pour forcer l'ajout du state dans de cookies, seulement en cas de soucis avec IE11 ou Edge
storeAuthStateInCookie: false,
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case msal.LogLevel.Error:
console.error(message);
return;
case msal.LogLevel.Info:
// console.info(message); // Décommenter en mode développement pour voir ce qui se passe, les appels réalisés au niveau du protocole OpenId
return;
case msal.LogLevel.Verbose:
// console.debug(message); // Décommenter en mode développement pour voir ce qui se passe, les appels réalisés au niveau du protocole OpenId
return;
case msal.LogLevel.Warning:
console.warn(message);
return;
}
}
}
}
};
const msalClient = new msal.PublicClientApplication(msalConfig);
var _openIdAuthenticationLogin = function () {
msalClient.loginRedirect();
};
var _openIdAuthenticationLogout = function () {
msalClient.logoutRedirect();
};
var _openIdAuthenticationRefresh = function (
retrieveProfilePicture,
fnOnOIDAuthenticationProcess_Started,
fnOnOIDAuthenticationProcess_Failed,
fnOnOIDAuthenticationEvent_Unsuccessfull,
fnOnOIDAuthenticationEvent_Successfull
) {
// Vérifier la présence des paramètres obligatoires...
if (typeof retrieveProfilePicture !== "boolean") {
console.error("Le paramètre booléen retrieveProfilePicture DOIT être fourni !");
return;
}
if (typeof fnOnOIDAuthenticationEvent_Successfull !== 'function') {
console.error("La méthode à appeler dans le paramètre fnOnOIDAuthenticationEvent_Successfull DOIT être fournie !");
return;
}
if (typeof fnOnOIDAuthenticationEvent_Unsuccessfull !== 'function') {
console.error("La méthode à appeler dans le paramètre fnOnOIDAuthenticationEvent_Unsuccessfull DOIT être fournie !");
return;
}
// Appeler l'écouteur fnOnOIDAuthenticationProcess_Started() si fourni
if (typeof fnOnOIDAuthenticationProcess_Started === 'function') {
fnOnOIDAuthenticationProcess_Started();
}
msalClient
.handleRedirectPromise()
.then(response => {
const accounts = msalClient.getAllAccounts();
if (accounts.length === 0) {
fnOnOIDAuthenticationEvent_Unsuccessfull();
}
else if (accounts.length === 1) {
const userAccount = accounts[0];
const graphClient = _getGraphClient(userAccount);
graphClient
.api('/me?$select=id,displayName,givenName,jobTitle,mobilePhone,mail,companyName,userPrincipalName,city,companyName,country,department,displayName,employeeId,givenName,id,jobTitle,postalCode,preferredLanguage,state,streetAddress,usageLocation,userPrincipalName')
.get()
.then(res => {
let allOpenIDProfileInfos = {
token: {
token_id: userAccount.idTokenClaims.uti,
expiration_not_before: new Date(userAccount.idTokenClaims.exp * 1000).toLocaleDateString('fr-CH') + ' ' + new Date(userAccount.idTokenClaims.exp * 1000).toLocaleTimeString('fr-CH'),
expiration_not_before_in_secs: Math.round(userAccount.idTokenClaims.exp - (new Date()).getTime() / 1000)
},
user: {
id: (res.id != null) ? res.id.toLowerCase() : null,
displayName: res.displayName,
mail: (res.mail != null) ? res.mail.toLowerCase() : null,
userPrincipalName: res.userPrincipalName,
preferredLanguage: res.preferredLanguage,
givenName: res.givenName,
mobilePhone: res.mobilePhone,
photo: null // Pour le moment, car la demande va être envoyée sous peu (si souhaité)
},
company: {
employeeId: res.employeeId,
jobTitle: res.jobTitle,
companyName: res.companyName,
department: res.department,
streetAddress: res.streetAddress,
postalCode: res.postalCode,
city: res.city,
state: res.state,
country: res.country,
usageLocation: res.usageLocation
}
};
// Doit-on faire une requête pour obtenir la photo du profil ?
if (retrieveProfilePicture === true) {
// Vérifier si la photo n'est pas déjà en cache pour ce token-là, histoire de gagner beaucoup de temps en évitant la requête inutile (car la photo ne change pas souvent...)
let cachedPictureInSession = JSON.parse(sessionStorage.getItem('profile-picture-cache'));
if ((cachedPictureInSession != null) && (allOpenIDProfileInfos.token.token_id === cachedPictureInSession.currentTokenId)) {
// Oui, alors la prendre du cache local et éviter la requête vers Azure qui prends du temps...
allOpenIDProfileInfos.user.photo = cachedPictureInSession.pictureData;
// Comptabiliser le nbre de fois qu'on a utilisé l'image du cache
cachedPictureInSession.howManyTimesUsed++;
sessionStorage.setItem('profile-picture-cache', JSON.stringify(cachedPictureInSession));
console.info('Appel Azure évité, photo du profil prise du cache local ' + cachedPictureInSession.howManyTimesUsed + ' fois...');
// Appeler notre client avec l'ensemble des données du profil reçues
fnOnOIDAuthenticationEvent_Successfull(allOpenIDProfileInfos);
} else {
// Requête pour obtenir la photo du profil (si les droits de le faire sont concédés...)
graphClient
.api('/me/photo/$value')
.responseType("blob")
.get()
.then(picture => {
if (picture) {
let reader = new FileReader();
reader.addEventListener("load", () => {
allOpenIDProfileInfos.user.photo = reader.result;
// Mettre la photo en cache pour gagner de la vitesse d'exécution car elle change pas souvent...
const cachedPictureInSession = {
currentTokenId: allOpenIDProfileInfos.token.token_id,
pictureData: allOpenIDProfileInfos.user.photo,
howManyTimesUsed: 0
};
sessionStorage.setItem('profile-picture-cache', JSON.stringify(cachedPictureInSession));
// Appeler notre client avec l'ensemble des données du profil reçues
fnOnOIDAuthenticationEvent_Successfull(allOpenIDProfileInfos);
}, false);
reader.readAsDataURL(picture);
} else {
// L'utilisateur ne semble pas avoir de photo dans son profil Azure
allOpenIDProfileInfos.user.photo = null;
// Appeler notre client avec l'ensemble des données du profil reçues
fnOnOIDAuthenticationEvent_Successfull(allOpenIDProfileInfos);
}
})
.catch(error => {
// L'utilisateur ne semble pas avoir de photo dans son profil Azure
// soit le serveur a répondu avec un 404 (l'utilisateur n'a pas de photo)
// soit il y a une autre erreur mais dans tous les cas on n'a pas de photo
// à se mettre sous la dent...
allOpenIDProfileInfos.user.photo = null;
// Appeler notre client avec l'ensemble des données du profil reçues
fnOnOIDAuthenticationEvent_Successfull(allOpenIDProfileInfos);
// Erreur "normale" car l'utilisateur n'a pas de photo dans son profil !
console.warn("Photo demandée pour un utilisateur qui n'en possède pas ou qui ne peut pas être obtenue !");
});
}
} else {
// Appeler notre client avec l'ensemble des données du profil reçues
fnOnOIDAuthenticationEvent_Successfull(allOpenIDProfileInfos);
}
})
.catch(error => {
console.error(error);
fnOnOIDAuthenticationEvent_Unsuccessfull();
if ((typeof fnOnOIDAuthenticationProcess_Failed !== 'undefined') && (fnOnOIDAuthenticationProcess_Failed instanceof Function)) {
fnOnOIDAuthenticationProcess_Failed(error);
}
});
} else {
const errorMsg = "Utilisation simultanée de plusieurs comptes Azure détectée !";
console.error(errorMsg);
fnOnOIDAuthenticationEvent_Unsuccessfull();
if ((typeof fnOnOIDAuthenticationProcess_Failed !== 'undefined') && (fnOnOIDAuthenticationProcess_Failed instanceof Function)) {
fnOnOIDAuthenticationProcess_Failed(error);
}
}
})
.catch(error => {
console.error(error);
fnOnOIDAuthenticationEvent_Unsuccessfull();
if ((typeof fnOnOIDAuthenticationProcess_Failed !== 'undefined') && (fnOnOIDAuthenticationProcess_Failed instanceof Function)) {
fnOnOIDAuthenticationProcess_Failed(error);
}
});
};
var _getGraphClient = function (account) {
const authProvider = new MSGraphAuthCodeMSALBrowserAuthProvider.AuthCodeMSALBrowserAuthenticationProvider(msalClient, {
account,
scopes: ['user.read'],
interactionType: msal.InteractionType.Redirect,
});
return MicrosoftGraph.Client.initWithMiddleware({ authProvider });
};
return {
openIdAuthenticationLogin: _openIdAuthenticationLogin,
openIdAuthenticationLogout: _openIdAuthenticationLogout,
openIdAuthenticationRefresh: _openIdAuthenticationRefresh
};
}();