@opengovsg/mockpass 2.7.9

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 (85) hide show
  1. package/.eslintrc.json +13 -0
  2. package/.gitattributes +2 -0
  3. package/.github/dependabot.yml +14 -0
  4. package/.github/mergify.yml +12 -0
  5. package/.github/workflows/ci.yml +27 -0
  6. package/.github/workflows/npmpublish.yml +22 -0
  7. package/.gitpod.yml +5 -0
  8. package/.husky/pre-commit +4 -0
  9. package/.husky/pre-push +4 -0
  10. package/.prettierrc.js +5 -0
  11. package/Dockerfile +11 -0
  12. package/LICENSE +21 -0
  13. package/README.md +99 -0
  14. package/commitlint.config.js +7 -0
  15. package/index.js +87 -0
  16. package/lib/assertions.js +319 -0
  17. package/lib/crypto/index.js +61 -0
  18. package/lib/crypto/myinfo-signature.js +153 -0
  19. package/lib/express/index.js +6 -0
  20. package/lib/express/myinfo/consent.js +160 -0
  21. package/lib/express/myinfo/controllers.js +179 -0
  22. package/lib/express/myinfo/index.js +10 -0
  23. package/lib/express/oidc.js +131 -0
  24. package/lib/express/saml.js +171 -0
  25. package/lib/express/sgid.js +168 -0
  26. package/lib/saml-artifact.js +32 -0
  27. package/package.json +81 -0
  28. package/public/mockpass/resources/css/animate.css +43 -0
  29. package/public/mockpass/resources/css/common.css +121 -0
  30. package/public/mockpass/resources/css/reset.css +95 -0
  31. package/public/mockpass/resources/css/style-baseline-small-media.css +567 -0
  32. package/public/mockpass/resources/css/style-baseline.css +1006 -0
  33. package/public/mockpass/resources/css/style-common-small-media.css +156 -0
  34. package/public/mockpass/resources/css/style-common.css +510 -0
  35. package/public/mockpass/resources/css/style-homepage-small-media.css +588 -0
  36. package/public/mockpass/resources/css/style-homepage.css +674 -0
  37. package/public/mockpass/resources/css/style-main.css +9 -0
  38. package/public/mockpass/resources/img/ajax-loader.gif +0 -0
  39. package/public/mockpass/resources/img/ask_cheryl_tab.png +0 -0
  40. package/public/mockpass/resources/img/background/large-device/sp_bg.jpg +0 -0
  41. package/public/mockpass/resources/img/background/medium-device/ipad-bg.jpg +0 -0
  42. package/public/mockpass/resources/img/background/medium-device/ipad-landscape-sp-bg.jpg +0 -0
  43. package/public/mockpass/resources/img/background/small-device/mobile-sp-bg.jpg +0 -0
  44. package/public/mockpass/resources/img/carousel/large-device/how-to-setup-2fa-icon.png +0 -0
  45. package/public/mockpass/resources/img/carousel/large-device/register-icon.png +0 -0
  46. package/public/mockpass/resources/img/carousel/large-device/reset-password-icon.png +0 -0
  47. package/public/mockpass/resources/img/carousel/large-device/setup-2fa-icon.png +0 -0
  48. package/public/mockpass/resources/img/carousel/large-device/update-acct-icon.png +0 -0
  49. package/public/mockpass/resources/img/carousel/medium-device/ipad-register-icon.png +0 -0
  50. package/public/mockpass/resources/img/carousel/medium-device/ipad-reset-password-icon.png +0 -0
  51. package/public/mockpass/resources/img/carousel/medium-device/ipad-setup-2fa-icon.png +0 -0
  52. package/public/mockpass/resources/img/carousel/medium-device/ipad-update-acct-icon.png +0 -0
  53. package/public/mockpass/resources/img/carousel/small-device/mobile-register.png +0 -0
  54. package/public/mockpass/resources/img/carousel/small-device/mobile-reset-password-icon.png +0 -0
  55. package/public/mockpass/resources/img/carousel/small-device/mobile-update-acct-icon.png +0 -0
  56. package/public/mockpass/resources/img/close.png +0 -0
  57. package/public/mockpass/resources/img/id-pw-icon.png +0 -0
  58. package/public/mockpass/resources/img/logo/mockpass-logo.png +0 -0
  59. package/public/mockpass/resources/img/logo/mockpass-placeholder-logo.png +0 -0
  60. package/public/mockpass/resources/img/logo/mockpass_watermark.png +0 -0
  61. package/public/mockpass/resources/img/qr-icon.png +0 -0
  62. package/public/mockpass/resources/img/qr-shadow.png +0 -0
  63. package/public/mockpass/resources/img/refresh.jpg +0 -0
  64. package/public/mockpass/resources/img/sidebar-icons.png +0 -0
  65. package/public/mockpass/resources/img/sp-qr-unavailable.png +0 -0
  66. package/public/mockpass/resources/img/utility-icon-black.png +0 -0
  67. package/public/mockpass/resources/js/bootstrap.min.js +7 -0
  68. package/public/mockpass/resources/js/jquery-3.5.1.js +10872 -0
  69. package/public/mockpass/resources/js/login-common.js +849 -0
  70. package/public/mockpass/resources/plugins/bootstrap-3.3.6/css/bootstrap.min.css +6 -0
  71. package/public/mockpass/resources/plugins/bootstrap-3.3.6/fonts/glyphicons-halflings-regular.woff2 +0 -0
  72. package/static/certs/csr.pem +17 -0
  73. package/static/certs/key.pem +28 -0
  74. package/static/certs/key.pub +9 -0
  75. package/static/certs/server.crt +21 -0
  76. package/static/certs/spcp-csr.pem +17 -0
  77. package/static/certs/spcp-key.pem +28 -0
  78. package/static/certs/spcp.crt +20 -0
  79. package/static/html/consent.html +40 -0
  80. package/static/html/login-page.html +271 -0
  81. package/static/myinfo/v2.json +6154 -0
  82. package/static/myinfo/v3.json +29386 -0
  83. package/static/saml/corppass.xml +21 -0
  84. package/static/saml/unsigned-assertion.xml +24 -0
  85. package/static/saml/unsigned-response.xml +19 -0
@@ -0,0 +1,849 @@
1
+ /**
2
+ * Global JS variables related to Login Tab
3
+ */
4
+ var qrCodeTab = "#qrcodeloginli";
5
+ var loginTab = "#loginli";
6
+
7
+ /**
8
+ * Global JS variables related to password login
9
+ */
10
+ var singPassID = "#loginID";
11
+ var password= "#password";
12
+ var captca = "#jCaptcha";
13
+ var captcaImg = "#logincap";
14
+
15
+ var loginBlock = "#LoginForm";
16
+ var logincaptchaBlock = "#loginc";
17
+ var loginerrorMessageBlock= "#errorMessage";
18
+ var captchaErrorMsgBlock= "#captchaErrMsg";
19
+
20
+ /**
21
+ * Global JS variables related to QR Code
22
+ */
23
+ var hasScanned = "has-scanned";
24
+ var isExpired = "is-expired";
25
+ var cantGen = "cant-gen";
26
+ var isUnavailable = "is-unavailable";
27
+ var isSuspended = "is-suspended";
28
+ var isLocked = "is-locked";
29
+
30
+ var isQrCodeGenerated = false;
31
+ var qrCodeInitStartTime = 0;
32
+
33
+ /*******************************************************************************
34
+ *SOFT TOKEN RELATED METHODS START
35
+ ******************************************************************************/
36
+
37
+ //this javascript method is used for app link for web kit singpass in esevice login
38
+ function initAppLauncher(spmurl) {
39
+ NativeAppLauncher.init({
40
+ appLauncherElId: 'qrcodelink', // Element Id of App Launcher button.
41
+ notSupportedMessage: 'Sorry, QR Login is not compatible with this browser. Please try another.', // Defaults to 'Not Supported!'
42
+ universalLinkUrl: spmurl,
43
+ appUri: 'https',
44
+ appDeepUri: 'spm',
45
+ androidAppId: 'sg.ndi.sp',
46
+ iOsAppStore: 'https://itunes.apple.com/app/singpass-mobile/id1340660807?ls=1&mt=8',
47
+ debug: false // Optional
48
+ });
49
+ }
50
+ /**
51
+ * This method will start the listener for 2 minutes.After 2 minutes.The
52
+ * listener expires.
53
+ *
54
+ * @param succesUrl
55
+ * @param logoutUrl
56
+ * @returns
57
+ */
58
+ function startTwofaPushNotifResponseListener(succesUrl) {
59
+
60
+ $.ajax({
61
+ url : "/spauth/tfa/userpnreslistener",
62
+ type : "GET",
63
+ cache: false,
64
+ complete : function(response) {
65
+ var responseObj = response.responseJSON;
66
+ if (responseObj.threadCounter < 2){
67
+ startTwofaPushNotifResponseListener(succesUrl);
68
+ } else if (responseObj.listenerStatus === "SUCCESS") {
69
+ window.location.href = succesUrl;
70
+ }
71
+ },
72
+ error : function(xhr, status, error) {
73
+ startTwofaPushNotifResponseListener(succesUrl);
74
+ }
75
+ })
76
+ };
77
+
78
+ /**
79
+ * This method is execute the scanned listener via ajax call.
80
+ *
81
+ *
82
+ * @returns
83
+ */
84
+ function startQRCodeScannedResponseListener() {
85
+
86
+ $.ajax({
87
+ url : spauthContextPath + "/login/qrscannedlistener",
88
+ type : "GET",
89
+ cache: false,
90
+ global: false,
91
+ complete : function(response) {
92
+ var responseObj = response.responseJSON;
93
+ // check if the request is timeout and validate the no of times the thread has been called for same transaction id
94
+ if (responseObj.listenerStatus === "SUCCESS" && responseObj.userStatus == null) {
95
+ if (browserLogin !== 'DESKTOP') {
96
+ if (document.visibilityState === 'visible') {
97
+ startQRCodeAcknowledgedResponseListener();
98
+ $('.qr__wrapper').addClass(hasScanned);
99
+ } else {
100
+ document.addEventListener("visibilitychange", function() {
101
+ if (document.visibilityState === 'visible') {
102
+ startQRCodeAcknowledgedResponseListener();
103
+ $('.qr__wrapper').addClass(hasScanned);
104
+ }
105
+ });
106
+ }
107
+ } else {
108
+ startQRCodeAcknowledgedResponseListener();
109
+ $('.qr__wrapper').addClass(hasScanned);
110
+ }
111
+ } else if (responseObj.listenerStatus === 'RETRY') {
112
+ if (browserLogin !== 'DESKTOP') {
113
+ if (document.visibilityState === 'visible') {
114
+ startQRCodeScannedResponseListener();
115
+ } else {
116
+ document.addEventListener("visibilitychange", function() {
117
+ if (document.visibilityState === 'visible') {
118
+ startQRCodeScannedResponseListener();
119
+ }
120
+ });
121
+ }
122
+ } else {
123
+ startQRCodeScannedResponseListener();
124
+ }
125
+ } else if (responseObj.listenerStatus === "ERROR") {
126
+ $('.qr__wrapper').addClass(cantGen);
127
+ } else if (responseObj.listenerStatus === "EXPIRED") {
128
+ $('.qr__wrapper').addClass(isExpired);
129
+ } else if (responseObj.userStatus === "SPCP004D" || responseObj.userStatus === "SPCP004E") {
130
+ $('.qr__wrapper').addClass(isLocked);
131
+ } else if (responseObj.userStatus === "SPCP003" || responseObj.userStatus === "SPCP004A" || responseObj.userStatus === "SPCP004B" || responseObj.userStatus === "SPCP004C" || responseObj.userStatus === "SPCP005" || responseObj.userStatus === "SPCP006") {
132
+ $('.qr__wrapper').addClass(isSuspended);
133
+ }
134
+ },
135
+ error : function(xhr, status, error) {
136
+ startQRCodeScannedResponseListener();
137
+ }
138
+ });
139
+ }
140
+
141
+ /**
142
+ * This method is execute the acknowledged listener via ajax call clean the previous
143
+ * active the scanned and acknowledged listener is session storage
144
+ *
145
+ * @returns
146
+ */
147
+ function startQRCodeAcknowledgedResponseListener() {
148
+ var form = $("#qrcodelogin");
149
+
150
+ $.ajax({
151
+ url : spauthContextPath + "/login/qracknowledgedlistener",
152
+ type : "POST",
153
+ cache: false,
154
+ global: false,
155
+ data: $(form).serialize() + "&wogaaid=" + initialiseWogaaId(),
156
+ complete : function(response) {
157
+ var responseObj = response.responseJSON;
158
+ if (responseObj.listenerStatus === "SUCCESS") {
159
+ setCookie("tabId","qrcodetab");
160
+
161
+ // to send wogaa request
162
+ if (!isSingpassHome) {
163
+ sendWogaaRequest(responseObj.wogaaUrl, responseObj.wogaaMessage);
164
+ }
165
+
166
+ // to redirect to given page
167
+ doPostRequest("verifyqrcodeauth");
168
+ } else if (responseObj.listenerStatus === 'RETRY') {
169
+ if (browserLogin !== 'DESKTOP') {
170
+ if (document.visibilityState === 'visible') {
171
+ startQRCodeAcknowledgedResponseListener();
172
+ } else {
173
+ document.addEventListener("visibilitychange", function() {
174
+ if (document.visibilityState === 'visible') {
175
+ startQRCodeAcknowledgedResponseListener();
176
+ }
177
+ });
178
+ }
179
+ } else {
180
+ startQRCodeAcknowledgedResponseListener();
181
+ }
182
+ } else if (responseObj.listenerStatus === "ERROR") {
183
+ $('.qr__wrapper').addClass(cantGen);
184
+ } else if (responseObj.listenerStatus === "EXPIRED") {
185
+ $('.qr__wrapper').addClass(isExpired);
186
+ }
187
+ },
188
+ error : function(xhr, status, error) {
189
+ startQRCodeAcknowledgedResponseListener();
190
+ }
191
+ });
192
+ }
193
+
194
+ function refreshQRCode() {
195
+ isQrCodeGenerated = false;
196
+ generateQRCode();
197
+ }
198
+
199
+ /**
200
+ * This method generate the QR code.
201
+ * Page will reload when the session has time out.
202
+ *
203
+ * @returns
204
+ */
205
+ function generateQRCode() {
206
+
207
+ if (isQrCodeGenerated) {
208
+ return;
209
+ } else {
210
+ $('.qr__wrapper').removeClass(cantGen);
211
+ $('.qr__wrapper').removeClass(isExpired);
212
+ $('.qr__wrapper').removeClass(isLocked);
213
+ $('.qr__wrapper').removeClass(isSuspended);
214
+ $('.qr__wrapper').removeClass(hasScanned);
215
+
216
+ $.ajax({
217
+ url : spauthContextPath + "/login/generateqrcode",
218
+ type : "POST",
219
+ cache: false,
220
+ success : function(response) {
221
+ var responseObj = response;
222
+
223
+ if (responseObj.error === 136) {
224
+ if (isSingpassHome === true) {
225
+ doPostRequest("/spauth/login/logout");
226
+ // To refresh the page so that user can successfully login
227
+ window.location.reload();
228
+ } else {
229
+ window.location.replace("/spauth/login/eservicelogout");
230
+ }
231
+ } else if (responseObj.qrcode_byte != null) {
232
+ $('#qrcodelink').addClass('flip').delay(500).queue(function(){
233
+ initAppLauncher(responseObj.spm_url);
234
+ $('#qrImage').attr("src", "data:image/png;base64," + responseObj.qrcode_byte);
235
+ $('#qrcodelink').removeClass('flip').dequeue();
236
+ });
237
+
238
+ isQrCodeGenerated = true;
239
+ startQRCodeScannedResponseListener();
240
+ qrCodeInitStartTime = Date.now();
241
+ } else if (responseObj.qrcode_is_unavailable) {
242
+ $('.qr__wrapper').addClass(isUnavailable);
243
+ } else {
244
+ $('.qr__wrapper').addClass(cantGen);
245
+ }
246
+ },
247
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
248
+ $('.qr__wrapper').addClass(cantGen);
249
+ }
250
+ });
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Helper method to open new tab upon clicking on qr code
256
+ * Close the window after one second ( for user login experience )
257
+ * @param url
258
+ * @returns
259
+ */
260
+ function redirectToSingPassMobile(url) {
261
+ var spmwindow = window.open(url, "_blank");
262
+ }
263
+
264
+ /**
265
+ * This method will get the QR Validity Time (value for loading screen timeout)
266
+ *
267
+ * @return time in milliseconds that will be used for setting the timeoutQRLoadingScreen global variable.
268
+ * Note that negative values will skip loading screen.
269
+ */
270
+ function getQRValidityTime() {
271
+ var timeoutQRLoadingScreen = 120000 - (Date.now() - qrCodeInitStartTime);
272
+ if (timeoutQRLoadingScreen == 0) {
273
+ timeoutQRLoadingScreen = -1;
274
+ }
275
+ return timeoutQRLoadingScreen;
276
+ }
277
+
278
+ /*******************************************************************************
279
+ *SOFT TOKEN RELATED METHODS ENDS
280
+ ******************************************************************************/
281
+
282
+ /*******************************************************************************
283
+ *CAPTCHA RELATED METHODS STARTS
284
+ ******************************************************************************/
285
+
286
+ /**
287
+ * This method is called when user clicked cancel button in captcha page
288
+ *
289
+ * @param divIdToHide
290
+ * @param divIdToDisplay
291
+ */
292
+ function doCancelCaptcha(divToHide, divToDisplay) {
293
+ $(divToHide).hide();
294
+ $(divToDisplay).show();
295
+ $("#jCaptcha").val("");
296
+ }
297
+
298
+ /**
299
+ * This method is called when user Singpass ID / Password retries is more than 3
300
+ * times.
301
+ *
302
+ * @param flow
303
+ * @param captchId
304
+ * @param hideList
305
+ * @param showList
306
+ * @param captchUrl
307
+ * @returns
308
+ */
309
+ function isUserRetriesReachedMax(captchId, hideList, showList, captchUrl) {
310
+ showElements(showList);
311
+ hideElements(hideList);
312
+ $(captchId).attr("src", captchUrl);
313
+ }
314
+
315
+ /**
316
+ * This method is called when user entered invalid captcha
317
+ * @param flow
318
+ * @param captchId
319
+ * @param errorMessageId
320
+ * @param clearList
321
+ * @param hideList
322
+ * @param showList
323
+ * @param captchUrl
324
+ * @returns
325
+ */
326
+ function invalCap(captchId, errorMessageId, clearList, hideList, showList, captchUrl){
327
+ clear(clearList);
328
+ showElements(showList);
329
+ $(captchId).attr("src", captchUrl);
330
+ $(errorMessageId).text("Incorrect code. Please try again.");
331
+ }
332
+
333
+ /*******************************************************************************
334
+ *CAPTCHA RELATED METHODS ENDS
335
+ ******************************************************************************/
336
+
337
+ /*******************************************************************************
338
+ * COMMON LOGIN RELATED METHODS STARTS
339
+ ******************************************************************************/
340
+
341
+ /**
342
+ * This is common method to show the elements based on the idlist given as
343
+ * parameter
344
+ *
345
+ * @param {List}
346
+ * IdList
347
+ * @returns
348
+ */
349
+ function showElements(IdList) {
350
+ var len = IdList.length;
351
+ for (i = 0; i < len; i++) {
352
+ $(IdList[i]).show();
353
+ }
354
+ }
355
+
356
+ /**
357
+ * This is common method to hide the elements based on the idlist given as
358
+ * parameter
359
+ *
360
+ * @param IdList
361
+ * @returns
362
+ */
363
+ function hideElements(IdList) {
364
+ var len = IdList.length;
365
+ for (i = 0; i < len; i++) {
366
+ $(IdList[i]).hide();
367
+ }
368
+ }
369
+
370
+ /**
371
+ * This is common method to clear the values based on idlist given as the
372
+ * parameter
373
+ *
374
+ * @param IdList
375
+ * @returns
376
+ */
377
+ function clear(IdList) {
378
+ var len = IdList.length;
379
+ for (i = 0; i < len; i++) {
380
+ $(IdList[i]).val("");
381
+ }
382
+ }
383
+
384
+ /**
385
+ * This is method that trim the value with value given in the parameter
386
+ * @param x
387
+ * @returns x after trim.
388
+ */
389
+ function myTrim(x) {
390
+ return x.replace(/(^[ \t]*\n)/gm, "");
391
+ }
392
+
393
+ /**
394
+ * This is generic method called for onkeypress action.
395
+ *
396
+ * @param e
397
+ * @param action
398
+ * @param lflow
399
+ * @returns
400
+ */
401
+ function doKeyPress(e, action) {
402
+
403
+ var keynum;
404
+ if (window.event) { // IE
405
+ keynum = e.keyCode;
406
+ } else if (e.which) { // Netscape/Firefox/Opera
407
+ keynum = e.which;
408
+ }
409
+ if (keynum == 13) {
410
+ if (action == 'LOGIN' || action == 'captcha') {
411
+ doSubmit(action);
412
+ }
413
+ }
414
+ return;
415
+ }
416
+
417
+ function getCookie(key) {
418
+ var keyValue = document.cookie.match('(^|;) ?' + key + '=([^;]*)(;|$)');
419
+ return keyValue ? keyValue[2] : null;
420
+ }
421
+
422
+ function setRememberTab(){
423
+ var tabId = getCookie("tabId");
424
+ return tabId;
425
+ }
426
+
427
+ function setCookie(key, value) {
428
+ var expires = new Date();
429
+ expires.setTime(expires.getTime() + (1 * 24 * 60 * 60 * 1000));
430
+ document.cookie = key + '=' + value + ';expires=' + expires.toUTCString();
431
+ }
432
+
433
+ /**
434
+ * This is generic method to show qr code tab.
435
+ */
436
+ function showQRCodeTab() {
437
+ $(qrCodeTab).addClass('active');
438
+ $('#sectionB').addClass('active');
439
+ $(loginTab).removeClass('active');
440
+ $('#sectionA').removeClass('active');
441
+ generateQRCode();
442
+ }
443
+
444
+ /**
445
+ * This is generic method to show login tab.
446
+ */
447
+ function showLoginTab() {
448
+ $(qrCodeTab).removeClass('active');
449
+ $('#sectionB').removeClass('active');
450
+ $(loginTab).addClass('active');
451
+ $('#sectionA').addClass('active in');
452
+ }
453
+
454
+ function showLoadTab(){
455
+ var tabId = setRememberTab();
456
+ if (tabId == 'qrcodetab'){
457
+ showQRCodeTab();
458
+ } else {
459
+ showLoginTab();
460
+ }
461
+
462
+ toggleQRTooltip();
463
+ }
464
+
465
+ /**
466
+ * Shows QR Tooltip if login tab is active
467
+ */
468
+ function toggleQRTooltip() {
469
+ if(!$(qrCodeTab).hasClass('active')) {
470
+ $('#sp-mobile-tooltip').show();
471
+ } else {
472
+ $('#sp-mobile-tooltip').hide();
473
+ }
474
+ }
475
+
476
+ /*
477
+ * Sets the modal top position to be placed just below the mobile-header
478
+ */
479
+ function setModalTopPos() {
480
+ var modalTopPos = $('#mobile-header').position().top + $('#mobile-header').outerHeight();
481
+ modalTopPos > 0 ? $('#myModalHorizontal').find('.homepageLogin.modal-dialog').css('top', modalTopPos+'px') : $('#myModalHorizontal').find('.homepageLogin.modal-dialog').attr('style', '');
482
+ }
483
+
484
+ /**
485
+ * This method is to check if the user password is eight character.
486
+ * @param password
487
+ * @returns
488
+ */
489
+ function isEightChar(password) {
490
+ if (password.length == 8) {
491
+ return true;
492
+ }
493
+ }
494
+
495
+ /**
496
+ * This method is called when user clicked cancel button.
497
+ *
498
+ * @param URL
499
+ * @returns
500
+ */
501
+ function doCancel(URL) {
502
+ window.location = URL;
503
+ }
504
+
505
+ /**
506
+ * This method is to validate singPassID and Password entered is empty
507
+ * @param userId
508
+ * @param password
509
+ * @param errorDivId
510
+ * @returns false if values is is empty
511
+ */
512
+ function validateUserIdPassword(userId, password, errorDivId){
513
+ if (userId.length == 0 && password.length == 0) {
514
+ $(errorDivId).css("display", "block");
515
+ $(errorDivId).text("Please enter your SingPass ID and Password");
516
+ return false;
517
+ }
518
+ return true;
519
+ }
520
+
521
+ /**
522
+ * Method to validate the mandatory fields
523
+ */
524
+ function validateMandatoryFields(actionType) {
525
+ var userId = $("#loginID").val();
526
+ var password = $("#password").val();
527
+ var captchaVal = $("#jCaptcha").val();
528
+ if (actionType === 'LOGIN' && userId.length == 0 && password.length == 0) {
529
+ document.getElementById('errorMessage').style.display = "block";
530
+ document.getElementById('errorMessage').innerHTML = "Please enter your SingPass ID and Password";
531
+ return false;
532
+ } else if (actionType === 'LOGIN' && userId.length == 0) {
533
+ $('#password').val("");
534
+ document.getElementById('errorMessage').style.display = "block";
535
+ document.getElementById('errorMessage').innerHTML = "Please enter your SingPass ID.";
536
+ return false;
537
+ } else if (actionType === 'LOGIN' && password.length == 0) {
538
+ document.getElementById('errorMessage').style.display = "block";
539
+ document.getElementById('errorMessage').innerHTML = "Please enter your SingPass password.";
540
+ return false;
541
+ } else if (actionType === 'captcha' && captchaVal.length == 0) {
542
+ $('#captchaErrMsg').show();
543
+ $('#captchaErrMsg').text("Enter the code shown above.");
544
+ return false;
545
+ }
546
+ return true;
547
+ }
548
+
549
+ /**
550
+ * This method is to validate singPassID is empty or not
551
+ *
552
+ * @param userId
553
+ * @param errorDivId
554
+ * @param errorMessage
555
+ * @returns false if values is is empty
556
+ */
557
+ function validateUserId(userId, errorDivId, errorMessage,hideErrorDiv) {
558
+ if (userId.length == 0) {
559
+ $(errorDivId).css("display", "block");
560
+ $(errorDivId).text(errorMessage);
561
+ $("#plLockedErrorMessage").hide();
562
+ return false;
563
+ }
564
+ return true;
565
+ }
566
+
567
+ /**
568
+ * This method is validate password is empty or not
569
+ *
570
+ * @param password
571
+ * @param errorDivId
572
+ * @param errorMessage
573
+ * @returns if values is is empty
574
+ */
575
+ function validatePassword(password, errorDivId, errorMessage) {
576
+ if (password.length == 0) {
577
+ $(errorDivId).css("display", "block");
578
+ $(errorDivId).text(errorMessage);
579
+ $("#plpLockedErrorMessage").hide();
580
+ return false;
581
+ }
582
+ return true;
583
+ }
584
+
585
+ /**
586
+ * This method to check the mandatory validation
587
+ * @param userId
588
+ * @param password
589
+ * @param errorDivId
590
+ * @param errorMessage
591
+ * @param pErrorMessage
592
+ * @returns
593
+ */
594
+ function mandatoryValidation(userId, password, errorDivId, errorMessage, pErrorMessage){
595
+ var validation = validateUserIdPassword(userId, password, errorDivId);
596
+ if(validation){
597
+ validation = validateUserId(userId, errorDivId, errorMessage);
598
+ if(validation){
599
+ validation = validatePassword(password, errorDivId, pErrorMessage);
600
+ }
601
+ }
602
+ return validation;
603
+ }
604
+
605
+ /**
606
+ * This is a common methods used to set all the rba related details
607
+ *
608
+ * @param flow
609
+ * @param obj
610
+ * @param data
611
+ * @param modulussec
612
+ * @param deviceDetId
613
+ * @param encryptedRbaDeviceId
614
+ * @param rbaDeviceParamId
615
+ * @returns
616
+ */
617
+ function setRBAData(obj, data, modulussec, deviceDetId, encryptedRbaDeviceId, rbaDeviceParamId) {
618
+ var jsonString;
619
+ var encryptedRbaDevice;
620
+ var rbaDeviceParam2;
621
+ try {
622
+ var Exponent = obj.EXPONENT;
623
+ var Modulus = obj.RSA_PUBLIC_KEY;
624
+ var randomString16 = obj.RANDOM_STRING_16;
625
+ var rsaBlock = encryptVerifyNoUserRSABlock256(Exponent, Modulus, data,randomString16);
626
+ jsonString = JSON.stringify(jsonObj);
627
+ } catch (e) {
628
+ //doNothing
629
+ }
630
+ $(deviceDetId).val(jsonString);
631
+ }
632
+
633
+ /**
634
+ * This is a common method used to set all the randoms details for password encyrpt.
635
+ * @param obj
636
+ * @param password
637
+ * @param randomString16Id
638
+ * @param randomString32Id
639
+ * @param randomString64Id
640
+ * @param rsaBlockId
641
+ * @param rsaBlock1Id
642
+ * @param rsaBlock2Id
643
+ * @returns
644
+ */
645
+ function setRamdoms(obj, password, randomString16Id, randomString32Id, randomString64Id, rsaBlockId, rsaBlock1Id, rsaBlock2Id) {
646
+ var Exponent = obj.EXPONENT;
647
+ var Modulus = obj.RSA_PUBLIC_KEY;
648
+ var randomString16 = obj.RANDOM_STRING_16;
649
+ var randomString32 = obj.RANDOM_STRING_32;
650
+ var randomString64 = obj.RANDOM_STRING_64;
651
+
652
+ var rsaBlock = encryptVerifyNoUserRSABlock256(Exponent, Modulus, password,randomString16);
653
+ var rsaBlock1 = encryptMigratePwdNoVerifyNoUser256RSABlock512(Exponent,Modulus, password, randomString32, randomString64);
654
+ var rsaBlock2 = encryptVerifyStaticNoUserRSABlock512(Exponent, Modulus,password, randomString64);
655
+
656
+ $(randomString16Id).val(randomString16);
657
+ $(randomString32Id).val(randomString32);
658
+ $(randomString64Id).val(randomString64);
659
+ $(rsaBlockId).val(rsaBlock);
660
+ $(rsaBlock1Id).val(rsaBlock1);
661
+ $(rsaBlock2Id).val(rsaBlock2);
662
+ }
663
+
664
+
665
+ /**
666
+ * This method is to hide and show list when user is invalid user
667
+ *
668
+ * @param flow
669
+ * @param errorMessageId
670
+ * @param clearList
671
+ * @param hideList
672
+ * @param showList
673
+ * @param message
674
+ * @returns
675
+ */
676
+ function invalUsr(flow, errorMessageId, clearList, hideList, showList, message) {
677
+ clear(clearList);
678
+ hideElements(hideList);
679
+ showElements(showList);
680
+ $(errorMessageId).text(message);
681
+ }
682
+
683
+ /**
684
+ * This method is to hide and show list when user is locked/suspend/terminated user
685
+ * @param flow
686
+ * @param errorMessageId
687
+ * @param clearList
688
+ * @param hideList
689
+ * @param showList
690
+ * @param message
691
+ * @returns
692
+ */
693
+ function commErr(flow, errorMessageId, clearList, hideList, showList, message) {
694
+ clear(clearList);
695
+ hideElements(hideList);
696
+ showElements(showList);
697
+ $(errorMessageId).text(message);
698
+ }
699
+
700
+ /**
701
+ * This method hide and show login and captcha form elements based on the error message return.
702
+ * @param errorMessage
703
+ * @returns
704
+ */
705
+ function invalidLoginAction(errorMessage, captchaVal) {
706
+
707
+ if (errorMessage == 'invalUsr') {
708
+ // Login Form
709
+ $("#LoginForm").show();
710
+ $('#loginID').val("");
711
+ $('#password').val("");
712
+ $('#errorMessage').show();
713
+ $('#errorMessage').text("You have entered an invalid SingPass ID or Password.");
714
+ // Captcha Form
715
+ $("#loginc").hide();
716
+ $("#jCaptcha").val("");
717
+ $('#captchaErrMsg').hide();
718
+ } else if (errorMessage == 'commErr') {
719
+ // Login Form
720
+ $("#LoginForm").show();
721
+ $('#loginID').val("");
722
+ $('#password').val("");
723
+ $('#errorMessage').show();
724
+ $('#errorMessage').text("We are unable to verify your account. Please reset your password. Alternatively, you can contact the SingPass helpdesk for more information.");
725
+ // Captcha Form
726
+ $("#loginc").hide();
727
+ $("#jCaptcha").val("");
728
+ $('#captchaErrMsg').hide();
729
+ } else if (errorMessage == 'isUserRetriesReachedMax') {
730
+ // Login Form
731
+ $("#LoginForm").hide();
732
+ $('#errorMessage').hide();
733
+ // Captcha Form
734
+ $("#loginc").show();
735
+ $("#jCaptcha").val("");
736
+ $('#captchaErrMsg').hide();
737
+ $('#logincap').attr('src', captchaVal);
738
+ } else if (errorMessage == 'invalCap') {
739
+ // Login Form
740
+ $("#LoginForm").hide();
741
+ $('#errorMessage').hide();
742
+ // Captcha Form
743
+ $("#loginc").show();
744
+ $("#jCaptcha").val("");
745
+ $('#captchaErrMsg').show();
746
+ $('#captchaErrMsg').text("Incorrect code. Please try again.");
747
+ $('#logincap').attr('src', captchaVal);
748
+ } else if (errorMessage == 'notActiveIrasUsr') {
749
+ $("#LoginForm").show();
750
+ $('#loginID').val("");
751
+ $('#password').val("");
752
+ $('#errorMessage').show();
753
+ $('#errorMessage').html("Please set up the <a href='https://singpassmobile.sg/' target='_blank'>SingPass Mobile app</a> on your mobile device to access SingPass or IRAS Digital Services");
754
+ // Captcha Form
755
+ $("#loginc").hide();
756
+ $("#jCaptcha").val("");
757
+ $('#captchaErrMsg').hide();
758
+ } else {
759
+ $("#LoginForm").show();
760
+ $('#loginID').val("");
761
+ $('#password').val("");
762
+ $('#errorMessage').show();
763
+ $('#errorMessage').html(errorMessage);
764
+ // Captcha Form
765
+ $("#loginc").hide();
766
+ $("#jCaptcha").val("");
767
+ $('#captchaErrMsg').hide();
768
+ }
769
+ }
770
+
771
+ function hexEncode(str) {
772
+ var result = '';
773
+ for (var i = 0; i < str.length; i++) {
774
+ result += str.charCodeAt(i).toString(16);
775
+ }
776
+ return result;
777
+ }
778
+
779
+ function hexToBase64(hexString) {
780
+ return btoa(hexString.match(/\w{2}/g).map(function(a) {
781
+ return String.fromCharCode(parseInt(a, 16));
782
+ }).join(''));
783
+ }
784
+
785
+ function generateSamlArtFromCustomNric() {
786
+ var customNric = document.getElementById('customNric').value;
787
+ if (customNric.length !== 9) {
788
+ return false;
789
+ }
790
+ var hashedPartnerId = document.getElementById('hashedPartnerId').value;
791
+ var artifactDataHex = '00040000' + hashedPartnerId + hexEncode('customNric:' + customNric);
792
+ document.getElementById('customNricSamlArt').value = hexToBase64(artifactDataHex);
793
+ return true;
794
+ }
795
+
796
+ /*******************************************************************************
797
+ * WOGAA RELATED METHODS STARTS
798
+ ******************************************************************************/
799
+
800
+ /**
801
+ * This method will initialise WOGAA ID.
802
+ *
803
+ * @return wogaaId - WOGAA ID.
804
+ */
805
+ function initialiseWogaaId() {
806
+ var wogaaId = "";
807
+ try {
808
+ wogaaId = _satellite.getVisitorId().getMarketingCloudVisitorID();
809
+ if (wogaaId == null || wogaaId == undefined) {
810
+ wogaaId = "";
811
+ }
812
+ } catch (error) {
813
+ wogaaId = error.message;
814
+ }
815
+ return wogaaId;
816
+ }
817
+
818
+ /**
819
+ * This method will send a POST request to WOGAA.
820
+ *
821
+ * @param wogaaUrl
822
+ * contains WOGAA API URL.
823
+ * @param wogaaMessage
824
+ * contains a string to be sent to WOGAA.
825
+ */
826
+ function sendWogaaRequest(wogaaUrl, wogaaMessage) {
827
+
828
+ try {
829
+ if (wogaaUrl !== null && wogaaUrl !== undefined) {
830
+ var wogaaRequest = new XMLHttpRequest();
831
+ wogaaRequest.open("POST", wogaaUrl, true);
832
+ wogaaRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
833
+ wogaaRequest.onerror = function(error) {
834
+ // do nothing
835
+ }
836
+ wogaaRequest.send(wogaaMessage);
837
+ }
838
+ } catch (error) {
839
+ // do nothing
840
+ }
841
+ }
842
+
843
+ /*******************************************************************************
844
+ * WOGAA RELATED METHODS ENDS
845
+ ******************************************************************************/
846
+
847
+ /*******************************************************************************
848
+ * COMMON LOGIN RELATED METHODS ENDS
849
+ ******************************************************************************/