@nsshunt/stsoauth2plugin 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.eslintrc.json +27 -0
  2. package/.github/dependabot.yml +13 -0
  3. package/.github/workflows/npm-publish.yml +54 -0
  4. package/LICENSE +21 -0
  5. package/README.md +1 -0
  6. package/babel.config.json +6 -0
  7. package/build.sh +29 -0
  8. package/dist/Utils/CryptoUtils.js +32 -0
  9. package/dist/Utils/CryptoUtils.js.map +1 -0
  10. package/dist/Utils/QueryParams.js +49 -0
  11. package/dist/Utils/QueryParams.js.map +1 -0
  12. package/dist/index.js +3 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/index.test.js +8 -0
  15. package/dist/index.test.js.map +1 -0
  16. package/dist/stsStorage.js +152 -0
  17. package/dist/stsStorage.js.map +1 -0
  18. package/dist/stsoauth2manager.js +327 -0
  19. package/dist/stsoauth2manager.js.map +1 -0
  20. package/dist/stsoauth2types.js +29 -0
  21. package/dist/stsoauth2types.js.map +1 -0
  22. package/dist/stsoauth2worker.js +553 -0
  23. package/dist/stsoauth2worker.js.map +1 -0
  24. package/package.json +43 -0
  25. package/src/Utils/CryptoUtils.ts +32 -0
  26. package/src/Utils/QueryParams.ts +48 -0
  27. package/src/index.test.ts +10 -0
  28. package/src/index.ts +3 -0
  29. package/src/stsStorage.ts +158 -0
  30. package/src/stsoauth2manager.ts +350 -0
  31. package/src/stsoauth2types.ts +108 -0
  32. package/src/stsoauth2worker.ts +542 -0
  33. package/tsconfig.json +31 -0
  34. package/types/Utils/CryptoUtils.d.ts +7 -0
  35. package/types/Utils/CryptoUtils.d.ts.map +1 -0
  36. package/types/Utils/QueryParams.d.ts +8 -0
  37. package/types/Utils/QueryParams.d.ts.map +1 -0
  38. package/types/index.d.ts +3 -0
  39. package/types/index.d.ts.map +1 -0
  40. package/types/index.test.d.ts +1 -0
  41. package/types/index.test.d.ts.map +1 -0
  42. package/types/stsStorage.d.ts +22 -0
  43. package/types/stsStorage.d.ts.map +1 -0
  44. package/types/stsoauth2manager.d.ts +5 -0
  45. package/types/stsoauth2manager.d.ts.map +1 -0
  46. package/types/stsoauth2types.d.ts +89 -0
  47. package/types/stsoauth2types.d.ts.map +1 -0
  48. package/types/stsoauth2worker.d.ts +2 -0
  49. package/types/stsoauth2worker.d.ts.map +1 -0
@@ -0,0 +1,542 @@
1
+ import Debug from "debug";
2
+ const debug = Debug(`proc:${process.pid}:stsoauth2worker.ts`);
3
+
4
+ import 'colors'
5
+
6
+ import axios from "axios";
7
+
8
+ import { JSONObject, OAuth2ParameterType } from '@nsshunt/stsutils';
9
+
10
+ import CryptoUtils from './Utils/CryptoUtils'
11
+ import QueryParams from './Utils/QueryParams'
12
+
13
+ import jwt_decode from "jwt-decode"
14
+
15
+ import { IStsStorage, ClientStorageType, ClientStorageFactory } from './stsStorage'
16
+
17
+ import { StatusCodes } from 'http-status-codes'
18
+
19
+ import { AuthorizeOptionsResponseType, AuthorizeOptionsResponseMode, IAuthorizationCodeFlowParameters, IRefreshFlowParameters,
20
+ IAuthorizeOptions, ITokenResponse, IAuthorizeResponse, IAuthorizeErrorResponse, ITokenErrorResponse, OAuthGrantTypes, AuthenticateEvent,
21
+ IOauth2ListenerMessage, IOauth2ListenerCommand, IOauth2ListenerMessageResponse } from './stsoauth2types'
22
+
23
+ const CreateRandomString = (size = 43) => {
24
+ const randomValues = Array.from(self.crypto.getRandomValues(new Uint8Array(size)))
25
+ const b64 = window.btoa(String.fromCharCode(...randomValues));
26
+ return b64;
27
+ //return randomValues.toString('base64');
28
+ }
29
+
30
+ // STS Client SDK for SPAs
31
+ class STSOAuth2Worker {
32
+ //#storageManager = null;
33
+ #clientSessionStore: IStsStorage<ITokenResponse> = null; // In memory tokens while the client is logged in
34
+ #cUtils = new CryptoUtils();
35
+ #qParams = new QueryParams();
36
+ #STORAGE_AUTHORIZE_OPTIONS_KEY = 'authorize_options.stsmda.com.au';
37
+ #STORAGE_SESSION_KEY = 'session.stsmda.com.au';
38
+ #aic = null;
39
+ #errorCallback = null; //@@ will be replaced with a message back
40
+ //#store = null;
41
+ #handleAuthenticateEvent: AuthenticateEvent = null;
42
+ #oauthWorkerPort: MessagePort = null;
43
+ #currentMessageId = 0;
44
+
45
+ constructor(workerPort: MessagePort) {
46
+ //this.#store = app.config.globalProperties.$store;
47
+
48
+ // In memory storage for OAuth2 tokens for our valid session
49
+ this.#clientSessionStore = new ClientStorageFactory<ITokenResponse>({clientStorageType: ClientStorageType.MEMORY_STORAGE}).GetStorage();
50
+
51
+ //@@ needs to be sent the instrument manager controller port
52
+ //@@this.#aic = app.config.globalProperties.$sts.aic.PrimaryPublishInstrumentController;
53
+
54
+ //this.#handleAuthenticateEvent = handleAuthenticateEvent;
55
+ this.#handleAuthenticateEvent = (id_token: string) => {
56
+ const message: IOauth2ListenerMessage = {
57
+ messageId: -1, // un-solicited message
58
+ command: IOauth2ListenerCommand.AUTHENTICATE_EVENT
59
+ }
60
+ this.#ProcessCommand(message, id_token);
61
+ }
62
+
63
+ this.#errorCallback = (error: any) => {
64
+ const message: IOauth2ListenerMessage = {
65
+ messageId: -1, // un-solicited message
66
+ command: IOauth2ListenerCommand.ERROR
67
+ }
68
+ this.#ProcessCommand(message, error);
69
+ }
70
+
71
+ this.#oauthWorkerPort = workerPort;
72
+ this.SetupListener();
73
+ }
74
+
75
+ // Attempt to restore a previous session using the STSBroker
76
+ /*
77
+ { parameterType: OAuth2ParameterType.CLIENT_ID, errorType: authErrorType.CLIENT_ID_MISMATCH },
78
+ { parameterType: OAuth2ParameterType.SCOPE, errorType: authErrorType.SCOPE_MISMATCH }
79
+ { parameterType: OAuth2ParameterType.REDIRECT_URI, errorType: authErrorType.REDIRECT_URI_MISMATCH },
80
+ { parameterType: OAuth2ParameterType.AUDIENCE, errorType: authErrorType.SCOPE_MISMATCH }
81
+
82
+ Successful Response
83
+ {
84
+ "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
85
+ "token_type": "Bearer",
86
+ "expires_in": 3599,
87
+ "scope": "https%3A%2F%2Fgraph.microsoft.com%2Fmail.read",
88
+ "refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
89
+ "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD...",
90
+ }
91
+
92
+ Error Response
93
+ {
94
+ "error": "invalid_scope",
95
+ "error_description": "AADSTS70011: The provided value for the input parameter 'scope' is not valid. The scope https://foo.microsoft.com/mail.read is not valid.\r\nTrace ID: 255d1aef-8c98-452f-ac51-23d051240864\r\nCorrelation ID: fb3d2015-bc17-4bb9-bb85-30c5cf1aaaa7\r\nTimestamp: 2016-01-09 02:02:12Z",
96
+ "error_codes": [
97
+ 70011
98
+ ],
99
+ "timestamp": "2016-01-09 02:02:12Z",
100
+ }
101
+
102
+
103
+ */
104
+
105
+
106
+ SetupListener = () => {
107
+ this.#oauthWorkerPort.onmessage = async (data: MessageEvent) => {
108
+ const auth2ListenerMessage: IOauth2ListenerMessage = data.data as IOauth2ListenerMessage;
109
+ switch (auth2ListenerMessage.command) {
110
+ case IOauth2ListenerCommand.RESTORE_SESSION :
111
+ this.#ProcessCommand(auth2ListenerMessage, await this.#RestoreSession());
112
+ break;
113
+ case IOauth2ListenerCommand.AUTHORIZE :
114
+ this.#ProcessCommand(auth2ListenerMessage, await this.#Authorize());
115
+ break;
116
+ case IOauth2ListenerCommand.HANDLE_REDIRECT :
117
+ this.#ProcessCommand(auth2ListenerMessage, await this.#HandleRedirect(auth2ListenerMessage.payload));
118
+ break;
119
+ case IOauth2ListenerCommand.LOGOUT :
120
+ this.#ProcessCommand(auth2ListenerMessage, await this.#Logout());
121
+ break;
122
+ default :
123
+ throw new Error(`Command: [${auth2ListenerMessage.command}'] not found.`);
124
+ }
125
+ }
126
+ }
127
+
128
+ #ProcessCommand = async (auth2ListenerMessage: IOauth2ListenerMessage, response: any) => {
129
+ const messageResponse: IOauth2ListenerMessageResponse = {
130
+ messageId: auth2ListenerMessage.messageId,
131
+ command: auth2ListenerMessage.command,
132
+ payload: response
133
+ }
134
+ this.#oauthWorkerPort.postMessage(messageResponse);
135
+ }
136
+
137
+ #RestoreSession = async (): Promise<boolean> => {
138
+ //@@ attempt to get from client storage first
139
+
140
+ let restoredSessionData: ITokenResponse = null;
141
+ restoredSessionData = this.#clientSessionStore.get(this.#STORAGE_SESSION_KEY);
142
+ if (restoredSessionData !== null) {
143
+ console.log('Session restored from client storage.');
144
+ if (this.#aic) {
145
+ this.#aic.UpdateInstrument('m', { LogMessage: 'Session restored from client storage.' });
146
+ }
147
+ } else {
148
+ const url = `${process.env.BROKER_ENDPOINT}:${process.env.BROKER_PORT}${process.env.BROKER_API_ROOT}/session`;
149
+ console.log('RestoreSession');
150
+ console.log(url);
151
+ if (this.#aic) {
152
+ this.#aic.UpdateInstrument('m', { LogMessage: 'RestoreSession' });
153
+ this.#aic.UpdateInstrument('m', { LogMessage: url });
154
+ }
155
+ try {
156
+ const retVal = await axios({
157
+ method: "post",
158
+ url: url,
159
+ data: {
160
+ [OAuth2ParameterType.CLIENT_ID]: process.env.CLIENT_ID,
161
+ [OAuth2ParameterType.SCOPE]: process.env.SCOPE,
162
+ [OAuth2ParameterType.REDIRECT_URI]: process.env.REDIRECT_URI,
163
+ [OAuth2ParameterType.AUDIENCE]: process.env.AUDIENCE
164
+ },
165
+ withCredentials: true, // Ensure cookies are passed to the service
166
+ timeout: parseInt(process.env.TIMEOUT),
167
+ });
168
+ if (retVal.data.status === StatusCodes.OK) {
169
+ restoredSessionData = retVal.data.detail;
170
+ this.#clientSessionStore.set(this.#STORAGE_SESSION_KEY, restoredSessionData);
171
+ console.log('Session restored from server side cookie.');
172
+ //this.#store.commit('stsOAuth2SDK/SessionData', restoredSessionData);
173
+ } else {
174
+ //@@ handle error better
175
+ //this.#store.commit('stsOAuth2SDK/SessionData', null);
176
+ console.log('Could not restore previous session:-');
177
+ console.log(JSON.stringify(retVal.data));
178
+ }
179
+ } catch (error) {
180
+ //@@ handle error better
181
+ //this.#store.commit('stsOAuth2SDK/SessionData', null);
182
+ console.log('Could not restore previous session (error state):-');
183
+ console.log(error);
184
+ console.log(JSON.stringify(error));
185
+ }
186
+ }
187
+
188
+ //@@ must only use in-memory for this ...
189
+ //this.#store.commit('stsOAuth2SDK/SessionData', restoredSessionData);
190
+ if (restoredSessionData !== null) {
191
+ this.#handleAuthenticateEvent(restoredSessionData.id_token);
192
+ console.log('Refreshing tokens ...');
193
+ return this.#RefreshToken();
194
+ } else {
195
+ this.#handleAuthenticateEvent(null);
196
+ return false;
197
+ }
198
+ }
199
+
200
+ #Authorize = async (): Promise<JSONObject> => {
201
+ console.log('Authorize ...');
202
+
203
+ /* MS Example
204
+ --------------
205
+ https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
206
+ client_id=6731de76-14a6-49ae-97bc-6eba6914391e
207
+ &response_type=code
208
+ &redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
209
+ &response_mode=query
210
+ &scope=offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fuser.read%20api%3A%2F%2F
211
+ &state=12345
212
+ &code_challenge=YTFjNjI1OWYzMzA3MTI4ZDY2Njg5M2RkNmVjNDE5YmEyZGRhOGYyM2IzNjdmZWFhMTQ1ODg3NDcxY2Nl
213
+ &code_challenge_method=S256
214
+
215
+ Successful Response
216
+
217
+ GET http://localhost?
218
+ code=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrq...
219
+ &state=12345
220
+
221
+ Error Response
222
+ GET http://localhost?
223
+ error=access_denied
224
+ &error_description=the+user+canceled+the+authentication
225
+
226
+ << Hybrid Flow >>
227
+
228
+ https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
229
+ client_id=6731de76-14a6-49ae-97bc-6eba6914391e
230
+ &response_type=code%20id_token
231
+ &redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
232
+ &response_mode=fragment
233
+ &scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fuser.read
234
+ &state=12345
235
+ &nonce=abcde
236
+ &code_challenge=YTFjNjI1OWYzMzA3MTI4ZDY2Njg5M2RkNmVjNDE5YmEyZGRhOGYyM2IzNjdmZWFhMTQ1ODg3NDcxY2Nl
237
+ &code_challenge_method=S256
238
+
239
+ Successful Response
240
+
241
+ GET https://login.microsoftonline.com/common/oauth2/nativeclient#
242
+ code=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrq...
243
+ &id_token=eYj...
244
+ &state=12345
245
+
246
+ Notes:
247
+ The nonce is included as a claim inside the returned id_token
248
+ Ref: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
249
+ */
250
+
251
+ const client_id = process.env.CLIENT_ID;
252
+ const nonce = this.#cUtils.CreateRandomString();
253
+ const response_type = [ AuthorizeOptionsResponseType.CODE ]
254
+ const redirect_uri = process.env.REDIRECT_URI;
255
+ const response_mode = AuthorizeOptionsResponseMode.QUERY
256
+ const scope = process.env.SCOPE;
257
+ const state = this.#cUtils.CreateRandomString();
258
+ const code_verifier = this.#cUtils.CreateRandomString();
259
+ const code_challenge = await this.#cUtils.DigestMessage(code_verifier);
260
+ const code_challenge_method = 'S256';
261
+ //let audience = process.env.AUDIENCE;
262
+
263
+ const authorizeOptions: IAuthorizeOptions = {
264
+ client_id,
265
+ nonce,
266
+ response_type,
267
+ redirect_uri,
268
+ response_mode,
269
+ scope,
270
+ state,
271
+ code_challenge,
272
+ code_challenge_method
273
+ }
274
+
275
+ const url = `${process.env.AUTH_ENDPOINT}:${process.env.AUTH_PORT}${process.env.AUTH_APIROOT}?${this.#qParams.CreateQueryParams(authorizeOptions)}`;
276
+
277
+ console.log(url);
278
+
279
+ // Now add the code_verifier to the transaction data
280
+ authorizeOptions.code_verifier = code_verifier; //@@ Is this is the only thing required across the transaction ?
281
+
282
+ console.log(`Authorize:authorizeOptions: [${JSON.stringify(authorizeOptions)}]`);
283
+
284
+ return {
285
+ url,
286
+ authorizeOptions
287
+ }
288
+ //window.location.assign(url);
289
+ //@@ this may need to be a message back to the plugin to re-direct
290
+ //window.location.replace(url);
291
+ }
292
+
293
+ #HandleRedirect = async (payload: any): Promise<boolean> => {
294
+ const queryVars: IAuthorizeResponse | IAuthorizeErrorResponse = payload.queryVars;
295
+ const authorizeOptions: IAuthorizeOptions = payload.authorizeOptions
296
+
297
+ console.log('HandleRedirect');
298
+ // We have been re-direct back here from the /authorize end-point
299
+ console.log(`HandleRedirect:Query Vars: [${JSON.stringify(queryVars)}]`);
300
+
301
+ if (queryVars[OAuth2ParameterType.CODE]) {
302
+ const response: IAuthorizeResponse = queryVars as IAuthorizeResponse;
303
+
304
+ console.log(`authorizeOptions from transaction state: [${JSON.stringify(authorizeOptions)}]`);
305
+
306
+ const redirectState = response.state;
307
+ const authorizeOptionsState = authorizeOptions.state;
308
+
309
+ if (authorizeOptionsState.localeCompare(redirectState) === 0) {
310
+ console.log('redirected state (from queryVars) matched previously saved transaction authorizeOptions state'.green);
311
+
312
+ return await this.#GetToken(authorizeOptions, response);
313
+ } else {
314
+ console.log('redirected state (from queryVars) did NOT match previously saved transaction authorizeOptions state'.red);
315
+ this.#errorCallback({message: 'State un-matched'});
316
+ return false;
317
+ }
318
+ } else if (queryVars[OAuth2ParameterType.ERROR]) {
319
+ const response: IAuthorizeErrorResponse = queryVars as IAuthorizeErrorResponse;
320
+ //@@ pass error back to parent thread (to the plugin) as a message
321
+ const error = response.error;
322
+ const errorDescription = response.error_description;
323
+ this.#errorCallback({message: 'State un-matched'});
324
+ return false;
325
+ } else {
326
+ // Invalid redirect query params
327
+ const error = 'Invalid redirect query params'; //@@ fix
328
+ const errorDescription = 'Invalid redirect query params description'; //@@ fix
329
+ this.#errorCallback({message: 'State un-matched'});
330
+ return false;
331
+ }
332
+ }
333
+
334
+ /*
335
+ client_id=6731de76-14a6-49ae-97bc-6eba6914391e
336
+ &scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
337
+ &code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
338
+ &redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
339
+ &grant_type=authorization_code
340
+ &code_verifier=ThisIsntRandomButItNeedsToBe43CharactersLong
341
+ &client_secret=JqQX2PNo9bpM0uEihUPzyrh // NOTE: Only required for web apps. This secret needs to be URL-Encoded.
342
+
343
+ Successful Response
344
+ {
345
+ "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
346
+ "token_type": "Bearer",
347
+ "expires_in": 3599,
348
+ "scope": "https%3A%2F%2Fgraph.microsoft.com%2Fmail.read",
349
+ "refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
350
+ "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD...",
351
+ }
352
+ */
353
+
354
+ // Get access_token, refresh_token and id_token using OAuth2 Authorization Code Flow
355
+ #GetTokenFromBroker = async (authorizationCodeFlowParameters: IAuthorizationCodeFlowParameters | IRefreshFlowParameters): Promise<boolean> => {
356
+ console.log("#GetTokenFromBroker");
357
+
358
+ this.#clientSessionStore.remove(this.#STORAGE_SESSION_KEY);
359
+
360
+ const url = `${process.env.BROKER_ENDPOINT}:${process.env.BROKER_PORT}${process.env.BROKER_API_ROOT}/token`;
361
+ console.log(`#GetTokenFromBroker:url = [${url}]`);
362
+ console.log(authorizationCodeFlowParameters);
363
+
364
+ try {
365
+ const retVal = await axios({
366
+ method: "post",
367
+ url: url,
368
+ data: authorizationCodeFlowParameters,
369
+ withCredentials: true, // Ensure cookies are passed to the service
370
+ timeout: parseInt(process.env.TIMEOUT),
371
+ });
372
+ console.log(`retVal: ${JSON.stringify(retVal)}`);
373
+
374
+ if (retVal.status === StatusCodes.OK) {
375
+ console.log('Storing tokens...');
376
+ const tokenResponse: ITokenResponse = retVal.data as ITokenResponse;
377
+ //this.#store.commit('stsOAuth2SDK/SessionData', tokenResponse);
378
+ this.#handleAuthenticateEvent(tokenResponse.id_token);
379
+ this.#clientSessionStore.set(this.#STORAGE_SESSION_KEY, tokenResponse);
380
+ return true;
381
+ } else if (retVal.status === StatusCodes.UNAUTHORIZED) {
382
+ console.log('NOT Storing tokens...');
383
+ console.log(retVal.status);
384
+
385
+ //this.#store.commit('stsOAuth2SDK/SessionData', null);
386
+ this.#handleAuthenticateEvent(null);
387
+
388
+ const response: ITokenErrorResponse = retVal.data as ITokenErrorResponse;
389
+
390
+ //@@ store response in state
391
+ //@@ go to error page ??
392
+ return false;
393
+
394
+ } else {
395
+ // General error
396
+ console.log('NOT Storing tokens...');
397
+ console.log(retVal.status);
398
+
399
+ //this.#store.commit('stsOAuth2SDK/SessionData', null);
400
+ this.#handleAuthenticateEvent(null);
401
+
402
+ console.log('Could not obtain access_token from token end-point:-');
403
+ console.log(JSON.stringify(retVal.data));
404
+ //@@ store error in state to show in error page
405
+ return false;
406
+ }
407
+ } catch (error) {
408
+ //this.#store.commit('stsOAuth2SDK/SessionData', null);
409
+ this.#handleAuthenticateEvent(null);
410
+ //console.log('Could not restore previous session (error state):-');
411
+ console.log(error);
412
+ console.log(JSON.stringify(error));
413
+
414
+ //@@ store error in state to show in error page
415
+
416
+ return false;
417
+ }
418
+ }
419
+
420
+ // Get access_token, refresh_token and id_token using OAuth2 Authorization Code Flow
421
+ #GetToken = async (authorizeOptions: IAuthorizeOptions, authorizeResponse: IAuthorizeResponse): Promise<boolean> => {
422
+ console.log("#GetToken");
423
+ console.log(authorizeResponse);
424
+
425
+ this.#clientSessionStore.set(this.#STORAGE_SESSION_KEY, null);
426
+
427
+ const authorizationCodeFlowParameters: IAuthorizationCodeFlowParameters = {
428
+ client_id: process.env.CLIENT_ID,
429
+ scope: process.env.SCOPE,
430
+ code: authorizeResponse.code,
431
+ redirect_uri: process.env.REDIRECT_URI,
432
+ grant_type: OAuthGrantTypes.AUTHORIZATION_CODE,
433
+ code_verifier: authorizeOptions.code_verifier
434
+ }
435
+
436
+ return this.#GetTokenFromBroker(authorizationCodeFlowParameters);
437
+ }
438
+
439
+ /*
440
+ // Line breaks for legibility only
441
+
442
+ POST /{tenant}/oauth2/v2.0/token HTTP/1.1
443
+ Host: https://login.microsoftonline.com
444
+ Content-Type: application/x-www-form-urlencoded
445
+
446
+ client_id=535fb089-9ff3-47b6-9bfb-4f1264799865
447
+ &scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
448
+ &refresh_token=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq...
449
+ &grant_type=refresh_token
450
+ &client_secret=sampleCredentia1s // NOTE: Only required for web apps. This secret needs to be URL-Encoded
451
+
452
+ Error Response
453
+ {
454
+ "error": "invalid_scope",
455
+ "error_description": "AADSTS70011: The provided value for the input parameter 'scope' is not valid. The scope https://foo.microsoft.com/mail.read is not valid.\r\nTrace ID: 255d1aef-8c98-452f-ac51-23d051240864\r\nCorrelation ID: fb3d2015-bc17-4bb9-bb85-30c5cf1aaaa7\r\nTimestamp: 2016-01-09 02:02:12Z",
456
+ "error_codes": [
457
+ 70011
458
+ ],
459
+ "timestamp": "2016-01-09 02:02:12Z",
460
+ "trace_id": "255d1aef-8c98-452f-ac51-23d051240864",
461
+ "correlation_id": "fb3d2015-bc17-4bb9-bb85-30c5cf1aaaa7"
462
+ }
463
+ */
464
+
465
+ #RefreshToken = async (): Promise<boolean> => {
466
+ // Get access_token, refresh_token and id_token using OAuth2 Authorization Code Flow
467
+ console.log("RefreshToken");
468
+
469
+ //let currentSessionData = this.#store.getters['stsOAuth2SDK/SessionData'];
470
+ const currentSessionData: ITokenResponse = this.#clientSessionStore.get(this.#STORAGE_SESSION_KEY);
471
+ if (currentSessionData) {
472
+ const refreshFlowParameters: IRefreshFlowParameters = {
473
+ client_id: process.env.CLIENT_ID,
474
+ scope: process.env.SCOPE,
475
+ refresh_token: currentSessionData.refresh_token,
476
+ grant_type: OAuthGrantTypes.REFRESH_TOKEN
477
+ }
478
+
479
+ return this.#GetTokenFromBroker(refreshFlowParameters);
480
+ } else {
481
+ // show error
482
+ //@@ no valid session exists for refresh
483
+ return false;
484
+ }
485
+ }
486
+
487
+ // call broker to logout
488
+ // broker to logout of server
489
+ // delete cookie
490
+ // clear session storage
491
+ // clear all state from $store
492
+ #Logout = async (): Promise<boolean> => {
493
+ console.log('Logout');
494
+ const url = `${process.env.BROKER_ENDPOINT}:${process.env.BROKER_PORT}${process.env.BROKER_API_ROOT}/logout`;
495
+ console.log(url);
496
+
497
+ const currentSessionData: ITokenResponse = this.#clientSessionStore.get(this.#STORAGE_SESSION_KEY);
498
+ const refresh_token = currentSessionData.refresh_token;
499
+ console.log(refresh_token);
500
+
501
+ const decodedRefreshToken: JSONObject = jwt_decode<JSONObject>(refresh_token);
502
+ console.log(decodedRefreshToken);
503
+ const sessionId = decodedRefreshToken.sts_session;
504
+ console.log(sessionId);
505
+
506
+ this.#clientSessionStore.remove(this.#STORAGE_SESSION_KEY);
507
+ //this.#store.commit('stsOAuth2SDK/SessionData', null);
508
+ this.#handleAuthenticateEvent(null);
509
+
510
+ try {
511
+ const retVal = await axios({
512
+ method: "post",
513
+ url: url,
514
+ data: {
515
+ sessionId
516
+ },
517
+ withCredentials: true, // Ensure cookies are passed to the service
518
+ timeout: parseInt(process.env.TIMEOUT),
519
+ });
520
+ if (retVal.data.status === StatusCodes.OK) {
521
+ return true;
522
+ } else {
523
+ console.log('Error during logout (server side)');
524
+ console.log(JSON.stringify(retVal.data));
525
+ return false;
526
+ }
527
+ } catch (error) {
528
+ console.log('Error during logout (server side)');
529
+ console.log(error);
530
+ console.log(JSON.stringify(error));
531
+ return false;
532
+ }
533
+ }
534
+ }
535
+
536
+ let oAuth2Worker: STSOAuth2Worker = null;
537
+
538
+ onmessage = async function(data: MessageEvent)
539
+ {
540
+ const workerPort = data.data as MessagePort;
541
+ oAuth2Worker = new STSOAuth2Worker(workerPort);
542
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "extends": "@tsconfig/node18/tsconfig.json",
3
+ "include": ["src/**/*" ],
4
+ "exclude": ["node_modules", "**/node_modules/**/*", "**/*.spec.ts"],
5
+ "compilerOptions": {
6
+ "module": "esnext",
7
+ "target": "es2021",
8
+ "moduleResolution": "node",
9
+ "sourceMap": true,
10
+ "outDir": "dist",
11
+ "allowJs": true,
12
+ "declaration": true,
13
+ "declarationDir": "./types",
14
+ "declarationMap": true,
15
+
16
+ "noImplicitAny": false,
17
+ "strictNullChecks": false,
18
+
19
+ "lib": [
20
+ // Should target at least ES2016 in Vue 3
21
+ // Support for newer versions of language built-ins are
22
+ // left for the users to include, because that would require:
23
+ // - either the project doesn't need to support older versions of browsers;
24
+ // - or the project has properly included the necessary polyfills.
25
+ "ES2016",
26
+ "DOM",
27
+ "DOM.Iterable"
28
+ // No `ScriptHost` because Vue 3 dropped support for IE
29
+ ],
30
+ }
31
+ }
@@ -0,0 +1,7 @@
1
+ export declare class CryptoUtils {
2
+ DigestMessage: (message: any) => Promise<string>;
3
+ CreateRandomString: (size?: number) => string;
4
+ CreateRandomStringEx: () => string;
5
+ }
6
+ export default CryptoUtils;
7
+ //# sourceMappingURL=CryptoUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CryptoUtils.d.ts","sourceRoot":"","sources":["../../src/Utils/CryptoUtils.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAW;IACvB,aAAa,oCASZ;IAED,kBAAkB,4BAOjB;IAED,oBAAoB,eAOnB;CACD;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,8 @@
1
+ declare class QueryParams {
2
+ DecodeQueryParams: (params: any) => {};
3
+ CreateQueryParams: (params: any) => string;
4
+ _GetQueryParams: (param: any) => {};
5
+ GetQueryParams: () => {};
6
+ }
7
+ export default QueryParams;
8
+ //# sourceMappingURL=QueryParams.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryParams.d.ts","sourceRoot":"","sources":["../../src/Utils/QueryParams.ts"],"names":[],"mappings":"AAEA,cAAM,WAAW;IAChB,iBAAiB,sBAQhB;IAED,iBAAiB,0BAWhB;IAED,eAAe,qBAcd;IAED,cAAc,WAEb;CACD;AAED,eAAe,WAAW,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './stsoauth2types';
2
+ export * from './stsoauth2manager';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA;AAChC,cAAc,oBAAoB,CAAA"}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ import { JSONObject } from "@nsshunt/stsutils";
2
+ export interface IStsStorage<T> {
3
+ get(key: string): T;
4
+ set(key: string, value: T, options?: JSONObject): void;
5
+ remove(key: string): void;
6
+ }
7
+ export declare enum ClientStorageType {
8
+ LOCAL_STORAGE = "LocalStorage",
9
+ SESSION_STORAGE = "SessionStorage",
10
+ COOKIE_STORAGE = "CookieStorage",
11
+ MEMORY_STORAGE = "MemoryStorage"
12
+ }
13
+ export declare class ClientStorageOptions {
14
+ clientStorageType: ClientStorageType;
15
+ storageOptions?: JSONObject;
16
+ }
17
+ export declare class ClientStorageFactory<T> {
18
+ #private;
19
+ constructor(options: ClientStorageOptions);
20
+ GetStorage(): IStsStorage<T>;
21
+ }
22
+ //# sourceMappingURL=stsStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stsStorage.d.ts","sourceRoot":"","sources":["../src/stsStorage.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,WAAW,WAAW,CAAC,CAAC;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,CAAA;IACnB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAA;IACtD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,oBAAY,iBAAiB;IAC5B,aAAa,iBAAiB;IAC9B,eAAe,mBAAmB;IAClC,cAAc,kBAAkB;IAChC,cAAc,kBAAkB;CAChC;AA2GD,qBAAa,oBAAoB;IAChC,iBAAiB,EAAE,iBAAiB,CAAoC;IACxE,cAAc,CAAC,EAAE,UAAU,CAAA;CAC3B;AAED,qBAAa,oBAAoB,CAAC,CAAC;;gBAItB,OAAO,EAAE,oBAAoB;IAoBzC,UAAU,IAAI,WAAW,CAAC,CAAC,CAAC;CAI5B"}
@@ -0,0 +1,5 @@
1
+ declare const STSOAuth2ManagerPlugin: {
2
+ install: (app: any, router: any) => void;
3
+ };
4
+ export default STSOAuth2ManagerPlugin;
5
+ //# sourceMappingURL=stsoauth2manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stsoauth2manager.d.ts","sourceRoot":"","sources":["../src/stsoauth2manager.ts"],"names":[],"mappings":"AAsVA,QAAA,MAAM,sBAAsB;;CAK3B,CAAA;AAED,eAAe,sBAAsB,CAAC"}