@things-factory/auth-ui 7.0.1-rc.1 → 7.0.1-rc.11

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 (43) hide show
  1. package/client/auth-style-sign.ts +2 -1
  2. package/client/components/abstract-auth-page.ts +7 -2
  3. package/client/components/abstract-password-reset.ts +2 -0
  4. package/client/components/abstract-sign.ts +33 -74
  5. package/client/components/profile-component.ts +27 -87
  6. package/client/components/user-role-editor.ts +6 -2
  7. package/client/entries/auth/activate.ts +2 -1
  8. package/client/entries/auth/checkin.ts +5 -2
  9. package/client/entries/auth/result.ts +2 -1
  10. package/client/entries/auth/signin.ts +4 -0
  11. package/client/entries/auth/signup.ts +3 -0
  12. package/client/pages/role/role-management.ts +7 -4
  13. package/dist-client/auth-style-sign.js +2 -1
  14. package/dist-client/auth-style-sign.js.map +1 -1
  15. package/dist-client/components/abstract-auth-page.d.ts +1 -0
  16. package/dist-client/components/abstract-auth-page.js +6 -2
  17. package/dist-client/components/abstract-auth-page.js.map +1 -1
  18. package/dist-client/components/abstract-password-reset.js +2 -0
  19. package/dist-client/components/abstract-password-reset.js.map +1 -1
  20. package/dist-client/components/abstract-sign.d.ts +1 -1
  21. package/dist-client/components/abstract-sign.js +29 -62
  22. package/dist-client/components/abstract-sign.js.map +1 -1
  23. package/dist-client/components/profile-component.d.ts +1 -1
  24. package/dist-client/components/profile-component.js +25 -76
  25. package/dist-client/components/profile-component.js.map +1 -1
  26. package/dist-client/components/user-role-editor.js +6 -2
  27. package/dist-client/components/user-role-editor.js.map +1 -1
  28. package/dist-client/entries/auth/activate.js +2 -1
  29. package/dist-client/entries/auth/activate.js.map +1 -1
  30. package/dist-client/entries/auth/checkin.js +5 -2
  31. package/dist-client/entries/auth/checkin.js.map +1 -1
  32. package/dist-client/entries/auth/result.js +2 -1
  33. package/dist-client/entries/auth/result.js.map +1 -1
  34. package/dist-client/entries/auth/signin.d.ts +1 -0
  35. package/dist-client/entries/auth/signin.js +3 -0
  36. package/dist-client/entries/auth/signin.js.map +1 -1
  37. package/dist-client/entries/auth/signup.js +3 -0
  38. package/dist-client/entries/auth/signup.js.map +1 -1
  39. package/dist-client/pages/role/role-management.js +7 -4
  40. package/dist-client/pages/role/role-management.js.map +1 -1
  41. package/dist-client/tsconfig.tsbuildinfo +1 -1
  42. package/dist-server/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +4 -4
@@ -3,7 +3,8 @@ import { css } from 'lit'
3
3
  export const AUTH_STYLE_SIGN = css`
4
4
  :host {
5
5
  display: flex;
6
- background-color: var(--auth-background, var(--md-sys-color-primary));
6
+ background-color: var(--md-sys-color-primary);
7
+ color: var(--md-sys-color-on-primary);
7
8
  }
8
9
 
9
10
  :host *:focus {
@@ -84,6 +84,10 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
84
84
  description: string
85
85
  }
86
86
 
87
+ get autocompletable() {
88
+ return false
89
+ }
90
+
87
91
  render() {
88
92
  const { disableUserFavoredLanguage, languages } = this.data || {}
89
93
  var { icon, title, description } = this.applicationMeta
@@ -104,6 +108,7 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
104
108
  id="form"
105
109
  action="${this.actionUrl}"
106
110
  method="post"
111
+ autocomplete=${this.autocompletable ? "on": "off"}
107
112
  @keypress=${e => {
108
113
  if (e.key == 'Enter') this._onSubmit(e)
109
114
  }}
@@ -184,7 +189,7 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
184
189
  label=${String(i18next.t('field.email'))}
185
190
  required
186
191
  .value=${email}
187
- autocomplete="username"
192
+ autocomplete="off"
188
193
  autocapitalize="off"
189
194
  ><md-icon slot="leading-icon">mail</md-icon></md-filled-text-field
190
195
  >
@@ -194,7 +199,7 @@ export abstract class AbstractAuthPage extends localize(i18next)(LitElement) {
194
199
  name="password"
195
200
  type="password"
196
201
  label=${String(i18next.t('field.password'))}
197
- autocomplete="current-password"
202
+ autocomplete="off"
198
203
  required
199
204
  ><md-icon slot="leading-icon">vpn_key</md-icon></md-filled-text-field
200
205
  >
@@ -86,6 +86,7 @@ export abstract class AbstractPasswordReset extends AbstractAuthPage {
86
86
  .pattern=${this.passwordPattern}
87
87
  helper=${this.passwordHelp}
88
88
  helperPersistent
89
+ autocomplete="off"
89
90
  @input=${e => {
90
91
  var val = e.target.value
91
92
  this.confirmPass.setAttribute('pattern', val.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '[$&]'))
@@ -100,6 +101,7 @@ export abstract class AbstractPasswordReset extends AbstractAuthPage {
100
101
  name="confirm-password"
101
102
  type="password"
102
103
  label=${String(i18next.t('field.confirm password'))}
104
+ autocomplete="off"
103
105
  required
104
106
  ></md-filled-text-field>
105
107
  </div>
@@ -1,8 +1,8 @@
1
- import { html, nothing } from 'lit'
2
- import base64url from 'base64url'
3
-
4
1
  import '@operato/i18n/ox-i18n.js'
5
2
 
3
+ import { html, nothing } from 'lit'
4
+ import { startAuthentication } from '@simplewebauthn/browser'
5
+
6
6
  import { i18next } from '@operato/i18n'
7
7
  import { notify } from '@operato/layout'
8
8
 
@@ -45,6 +45,7 @@ export abstract class AbstractSign extends AbstractAuthPage {
45
45
 
46
46
  get formfields() {
47
47
  const email = this.data?.email || ''
48
+ const autocompletable = this.autocompletable
48
49
 
49
50
  return html`
50
51
  <input id="redirectTo" type="hidden" name="redirectTo" .value=${this.redirectTo || '/'} />
@@ -56,7 +57,7 @@ export abstract class AbstractSign extends AbstractAuthPage {
56
57
  label=${String(i18next.t('field.email'))}
57
58
  required
58
59
  .value=${email}
59
- autocomplete="username"
60
+ autocomplete=${autocompletable ? "username" : "off"}
60
61
  autocapitalize="off"
61
62
  @input=${(e: Event) => {
62
63
  const target = e.target as HTMLInputElement
@@ -74,7 +75,7 @@ export abstract class AbstractSign extends AbstractAuthPage {
74
75
  name="password"
75
76
  type="password"
76
77
  label=${String(i18next.t('field.password'))}
77
- autocomplete="current-password"
78
+ autocomplete=${autocompletable ? "current-password" : "off"}
78
79
  required
79
80
  ><md-icon slot="leading-icon">vpn_key</md-icon></md-filled-text-field
80
81
  >
@@ -85,84 +86,42 @@ export abstract class AbstractSign extends AbstractAuthPage {
85
86
  <ox-i18n msgid="field.${this.pageName}"> </ox-i18n>
86
87
  </md-elevated-button>
87
88
  ${isAvailableWebauthn
88
- ? html` <md-icon class="fingerprint" raised @click=${e => this.signinWithAuthn()}> fingerprint </md-icon>`
89
+ ? html` <md-icon class="fingerprint" raised @click=${e => this.authenticateUser()}> fingerprint </md-icon>`
89
90
  : nothing}
90
91
  </div>
91
92
  `
92
93
  }
93
94
 
94
- async signinWithAuthn() {
95
- const response = await fetch('/auth/signin-webauthn/challenge', {
96
- method: 'POST',
97
- headers: {
98
- 'Content-Type': 'application/json',
99
- Accept: 'application/json'
100
- },
101
- credentials: 'include'
102
- })
103
-
104
- if (!response.ok) {
105
- notify({
106
- level: 'error',
107
- message: await response.text()
108
- })
109
-
110
- return
111
- }
112
-
113
- const options = await response.json()
114
- const credential = (await navigator.credentials.get({
115
- publicKey: {
116
- challenge: Buffer.from(options.challenge, 'base64')
117
- }
118
- })) as PublicKeyCredential
119
-
120
- if (!credential) {
121
- notify({
122
- level: 'error',
123
- message: 'can not get user credential'
124
- })
125
-
126
- return
127
- }
128
-
129
- const assertionResponse = {
130
- id: credential.id,
131
- response: {
132
- clientDataJSON: base64url.encode(Buffer.from(credential.response.clientDataJSON)),
133
- authenticatorData: base64url.encode((credential.response as any).authenticatorData),
134
- signature: base64url.encode((credential.response as any).signature),
135
- userHandle: (credential.response as any).userHandle
136
- ? base64url.encode((credential.response as any).userHandle)
137
- : null
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
+ })
138
118
  }
139
- } as AssertionResponse
140
-
141
- if (credential.authenticatorAttachment) {
142
- assertionResponse.authenticatorAttachment = credential.authenticatorAttachment as AuthenticatorAttachment
143
- }
144
-
145
- const signinResponse = await fetch('/auth/signin-webauthn', {
146
- method: 'POST',
147
- headers: {
148
- 'Content-Type': 'application/json',
149
- Accept: 'application/json'
150
- },
151
- body: JSON.stringify(assertionResponse),
152
- credentials: 'include'
153
- })
154
-
155
- if (!signinResponse.ok) {
119
+
120
+ } catch (error) {
156
121
  notify({
157
122
  level: 'error',
158
- message: await signinResponse.text()
123
+ message: i18next.t('error.authn verification failed')
159
124
  })
160
- } else {
161
- const { redirectURL } = await signinResponse.json()
162
-
163
- if (redirectURL) {
164
- window.location.href = redirectURL
165
- }
166
125
  }
167
126
  }
168
127
  }
@@ -7,6 +7,7 @@ import './my-login-history'
7
7
  import base64url from 'base64url'
8
8
  import { css, html, LitElement, nothing } from 'lit'
9
9
  import { customElement, property, query, state } from 'lit/decorators.js'
10
+ import { startRegistration } from '@simplewebauthn/browser'
10
11
 
11
12
  import { i18next, localize } from '@operato/i18n'
12
13
  import { notify, openPopup } from '@operato/layout'
@@ -164,7 +165,7 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
164
165
 
165
166
  ${isAvailableWebauthn
166
167
  ? html`
167
- <md-text-button @click=${() => this.registerWebAuthn()}
168
+ <md-text-button @click=${() => this.registerUser()}
168
169
  >${i18next.t('button.security-key registration')}</md-text-button
169
170
  >
170
171
  `
@@ -244,96 +245,35 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
244
245
  })
245
246
  }
246
247
 
247
- async registerWebAuthn() {
248
- const challenge = await fetch('/auth/register-webauthn/challenge', {
249
- method: 'POST',
250
- headers: {
251
- 'Content-Type': 'application/json',
252
- Accept: 'application/json'
253
- },
254
- body: JSON.stringify({ id: this.userId }),
255
- credentials: 'include'
256
- })
257
-
258
- if (!challenge.ok) {
259
- notify({
260
- level: 'error',
261
- message: await challenge.text()
262
- })
263
-
264
- return
265
- }
266
-
267
- const options = await challenge.json()
268
-
269
- const credential = (await navigator.credentials.create({
270
- publicKey: {
271
- rp: {
272
- name: this.applicationMeta.title
273
- },
274
- user: {
275
- id: Uint8Array.from(base64url.toBuffer(options.user.id)).buffer,
276
- name: options.user.name,
277
- displayName: options.user.displayName
248
+ async registerUser() {
249
+ try {
250
+ const options = await fetch('/auth/register-webauthn/challenge').then(res => res.json())
251
+ const attResp = await startRegistration(options)
252
+ const verification = await fetch('/auth/verify-registration', {
253
+ method: 'POST',
254
+ headers: {
255
+ 'Content-Type': 'application/json'
278
256
  },
279
- challenge: Uint8Array.from(atob(options.challenge), c => c.charCodeAt(0)),
280
- pubKeyCredParams: [
281
- {
282
- type: 'public-key',
283
- alg: -7 // ES256
284
- },
285
- {
286
- type: 'public-key',
287
- alg: -257 // RS256
288
- }
289
- ],
290
- authenticatorSelection: {
291
- userVerification: 'preferred'
292
- }
257
+ body: JSON.stringify(attResp)
258
+ }).then(res => res.json())
259
+
260
+ if (verification.verified) {
261
+ notify({
262
+ level: 'info',
263
+ message: i18next.t('text.user credential registered successfully')
264
+ })
265
+ } else {
266
+ console.error(await verification.text())
267
+
268
+ notify({
269
+ level: 'error',
270
+ message: i18next.t('error.user credential registeration not allowed')
271
+ })
293
272
  }
294
- })) as PublicKeyCredential
295
-
296
- if (!credential) {
273
+ } catch (error) {
297
274
  notify({
298
275
  level: 'error',
299
- message: 'can not get user credential'
300
- })
301
-
302
- return
303
- }
304
-
305
- const response = credential.response as AuthenticatorAttestationResponse
306
-
307
- var body = {
308
- response: {
309
- clientDataJSON: Buffer.from(response.clientDataJSON).toString('base64'),
310
- attestationObject: Buffer.from(response.attestationObject).toString('base64')
311
- } as any
312
- }
313
-
314
- if (response.getTransports) {
315
- body.response.transports = response.getTransports()
316
- }
317
-
318
- const signinResponse = await fetch('/auth/signin-webauthn', {
319
- method: 'POST',
320
- headers: {
321
- 'Content-Type': 'application/json',
322
- Accept: 'application/json'
323
- },
324
- body: JSON.stringify(body),
325
- credentials: 'include'
326
- })
327
-
328
- if (!signinResponse.ok) {
329
- notify({
330
- level: 'error',
331
- message: await signinResponse.text()
332
- })
333
- } else {
334
- notify({
335
- level: 'info',
336
- message: i18next.t('text.user credential registered successfully')
276
+ message: i18next.t('error.user credential registeration failed')
337
277
  })
338
278
  }
339
279
  }
@@ -16,6 +16,7 @@ import { store } from '@operato/shell'
16
16
  import { OxPrompt } from '@operato/popup/ox-prompt.js'
17
17
  import { ButtonContainerStyles } from '@operato/styles'
18
18
  import { RoleSelector } from './role-selector'
19
+ import { InheritedValueType } from '@operato/data-grist'
19
20
 
20
21
  @customElement('user-role-editor')
21
22
  class UserRoleEditor extends connect(store)(LitElement) {
@@ -275,8 +276,8 @@ class UserRoleEditor extends connect(store)(LitElement) {
275
276
  async fetchAvailableRoles() {
276
277
  const response = await client.query({
277
278
  query: gql`
278
- query {
279
- roles {
279
+ query roles($inherited: InheritedValueType) {
280
+ roles(inherited: $inherited) {
280
281
  items {
281
282
  id
282
283
  domain {
@@ -300,6 +301,9 @@ class UserRoleEditor extends connect(store)(LitElement) {
300
301
  }
301
302
  }
302
303
  `,
304
+ variables: {
305
+ inherited: InheritedValueType.Include
306
+ },
303
307
  context: gqlContext()
304
308
  })
305
309
 
@@ -20,7 +20,8 @@ export class AuthActivate extends localize(i18next)(LitElement) {
20
20
  width: 100vw;
21
21
  height: 100vh;
22
22
  height: 100dvh;
23
- background-color: var(--auth-background, var(--md-sys-color-primary));
23
+ background-color: var(--md-sys-color-primary);
24
+ color: var(--md-sys-color-on-primary);
24
25
  }
25
26
 
26
27
  .wrap {
@@ -21,12 +21,14 @@ export class AuthCheckIn extends localize(i18next)(LitElement) {
21
21
  display: flex;
22
22
  flex-direction: column;
23
23
  margin: auto;
24
- background-color: var(--auth-background, var(--md-sys-color-primary));
24
+ background-color: var(--md-sys-color-primary);
25
+ color: var(--md-sys-color-on-primary);
25
26
  height: 100vh;
26
27
  height: 100dvh;
27
28
  }
28
29
  .header {
29
30
  background-color: var(--md-sys-color-primary);
31
+ color: var(--md-sys-color-on-primary);
30
32
  height: var(--checkin-header-height);
31
33
  }
32
34
  .content {
@@ -38,6 +40,7 @@ export class AuthCheckIn extends localize(i18next)(LitElement) {
38
40
  margin: var(--margin-wide) 0;
39
41
  padding: 0;
40
42
  background-color: var(--md-sys-color-surface);
43
+ color: var(--md-sys-color-on-surface);
41
44
  border-radius: var(--border-radius);
42
45
  border: var(--border-dim-color);
43
46
  list-style: none;
@@ -47,13 +50,13 @@ export class AuthCheckIn extends localize(i18next)(LitElement) {
47
50
  margin-bottom: -1px;
48
51
  padding: var(--padding-default) var(--padding-wide);
49
52
  font-size: 18px;
50
- color: var(--md-sys-color-secondary);
51
53
  text-align: left;
52
54
 
53
55
  cursor: pointer;
54
56
  }
55
57
  li:hover {
56
58
  background-color: var(--md-sys-color-primary-container);
59
+ color: var(--md-sys-color-on-primary-container);
57
60
  }
58
61
  li span {
59
62
  display: block;
@@ -18,7 +18,8 @@ export class AuthResult extends localize(i18next)(LitElement) {
18
18
  width: 100vw;
19
19
  height: 100vh;
20
20
  height: 100dvh;
21
- background-color: var(--auth-background, var(--md-sys-color-primary));
21
+ background-color: var(--md-sys-color-primary);
22
+ color: var(--md-sys-color-on-primary);
22
23
  }
23
24
  .wrap {
24
25
  display: block;
@@ -11,4 +11,8 @@ export class AuthSignin extends AbstractSign {
11
11
  get actionUrl() {
12
12
  return '/auth/signin'
13
13
  }
14
+
15
+ get autocompletable() {
16
+ return true
17
+ }
14
18
  }
@@ -47,6 +47,7 @@ export class AuthSignup extends AbstractSign {
47
47
  required
48
48
  .value=${email}
49
49
  autocapitalize="off"
50
+ autocomplete="off"
50
51
  @input=${(e: Event) => {
51
52
  const target = e.target as HTMLInputElement
52
53
  if (target.validity.typeMismatch) {
@@ -67,6 +68,7 @@ export class AuthSignup extends AbstractSign {
67
68
  helper=${this.passwordHelp || ''}
68
69
  helperPersistent
69
70
  required
71
+ autocomplete="off"
70
72
  @input=${e => {
71
73
  var val = e.target.value
72
74
  this.confirmPass.setAttribute('pattern', val.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '[$&]'))
@@ -80,6 +82,7 @@ export class AuthSignup extends AbstractSign {
80
82
  id="confirm-password"
81
83
  name="confirm-password"
82
84
  type="password"
85
+ autocomplete="off"
83
86
  label=${String(i18next.t('field.confirm password'))}
84
87
  required
85
88
  >
@@ -10,7 +10,7 @@ import { connect } from 'pwa-helpers/connect-mixin.js'
10
10
 
11
11
  import { client, gqlContext } from '@operato/graphql'
12
12
  import { i18next } from '@operato/i18n'
13
- import { PageView, store } from '@operato/shell'
13
+ import { InheritedValueType, PageView, store } from '@operato/shell'
14
14
 
15
15
  @customElement('role-management')
16
16
  class RoleManagement extends connect(store)(PageView) {
@@ -105,8 +105,8 @@ class RoleManagement extends connect(store)(PageView) {
105
105
  async fetchRoles() {
106
106
  const response = await client.query({
107
107
  query: gql`
108
- query roles($sortings: [Sorting!]) {
109
- roles(sortings: $sortings) {
108
+ query roles($sortings: [Sorting!], $inherited: InheritedValueType) {
109
+ roles(sortings: $sortings, inherited: $inherited) {
110
110
  items {
111
111
  id
112
112
  name
@@ -120,7 +120,10 @@ class RoleManagement extends connect(store)(PageView) {
120
120
  }
121
121
  }
122
122
  `,
123
- variables: { sortings: [{ name: 'name' }] },
123
+ variables: {
124
+ sortings: [{ name: 'name' }],
125
+ inherited: InheritedValueType.Only
126
+ },
124
127
  context: gqlContext()
125
128
  })
126
129
 
@@ -2,7 +2,8 @@ import { css } from 'lit';
2
2
  export const AUTH_STYLE_SIGN = css `
3
3
  :host {
4
4
  display: flex;
5
- background-color: var(--auth-background, var(--md-sys-color-primary));
5
+ background-color: var(--md-sys-color-primary);
6
+ color: var(--md-sys-color-on-primary);
6
7
  }
7
8
 
8
9
  :host *:focus {
@@ -1 +1 @@
1
- {"version":3,"file":"auth-style-sign.js","sourceRoot":"","sources":["../client/auth-style-sign.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4LjC,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport const AUTH_STYLE_SIGN = css`\n :host {\n display: flex;\n background-color: var(--auth-background, var(--md-sys-color-primary));\n }\n\n :host *:focus {\n outline: none;\n }\n\n :host * {\n box-sizing: border-box;\n }\n\n .wrap {\n display: block;\n width: 450px;\n min-width: 350px;\n margin: 0 auto;\n padding-bottom: 100px;\n text-align: center;\n }\n\n .auth-brand {\n color: var(--md-sys-color-on-primary);\n }\n\n .auth-brand img {\n margin: 15% auto 5px auto;\n width: 100px;\n border: 3px solid var(--md-sys-color-on-primary);\n border-radius: 25px;\n box-shadow: var(--box-shadow);\n }\n .name {\n display: block;\n font: var(--auth-brand-name);\n text-shadow: var(--auth-brand-name-shadow);\n }\n .auth-brand .welcome-msg {\n font: var(--auth-brand-welcome-msg);\n }\n .auth-form {\n display: grid;\n grid-gap: var(--margin-default);\n grid-template-columns: 1fr 1fr;\n }\n\n form {\n grid-column: 1 / -1;\n display: grid;\n grid-template-columns: 1fr 1fr;\n grid-gap: var(--margin-default);\n align-items: center;\n }\n\n h3 {\n grid-column: 1 / -1;\n margin: 50px 0 0 0;\n font: var(--auth-title-font);\n color: var(--auth-title-color, var(--md-sys-color-on-primary));\n text-transform: uppercase;\n }\n\n .field {\n grid-column: 1 / -1;\n text-align: left;\n }\n\n .submit-buttons-container {\n grid-column: 1 / -1;\n text-align: center;\n\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .fingerprint {\n color: var(--md-sys-color-on-primary);\n border: 1.5px solid var(--md-sys-color-on-primary);\n border-radius: 20%;\n width: 36px;\n height: 36px;\n }\n\n .field md-filled-text-field {\n grid-column: 1 / -1;\n width: 100%;\n }\n\n md-text-button,\n md-elevated-button {\n grid-column: 1 / -1;\n flex: 1;\n }\n\n .wrap .link {\n text-decoration: none;\n justify-self: flex-start;\n }\n\n .wrap .link md-text-button {\n --md-text-button-label-text-color: var(--md-sys-color-on-primary);\n --md-text-button-focus-label-text-color: var(--md-sys-color-on-primary);\n --md-text-button-hover-label-text-color: var(--md-sys-color-on-primary);\n }\n\n .wrap .link md-icon {\n color: var(--md-sys-color-on-primary);\n }\n\n #locale-area {\n display: flex;\n grid-column: 1 / -1;\n padding: 0 var(--padding-default);\n }\n\n #locale-area > label {\n display: flex;\n align-items: center;\n color: var(--md-sys-color-on-primary);\n --md-icon-size: 16px;\n }\n\n #locale-selector {\n font-size: 16px;\n width: 100%;\n }\n\n #locale-selector {\n --i18n-selector-field-border: none;\n --i18n-selector-field-background-color: none;\n --i18n-selector-field-font-size: 14px;\n --i18n-selector-field-color: var(--md-sys-color-on-primary);\n }\n\n .lottie-container {\n width: 100%;\n height: 300px;\n position: absolute;\n left: 0;\n bottom: 0;\n pointer-events: none;\n }\n .lottie-container lottie-player {\n position: absolute;\n bottom: -6%;\n width: 100%;\n height: auto;\n }\n\n @media (max-width: 450px) {\n .wrap {\n width: 85%;\n min-width: 320px;\n }\n .auth-form {\n grid-template-columns: 1fr;\n }\n .auth-brand img {\n margin: 12% auto 5px auto;\n width: 75px;\n }\n h3 {\n margin: 15px 0 0 0;\n }\n .lottie-container {\n overflow: hidden;\n height: 200px;\n pointer-events: none;\n }\n .lottie-container lottie-player {\n width: 1200px;\n left: -30%;\n }\n }\n\n @media screen and (min-width: 1400px) {\n .wrap {\n padding-bottom: 150px;\n }\n }\n @media screen and (min-width: 2500px) {\n .wrap {\n padding-bottom: 280px;\n }\n }\n`\n"]}
1
+ {"version":3,"file":"auth-style-sign.js","sourceRoot":"","sources":["../client/auth-style-sign.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6LjC,CAAA","sourcesContent":["import { css } from 'lit'\n\nexport const AUTH_STYLE_SIGN = css`\n :host {\n display: flex;\n background-color: var(--md-sys-color-primary);\n color: var(--md-sys-color-on-primary);\n }\n\n :host *:focus {\n outline: none;\n }\n\n :host * {\n box-sizing: border-box;\n }\n\n .wrap {\n display: block;\n width: 450px;\n min-width: 350px;\n margin: 0 auto;\n padding-bottom: 100px;\n text-align: center;\n }\n\n .auth-brand {\n color: var(--md-sys-color-on-primary);\n }\n\n .auth-brand img {\n margin: 15% auto 5px auto;\n width: 100px;\n border: 3px solid var(--md-sys-color-on-primary);\n border-radius: 25px;\n box-shadow: var(--box-shadow);\n }\n .name {\n display: block;\n font: var(--auth-brand-name);\n text-shadow: var(--auth-brand-name-shadow);\n }\n .auth-brand .welcome-msg {\n font: var(--auth-brand-welcome-msg);\n }\n .auth-form {\n display: grid;\n grid-gap: var(--margin-default);\n grid-template-columns: 1fr 1fr;\n }\n\n form {\n grid-column: 1 / -1;\n display: grid;\n grid-template-columns: 1fr 1fr;\n grid-gap: var(--margin-default);\n align-items: center;\n }\n\n h3 {\n grid-column: 1 / -1;\n margin: 50px 0 0 0;\n font: var(--auth-title-font);\n color: var(--auth-title-color, var(--md-sys-color-on-primary));\n text-transform: uppercase;\n }\n\n .field {\n grid-column: 1 / -1;\n text-align: left;\n }\n\n .submit-buttons-container {\n grid-column: 1 / -1;\n text-align: center;\n\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .fingerprint {\n color: var(--md-sys-color-on-primary);\n border: 1.5px solid var(--md-sys-color-on-primary);\n border-radius: 20%;\n width: 36px;\n height: 36px;\n }\n\n .field md-filled-text-field {\n grid-column: 1 / -1;\n width: 100%;\n }\n\n md-text-button,\n md-elevated-button {\n grid-column: 1 / -1;\n flex: 1;\n }\n\n .wrap .link {\n text-decoration: none;\n justify-self: flex-start;\n }\n\n .wrap .link md-text-button {\n --md-text-button-label-text-color: var(--md-sys-color-on-primary);\n --md-text-button-focus-label-text-color: var(--md-sys-color-on-primary);\n --md-text-button-hover-label-text-color: var(--md-sys-color-on-primary);\n }\n\n .wrap .link md-icon {\n color: var(--md-sys-color-on-primary);\n }\n\n #locale-area {\n display: flex;\n grid-column: 1 / -1;\n padding: 0 var(--padding-default);\n }\n\n #locale-area > label {\n display: flex;\n align-items: center;\n color: var(--md-sys-color-on-primary);\n --md-icon-size: 16px;\n }\n\n #locale-selector {\n font-size: 16px;\n width: 100%;\n }\n\n #locale-selector {\n --i18n-selector-field-border: none;\n --i18n-selector-field-background-color: none;\n --i18n-selector-field-font-size: 14px;\n --i18n-selector-field-color: var(--md-sys-color-on-primary);\n }\n\n .lottie-container {\n width: 100%;\n height: 300px;\n position: absolute;\n left: 0;\n bottom: 0;\n pointer-events: none;\n }\n .lottie-container lottie-player {\n position: absolute;\n bottom: -6%;\n width: 100%;\n height: auto;\n }\n\n @media (max-width: 450px) {\n .wrap {\n width: 85%;\n min-width: 320px;\n }\n .auth-form {\n grid-template-columns: 1fr;\n }\n .auth-brand img {\n margin: 12% auto 5px auto;\n width: 75px;\n }\n h3 {\n margin: 15px 0 0 0;\n }\n .lottie-container {\n overflow: hidden;\n height: 200px;\n pointer-events: none;\n }\n .lottie-container lottie-player {\n width: 1200px;\n left: -30%;\n }\n }\n\n @media screen and (min-width: 1400px) {\n .wrap {\n padding-bottom: 150px;\n }\n }\n @media screen and (min-width: 2500px) {\n .wrap {\n padding-bottom: 280px;\n }\n }\n`\n"]}
@@ -16,6 +16,7 @@ export declare abstract class AbstractAuthPage extends AbstractAuthPage_base {
16
16
  redirectTo?: string;
17
17
  formEl: HTMLFormElement;
18
18
  private _applicationMeta?;
19
+ get autocompletable(): boolean;
19
20
  render(): import("lit-html").TemplateResult<1>;
20
21
  firstUpdated(): void;
21
22
  updated(changed: any): void;
@@ -14,6 +14,9 @@ import { ScrollbarStyles } from '@operato/styles';
14
14
  import { isSafari } from '@operato/utils';
15
15
  import { AUTH_STYLE_SIGN } from '../auth-style-sign.js';
16
16
  export class AbstractAuthPage extends localize(i18next)(LitElement) {
17
+ get autocompletable() {
18
+ return false;
19
+ }
17
20
  render() {
18
21
  const { disableUserFavoredLanguage, languages } = this.data || {};
19
22
  var { icon, title, description } = this.applicationMeta;
@@ -33,6 +36,7 @@ export class AbstractAuthPage extends localize(i18next)(LitElement) {
33
36
  id="form"
34
37
  action="${this.actionUrl}"
35
38
  method="post"
39
+ autocomplete=${this.autocompletable ? "on" : "off"}
36
40
  @keypress=${e => {
37
41
  if (e.key == 'Enter')
38
42
  this._onSubmit(e);
@@ -107,7 +111,7 @@ export class AbstractAuthPage extends localize(i18next)(LitElement) {
107
111
  label=${String(i18next.t('field.email'))}
108
112
  required
109
113
  .value=${email}
110
- autocomplete="username"
114
+ autocomplete="off"
111
115
  autocapitalize="off"
112
116
  ><md-icon slot="leading-icon">mail</md-icon></md-filled-text-field
113
117
  >
@@ -117,7 +121,7 @@ export class AbstractAuthPage extends localize(i18next)(LitElement) {
117
121
  name="password"
118
122
  type="password"
119
123
  label=${String(i18next.t('field.password'))}
120
- autocomplete="current-password"
124
+ autocomplete="off"
121
125
  required
122
126
  ><md-icon slot="leading-icon">vpn_key</md-icon></md-filled-text-field
123
127
  >