@smileid/web-components 2.0.0 → 2.0.2
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.
- package/package.json +58 -58
- package/src/components/README.md +14 -14
- package/src/components/attribution/PoweredBySmileId.js +42 -42
- package/src/components/camera-permission/CameraPermission.js +140 -140
- package/src/components/camera-permission/CameraPermission.stories.js +27 -27
- package/src/components/combobox/src/Combobox.js +589 -589
- package/src/components/combobox/src/index.js +1 -1
- package/src/components/document/src/DocumentCaptureScreens.js +409 -409
- package/src/components/document/src/DocumentCaptureScreens.stories.js +57 -57
- package/src/components/document/src/README.md +111 -111
- package/src/components/document/src/document-capture/DocumentCapture.js +760 -760
- package/src/components/document/src/document-capture/DocumentCapture.stories.js +78 -78
- package/src/components/document/src/document-capture/README.md +90 -90
- package/src/components/document/src/document-capture/index.js +3 -3
- package/src/components/document/src/document-capture-instructions/DocumentCaptureInstructions.js +499 -499
- package/src/components/document/src/document-capture-instructions/DocumentCaptureInstructions.stories.js +24 -24
- package/src/components/document/src/document-capture-instructions/README.md +56 -56
- package/src/components/document/src/document-capture-instructions/index.js +3 -3
- package/src/components/document/src/document-capture-review/DocumentCaptureReview.js +362 -362
- package/src/components/document/src/document-capture-review/DocumentCaptureReview.stories.js +24 -24
- package/src/components/document/src/document-capture-review/README.md +79 -79
- package/src/components/document/src/document-capture-review/index.js +3 -3
- package/src/components/document/src/index.js +3 -3
- package/src/components/end-user-consent/src/EndUserConsent.js +795 -795
- package/src/components/end-user-consent/src/EndUserConsent.stories.js +29 -29
- package/src/components/end-user-consent/src/index.js +4 -4
- package/src/components/navigation/src/Navigation.js +171 -171
- package/src/components/navigation/src/Navigation.stories.js +24 -24
- package/src/components/navigation/src/index.js +3 -3
- package/src/components/selfie/README.md +225 -225
- package/src/components/selfie/src/SelfieCaptureScreens.js +282 -282
- package/src/components/selfie/src/SelfieCaptureScreens.stories.js +29 -29
- package/src/components/selfie/src/index.js +5 -5
- package/src/components/selfie/src/selfie-capture/SelfieCapture.js +1041 -1010
- package/src/components/selfie/src/selfie-capture/SelfieCapture.stories.js +36 -36
- package/src/components/selfie/src/selfie-capture/index.js +3 -3
- package/src/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.js +657 -648
- package/src/components/selfie/src/selfie-capture-instructions/SelfieCaptureInstructions.stories.js +23 -23
- package/src/components/selfie/src/selfie-capture-instructions/index.js +3 -3
- package/src/components/selfie/src/selfie-capture-review/SelfieCaptureReview.js +347 -347
- package/src/components/selfie/src/selfie-capture-review/SelfieCaptureReview.stories.js +24 -24
- package/src/components/selfie/src/selfie-capture-review/index.js +3 -3
- package/src/components/signature-pad/package-lock.json +3009 -3009
- package/src/components/signature-pad/package.json +30 -30
- package/src/components/signature-pad/src/SignaturePad.js +484 -484
- package/src/components/signature-pad/src/SignaturePad.stories.js +32 -32
- package/src/components/signature-pad/src/index.js +3 -3
- package/src/components/smart-camera-web/src/README.md +207 -207
- package/src/components/smart-camera-web/src/SmartCameraWeb.js +299 -299
- package/src/components/smart-camera-web/src/SmartCameraWeb.stories.js +57 -57
- package/src/components/totp-consent/src/TotpConsent.js +949 -949
- package/src/components/totp-consent/src/index.js +4 -4
- package/src/domain/camera/src/README.md +38 -38
- package/src/domain/camera/src/SmartCamera.js +109 -109
- package/src/domain/constants/src/Constants.js +27 -27
- package/src/domain/file-upload/README.md +35 -35
- package/src/domain/file-upload/src/SmartFileUpload.js +65 -65
- package/src/index.js +5 -5
- package/src/styles/README.md +3 -3
- package/src/styles/src/styles.js +359 -359
- package/src/styles/src/typography.js +52 -52
|
@@ -1,949 +1,949 @@
|
|
|
1
|
-
import validate from 'validate.js';
|
|
2
|
-
|
|
3
|
-
function postData(url, data) {
|
|
4
|
-
return fetch(url, {
|
|
5
|
-
body: JSON.stringify(data),
|
|
6
|
-
cache: 'no-cache',
|
|
7
|
-
headers: {
|
|
8
|
-
Accept: 'application/json',
|
|
9
|
-
'Content-Type': 'application/json',
|
|
10
|
-
},
|
|
11
|
-
method: 'POST',
|
|
12
|
-
mode: 'cors',
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function markup() {
|
|
17
|
-
return `
|
|
18
|
-
<style>
|
|
19
|
-
*,
|
|
20
|
-
*::before,
|
|
21
|
-
*::after {
|
|
22
|
-
box-sizing: border-box;
|
|
23
|
-
margin: 0;
|
|
24
|
-
padding: 0;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
:host {
|
|
28
|
-
--flow-space: 1.5rem;
|
|
29
|
-
|
|
30
|
-
--color-dark: #404040;
|
|
31
|
-
--color-grey: #555B69;
|
|
32
|
-
|
|
33
|
-
--color-success: #1EB244;
|
|
34
|
-
--color-failure: #FFEDEB;
|
|
35
|
-
--color-failure-tint: #F86B58;
|
|
36
|
-
|
|
37
|
-
--color-richblue: #043C93;
|
|
38
|
-
--color-theme: ${this.themeColor};
|
|
39
|
-
|
|
40
|
-
--color-active: #2D2B2A;
|
|
41
|
-
--color-default: #001096;
|
|
42
|
-
--color-disabled: #848282;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
html {
|
|
46
|
-
font-family: 'DM Sans', sans-serif;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
[hidden] {
|
|
50
|
-
display: none !important;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
[disabled] {
|
|
54
|
-
cursor: not-allowed !important;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.visually-hidden {
|
|
58
|
-
border: 0;
|
|
59
|
-
clip: rect(1px 1px 1px 1px);
|
|
60
|
-
clip: rect(1px, 1px, 1px, 1px);
|
|
61
|
-
height: auto;
|
|
62
|
-
margin: 0;
|
|
63
|
-
overflow: hidden;
|
|
64
|
-
padding: 0;
|
|
65
|
-
position: absolute;
|
|
66
|
-
white-space: nowrap;
|
|
67
|
-
width: 1px;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.color-dark {
|
|
71
|
-
color: var(--color-dark);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.color-grey {
|
|
75
|
-
color: var(--color-grey);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.flow > * + * {
|
|
79
|
-
margin-top: var(--flow-space);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.center {
|
|
83
|
-
margin-left: auto;
|
|
84
|
-
margin-right: auto;
|
|
85
|
-
|
|
86
|
-
text-align: center;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
h1 {
|
|
90
|
-
font-size: 1.5rem;
|
|
91
|
-
font-weight: 700;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
button, input, select, textarea {
|
|
95
|
-
font: inherit
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
label,
|
|
99
|
-
input,
|
|
100
|
-
select,
|
|
101
|
-
textarea {
|
|
102
|
-
--flow-space: .5rem;
|
|
103
|
-
display: block;
|
|
104
|
-
width: 100%;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
input,
|
|
108
|
-
select,
|
|
109
|
-
textarea {
|
|
110
|
-
border: 1px solid #d1d8d6;
|
|
111
|
-
border-radius: .5rem;
|
|
112
|
-
padding: .75rem 1rem;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
button {
|
|
116
|
-
--button-color: var(--color-default);
|
|
117
|
-
--flow-space: 3rem;
|
|
118
|
-
-webkit-appearance: none;
|
|
119
|
-
-moz-appearance: none;
|
|
120
|
-
align-items: center;
|
|
121
|
-
appearance: none;
|
|
122
|
-
background-color: transparent;
|
|
123
|
-
border-radius: 2.5rem;
|
|
124
|
-
border: none;
|
|
125
|
-
color: #ffffff;
|
|
126
|
-
cursor: pointer;
|
|
127
|
-
display: inline-flex;
|
|
128
|
-
font-size: 20px;
|
|
129
|
-
font-weight: 500;
|
|
130
|
-
inline-size: 100%;
|
|
131
|
-
justify-content: center;
|
|
132
|
-
letter-spacing: .05ch;
|
|
133
|
-
line-height: 1;
|
|
134
|
-
padding: 1rem 2.5rem;
|
|
135
|
-
text-align: center;
|
|
136
|
-
text-decoration: none;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
button[data-variant='solid'] {
|
|
140
|
-
background-color: var(--button-color);
|
|
141
|
-
border: 2px solid var(--button-color);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
button[data-variant='outline'] {
|
|
145
|
-
color: var(--button-color);
|
|
146
|
-
border: 2px solid var(--button-color);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
button[data-variant='ghost'] {
|
|
150
|
-
color: var(--button-color);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
button:hover,
|
|
154
|
-
button:focus,
|
|
155
|
-
button:active {
|
|
156
|
-
--button-color: var(--color-active);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
button:disabled {
|
|
160
|
-
--button-color: var(--color-disabled);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
button[data-type='icon'] {
|
|
164
|
-
height: 2rem;
|
|
165
|
-
padding: 0;
|
|
166
|
-
width: 2rem;
|
|
167
|
-
background: transparent;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
input {
|
|
171
|
-
font: inherit;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
fieldset {
|
|
175
|
-
margin: 0;
|
|
176
|
-
border: none;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.font-weight:bold {
|
|
180
|
-
font-weight: bold;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.justify-right {
|
|
184
|
-
justify-content: end !important;
|
|
185
|
-
}
|
|
186
|
-
.nav {
|
|
187
|
-
display: flex;
|
|
188
|
-
justify-content: space-between;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
.back-wrapper {
|
|
192
|
-
display: flex;
|
|
193
|
-
align-items: center;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
.back-button-text {
|
|
197
|
-
font-size: 11px;
|
|
198
|
-
line-height: 11px;
|
|
199
|
-
color: ${this.themeColor || 'rgb(21, 31, 114)'};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
#error,
|
|
203
|
-
.validation-message {
|
|
204
|
-
color: red;
|
|
205
|
-
text-transform: capitalize;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.input-group {
|
|
209
|
-
--flow-space: 1.5rem;
|
|
210
|
-
text-align: initial;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.input-radio {
|
|
214
|
-
--flow-space: 1.5rem;
|
|
215
|
-
background-color: #F8F8F8;
|
|
216
|
-
border-radius: .5rem;
|
|
217
|
-
padding: .625rem 1rem;
|
|
218
|
-
display: flex;
|
|
219
|
-
align-items: center;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
.otp-mode {
|
|
223
|
-
display: flex;
|
|
224
|
-
align-items: center;
|
|
225
|
-
text-align: initial;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
.otp-mode :first-child {
|
|
229
|
-
margin: 0;
|
|
230
|
-
margin-inline-end: 1rem;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
.otp-mode :nth-child(2n) {
|
|
234
|
-
--flow-space: .5rem;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
.input-radio [type='radio'] {
|
|
238
|
-
border-radius: 50%;
|
|
239
|
-
inline-size: 2rem;
|
|
240
|
-
block-size: 2rem;
|
|
241
|
-
margin-inline-end: .5rem;
|
|
242
|
-
background-color: white;
|
|
243
|
-
border: .125rem solid #f5f5f5;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
#totp-token {
|
|
247
|
-
block-size: 3rem;
|
|
248
|
-
inline-size: 20rem;
|
|
249
|
-
max-inline-size: 100%;
|
|
250
|
-
background-color: #F5F5F5;
|
|
251
|
-
border: none;
|
|
252
|
-
border-bottom: 2px solid #2F718D;
|
|
253
|
-
font-size: 1.5rem;
|
|
254
|
-
text-align: center;
|
|
255
|
-
font-weight: 700;
|
|
256
|
-
letter-spacing: 2rem;
|
|
257
|
-
padding: .5rem 1rem;
|
|
258
|
-
margin-inline: auto;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
@keyframes spin {
|
|
262
|
-
0% {
|
|
263
|
-
transform: translate3d(-50%, -50%, 0) rotate(0deg);
|
|
264
|
-
}
|
|
265
|
-
100% {
|
|
266
|
-
transform: translate3d(-50%, -50%, 0) rotate(360deg);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
.spinner {
|
|
271
|
-
animation: 1.5s linear infinite spin;
|
|
272
|
-
animation-play-state: inherit;
|
|
273
|
-
border: solid 5px #cfd0d1;
|
|
274
|
-
border-bottom-color: var(--color-active);
|
|
275
|
-
border-radius: 50%;
|
|
276
|
-
content: "";
|
|
277
|
-
display: block;
|
|
278
|
-
height: 25px;
|
|
279
|
-
width: 25px;
|
|
280
|
-
will-change: transform;
|
|
281
|
-
position: relative;
|
|
282
|
-
top: .675rem;
|
|
283
|
-
left: 1.25rem;
|
|
284
|
-
}
|
|
285
|
-
</style>
|
|
286
|
-
|
|
287
|
-
<div class='flow center' id='id-entry'>
|
|
288
|
-
<div class="nav">
|
|
289
|
-
<div class="back-wrapper">
|
|
290
|
-
<button type='button' data-type='icon' id="back-button" class="back-button">
|
|
291
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
|
292
|
-
<path fill="#DBDBC4" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z" opacity=".4"/>
|
|
293
|
-
<path fill="${this.themeColor}" d="M15.5 11.25h-5.19l1.72-1.72c.29-.29.29-.77 0-1.06a.754.754 0 0 0-1.06 0l-3 3c-.29.29-.29.77 0 1.06l3 3c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06l-1.72-1.72h5.19c.41 0 .75-.34.75-.75s-.34-.75-.75-.75Z"/>
|
|
294
|
-
</svg>
|
|
295
|
-
</button>
|
|
296
|
-
<div class="back-button-text">Back</div>
|
|
297
|
-
</div>
|
|
298
|
-
<button data-type='icon' type='button' class='close-iframe'>
|
|
299
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
|
300
|
-
<path fill="#DBDBC4" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z" opacity=".4"/>
|
|
301
|
-
<path fill="#91190F" d="m13.06 12 2.3-2.3c.29-.29.29-.77 0-1.06a.754.754 0 0 0-1.06 0l-2.3 2.3-2.3-2.3a.754.754 0 0 0-1.06 0c-.29.29-.29.77 0 1.06l2.3 2.3-2.3 2.3c-.29.29-.29.77 0 1.06.15.15.34.22.53.22s.38-.07.53-.22l2.3-2.3 2.3 2.3c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06l-2.3-2.3Z"/>
|
|
302
|
-
</svg>
|
|
303
|
-
<span class='visually-hidden'>Close SmileIdentity Verification frame</span>
|
|
304
|
-
</button>
|
|
305
|
-
</div>
|
|
306
|
-
<h1>
|
|
307
|
-
Enter your ${this.idTypeLabel}
|
|
308
|
-
</h1>
|
|
309
|
-
|
|
310
|
-
<form name='id-entry-form' class='flow' novalidate style='--flow-space: 5.5rem'>
|
|
311
|
-
<div id='id-number' class="input-group flow">
|
|
312
|
-
<label class='required' for="id_number">
|
|
313
|
-
${this.idTypeLabel}
|
|
314
|
-
</label>
|
|
315
|
-
|
|
316
|
-
<input aria-required='true' id="id_number" name="id_number"
|
|
317
|
-
maxlength='11' placeholder='' />
|
|
318
|
-
|
|
319
|
-
<p>
|
|
320
|
-
<small>${this.idHint}</small>
|
|
321
|
-
</p>
|
|
322
|
-
</div>
|
|
323
|
-
|
|
324
|
-
<button data-variant='solid' id='query-otp-modes' type='submit'>
|
|
325
|
-
<span class='text'>Continue</span>
|
|
326
|
-
<svg aria-hidden='true' width="25" height="24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
327
|
-
<path d="M7 12h11m0 0-4.588-4M18 12l-4.588 4" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
328
|
-
</svg>
|
|
329
|
-
<span hidden class='spinner'></span>
|
|
330
|
-
</button>
|
|
331
|
-
</form>
|
|
332
|
-
</div>
|
|
333
|
-
|
|
334
|
-
<div hidden class='flow center' id='select-mode'>
|
|
335
|
-
<div class="nav">
|
|
336
|
-
<div class="back-wrapper">
|
|
337
|
-
<button type='button' data-type='icon' id="back-to-entry-button" class="back-button">
|
|
338
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
|
339
|
-
<path fill="#DBDBC4" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z" opacity=".4"/>
|
|
340
|
-
<path fill="${this.themeColor}" d="M15.5 11.25h-5.19l1.72-1.72c.29-.29.29-.77 0-1.06a.754.754 0 0 0-1.06 0l-3 3c-.29.29-.29.77 0 1.06l3 3c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06l-1.72-1.72h5.19c.41 0 .75-.34.75-.75s-.34-.75-.75-.75Z"/>
|
|
341
|
-
</svg>
|
|
342
|
-
</button>
|
|
343
|
-
<div class="back-button-text">Back</div>
|
|
344
|
-
</div>
|
|
345
|
-
<button data-type='icon' type='button' class='close-iframe'>
|
|
346
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
|
347
|
-
<path fill="#DBDBC4" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z" opacity=".4"/>
|
|
348
|
-
<path fill="#91190F" d="m13.06 12 2.3-2.3c.29-.29.29-.77 0-1.06a.754.754 0 0 0-1.06 0l-2.3 2.3-2.3-2.3a.754.754 0 0 0-1.06 0c-.29.29-.29.77 0 1.06l2.3 2.3-2.3 2.3c-.29.29-.29.77 0 1.06.15.15.34.22.53.22s.38-.07.53-.22l2.3-2.3 2.3 2.3c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06l-2.3-2.3Z"/>
|
|
349
|
-
</svg>
|
|
350
|
-
<span class='visually-hidden'>Close SmileIdentity Verification frame</span>
|
|
351
|
-
</button>
|
|
352
|
-
</div>
|
|
353
|
-
<h1>
|
|
354
|
-
Select contact method
|
|
355
|
-
</h1>
|
|
356
|
-
|
|
357
|
-
<form name='select-mode-form' novalidate style='--flow-space: 4.25rem' id='otp-entry' class='flow center'>
|
|
358
|
-
<fieldset class='flow center'>
|
|
359
|
-
<legend class='flow' style='--flow-space: 1.5rem'>
|
|
360
|
-
<p>
|
|
361
|
-
NIBSS, the data custodian of BVN,
|
|
362
|
-
will send you a One-Time Password (OTP)
|
|
363
|
-
</p>
|
|
364
|
-
|
|
365
|
-
<p>
|
|
366
|
-
<small>
|
|
367
|
-
The request will be from Chams Plc, who is NIBSS' technical partner.
|
|
368
|
-
</small>
|
|
369
|
-
</p>
|
|
370
|
-
</legend>
|
|
371
|
-
|
|
372
|
-
<div class='flow center'>
|
|
373
|
-
${
|
|
374
|
-
this.modes.length
|
|
375
|
-
? this.modes
|
|
376
|
-
.map(
|
|
377
|
-
(mode) => `<label class='input-radio'>
|
|
378
|
-
<input type="radio" id="" name="mode" value="${Object.keys(mode)[0]}">
|
|
379
|
-
<div class='otp-mode'>
|
|
380
|
-
${
|
|
381
|
-
Object.keys(mode)[0].includes('sms')
|
|
382
|
-
? `
|
|
383
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="29" height="37" fill="none">
|
|
384
|
-
<path stroke="#2F718D" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16.697 24.12c4.914 0 7.37 0 8.897-1.652 1.527-1.651 1.527-4.31 1.527-9.625 0-5.316 0-7.974-1.527-9.625-1.526-1.651-3.983-1.651-8.897-1.651h-5.211c-4.914 0-7.37 0-8.897 1.651-1.527 1.651-1.527 4.31-1.527 9.625 0 5.316 0 7.974 1.527 9.625.85.92 1.991 1.328 3.685 1.508"/>
|
|
385
|
-
<g filter="url(#sms)">
|
|
386
|
-
<path stroke="#2F718D" stroke-linecap="round" stroke-width="2" d="M16.697 24.12c-1.61 0-3.384.703-5.005 1.613-2.602 1.462-3.903 2.193-4.545 1.727-.64-.465-.52-1.91-.277-4.799l.055-.656" shape-rendering="crispEdges"/>
|
|
387
|
-
</g>
|
|
388
|
-
<defs>
|
|
389
|
-
<filter id="sms" width="20.023" height="15.595" x="1.675" y="21.005" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
|
390
|
-
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
391
|
-
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
|
392
|
-
<feOffset dy="4"/>
|
|
393
|
-
<feGaussianBlur stdDeviation="2"/>
|
|
394
|
-
<feComposite in2="hardAlpha" operator="out"/>
|
|
395
|
-
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
|
396
|
-
<feBlend in2="BackgroundImageFix" result="effect1_dropShadow_2_404"/>
|
|
397
|
-
<feBlend in="SourceGraphic" in2="effect1_dropShadow_2_404" result="shape"/>
|
|
398
|
-
</filter>
|
|
399
|
-
</defs>
|
|
400
|
-
</svg>
|
|
401
|
-
`
|
|
402
|
-
: `
|
|
403
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="35" height="24" fill="none">
|
|
404
|
-
<path stroke="#2F718D" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.062 4.367c0-1.437 1.221-2.603 2.727-2.603h21.815c1.506 0 2.727 1.166 2.727 2.603v15.62c0 1.438-1.221 2.604-2.727 2.604H6.789c-1.506 0-2.727-1.166-2.727-2.604V4.367Z"/>
|
|
405
|
-
<g filter="url(#message)">
|
|
406
|
-
<path stroke="#2F718D" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m5.426 3.066 8.647 7.338c2.067 1.754 5.18 1.754 7.247 0l8.648-7.338" shape-rendering="crispEdges"/>
|
|
407
|
-
</g>
|
|
408
|
-
<defs>
|
|
409
|
-
<filter id="message" width="34.042" height="18.154" x=".676" y="2.316" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
|
410
|
-
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
411
|
-
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
|
412
|
-
<feOffset dy="4"/>
|
|
413
|
-
<feGaussianBlur stdDeviation="2"/>
|
|
414
|
-
<feComposite in2="hardAlpha" operator="out"/>
|
|
415
|
-
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
|
416
|
-
<feBlend in2="BackgroundImageFix" result="effect1_dropShadow_2_394"/>
|
|
417
|
-
<feBlend in="SourceGraphic" in2="effect1_dropShadow_2_394" result="shape"/>
|
|
418
|
-
</filter>
|
|
419
|
-
</defs>
|
|
420
|
-
</svg>
|
|
421
|
-
`
|
|
422
|
-
}
|
|
423
|
-
<div class='flow'>
|
|
424
|
-
<p>
|
|
425
|
-
${Object.values(mode)[0]}
|
|
426
|
-
</p>
|
|
427
|
-
<p>
|
|
428
|
-
<small>
|
|
429
|
-
An OTP will be sent by ${
|
|
430
|
-
Object.keys(mode)[0].includes(
|
|
431
|
-
'sms',
|
|
432
|
-
)
|
|
433
|
-
? 'sms'
|
|
434
|
-
: 'email'
|
|
435
|
-
} to verify your identity
|
|
436
|
-
</small>
|
|
437
|
-
</p>
|
|
438
|
-
</div>
|
|
439
|
-
</div>
|
|
440
|
-
</label>`,
|
|
441
|
-
)
|
|
442
|
-
.join('\n')
|
|
443
|
-
: 'No modes yet'
|
|
444
|
-
}
|
|
445
|
-
</div>
|
|
446
|
-
</fieldset>
|
|
447
|
-
|
|
448
|
-
<button data-variant='ghost' id='contact-methods-outdated' style='--flow-space: .5rem' class='' type='button'>
|
|
449
|
-
I am no longer using any of these options
|
|
450
|
-
</button>
|
|
451
|
-
|
|
452
|
-
<button data-variant='solid' id='select-otp-mode' type='submit'>
|
|
453
|
-
<span class='text'>Continue</span>
|
|
454
|
-
<svg aria-hidden='true' width="25" height="24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
455
|
-
<path d="M7 12h11m0 0-4.588-4M18 12l-4.588 4" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
456
|
-
</svg>
|
|
457
|
-
<span hidden class='spinner'></span>
|
|
458
|
-
</button>
|
|
459
|
-
</form>
|
|
460
|
-
</div>
|
|
461
|
-
|
|
462
|
-
<div hidden class='flow center' id='otp-verification'>
|
|
463
|
-
<div class="nav justify-right">
|
|
464
|
-
<button data-type='icon' type='button' class='close-iframe'>
|
|
465
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
|
466
|
-
<path fill="#DBDBC4" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z" opacity=".4"/>
|
|
467
|
-
<path fill="#91190F" d="m13.06 12 2.3-2.3c.29-.29.29-.77 0-1.06a.754.754 0 0 0-1.06 0l-2.3 2.3-2.3-2.3a.754.754 0 0 0-1.06 0c-.29.29-.29.77 0 1.06l2.3 2.3-2.3 2.3c-.29.29-.29.77 0 1.06.15.15.34.22.53.22s.38-.07.53-.22l2.3-2.3 2.3 2.3c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06l-2.3-2.3Z"/>
|
|
468
|
-
</svg>
|
|
469
|
-
<span class='visually-hidden'>Close SmileIdentity Verification frame</span>
|
|
470
|
-
</button>
|
|
471
|
-
</div>
|
|
472
|
-
<h1>
|
|
473
|
-
OTP Verification
|
|
474
|
-
</h1>
|
|
475
|
-
|
|
476
|
-
<div style='--flow-space: 4.25rem' id='otp-entry'>
|
|
477
|
-
<form name='otp-submission-form' novalidate style='--flow-space: 1.5rem' class='flow center'>
|
|
478
|
-
<label for='totp-token'>
|
|
479
|
-
Enter the OTP sent to <span class='font-weight:bold'>${
|
|
480
|
-
this.selectedOtpDeliveryMode
|
|
481
|
-
}</span>
|
|
482
|
-
</label>
|
|
483
|
-
<input type='text' id='totp-token' maxlength='6' inputmode='numeric' autocomplete='one-time-code' />
|
|
484
|
-
|
|
485
|
-
<p>
|
|
486
|
-
Didn't receive the OTP${
|
|
487
|
-
!this.selectedOtpDeliveryMode
|
|
488
|
-
? '?'
|
|
489
|
-
: ` at <span class='font-weight:bold'>${this.selectedOtpDeliveryMode}</span>?`
|
|
490
|
-
}
|
|
491
|
-
</p>
|
|
492
|
-
|
|
493
|
-
<button style='--flow-space: .5rem' data-variant='ghost' class='try-another-method' type='button'>
|
|
494
|
-
Try another contact method
|
|
495
|
-
</button>
|
|
496
|
-
|
|
497
|
-
<button data-variant='solid' id='submit-otp' type='submit'>
|
|
498
|
-
<span class='text'>Submit</span>
|
|
499
|
-
<svg aria-hidden='true' width="25" height="24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
500
|
-
<path d="M7 12h11m0 0-4.588-4M18 12l-4.588 4" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
501
|
-
</svg>
|
|
502
|
-
<span hidden class='spinner'></span>
|
|
503
|
-
</button>
|
|
504
|
-
</form>
|
|
505
|
-
</div>
|
|
506
|
-
</div>
|
|
507
|
-
`;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
class TotpConsent extends HTMLElement {
|
|
511
|
-
constructor() {
|
|
512
|
-
super();
|
|
513
|
-
|
|
514
|
-
this.templateString = markup.bind(this);
|
|
515
|
-
this.render = () => this.templateString();
|
|
516
|
-
|
|
517
|
-
this.attachShadow({ mode: 'open' });
|
|
518
|
-
|
|
519
|
-
this.modes = [];
|
|
520
|
-
this['otp-delivery-mode'] = '';
|
|
521
|
-
|
|
522
|
-
this.queryOtpModes = this.queryOtpModes.bind(this);
|
|
523
|
-
this.selectOtpMode = this.selectOtpMode.bind(this);
|
|
524
|
-
this.submitOtp = this.submitOtp.bind(this);
|
|
525
|
-
this.switchContactMethod = this.switchContactMethod.bind(this);
|
|
526
|
-
this.handleTotpConsentGrant = this.handleTotpConsentGrant.bind(this);
|
|
527
|
-
this.handleTotpConsentContactMethodsOutdated =
|
|
528
|
-
this.handleTotpConsentContactMethodsOutdated.bind(this);
|
|
529
|
-
this.pages = [];
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
static get observedAttributes() {
|
|
533
|
-
return ['modes', 'otp-delivery-mode'];
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
attributeChangedCallback(name) {
|
|
537
|
-
switch (name) {
|
|
538
|
-
case 'modes':
|
|
539
|
-
case 'otp-delivery-mode': {
|
|
540
|
-
const updatedTemplate = document.createElement('template');
|
|
541
|
-
updatedTemplate.innerHTML = this.render();
|
|
542
|
-
const updatedNode = updatedTemplate.content
|
|
543
|
-
.cloneNode(true)
|
|
544
|
-
.querySelector(`#${this.activeScreen.id}`);
|
|
545
|
-
updatedNode.hidden = false;
|
|
546
|
-
this.shadowRoot.replaceChild(updatedNode, this.activeScreen);
|
|
547
|
-
this.setUpEventListeners();
|
|
548
|
-
this.setActiveScreen(updatedNode);
|
|
549
|
-
break;
|
|
550
|
-
}
|
|
551
|
-
default:
|
|
552
|
-
break;
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
setUpEventListeners() {
|
|
557
|
-
// Screens
|
|
558
|
-
this.idEntryScreen = this.shadowRoot.querySelector('#id-entry');
|
|
559
|
-
this.selectModeScreen = this.shadowRoot.querySelector('#select-mode');
|
|
560
|
-
this.otpVerificationScreen =
|
|
561
|
-
this.shadowRoot.querySelector('#otp-verification');
|
|
562
|
-
|
|
563
|
-
if (!this.activeScreen) {
|
|
564
|
-
this.activeScreen = this.idEntryScreen;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// Buttons
|
|
568
|
-
this.queryOtpModesButton =
|
|
569
|
-
this.idEntryScreen.querySelector('#query-otp-modes');
|
|
570
|
-
this.backButton = this.idEntryScreen.querySelector('#back-button');
|
|
571
|
-
this.selectOtpModeButton =
|
|
572
|
-
this.selectModeScreen.querySelector('#select-otp-mode');
|
|
573
|
-
this.entryBackbutton = this.selectModeScreen.querySelector(
|
|
574
|
-
'#back-to-entry-button',
|
|
575
|
-
);
|
|
576
|
-
this.contactMethodsOutdatedButton = this.selectModeScreen.querySelector(
|
|
577
|
-
'#contact-methods-outdated',
|
|
578
|
-
);
|
|
579
|
-
this.submitOtpButton =
|
|
580
|
-
this.otpVerificationScreen.querySelector('#submit-otp');
|
|
581
|
-
this.switchContactMethodButton = this.otpVerificationScreen.querySelector(
|
|
582
|
-
'.try-another-method',
|
|
583
|
-
);
|
|
584
|
-
const CloseIframeButtons =
|
|
585
|
-
this.shadowRoot.querySelectorAll('.close-iframe');
|
|
586
|
-
|
|
587
|
-
// Input Elements
|
|
588
|
-
this.idNumberInput = this.idEntryScreen.querySelector('#id_number');
|
|
589
|
-
this.modeInputs = this.selectModeScreen.querySelectorAll('[name="mode"]');
|
|
590
|
-
this.otpInput = this.otpVerificationScreen.querySelector('#totp-token');
|
|
591
|
-
|
|
592
|
-
// Event Handlers
|
|
593
|
-
this.queryOtpModesButton.addEventListener('click', (e) =>
|
|
594
|
-
this.queryOtpModes(e),
|
|
595
|
-
);
|
|
596
|
-
this.selectOtpModeButton.addEventListener('click', (e) =>
|
|
597
|
-
this.selectOtpMode(e),
|
|
598
|
-
);
|
|
599
|
-
this.submitOtpButton.addEventListener('click', (e) => this.submitOtp(e));
|
|
600
|
-
this.switchContactMethodButton.addEventListener('click', (e) =>
|
|
601
|
-
this.switchContactMethod(e),
|
|
602
|
-
);
|
|
603
|
-
this.contactMethodsOutdatedButton.addEventListener('click', (e) =>
|
|
604
|
-
this.handleTotpConsentContactMethodsOutdated(e),
|
|
605
|
-
);
|
|
606
|
-
|
|
607
|
-
this.entryBackbutton.addEventListener('click', () => {
|
|
608
|
-
this.handleBackClick();
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
this.backButton.addEventListener('click', () => {
|
|
612
|
-
this.handleBackClick();
|
|
613
|
-
});
|
|
614
|
-
|
|
615
|
-
CloseIframeButtons.forEach((button) => {
|
|
616
|
-
button.addEventListener(
|
|
617
|
-
'click',
|
|
618
|
-
() => {
|
|
619
|
-
this.closeWindow();
|
|
620
|
-
},
|
|
621
|
-
false,
|
|
622
|
-
);
|
|
623
|
-
});
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
closeWindow() {
|
|
627
|
-
const referenceWindow = window.parent;
|
|
628
|
-
[referenceWindow.parent, referenceWindow].forEach((win) => {
|
|
629
|
-
win.postMessage('SmileIdentity::Close', '*');
|
|
630
|
-
});
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
handleBackClick() {
|
|
634
|
-
const page = this.pages.pop();
|
|
635
|
-
if (page) {
|
|
636
|
-
this.setActiveScreen(page);
|
|
637
|
-
} else {
|
|
638
|
-
this.dispatchEvent(
|
|
639
|
-
new CustomEvent('end-user-consent.totp.cancelled', {}),
|
|
640
|
-
);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
connectedCallback() {
|
|
645
|
-
const template = document.createElement('template');
|
|
646
|
-
template.innerHTML = this.render();
|
|
647
|
-
|
|
648
|
-
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
|
649
|
-
this.setUpEventListeners();
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
switchContactMethod() {
|
|
653
|
-
this.queryOtpModes();
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
resetForm() {
|
|
657
|
-
const invalidElements =
|
|
658
|
-
this.activeScreen.querySelectorAll('[aria-invalid]');
|
|
659
|
-
invalidElements.forEach((el) => el.removeAttribute('aria-invalid'));
|
|
660
|
-
|
|
661
|
-
const validationMessages = this.activeScreen.querySelectorAll(
|
|
662
|
-
'.validation-message',
|
|
663
|
-
);
|
|
664
|
-
validationMessages.forEach((el) => el.remove());
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
handleIdNumberValidationErrors(errors) {
|
|
668
|
-
const fields = Object.keys(errors);
|
|
669
|
-
|
|
670
|
-
fields.forEach((field) => {
|
|
671
|
-
const input = this.activeScreen.querySelector(`#${field}`);
|
|
672
|
-
input.setAttribute('aria-invalid', 'true');
|
|
673
|
-
input.setAttribute('aria-describedby', `${field}-hint`);
|
|
674
|
-
|
|
675
|
-
const errorDiv = document.createElement('div');
|
|
676
|
-
errorDiv.setAttribute('id', `${field}-hint`);
|
|
677
|
-
errorDiv.setAttribute('class', 'validation-message');
|
|
678
|
-
// eslint-disable-next-line prefer-destructuring
|
|
679
|
-
errorDiv.textContent = errors[field][0];
|
|
680
|
-
|
|
681
|
-
input.insertAdjacentElement('afterend', errorDiv);
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
handleActiveScreenErrors(error) {
|
|
686
|
-
const submitButton = this.activeScreen.querySelector('[type="submit"]');
|
|
687
|
-
const errorDiv = document.createElement('div');
|
|
688
|
-
errorDiv.setAttribute('class', 'validation-message');
|
|
689
|
-
errorDiv.textContent = error;
|
|
690
|
-
submitButton.insertAdjacentElement('beforebegin', errorDiv);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
validateIdNumber(idNumber) {
|
|
694
|
-
const validationConstraints = {
|
|
695
|
-
id_number: {
|
|
696
|
-
format: new RegExp(this.idRegex),
|
|
697
|
-
presence: {
|
|
698
|
-
allowEmpty: false,
|
|
699
|
-
message: 'is required',
|
|
700
|
-
},
|
|
701
|
-
},
|
|
702
|
-
};
|
|
703
|
-
|
|
704
|
-
const errors = validate({ id_number: idNumber }, validationConstraints);
|
|
705
|
-
|
|
706
|
-
if (errors) {
|
|
707
|
-
this.handleIdNumberValidationErrors(errors);
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
return errors;
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
async queryOtpModes(event) {
|
|
714
|
-
if (event) {
|
|
715
|
-
// ACTION: disable another submission
|
|
716
|
-
event.preventDefault();
|
|
717
|
-
|
|
718
|
-
// ACTION: Reset any form validation errors'
|
|
719
|
-
this.resetForm();
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
// ACTION: Validate idNumber
|
|
723
|
-
const validationErrors = this.validateIdNumber(this.idNumberInput.value);
|
|
724
|
-
|
|
725
|
-
// ACTION: Get and set idNumber
|
|
726
|
-
localStorage.setItem('idNumber', this.idNumberInput.value || this.idNumber);
|
|
727
|
-
|
|
728
|
-
if (!validationErrors) {
|
|
729
|
-
const data = {
|
|
730
|
-
country: this.country,
|
|
731
|
-
id_number: this.idNumber,
|
|
732
|
-
id_type: this.idType,
|
|
733
|
-
partner_id: this.partnerId,
|
|
734
|
-
token: this.token,
|
|
735
|
-
};
|
|
736
|
-
const url = `${this.baseUrl}/totp_consent`;
|
|
737
|
-
|
|
738
|
-
try {
|
|
739
|
-
this.toggleLoading();
|
|
740
|
-
const response = await postData(url, data);
|
|
741
|
-
const json = await response.json();
|
|
742
|
-
this.toggleLoading();
|
|
743
|
-
|
|
744
|
-
if (!response.ok) {
|
|
745
|
-
this.handleActiveScreenErrors(json.error);
|
|
746
|
-
} else {
|
|
747
|
-
this.sessionId = json.session_id;
|
|
748
|
-
this.modes = json.modes;
|
|
749
|
-
this.setActiveScreen(this.selectModeScreen);
|
|
750
|
-
this.setAttribute('modes', json.modes);
|
|
751
|
-
}
|
|
752
|
-
} catch (error) {
|
|
753
|
-
this.toggleLoading();
|
|
754
|
-
this.handleActiveScreenErrors(error.message);
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
async selectOtpMode(event) {
|
|
760
|
-
// ACTION: disable another submission
|
|
761
|
-
event.preventDefault();
|
|
762
|
-
|
|
763
|
-
// ACTION: Reset any form validation errors'
|
|
764
|
-
this.resetForm();
|
|
765
|
-
|
|
766
|
-
// ACTION: Get mode
|
|
767
|
-
this.mode = Array.prototype.find.call(
|
|
768
|
-
this.modeInputs,
|
|
769
|
-
(node) => node.checked,
|
|
770
|
-
).value;
|
|
771
|
-
const data = {
|
|
772
|
-
country: this.country,
|
|
773
|
-
id_number: this.idNumber,
|
|
774
|
-
id_type: this.idType,
|
|
775
|
-
mode: this.mode,
|
|
776
|
-
partner_id: this.partnerId,
|
|
777
|
-
session_id: this.sessionId,
|
|
778
|
-
token: this.token,
|
|
779
|
-
};
|
|
780
|
-
const url = `${this.baseUrl}/totp_consent/mode`;
|
|
781
|
-
|
|
782
|
-
try {
|
|
783
|
-
this.toggleLoading();
|
|
784
|
-
const response = await postData(url, data);
|
|
785
|
-
const json = await response.json();
|
|
786
|
-
this.toggleLoading();
|
|
787
|
-
|
|
788
|
-
if (!response.ok) {
|
|
789
|
-
this.handleActiveScreenErrors(json.error);
|
|
790
|
-
} else {
|
|
791
|
-
this.selectedOtpDeliveryMode = this.modes.filter(
|
|
792
|
-
(mode) => mode[this.mode],
|
|
793
|
-
)[0][this.mode];
|
|
794
|
-
this.setActiveScreen(this.otpVerificationScreen);
|
|
795
|
-
this.setAttribute('otp-delivery-mode', this.selectedOtpDeliveryMode);
|
|
796
|
-
}
|
|
797
|
-
} catch (error) {
|
|
798
|
-
this.toggleLoading();
|
|
799
|
-
this.handleActiveScreenErrors(error.message);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
async submitOtp(event) {
|
|
804
|
-
// ACTION: disable another submission
|
|
805
|
-
event.preventDefault();
|
|
806
|
-
|
|
807
|
-
// ACTION: Reset any form validation errors'
|
|
808
|
-
this.resetForm();
|
|
809
|
-
|
|
810
|
-
this.otp = this.otpInput.value;
|
|
811
|
-
|
|
812
|
-
const data = {
|
|
813
|
-
country: this.country,
|
|
814
|
-
id_number: this.idNumber,
|
|
815
|
-
id_type: this.idType,
|
|
816
|
-
otp: this.otp,
|
|
817
|
-
partner_id: this.partnerId,
|
|
818
|
-
session_id: this.sessionId,
|
|
819
|
-
token: this.token,
|
|
820
|
-
};
|
|
821
|
-
const url = `${this.baseUrl}/totp_consent/otp`;
|
|
822
|
-
|
|
823
|
-
try {
|
|
824
|
-
this.toggleLoading();
|
|
825
|
-
const response = await postData(url, data);
|
|
826
|
-
const json = await response.json();
|
|
827
|
-
this.toggleLoading();
|
|
828
|
-
|
|
829
|
-
if (!response.ok) {
|
|
830
|
-
this.handleActiveScreenErrors(json.error);
|
|
831
|
-
} else {
|
|
832
|
-
this.handleTotpConsentGrant(event);
|
|
833
|
-
}
|
|
834
|
-
} catch (error) {
|
|
835
|
-
this.toggleLoading();
|
|
836
|
-
this.handleActiveScreenErrors(error.message);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
toggleLoading() {
|
|
841
|
-
const button = this.activeScreen.querySelector('button[type="submit"]');
|
|
842
|
-
const text = button.querySelector('.text');
|
|
843
|
-
const arrow = button.querySelector('svg');
|
|
844
|
-
const spinner = button.querySelector('.spinner');
|
|
845
|
-
|
|
846
|
-
button.toggleAttribute('disabled');
|
|
847
|
-
text.toggleAttribute('hidden');
|
|
848
|
-
arrow.toggleAttribute('hidden');
|
|
849
|
-
spinner.toggleAttribute('hidden');
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
setActiveScreen(screen) {
|
|
853
|
-
this.activeScreen.hidden = true;
|
|
854
|
-
screen.hidden = false;
|
|
855
|
-
this.activeScreen = screen;
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
get baseUrl() {
|
|
859
|
-
return this.getAttribute('base-url');
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
get country() {
|
|
863
|
-
return this.getAttribute('country');
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
get idHint() {
|
|
867
|
-
return this.getAttribute('id-hint') || 'Your BVN should be 11 digits long';
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
get idNumber() {
|
|
871
|
-
return localStorage.getItem('idNumber');
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
get idRegex() {
|
|
875
|
-
return this.getAttribute('id-regex');
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
get idType() {
|
|
879
|
-
return this.getAttribute('id-type');
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
get idTypeLabel() {
|
|
883
|
-
return this.getAttribute('id-type-label');
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
get partnerId() {
|
|
887
|
-
return this.getAttribute('partner-id');
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
get partnerName() {
|
|
891
|
-
return this.getAttribute('partner-name');
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
get token() {
|
|
895
|
-
return this.getAttribute('token');
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
get themeColor() {
|
|
899
|
-
return this.getAttribute('theme-color') || '#001096';
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
get hideBack() {
|
|
903
|
-
return this.hasAttribute('hide-back');
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
get showNavigation() {
|
|
907
|
-
return this.hasAttribute('show-navigation');
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
handleTotpConsentGrant() {
|
|
911
|
-
const customEvent = new CustomEvent('end-user-consent.totp.granted', {
|
|
912
|
-
detail: {
|
|
913
|
-
consented: {
|
|
914
|
-
contact_information: true,
|
|
915
|
-
document_information: true,
|
|
916
|
-
personal_details: true,
|
|
917
|
-
},
|
|
918
|
-
id_number: this.idNumber,
|
|
919
|
-
session_id: this.sessionId,
|
|
920
|
-
},
|
|
921
|
-
});
|
|
922
|
-
|
|
923
|
-
this.dispatchEvent(customEvent);
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
handleTotpConsentContactMethodsOutdated() {
|
|
927
|
-
const tag = 'end-user-consent.totp.denied.contact-methods-outdated';
|
|
928
|
-
const customEvent = new CustomEvent(tag, {
|
|
929
|
-
detail: {
|
|
930
|
-
data: {
|
|
931
|
-
id_number: this.idNumber,
|
|
932
|
-
session_id: this.sessionId,
|
|
933
|
-
},
|
|
934
|
-
message: tag,
|
|
935
|
-
},
|
|
936
|
-
});
|
|
937
|
-
|
|
938
|
-
this.dispatchEvent(customEvent);
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
if ('customElements' in window && !window.customElements.get('totp-consent')) {
|
|
943
|
-
window.customElements.define('totp-consent', TotpConsent);
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
export {
|
|
947
|
-
// eslint-disable-next-line import/prefer-default-export
|
|
948
|
-
TotpConsent,
|
|
949
|
-
};
|
|
1
|
+
import validate from 'validate.js';
|
|
2
|
+
|
|
3
|
+
function postData(url, data) {
|
|
4
|
+
return fetch(url, {
|
|
5
|
+
body: JSON.stringify(data),
|
|
6
|
+
cache: 'no-cache',
|
|
7
|
+
headers: {
|
|
8
|
+
Accept: 'application/json',
|
|
9
|
+
'Content-Type': 'application/json',
|
|
10
|
+
},
|
|
11
|
+
method: 'POST',
|
|
12
|
+
mode: 'cors',
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function markup() {
|
|
17
|
+
return `
|
|
18
|
+
<style>
|
|
19
|
+
*,
|
|
20
|
+
*::before,
|
|
21
|
+
*::after {
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
margin: 0;
|
|
24
|
+
padding: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
:host {
|
|
28
|
+
--flow-space: 1.5rem;
|
|
29
|
+
|
|
30
|
+
--color-dark: #404040;
|
|
31
|
+
--color-grey: #555B69;
|
|
32
|
+
|
|
33
|
+
--color-success: #1EB244;
|
|
34
|
+
--color-failure: #FFEDEB;
|
|
35
|
+
--color-failure-tint: #F86B58;
|
|
36
|
+
|
|
37
|
+
--color-richblue: #043C93;
|
|
38
|
+
--color-theme: ${this.themeColor};
|
|
39
|
+
|
|
40
|
+
--color-active: #2D2B2A;
|
|
41
|
+
--color-default: #001096;
|
|
42
|
+
--color-disabled: #848282;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
html {
|
|
46
|
+
font-family: 'DM Sans', sans-serif;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[hidden] {
|
|
50
|
+
display: none !important;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
[disabled] {
|
|
54
|
+
cursor: not-allowed !important;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.visually-hidden {
|
|
58
|
+
border: 0;
|
|
59
|
+
clip: rect(1px 1px 1px 1px);
|
|
60
|
+
clip: rect(1px, 1px, 1px, 1px);
|
|
61
|
+
height: auto;
|
|
62
|
+
margin: 0;
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
padding: 0;
|
|
65
|
+
position: absolute;
|
|
66
|
+
white-space: nowrap;
|
|
67
|
+
width: 1px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.color-dark {
|
|
71
|
+
color: var(--color-dark);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.color-grey {
|
|
75
|
+
color: var(--color-grey);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.flow > * + * {
|
|
79
|
+
margin-top: var(--flow-space);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.center {
|
|
83
|
+
margin-left: auto;
|
|
84
|
+
margin-right: auto;
|
|
85
|
+
|
|
86
|
+
text-align: center;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
h1 {
|
|
90
|
+
font-size: 1.5rem;
|
|
91
|
+
font-weight: 700;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
button, input, select, textarea {
|
|
95
|
+
font: inherit
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
label,
|
|
99
|
+
input,
|
|
100
|
+
select,
|
|
101
|
+
textarea {
|
|
102
|
+
--flow-space: .5rem;
|
|
103
|
+
display: block;
|
|
104
|
+
width: 100%;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
input,
|
|
108
|
+
select,
|
|
109
|
+
textarea {
|
|
110
|
+
border: 1px solid #d1d8d6;
|
|
111
|
+
border-radius: .5rem;
|
|
112
|
+
padding: .75rem 1rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
button {
|
|
116
|
+
--button-color: var(--color-default);
|
|
117
|
+
--flow-space: 3rem;
|
|
118
|
+
-webkit-appearance: none;
|
|
119
|
+
-moz-appearance: none;
|
|
120
|
+
align-items: center;
|
|
121
|
+
appearance: none;
|
|
122
|
+
background-color: transparent;
|
|
123
|
+
border-radius: 2.5rem;
|
|
124
|
+
border: none;
|
|
125
|
+
color: #ffffff;
|
|
126
|
+
cursor: pointer;
|
|
127
|
+
display: inline-flex;
|
|
128
|
+
font-size: 20px;
|
|
129
|
+
font-weight: 500;
|
|
130
|
+
inline-size: 100%;
|
|
131
|
+
justify-content: center;
|
|
132
|
+
letter-spacing: .05ch;
|
|
133
|
+
line-height: 1;
|
|
134
|
+
padding: 1rem 2.5rem;
|
|
135
|
+
text-align: center;
|
|
136
|
+
text-decoration: none;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
button[data-variant='solid'] {
|
|
140
|
+
background-color: var(--button-color);
|
|
141
|
+
border: 2px solid var(--button-color);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
button[data-variant='outline'] {
|
|
145
|
+
color: var(--button-color);
|
|
146
|
+
border: 2px solid var(--button-color);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
button[data-variant='ghost'] {
|
|
150
|
+
color: var(--button-color);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
button:hover,
|
|
154
|
+
button:focus,
|
|
155
|
+
button:active {
|
|
156
|
+
--button-color: var(--color-active);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
button:disabled {
|
|
160
|
+
--button-color: var(--color-disabled);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
button[data-type='icon'] {
|
|
164
|
+
height: 2rem;
|
|
165
|
+
padding: 0;
|
|
166
|
+
width: 2rem;
|
|
167
|
+
background: transparent;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
input {
|
|
171
|
+
font: inherit;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
fieldset {
|
|
175
|
+
margin: 0;
|
|
176
|
+
border: none;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.font-weight:bold {
|
|
180
|
+
font-weight: bold;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.justify-right {
|
|
184
|
+
justify-content: end !important;
|
|
185
|
+
}
|
|
186
|
+
.nav {
|
|
187
|
+
display: flex;
|
|
188
|
+
justify-content: space-between;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.back-wrapper {
|
|
192
|
+
display: flex;
|
|
193
|
+
align-items: center;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.back-button-text {
|
|
197
|
+
font-size: 11px;
|
|
198
|
+
line-height: 11px;
|
|
199
|
+
color: ${this.themeColor || 'rgb(21, 31, 114)'};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#error,
|
|
203
|
+
.validation-message {
|
|
204
|
+
color: red;
|
|
205
|
+
text-transform: capitalize;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.input-group {
|
|
209
|
+
--flow-space: 1.5rem;
|
|
210
|
+
text-align: initial;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.input-radio {
|
|
214
|
+
--flow-space: 1.5rem;
|
|
215
|
+
background-color: #F8F8F8;
|
|
216
|
+
border-radius: .5rem;
|
|
217
|
+
padding: .625rem 1rem;
|
|
218
|
+
display: flex;
|
|
219
|
+
align-items: center;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.otp-mode {
|
|
223
|
+
display: flex;
|
|
224
|
+
align-items: center;
|
|
225
|
+
text-align: initial;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.otp-mode :first-child {
|
|
229
|
+
margin: 0;
|
|
230
|
+
margin-inline-end: 1rem;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.otp-mode :nth-child(2n) {
|
|
234
|
+
--flow-space: .5rem;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.input-radio [type='radio'] {
|
|
238
|
+
border-radius: 50%;
|
|
239
|
+
inline-size: 2rem;
|
|
240
|
+
block-size: 2rem;
|
|
241
|
+
margin-inline-end: .5rem;
|
|
242
|
+
background-color: white;
|
|
243
|
+
border: .125rem solid #f5f5f5;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
#totp-token {
|
|
247
|
+
block-size: 3rem;
|
|
248
|
+
inline-size: 20rem;
|
|
249
|
+
max-inline-size: 100%;
|
|
250
|
+
background-color: #F5F5F5;
|
|
251
|
+
border: none;
|
|
252
|
+
border-bottom: 2px solid #2F718D;
|
|
253
|
+
font-size: 1.5rem;
|
|
254
|
+
text-align: center;
|
|
255
|
+
font-weight: 700;
|
|
256
|
+
letter-spacing: 2rem;
|
|
257
|
+
padding: .5rem 1rem;
|
|
258
|
+
margin-inline: auto;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@keyframes spin {
|
|
262
|
+
0% {
|
|
263
|
+
transform: translate3d(-50%, -50%, 0) rotate(0deg);
|
|
264
|
+
}
|
|
265
|
+
100% {
|
|
266
|
+
transform: translate3d(-50%, -50%, 0) rotate(360deg);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.spinner {
|
|
271
|
+
animation: 1.5s linear infinite spin;
|
|
272
|
+
animation-play-state: inherit;
|
|
273
|
+
border: solid 5px #cfd0d1;
|
|
274
|
+
border-bottom-color: var(--color-active);
|
|
275
|
+
border-radius: 50%;
|
|
276
|
+
content: "";
|
|
277
|
+
display: block;
|
|
278
|
+
height: 25px;
|
|
279
|
+
width: 25px;
|
|
280
|
+
will-change: transform;
|
|
281
|
+
position: relative;
|
|
282
|
+
top: .675rem;
|
|
283
|
+
left: 1.25rem;
|
|
284
|
+
}
|
|
285
|
+
</style>
|
|
286
|
+
|
|
287
|
+
<div class='flow center' id='id-entry'>
|
|
288
|
+
<div class="nav">
|
|
289
|
+
<div class="back-wrapper">
|
|
290
|
+
<button type='button' data-type='icon' id="back-button" class="back-button">
|
|
291
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
|
292
|
+
<path fill="#DBDBC4" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z" opacity=".4"/>
|
|
293
|
+
<path fill="${this.themeColor}" d="M15.5 11.25h-5.19l1.72-1.72c.29-.29.29-.77 0-1.06a.754.754 0 0 0-1.06 0l-3 3c-.29.29-.29.77 0 1.06l3 3c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06l-1.72-1.72h5.19c.41 0 .75-.34.75-.75s-.34-.75-.75-.75Z"/>
|
|
294
|
+
</svg>
|
|
295
|
+
</button>
|
|
296
|
+
<div class="back-button-text">Back</div>
|
|
297
|
+
</div>
|
|
298
|
+
<button data-type='icon' type='button' class='close-iframe'>
|
|
299
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
|
300
|
+
<path fill="#DBDBC4" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z" opacity=".4"/>
|
|
301
|
+
<path fill="#91190F" d="m13.06 12 2.3-2.3c.29-.29.29-.77 0-1.06a.754.754 0 0 0-1.06 0l-2.3 2.3-2.3-2.3a.754.754 0 0 0-1.06 0c-.29.29-.29.77 0 1.06l2.3 2.3-2.3 2.3c-.29.29-.29.77 0 1.06.15.15.34.22.53.22s.38-.07.53-.22l2.3-2.3 2.3 2.3c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06l-2.3-2.3Z"/>
|
|
302
|
+
</svg>
|
|
303
|
+
<span class='visually-hidden'>Close SmileIdentity Verification frame</span>
|
|
304
|
+
</button>
|
|
305
|
+
</div>
|
|
306
|
+
<h1>
|
|
307
|
+
Enter your ${this.idTypeLabel}
|
|
308
|
+
</h1>
|
|
309
|
+
|
|
310
|
+
<form name='id-entry-form' class='flow' novalidate style='--flow-space: 5.5rem'>
|
|
311
|
+
<div id='id-number' class="input-group flow">
|
|
312
|
+
<label class='required' for="id_number">
|
|
313
|
+
${this.idTypeLabel}
|
|
314
|
+
</label>
|
|
315
|
+
|
|
316
|
+
<input aria-required='true' id="id_number" name="id_number"
|
|
317
|
+
maxlength='11' placeholder='' />
|
|
318
|
+
|
|
319
|
+
<p>
|
|
320
|
+
<small>${this.idHint}</small>
|
|
321
|
+
</p>
|
|
322
|
+
</div>
|
|
323
|
+
|
|
324
|
+
<button data-variant='solid' id='query-otp-modes' type='submit'>
|
|
325
|
+
<span class='text'>Continue</span>
|
|
326
|
+
<svg aria-hidden='true' width="25" height="24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
327
|
+
<path d="M7 12h11m0 0-4.588-4M18 12l-4.588 4" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
328
|
+
</svg>
|
|
329
|
+
<span hidden class='spinner'></span>
|
|
330
|
+
</button>
|
|
331
|
+
</form>
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
<div hidden class='flow center' id='select-mode'>
|
|
335
|
+
<div class="nav">
|
|
336
|
+
<div class="back-wrapper">
|
|
337
|
+
<button type='button' data-type='icon' id="back-to-entry-button" class="back-button">
|
|
338
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
|
339
|
+
<path fill="#DBDBC4" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z" opacity=".4"/>
|
|
340
|
+
<path fill="${this.themeColor}" d="M15.5 11.25h-5.19l1.72-1.72c.29-.29.29-.77 0-1.06a.754.754 0 0 0-1.06 0l-3 3c-.29.29-.29.77 0 1.06l3 3c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06l-1.72-1.72h5.19c.41 0 .75-.34.75-.75s-.34-.75-.75-.75Z"/>
|
|
341
|
+
</svg>
|
|
342
|
+
</button>
|
|
343
|
+
<div class="back-button-text">Back</div>
|
|
344
|
+
</div>
|
|
345
|
+
<button data-type='icon' type='button' class='close-iframe'>
|
|
346
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
|
347
|
+
<path fill="#DBDBC4" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z" opacity=".4"/>
|
|
348
|
+
<path fill="#91190F" d="m13.06 12 2.3-2.3c.29-.29.29-.77 0-1.06a.754.754 0 0 0-1.06 0l-2.3 2.3-2.3-2.3a.754.754 0 0 0-1.06 0c-.29.29-.29.77 0 1.06l2.3 2.3-2.3 2.3c-.29.29-.29.77 0 1.06.15.15.34.22.53.22s.38-.07.53-.22l2.3-2.3 2.3 2.3c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06l-2.3-2.3Z"/>
|
|
349
|
+
</svg>
|
|
350
|
+
<span class='visually-hidden'>Close SmileIdentity Verification frame</span>
|
|
351
|
+
</button>
|
|
352
|
+
</div>
|
|
353
|
+
<h1>
|
|
354
|
+
Select contact method
|
|
355
|
+
</h1>
|
|
356
|
+
|
|
357
|
+
<form name='select-mode-form' novalidate style='--flow-space: 4.25rem' id='otp-entry' class='flow center'>
|
|
358
|
+
<fieldset class='flow center'>
|
|
359
|
+
<legend class='flow' style='--flow-space: 1.5rem'>
|
|
360
|
+
<p>
|
|
361
|
+
NIBSS, the data custodian of BVN,
|
|
362
|
+
will send you a One-Time Password (OTP)
|
|
363
|
+
</p>
|
|
364
|
+
|
|
365
|
+
<p>
|
|
366
|
+
<small>
|
|
367
|
+
The request will be from Chams Plc, who is NIBSS' technical partner.
|
|
368
|
+
</small>
|
|
369
|
+
</p>
|
|
370
|
+
</legend>
|
|
371
|
+
|
|
372
|
+
<div class='flow center'>
|
|
373
|
+
${
|
|
374
|
+
this.modes.length
|
|
375
|
+
? this.modes
|
|
376
|
+
.map(
|
|
377
|
+
(mode) => `<label class='input-radio'>
|
|
378
|
+
<input type="radio" id="" name="mode" value="${Object.keys(mode)[0]}">
|
|
379
|
+
<div class='otp-mode'>
|
|
380
|
+
${
|
|
381
|
+
Object.keys(mode)[0].includes('sms')
|
|
382
|
+
? `
|
|
383
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="29" height="37" fill="none">
|
|
384
|
+
<path stroke="#2F718D" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16.697 24.12c4.914 0 7.37 0 8.897-1.652 1.527-1.651 1.527-4.31 1.527-9.625 0-5.316 0-7.974-1.527-9.625-1.526-1.651-3.983-1.651-8.897-1.651h-5.211c-4.914 0-7.37 0-8.897 1.651-1.527 1.651-1.527 4.31-1.527 9.625 0 5.316 0 7.974 1.527 9.625.85.92 1.991 1.328 3.685 1.508"/>
|
|
385
|
+
<g filter="url(#sms)">
|
|
386
|
+
<path stroke="#2F718D" stroke-linecap="round" stroke-width="2" d="M16.697 24.12c-1.61 0-3.384.703-5.005 1.613-2.602 1.462-3.903 2.193-4.545 1.727-.64-.465-.52-1.91-.277-4.799l.055-.656" shape-rendering="crispEdges"/>
|
|
387
|
+
</g>
|
|
388
|
+
<defs>
|
|
389
|
+
<filter id="sms" width="20.023" height="15.595" x="1.675" y="21.005" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
|
390
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
391
|
+
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
|
392
|
+
<feOffset dy="4"/>
|
|
393
|
+
<feGaussianBlur stdDeviation="2"/>
|
|
394
|
+
<feComposite in2="hardAlpha" operator="out"/>
|
|
395
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
|
396
|
+
<feBlend in2="BackgroundImageFix" result="effect1_dropShadow_2_404"/>
|
|
397
|
+
<feBlend in="SourceGraphic" in2="effect1_dropShadow_2_404" result="shape"/>
|
|
398
|
+
</filter>
|
|
399
|
+
</defs>
|
|
400
|
+
</svg>
|
|
401
|
+
`
|
|
402
|
+
: `
|
|
403
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="35" height="24" fill="none">
|
|
404
|
+
<path stroke="#2F718D" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.062 4.367c0-1.437 1.221-2.603 2.727-2.603h21.815c1.506 0 2.727 1.166 2.727 2.603v15.62c0 1.438-1.221 2.604-2.727 2.604H6.789c-1.506 0-2.727-1.166-2.727-2.604V4.367Z"/>
|
|
405
|
+
<g filter="url(#message)">
|
|
406
|
+
<path stroke="#2F718D" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="m5.426 3.066 8.647 7.338c2.067 1.754 5.18 1.754 7.247 0l8.648-7.338" shape-rendering="crispEdges"/>
|
|
407
|
+
</g>
|
|
408
|
+
<defs>
|
|
409
|
+
<filter id="message" width="34.042" height="18.154" x=".676" y="2.316" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse">
|
|
410
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
411
|
+
<feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
|
412
|
+
<feOffset dy="4"/>
|
|
413
|
+
<feGaussianBlur stdDeviation="2"/>
|
|
414
|
+
<feComposite in2="hardAlpha" operator="out"/>
|
|
415
|
+
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
|
416
|
+
<feBlend in2="BackgroundImageFix" result="effect1_dropShadow_2_394"/>
|
|
417
|
+
<feBlend in="SourceGraphic" in2="effect1_dropShadow_2_394" result="shape"/>
|
|
418
|
+
</filter>
|
|
419
|
+
</defs>
|
|
420
|
+
</svg>
|
|
421
|
+
`
|
|
422
|
+
}
|
|
423
|
+
<div class='flow'>
|
|
424
|
+
<p>
|
|
425
|
+
${Object.values(mode)[0]}
|
|
426
|
+
</p>
|
|
427
|
+
<p>
|
|
428
|
+
<small>
|
|
429
|
+
An OTP will be sent by ${
|
|
430
|
+
Object.keys(mode)[0].includes(
|
|
431
|
+
'sms',
|
|
432
|
+
)
|
|
433
|
+
? 'sms'
|
|
434
|
+
: 'email'
|
|
435
|
+
} to verify your identity
|
|
436
|
+
</small>
|
|
437
|
+
</p>
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
440
|
+
</label>`,
|
|
441
|
+
)
|
|
442
|
+
.join('\n')
|
|
443
|
+
: 'No modes yet'
|
|
444
|
+
}
|
|
445
|
+
</div>
|
|
446
|
+
</fieldset>
|
|
447
|
+
|
|
448
|
+
<button data-variant='ghost' id='contact-methods-outdated' style='--flow-space: .5rem' class='' type='button'>
|
|
449
|
+
I am no longer using any of these options
|
|
450
|
+
</button>
|
|
451
|
+
|
|
452
|
+
<button data-variant='solid' id='select-otp-mode' type='submit'>
|
|
453
|
+
<span class='text'>Continue</span>
|
|
454
|
+
<svg aria-hidden='true' width="25" height="24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
455
|
+
<path d="M7 12h11m0 0-4.588-4M18 12l-4.588 4" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
456
|
+
</svg>
|
|
457
|
+
<span hidden class='spinner'></span>
|
|
458
|
+
</button>
|
|
459
|
+
</form>
|
|
460
|
+
</div>
|
|
461
|
+
|
|
462
|
+
<div hidden class='flow center' id='otp-verification'>
|
|
463
|
+
<div class="nav justify-right">
|
|
464
|
+
<button data-type='icon' type='button' class='close-iframe'>
|
|
465
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
|
466
|
+
<path fill="#DBDBC4" d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10Z" opacity=".4"/>
|
|
467
|
+
<path fill="#91190F" d="m13.06 12 2.3-2.3c.29-.29.29-.77 0-1.06a.754.754 0 0 0-1.06 0l-2.3 2.3-2.3-2.3a.754.754 0 0 0-1.06 0c-.29.29-.29.77 0 1.06l2.3 2.3-2.3 2.3c-.29.29-.29.77 0 1.06.15.15.34.22.53.22s.38-.07.53-.22l2.3-2.3 2.3 2.3c.15.15.34.22.53.22s.38-.07.53-.22c.29-.29.29-.77 0-1.06l-2.3-2.3Z"/>
|
|
468
|
+
</svg>
|
|
469
|
+
<span class='visually-hidden'>Close SmileIdentity Verification frame</span>
|
|
470
|
+
</button>
|
|
471
|
+
</div>
|
|
472
|
+
<h1>
|
|
473
|
+
OTP Verification
|
|
474
|
+
</h1>
|
|
475
|
+
|
|
476
|
+
<div style='--flow-space: 4.25rem' id='otp-entry'>
|
|
477
|
+
<form name='otp-submission-form' novalidate style='--flow-space: 1.5rem' class='flow center'>
|
|
478
|
+
<label for='totp-token'>
|
|
479
|
+
Enter the OTP sent to <span class='font-weight:bold'>${
|
|
480
|
+
this.selectedOtpDeliveryMode
|
|
481
|
+
}</span>
|
|
482
|
+
</label>
|
|
483
|
+
<input type='text' id='totp-token' maxlength='6' inputmode='numeric' autocomplete='one-time-code' />
|
|
484
|
+
|
|
485
|
+
<p>
|
|
486
|
+
Didn't receive the OTP${
|
|
487
|
+
!this.selectedOtpDeliveryMode
|
|
488
|
+
? '?'
|
|
489
|
+
: ` at <span class='font-weight:bold'>${this.selectedOtpDeliveryMode}</span>?`
|
|
490
|
+
}
|
|
491
|
+
</p>
|
|
492
|
+
|
|
493
|
+
<button style='--flow-space: .5rem' data-variant='ghost' class='try-another-method' type='button'>
|
|
494
|
+
Try another contact method
|
|
495
|
+
</button>
|
|
496
|
+
|
|
497
|
+
<button data-variant='solid' id='submit-otp' type='submit'>
|
|
498
|
+
<span class='text'>Submit</span>
|
|
499
|
+
<svg aria-hidden='true' width="25" height="24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
500
|
+
<path d="M7 12h11m0 0-4.588-4M18 12l-4.588 4" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
501
|
+
</svg>
|
|
502
|
+
<span hidden class='spinner'></span>
|
|
503
|
+
</button>
|
|
504
|
+
</form>
|
|
505
|
+
</div>
|
|
506
|
+
</div>
|
|
507
|
+
`;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
class TotpConsent extends HTMLElement {
|
|
511
|
+
constructor() {
|
|
512
|
+
super();
|
|
513
|
+
|
|
514
|
+
this.templateString = markup.bind(this);
|
|
515
|
+
this.render = () => this.templateString();
|
|
516
|
+
|
|
517
|
+
this.attachShadow({ mode: 'open' });
|
|
518
|
+
|
|
519
|
+
this.modes = [];
|
|
520
|
+
this['otp-delivery-mode'] = '';
|
|
521
|
+
|
|
522
|
+
this.queryOtpModes = this.queryOtpModes.bind(this);
|
|
523
|
+
this.selectOtpMode = this.selectOtpMode.bind(this);
|
|
524
|
+
this.submitOtp = this.submitOtp.bind(this);
|
|
525
|
+
this.switchContactMethod = this.switchContactMethod.bind(this);
|
|
526
|
+
this.handleTotpConsentGrant = this.handleTotpConsentGrant.bind(this);
|
|
527
|
+
this.handleTotpConsentContactMethodsOutdated =
|
|
528
|
+
this.handleTotpConsentContactMethodsOutdated.bind(this);
|
|
529
|
+
this.pages = [];
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
static get observedAttributes() {
|
|
533
|
+
return ['modes', 'otp-delivery-mode'];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
attributeChangedCallback(name) {
|
|
537
|
+
switch (name) {
|
|
538
|
+
case 'modes':
|
|
539
|
+
case 'otp-delivery-mode': {
|
|
540
|
+
const updatedTemplate = document.createElement('template');
|
|
541
|
+
updatedTemplate.innerHTML = this.render();
|
|
542
|
+
const updatedNode = updatedTemplate.content
|
|
543
|
+
.cloneNode(true)
|
|
544
|
+
.querySelector(`#${this.activeScreen.id}`);
|
|
545
|
+
updatedNode.hidden = false;
|
|
546
|
+
this.shadowRoot.replaceChild(updatedNode, this.activeScreen);
|
|
547
|
+
this.setUpEventListeners();
|
|
548
|
+
this.setActiveScreen(updatedNode);
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
default:
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
setUpEventListeners() {
|
|
557
|
+
// Screens
|
|
558
|
+
this.idEntryScreen = this.shadowRoot.querySelector('#id-entry');
|
|
559
|
+
this.selectModeScreen = this.shadowRoot.querySelector('#select-mode');
|
|
560
|
+
this.otpVerificationScreen =
|
|
561
|
+
this.shadowRoot.querySelector('#otp-verification');
|
|
562
|
+
|
|
563
|
+
if (!this.activeScreen) {
|
|
564
|
+
this.activeScreen = this.idEntryScreen;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Buttons
|
|
568
|
+
this.queryOtpModesButton =
|
|
569
|
+
this.idEntryScreen.querySelector('#query-otp-modes');
|
|
570
|
+
this.backButton = this.idEntryScreen.querySelector('#back-button');
|
|
571
|
+
this.selectOtpModeButton =
|
|
572
|
+
this.selectModeScreen.querySelector('#select-otp-mode');
|
|
573
|
+
this.entryBackbutton = this.selectModeScreen.querySelector(
|
|
574
|
+
'#back-to-entry-button',
|
|
575
|
+
);
|
|
576
|
+
this.contactMethodsOutdatedButton = this.selectModeScreen.querySelector(
|
|
577
|
+
'#contact-methods-outdated',
|
|
578
|
+
);
|
|
579
|
+
this.submitOtpButton =
|
|
580
|
+
this.otpVerificationScreen.querySelector('#submit-otp');
|
|
581
|
+
this.switchContactMethodButton = this.otpVerificationScreen.querySelector(
|
|
582
|
+
'.try-another-method',
|
|
583
|
+
);
|
|
584
|
+
const CloseIframeButtons =
|
|
585
|
+
this.shadowRoot.querySelectorAll('.close-iframe');
|
|
586
|
+
|
|
587
|
+
// Input Elements
|
|
588
|
+
this.idNumberInput = this.idEntryScreen.querySelector('#id_number');
|
|
589
|
+
this.modeInputs = this.selectModeScreen.querySelectorAll('[name="mode"]');
|
|
590
|
+
this.otpInput = this.otpVerificationScreen.querySelector('#totp-token');
|
|
591
|
+
|
|
592
|
+
// Event Handlers
|
|
593
|
+
this.queryOtpModesButton.addEventListener('click', (e) =>
|
|
594
|
+
this.queryOtpModes(e),
|
|
595
|
+
);
|
|
596
|
+
this.selectOtpModeButton.addEventListener('click', (e) =>
|
|
597
|
+
this.selectOtpMode(e),
|
|
598
|
+
);
|
|
599
|
+
this.submitOtpButton.addEventListener('click', (e) => this.submitOtp(e));
|
|
600
|
+
this.switchContactMethodButton.addEventListener('click', (e) =>
|
|
601
|
+
this.switchContactMethod(e),
|
|
602
|
+
);
|
|
603
|
+
this.contactMethodsOutdatedButton.addEventListener('click', (e) =>
|
|
604
|
+
this.handleTotpConsentContactMethodsOutdated(e),
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
this.entryBackbutton.addEventListener('click', () => {
|
|
608
|
+
this.handleBackClick();
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
this.backButton.addEventListener('click', () => {
|
|
612
|
+
this.handleBackClick();
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
CloseIframeButtons.forEach((button) => {
|
|
616
|
+
button.addEventListener(
|
|
617
|
+
'click',
|
|
618
|
+
() => {
|
|
619
|
+
this.closeWindow();
|
|
620
|
+
},
|
|
621
|
+
false,
|
|
622
|
+
);
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
closeWindow() {
|
|
627
|
+
const referenceWindow = window.parent;
|
|
628
|
+
[referenceWindow.parent, referenceWindow].forEach((win) => {
|
|
629
|
+
win.postMessage('SmileIdentity::Close', '*');
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
handleBackClick() {
|
|
634
|
+
const page = this.pages.pop();
|
|
635
|
+
if (page) {
|
|
636
|
+
this.setActiveScreen(page);
|
|
637
|
+
} else {
|
|
638
|
+
this.dispatchEvent(
|
|
639
|
+
new CustomEvent('end-user-consent.totp.cancelled', {}),
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
connectedCallback() {
|
|
645
|
+
const template = document.createElement('template');
|
|
646
|
+
template.innerHTML = this.render();
|
|
647
|
+
|
|
648
|
+
this.shadowRoot.appendChild(template.content.cloneNode(true));
|
|
649
|
+
this.setUpEventListeners();
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
switchContactMethod() {
|
|
653
|
+
this.queryOtpModes();
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
resetForm() {
|
|
657
|
+
const invalidElements =
|
|
658
|
+
this.activeScreen.querySelectorAll('[aria-invalid]');
|
|
659
|
+
invalidElements.forEach((el) => el.removeAttribute('aria-invalid'));
|
|
660
|
+
|
|
661
|
+
const validationMessages = this.activeScreen.querySelectorAll(
|
|
662
|
+
'.validation-message',
|
|
663
|
+
);
|
|
664
|
+
validationMessages.forEach((el) => el.remove());
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
handleIdNumberValidationErrors(errors) {
|
|
668
|
+
const fields = Object.keys(errors);
|
|
669
|
+
|
|
670
|
+
fields.forEach((field) => {
|
|
671
|
+
const input = this.activeScreen.querySelector(`#${field}`);
|
|
672
|
+
input.setAttribute('aria-invalid', 'true');
|
|
673
|
+
input.setAttribute('aria-describedby', `${field}-hint`);
|
|
674
|
+
|
|
675
|
+
const errorDiv = document.createElement('div');
|
|
676
|
+
errorDiv.setAttribute('id', `${field}-hint`);
|
|
677
|
+
errorDiv.setAttribute('class', 'validation-message');
|
|
678
|
+
// eslint-disable-next-line prefer-destructuring
|
|
679
|
+
errorDiv.textContent = errors[field][0];
|
|
680
|
+
|
|
681
|
+
input.insertAdjacentElement('afterend', errorDiv);
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
handleActiveScreenErrors(error) {
|
|
686
|
+
const submitButton = this.activeScreen.querySelector('[type="submit"]');
|
|
687
|
+
const errorDiv = document.createElement('div');
|
|
688
|
+
errorDiv.setAttribute('class', 'validation-message');
|
|
689
|
+
errorDiv.textContent = error;
|
|
690
|
+
submitButton.insertAdjacentElement('beforebegin', errorDiv);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
validateIdNumber(idNumber) {
|
|
694
|
+
const validationConstraints = {
|
|
695
|
+
id_number: {
|
|
696
|
+
format: new RegExp(this.idRegex),
|
|
697
|
+
presence: {
|
|
698
|
+
allowEmpty: false,
|
|
699
|
+
message: 'is required',
|
|
700
|
+
},
|
|
701
|
+
},
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
const errors = validate({ id_number: idNumber }, validationConstraints);
|
|
705
|
+
|
|
706
|
+
if (errors) {
|
|
707
|
+
this.handleIdNumberValidationErrors(errors);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
return errors;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
async queryOtpModes(event) {
|
|
714
|
+
if (event) {
|
|
715
|
+
// ACTION: disable another submission
|
|
716
|
+
event.preventDefault();
|
|
717
|
+
|
|
718
|
+
// ACTION: Reset any form validation errors'
|
|
719
|
+
this.resetForm();
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// ACTION: Validate idNumber
|
|
723
|
+
const validationErrors = this.validateIdNumber(this.idNumberInput.value);
|
|
724
|
+
|
|
725
|
+
// ACTION: Get and set idNumber
|
|
726
|
+
localStorage.setItem('idNumber', this.idNumberInput.value || this.idNumber);
|
|
727
|
+
|
|
728
|
+
if (!validationErrors) {
|
|
729
|
+
const data = {
|
|
730
|
+
country: this.country,
|
|
731
|
+
id_number: this.idNumber,
|
|
732
|
+
id_type: this.idType,
|
|
733
|
+
partner_id: this.partnerId,
|
|
734
|
+
token: this.token,
|
|
735
|
+
};
|
|
736
|
+
const url = `${this.baseUrl}/totp_consent`;
|
|
737
|
+
|
|
738
|
+
try {
|
|
739
|
+
this.toggleLoading();
|
|
740
|
+
const response = await postData(url, data);
|
|
741
|
+
const json = await response.json();
|
|
742
|
+
this.toggleLoading();
|
|
743
|
+
|
|
744
|
+
if (!response.ok) {
|
|
745
|
+
this.handleActiveScreenErrors(json.error);
|
|
746
|
+
} else {
|
|
747
|
+
this.sessionId = json.session_id;
|
|
748
|
+
this.modes = json.modes;
|
|
749
|
+
this.setActiveScreen(this.selectModeScreen);
|
|
750
|
+
this.setAttribute('modes', json.modes);
|
|
751
|
+
}
|
|
752
|
+
} catch (error) {
|
|
753
|
+
this.toggleLoading();
|
|
754
|
+
this.handleActiveScreenErrors(error.message);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
async selectOtpMode(event) {
|
|
760
|
+
// ACTION: disable another submission
|
|
761
|
+
event.preventDefault();
|
|
762
|
+
|
|
763
|
+
// ACTION: Reset any form validation errors'
|
|
764
|
+
this.resetForm();
|
|
765
|
+
|
|
766
|
+
// ACTION: Get mode
|
|
767
|
+
this.mode = Array.prototype.find.call(
|
|
768
|
+
this.modeInputs,
|
|
769
|
+
(node) => node.checked,
|
|
770
|
+
).value;
|
|
771
|
+
const data = {
|
|
772
|
+
country: this.country,
|
|
773
|
+
id_number: this.idNumber,
|
|
774
|
+
id_type: this.idType,
|
|
775
|
+
mode: this.mode,
|
|
776
|
+
partner_id: this.partnerId,
|
|
777
|
+
session_id: this.sessionId,
|
|
778
|
+
token: this.token,
|
|
779
|
+
};
|
|
780
|
+
const url = `${this.baseUrl}/totp_consent/mode`;
|
|
781
|
+
|
|
782
|
+
try {
|
|
783
|
+
this.toggleLoading();
|
|
784
|
+
const response = await postData(url, data);
|
|
785
|
+
const json = await response.json();
|
|
786
|
+
this.toggleLoading();
|
|
787
|
+
|
|
788
|
+
if (!response.ok) {
|
|
789
|
+
this.handleActiveScreenErrors(json.error);
|
|
790
|
+
} else {
|
|
791
|
+
this.selectedOtpDeliveryMode = this.modes.filter(
|
|
792
|
+
(mode) => mode[this.mode],
|
|
793
|
+
)[0][this.mode];
|
|
794
|
+
this.setActiveScreen(this.otpVerificationScreen);
|
|
795
|
+
this.setAttribute('otp-delivery-mode', this.selectedOtpDeliveryMode);
|
|
796
|
+
}
|
|
797
|
+
} catch (error) {
|
|
798
|
+
this.toggleLoading();
|
|
799
|
+
this.handleActiveScreenErrors(error.message);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
async submitOtp(event) {
|
|
804
|
+
// ACTION: disable another submission
|
|
805
|
+
event.preventDefault();
|
|
806
|
+
|
|
807
|
+
// ACTION: Reset any form validation errors'
|
|
808
|
+
this.resetForm();
|
|
809
|
+
|
|
810
|
+
this.otp = this.otpInput.value;
|
|
811
|
+
|
|
812
|
+
const data = {
|
|
813
|
+
country: this.country,
|
|
814
|
+
id_number: this.idNumber,
|
|
815
|
+
id_type: this.idType,
|
|
816
|
+
otp: this.otp,
|
|
817
|
+
partner_id: this.partnerId,
|
|
818
|
+
session_id: this.sessionId,
|
|
819
|
+
token: this.token,
|
|
820
|
+
};
|
|
821
|
+
const url = `${this.baseUrl}/totp_consent/otp`;
|
|
822
|
+
|
|
823
|
+
try {
|
|
824
|
+
this.toggleLoading();
|
|
825
|
+
const response = await postData(url, data);
|
|
826
|
+
const json = await response.json();
|
|
827
|
+
this.toggleLoading();
|
|
828
|
+
|
|
829
|
+
if (!response.ok) {
|
|
830
|
+
this.handleActiveScreenErrors(json.error);
|
|
831
|
+
} else {
|
|
832
|
+
this.handleTotpConsentGrant(event);
|
|
833
|
+
}
|
|
834
|
+
} catch (error) {
|
|
835
|
+
this.toggleLoading();
|
|
836
|
+
this.handleActiveScreenErrors(error.message);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
toggleLoading() {
|
|
841
|
+
const button = this.activeScreen.querySelector('button[type="submit"]');
|
|
842
|
+
const text = button.querySelector('.text');
|
|
843
|
+
const arrow = button.querySelector('svg');
|
|
844
|
+
const spinner = button.querySelector('.spinner');
|
|
845
|
+
|
|
846
|
+
button.toggleAttribute('disabled');
|
|
847
|
+
text.toggleAttribute('hidden');
|
|
848
|
+
arrow.toggleAttribute('hidden');
|
|
849
|
+
spinner.toggleAttribute('hidden');
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
setActiveScreen(screen) {
|
|
853
|
+
this.activeScreen.hidden = true;
|
|
854
|
+
screen.hidden = false;
|
|
855
|
+
this.activeScreen = screen;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
get baseUrl() {
|
|
859
|
+
return this.getAttribute('base-url');
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
get country() {
|
|
863
|
+
return this.getAttribute('country');
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
get idHint() {
|
|
867
|
+
return this.getAttribute('id-hint') || 'Your BVN should be 11 digits long';
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
get idNumber() {
|
|
871
|
+
return localStorage.getItem('idNumber');
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
get idRegex() {
|
|
875
|
+
return this.getAttribute('id-regex');
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
get idType() {
|
|
879
|
+
return this.getAttribute('id-type');
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
get idTypeLabel() {
|
|
883
|
+
return this.getAttribute('id-type-label');
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
get partnerId() {
|
|
887
|
+
return this.getAttribute('partner-id');
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
get partnerName() {
|
|
891
|
+
return this.getAttribute('partner-name');
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
get token() {
|
|
895
|
+
return this.getAttribute('token');
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
get themeColor() {
|
|
899
|
+
return this.getAttribute('theme-color') || '#001096';
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
get hideBack() {
|
|
903
|
+
return this.hasAttribute('hide-back');
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
get showNavigation() {
|
|
907
|
+
return this.hasAttribute('show-navigation');
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
handleTotpConsentGrant() {
|
|
911
|
+
const customEvent = new CustomEvent('end-user-consent.totp.granted', {
|
|
912
|
+
detail: {
|
|
913
|
+
consented: {
|
|
914
|
+
contact_information: true,
|
|
915
|
+
document_information: true,
|
|
916
|
+
personal_details: true,
|
|
917
|
+
},
|
|
918
|
+
id_number: this.idNumber,
|
|
919
|
+
session_id: this.sessionId,
|
|
920
|
+
},
|
|
921
|
+
});
|
|
922
|
+
|
|
923
|
+
this.dispatchEvent(customEvent);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
handleTotpConsentContactMethodsOutdated() {
|
|
927
|
+
const tag = 'end-user-consent.totp.denied.contact-methods-outdated';
|
|
928
|
+
const customEvent = new CustomEvent(tag, {
|
|
929
|
+
detail: {
|
|
930
|
+
data: {
|
|
931
|
+
id_number: this.idNumber,
|
|
932
|
+
session_id: this.sessionId,
|
|
933
|
+
},
|
|
934
|
+
message: tag,
|
|
935
|
+
},
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
this.dispatchEvent(customEvent);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if ('customElements' in window && !window.customElements.get('totp-consent')) {
|
|
943
|
+
window.customElements.define('totp-consent', TotpConsent);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
export {
|
|
947
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
948
|
+
TotpConsent,
|
|
949
|
+
};
|