@things-factory/auth-ui 8.0.0 → 9.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/dist-client/components/abstract-auth-page.js +10 -10
  2. package/dist-client/components/abstract-auth-page.js.map +1 -1
  3. package/dist-client/components/abstract-password-reset.d.ts +1 -2
  4. package/dist-client/components/abstract-password-reset.js +7 -14
  5. package/dist-client/components/abstract-password-reset.js.map +1 -1
  6. package/dist-client/components/abstract-sign.js +12 -11
  7. package/dist-client/components/abstract-sign.js.map +1 -1
  8. package/dist-client/components/contact-us.d.ts +1 -1
  9. package/dist-client/components/contact-us.js +10 -7
  10. package/dist-client/components/contact-us.js.map +1 -1
  11. package/dist-client/components/create-user.js +28 -5
  12. package/dist-client/components/create-user.js.map +1 -1
  13. package/dist-client/components/invite-user.js +19 -11
  14. package/dist-client/components/invite-user.js.map +1 -1
  15. package/dist-client/components/ownership-transfer-popup.js +3 -3
  16. package/dist-client/components/ownership-transfer-popup.js.map +1 -1
  17. package/dist-client/components/profile-component.d.ts +5 -1
  18. package/dist-client/components/profile-component.js +64 -4
  19. package/dist-client/components/profile-component.js.map +1 -1
  20. package/dist-client/components/role-privilege-editor.js +2 -1
  21. package/dist-client/components/role-privilege-editor.js.map +1 -1
  22. package/dist-client/components/user-role-editor.js +18 -18
  23. package/dist-client/components/user-role-editor.js.map +1 -1
  24. package/dist-client/entries/auth/checkin.js +1 -1
  25. package/dist-client/entries/auth/checkin.js.map +1 -1
  26. package/dist-client/entries/auth/forgot-password.js +11 -2
  27. package/dist-client/entries/auth/forgot-password.js.map +1 -1
  28. package/dist-client/entries/auth/signup.js +13 -7
  29. package/dist-client/entries/auth/signup.js.map +1 -1
  30. package/dist-client/index.js +1 -1
  31. package/dist-client/index.js.map +1 -1
  32. package/dist-client/pages/user/user-management.d.ts +5 -1
  33. package/dist-client/pages/user/user-management.js +6 -7
  34. package/dist-client/pages/user/user-management.js.map +1 -1
  35. package/dist-client/tsconfig.tsbuildinfo +1 -1
  36. package/package.json +12 -12
  37. package/translations/en.json +6 -2
  38. package/translations/ja.json +6 -2
  39. package/translations/ko.json +6 -2
  40. package/translations/ms.json +6 -2
  41. package/translations/zh.json +6 -2
  42. package/client/auth-style-sign.ts +0 -194
  43. package/client/bootstrap.ts +0 -51
  44. package/client/components/abstract-auth-page.ts +0 -301
  45. package/client/components/abstract-password-reset.ts +0 -168
  46. package/client/components/abstract-sign.ts +0 -127
  47. package/client/components/change-password.ts +0 -153
  48. package/client/components/contact-us.ts +0 -113
  49. package/client/components/create-domain-popup.ts +0 -141
  50. package/client/components/create-role.ts +0 -123
  51. package/client/components/create-user.ts +0 -95
  52. package/client/components/credential-manager.ts +0 -64
  53. package/client/components/delete-user-popup.ts +0 -117
  54. package/client/components/domain-switch.ts +0 -127
  55. package/client/components/invite-customer.ts +0 -104
  56. package/client/components/invite-user.ts +0 -96
  57. package/client/components/my-login-history.ts +0 -101
  58. package/client/components/ownership-transfer-popup.ts +0 -110
  59. package/client/components/partner-info-card.ts +0 -89
  60. package/client/components/partner-role-editor.ts +0 -153
  61. package/client/components/profile-component.ts +0 -332
  62. package/client/components/role-edit-form.ts +0 -92
  63. package/client/components/role-privilege-editor.ts +0 -267
  64. package/client/components/role-selector.ts +0 -102
  65. package/client/components/user-role-editor.ts +0 -499
  66. package/client/constants/application.ts +0 -9
  67. package/client/constants/index.ts +0 -1
  68. package/client/entries/auth/activate.ts +0 -272
  69. package/client/entries/auth/checkin.ts +0 -190
  70. package/client/entries/auth/forgot-password.ts +0 -103
  71. package/client/entries/auth/reset-password.ts +0 -22
  72. package/client/entries/auth/result.ts +0 -193
  73. package/client/entries/auth/signin.ts +0 -18
  74. package/client/entries/auth/signup.ts +0 -109
  75. package/client/entries/auth/unlock-user.ts +0 -22
  76. package/client/entries/oauth2/oauth2-decision-error-page.ts +0 -50
  77. package/client/entries/oauth2/oauth2-decision-page.ts +0 -196
  78. package/client/entries/public/home.ts +0 -246
  79. package/client/index.ts +0 -124
  80. package/client/pages/app-binding/app-binding.ts +0 -423
  81. package/client/pages/app-binding/app-bindings.ts +0 -171
  82. package/client/pages/appliance/appliance.ts +0 -452
  83. package/client/pages/appliance/home.ts +0 -177
  84. package/client/pages/appliance/register.ts +0 -183
  85. package/client/pages/application/application.ts +0 -428
  86. package/client/pages/application/applications.ts +0 -182
  87. package/client/pages/application/register.ts +0 -211
  88. package/client/pages/attribute/attribute-set-item-list.ts +0 -237
  89. package/client/pages/attribute/attribute-set-management.ts +0 -282
  90. package/client/pages/auth-provider/auth-provider-management.ts +0 -381
  91. package/client/pages/domain/domain-management.ts +0 -410
  92. package/client/pages/partner/partner-management.ts +0 -112
  93. package/client/pages/profile.ts +0 -32
  94. package/client/pages/role/role-management.ts +0 -134
  95. package/client/pages/user/user-management.ts +0 -224
  96. package/client/route.ts +0 -67
  97. package/client/themes/auth-theme.css +0 -65
  98. package/client/utils/password-rule.ts +0 -37
  99. package/server/index.ts +0 -0
@@ -1,301 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@material/web/button/elevated-button.js'
3
- import '@material/web/button/text-button.js'
4
- import '@material/web/textfield/filled-text-field.js'
5
-
6
- import '@operato/lottie-player'
7
- import '@operato/i18n/ox-i18n.js'
8
- import '@operato/i18n/ox-i18n-selector.js'
9
- import '@operato/layout/ox-snack-bar.js'
10
-
11
- import { css, html, LitElement, nothing } from 'lit'
12
- import { property, query, state } from 'lit/decorators.js'
13
-
14
- import { i18next, localize } from '@operato/i18n'
15
- import { ScrollbarStyles } from '@operato/styles'
16
- import { isSafari } from '@operato/utils'
17
-
18
- import { AUTH_STYLE_SIGN } from '../auth-style-sign.js'
19
-
20
- export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
21
- static styles = [
22
- ScrollbarStyles,
23
- css`
24
- :host {
25
- position: relative;
26
- overflow: hidden;
27
-
28
- display: flex;
29
- flex-direction: row;
30
-
31
- width: 100vw;
32
- height: 100vh;
33
- height: 100dvh;
34
- }
35
-
36
- .content {
37
- flex: 1;
38
- overflow: auto;
39
- }
40
-
41
- .home {
42
- position: absolute;
43
- padding: var(--padding-dufault, 9px);
44
- left: 20px;
45
- top: 10px;
46
- color: var(--md-sys-color-on-primary);
47
- }
48
-
49
- div.field {
50
- margin-bottom: var(--spacing-medium);
51
- }
52
-
53
- [hidden] {
54
- display: none;
55
- }
56
-
57
- #snackbar {
58
- width: 100%;
59
- z-index: 10;
60
- }
61
-
62
- @media print {
63
- :host {
64
- width: 100%;
65
- height: 100%;
66
- min-height: 100vh;
67
- min-height: 100dvh;
68
- }
69
- }
70
- `,
71
- AUTH_STYLE_SIGN
72
- ]
73
-
74
- @property({ type: Object }) data: any
75
- @property({ type: String }) message?: string
76
- @property({ type: Object }) detail: any
77
- @property({ type: String }) redirectTo?: string
78
-
79
- @query('#form') formEl!: HTMLFormElement
80
-
81
- private _applicationMeta?: {
82
- icon: string
83
- title: string
84
- description: string
85
- }
86
-
87
- get autocompletable() {
88
- return false
89
- }
90
-
91
- render() {
92
- const { disableUserFavoredLanguage, languages } = this.data || {}
93
- var { icon, title, description } = this.applicationMeta
94
-
95
- return html`
96
- <div class="content md-typescale-display-medium">
97
- <div class="wrap">
98
- <div class="auth-brand">
99
- <img src=${icon} />
100
- <strong class="name">${title}</strong>
101
- <span class="welcome-msg">${description}</span>
102
- </div>
103
-
104
- <div class="auth-form">
105
- <h3><ox-i18n msgid="title.${this.pageName}"></ox-i18n></h3>
106
-
107
- <form
108
- id="form"
109
- action="${this.actionUrl}"
110
- method="post"
111
- autocomplete=${this.autocompletable ? 'on' : 'off'}
112
- @keypress=${e => {
113
- if (e.key == 'Enter') this._onSubmit(e)
114
- }}
115
- >
116
- ${this.formfields}
117
- </form>
118
- ${this.links}
119
- ${!disableUserFavoredLanguage
120
- ? html` <div id="locale-area">
121
- <label for="locale-selector"><md-icon>language</md-icon></label>
122
- <ox-i18n-selector
123
- id="locale-selector"
124
- value=${i18next.language || 'en-US'}
125
- .languages=${languages}
126
- @change=${e => {
127
- var locale = e.detail
128
- if (!locale) return
129
-
130
- i18next.changeLanguage(locale)
131
- }}
132
- ></ox-i18n-selector>
133
- </div>`
134
- : nothing}
135
- </div>
136
- </div>
137
-
138
- <md-icon-button class="home" @click=${e => (window.location.href = '/')}
139
- ><md-icon>home</md-icon></md-icon-button
140
- >
141
- <ox-snack-bar id="snackbar" level="error" .message=${this.message}></ox-snack-bar>
142
-
143
- ${isSafari()
144
- ? html``
145
- : html`
146
- <div class="lottie-container">
147
- <lottie-player autoplay loop src="../../assets/images/background-animation.json"></lottie-player>
148
- </div>
149
- `}
150
- </div>
151
- `
152
- }
153
-
154
- firstUpdated() {
155
- setTimeout(() => {
156
- ;(this.renderRoot.querySelector('md-filled-text-field') as any).focus()
157
- }, 100)
158
-
159
- this.formEl.reset = () => {
160
- this.formElements.filter(el => !(el.hidden || el.type == 'hidden')).forEach(el => (el.value = ''))
161
- }
162
- }
163
-
164
- updated(changed) {
165
- if (changed.has('data') && this.data) {
166
- this.message = this.data.message
167
- this.redirectTo = this.data.redirectTo
168
- }
169
- }
170
-
171
- abstract get pageName(): string
172
- abstract get actionUrl(): string
173
-
174
- get formElements(): HTMLInputElement[] {
175
- return Array.from(this.formEl.querySelectorAll('[name]'))
176
- }
177
-
178
- get formfields() {
179
- const email = this.data?.email || ''
180
- // .validationMessage=${String(i18next.t('text.invalid-email'))}
181
-
182
- return html`
183
- <input id="redirectTo" type="hidden" name="redirectTo" .value=${this.redirectTo || '/'} />
184
-
185
- <div class="field">
186
- <md-filled-text-field
187
- name="email"
188
- type="email"
189
- label=${String(i18next.t('field.email'))}
190
- required
191
- .value=${email}
192
- autocomplete="off"
193
- autocapitalize="off"
194
- ><md-icon slot="leading-icon">mail</md-icon></md-filled-text-field
195
- >
196
- </div>
197
- <div class="field">
198
- <md-filled-text-field
199
- name="password"
200
- type="password"
201
- label=${String(i18next.t('field.password'))}
202
- autocomplete="off"
203
- required
204
- ><md-icon slot="leading-icon">vpn_key</md-icon></md-filled-text-field
205
- >
206
- </div>
207
-
208
- <md-elevated-button class="ui" type="submit" raised @click=${e => this._onSubmit(e)}>
209
- <ox-i18n msgid="field.${this.pageName}"> </ox-i18n>
210
- </md-elevated-button>
211
- `
212
- }
213
-
214
- get links() {
215
- const { disableUserSignupProcess, ssoLinks = [] } = this.data || {}
216
-
217
- return html`
218
- ${!disableUserSignupProcess
219
- ? html`
220
- <a class="link" href="/auth/signup">
221
- <md-text-button>
222
- <md-icon slot="icon">add_task</md-icon>
223
- <ox-i18n msgid="field.sign up"></ox-i18n>
224
- </md-text-button>
225
- </a>
226
- <a class="link" href="/auth/forgot-password">
227
- <md-text-button>
228
- <md-icon slot="icon">lock_open</md-icon>
229
- <ox-i18n msgid="field.forgot-password"></ox-i18n>
230
- </md-text-button>
231
- </a>
232
- `
233
- : nothing}
234
- ${ssoLinks.map(
235
- sso => html`
236
- <a class="link" href=${sso.link}>
237
- <md-text-button>
238
- <md-icon slot="icon">badge</md-icon>
239
- ${i18next.t('label.signin with', { title: sso.title })}
240
- </md-text-button>
241
- </a>
242
- `
243
- )}
244
- `
245
- }
246
-
247
- async _onSubmit(e) {
248
- if (this.checkValidity()) {
249
- this.submit()
250
- }
251
- }
252
-
253
- checkValidity() {
254
- return this.formElements.every(el => el.checkValidity())
255
- }
256
-
257
- abstract submit()
258
-
259
- showSnackbar({ level, message, timer = 3000 }: { level?: string; message?: string; timer?: number } = {}) {
260
- const snackbar = this.renderRoot.querySelector('#snackbar') as HTMLElement & {
261
- level: string
262
- message: string
263
- active: boolean
264
- }
265
-
266
- if (level) snackbar.level = level
267
- if (message) snackbar.message = message
268
- snackbar.active = true
269
-
270
- if (timer > -1)
271
- setTimeout(() => {
272
- this.hideSnackbar()
273
- }, timer)
274
- }
275
-
276
- hideSnackbar() {
277
- const snackbar = this.renderRoot.querySelector('#snackbar') as HTMLElement & {
278
- level: string
279
- message: string
280
- active: boolean
281
- }
282
-
283
- snackbar.active = false
284
- }
285
-
286
- get applicationMeta() {
287
- if (!this._applicationMeta) {
288
- var iconLink: HTMLLinkElement | null = document.querySelector('link[rel="application-icon"]')
289
- var titleMeta: HTMLMetaElement | null = document.querySelector('meta[name="application-name"]')
290
- var descriptionMeta: HTMLMetaElement | null = document.querySelector('meta[name="application-description"]')
291
-
292
- this._applicationMeta = {
293
- icon: iconLink?.href || '',
294
- title: titleMeta?.content || 'Things Factory',
295
- description: descriptionMeta?.content || 'Reimagining Software'
296
- }
297
- }
298
-
299
- return this._applicationMeta
300
- }
301
- }
@@ -1,168 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@material/web/button/elevated-button.js'
3
- import '@material/web/textfield/filled-text-field.js'
4
-
5
- import '@operato/lottie-player'
6
- import '../components/profile-component'
7
- import '@operato/i18n/ox-i18n.js'
8
- import '@operato/i18n/ox-i18n-selector.js'
9
- import '@operato/layout/ox-snack-bar.js'
10
-
11
- import { css, html, nothing } from 'lit'
12
- import { property, query } from 'lit/decorators.js'
13
-
14
- import { i18next } from '@operato/i18n'
15
- import { isSafari } from '@operato/utils'
16
-
17
- import { AUTH_STYLE_SIGN } from '../auth-style-sign'
18
- import { generatePasswordPatternHelp, generatePasswordPatternRegExp } from '../utils/password-rule'
19
- import { AbstractAuthPage } from './abstract-auth-page'
20
-
21
- export abstract class AbstractPasswordReset extends AbstractAuthPage {
22
- static styles = [
23
- css`
24
- :host {
25
- position: relative;
26
- overflow: hidden;
27
-
28
- display: flex;
29
- flex-direction: row;
30
-
31
- width: 100vw;
32
- height: 100vh;
33
- height: 100dvh;
34
- }
35
-
36
- @media print {
37
- :host {
38
- width: 100%;
39
- height: 100%;
40
- min-height: 100vh;
41
- min-height: 100dvh;
42
- }
43
- }
44
- `,
45
- AUTH_STYLE_SIGN
46
- ]
47
-
48
- @property({ type: Object }) data: any
49
- @property({ type: String }) token?: string
50
-
51
- @query('#confirm-password') confirmPass!: HTMLElement
52
-
53
- private passwordPattern: string = ''
54
- private passwordHelp: string = ''
55
-
56
- abstract get submitButtonLabel(): string
57
-
58
- render() {
59
- var { icon, title, description } = this.applicationMeta
60
- const { disableUserFavoredLanguage, languages } = this.data || {}
61
-
62
- return html`
63
- <div class="wrap">
64
- <div class="auth-brand">
65
- <img src=${icon} />
66
- <strong class="name">${title}</strong>
67
- <span class="welcome-msg">${description}</span>
68
- </div>
69
-
70
- <div class="auth-form">
71
- <h3><ox-i18n msgid="title.${this.title}"></ox-i18n></h3>
72
- <form
73
- id="form"
74
- action="${this.actionUrl}"
75
- method="post"
76
- @keypress=${e => {
77
- if (e.key == 'Enter') this._onSubmit(e)
78
- }}
79
- >
80
- <input name="token" type="hidden" .value=${this.token || ''} required />
81
- <div class="field">
82
- <md-filled-text-field
83
- name="password"
84
- type="password"
85
- label=${String(i18next.t('label.password'))}
86
- pattern=${this.passwordPattern || ''}
87
- supporting-text=${this.passwordHelp}
88
- autocomplete="off"
89
- @input=${e => {
90
- var val = e.target.value
91
- this.confirmPass.setAttribute('pattern', val.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '[$&]'))
92
- }}
93
- required
94
- ></md-filled-text-field>
95
- </div>
96
-
97
- <div class="field">
98
- <md-filled-text-field
99
- id="confirm-password"
100
- name="confirm-password"
101
- type="password"
102
- label=${String(i18next.t('field.confirm password'))}
103
- autocomplete="off"
104
- required
105
- ></md-filled-text-field>
106
- </div>
107
-
108
- <md-elevated-button id="submit-button" type="submit" @click=${e => this._onSubmit(e)}>
109
- <ox-i18n msgid="${this.submitButtonLabel}"></ox-i18n>
110
- </md-elevated-button>
111
-
112
- ${!disableUserFavoredLanguage
113
- ? html` <div id="locale-area">
114
- <label for="locale-selector"><md-icon>language</md-icon></label>
115
- <ox-i18n-selector
116
- id="locale-selector"
117
- value=${i18next.language || 'en-US'}
118
- .languages=${languages}
119
- @change=${e => {
120
- var locale = e.detail
121
- if (!locale) return
122
-
123
- i18next.changeLanguage(locale)
124
- }}
125
- ></ox-i18n-selector>
126
- </div>`
127
- : nothing}
128
- </form>
129
- </div>
130
- </div>
131
- <ox-snack-bar id="snackbar" level="error" .message=${this.message}></ox-snack-bar>
132
-
133
- ${isSafari()
134
- ? html``
135
- : html`
136
- <div class="lottie-container">
137
- <lottie-player autoplay loop src="../../assets/images/background-animation.json"></lottie-player>
138
- </div>
139
- `}
140
- `
141
- }
142
-
143
- updated(changed) {
144
- super.updated(changed)
145
- if (changed.has('data')) {
146
- this.token = this.data.token
147
- }
148
-
149
- if (changed.has('message')) {
150
- if (!this.message) {
151
- this.hideSnackbar()
152
- } else {
153
- this.showSnackbar({
154
- timer: -1
155
- })
156
- }
157
- }
158
- }
159
-
160
- languageUpdated() {
161
- this.passwordPattern = generatePasswordPatternRegExp(this.data.passwordRule).source
162
- this.passwordHelp = generatePasswordPatternHelp(this.data.passwordRule)
163
- }
164
-
165
- async submit() {
166
- this.formEl.submit()
167
- }
168
- }
@@ -1,127 +0,0 @@
1
- import '@operato/i18n/ox-i18n.js'
2
-
3
- import { html, nothing } from 'lit'
4
- import { startAuthentication } from '@simplewebauthn/browser'
5
-
6
- import { i18next } from '@operato/i18n'
7
- import { notify } from '@operato/layout'
8
-
9
- import { AbstractAuthPage } from './abstract-auth-page.js'
10
-
11
- const isAvailableWebauthn = 'PublicKeyCredential' in window
12
-
13
- interface AssertionResponse {
14
- id: string
15
- rawId?: number[]
16
- response: {
17
- authenticatorData: string
18
- clientDataJSON: string
19
- signature: string
20
- userHandle: string | null
21
- }
22
- type: PublicKeyCredentialType
23
- authenticatorAttachment?: AuthenticatorAttachment
24
- }
25
-
26
- export abstract class AbstractSign extends AbstractAuthPage {
27
- async submit() {
28
- this.formEl.submit()
29
- }
30
-
31
- updated(changed) {
32
- super.updated(changed)
33
-
34
- if (changed.has('message')) {
35
- if (!this.message) {
36
- this.hideSnackbar()
37
- } else {
38
- this.showSnackbar({
39
- level: 'error',
40
- timer: -1
41
- })
42
- }
43
- }
44
- }
45
-
46
- get formfields() {
47
- const email = this.data?.email || ''
48
- const autocompletable = this.autocompletable
49
-
50
- return html`
51
- <input id="redirectTo" type="hidden" name="redirectTo" .value=${this.redirectTo || '/'} />
52
-
53
- <div class="field">
54
- <md-filled-text-field
55
- name="email"
56
- type="email"
57
- label=${String(i18next.t('field.email'))}
58
- required
59
- .value=${email}
60
- autocomplete=${autocompletable ? "username" : "off"}
61
- autocapitalize="off"
62
- @input=${(e: Event) => {
63
- const target = e.target as HTMLInputElement
64
- if (target.validity.typeMismatch) {
65
- target.setCustomValidity(i18next.t('text.invalid-email'))
66
- } else {
67
- target.setCustomValidity('')
68
- }
69
- }}
70
- ><md-icon slot="leading-icon">mail</md-icon></md-filled-text-field
71
- >
72
- </div>
73
- <div class="field">
74
- <md-filled-text-field
75
- name="password"
76
- type="password"
77
- label=${String(i18next.t('field.password'))}
78
- autocomplete=${autocompletable ? "current-password" : "off"}
79
- required
80
- ><md-icon slot="leading-icon">vpn_key</md-icon></md-filled-text-field
81
- >
82
- </div>
83
-
84
- <div class="submit-buttons-container">
85
- <md-elevated-button class="submit-button" type="submit" raised @click=${e => this._onSubmit(e)}>
86
- <ox-i18n msgid="field.${this.pageName}"> </ox-i18n>
87
- </md-elevated-button>
88
- ${isAvailableWebauthn
89
- ? html` <md-icon class="fingerprint" raised @click=${e => this.authenticateUser()}> fingerprint </md-icon>`
90
- : nothing}
91
- </div>
92
- `
93
- }
94
-
95
- async authenticateUser() {
96
- try {
97
- const options = await fetch('/auth/signin-webauthn/challenge').then(res => res.json())
98
- const assertionResp = await startAuthentication(options)
99
- const verification = await fetch('/auth/signin-webauthn', {
100
- method: 'POST',
101
- headers: {
102
- 'Content-Type': 'application/json'
103
- },
104
- body: JSON.stringify(assertionResp)
105
- }).then(res => res.json())
106
-
107
- if (verification.verified) {
108
- const { redirectURL } = verification
109
-
110
- if (redirectURL) {
111
- window.location.href = redirectURL
112
- }
113
- } else {
114
- notify({
115
- level: 'error',
116
- message: verification.message
117
- })
118
- }
119
-
120
- } catch (error) {
121
- notify({
122
- level: 'error',
123
- message: i18next.t('error.authn verification failed')
124
- })
125
- }
126
- }
127
- }