@things-factory/auth-ui 7.0.1-alpha.9 → 7.0.1-alpha.90

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 (166) hide show
  1. package/client/auth-style-sign.ts +29 -18
  2. package/client/bootstrap.ts +6 -10
  3. package/client/components/abstract-auth-page.ts +41 -22
  4. package/client/components/abstract-password-reset.ts +11 -9
  5. package/client/components/abstract-sign.ts +138 -0
  6. package/client/components/change-password.ts +3 -3
  7. package/client/components/contact-us.ts +19 -23
  8. package/client/components/create-domain-popup.ts +11 -7
  9. package/client/components/create-role.ts +8 -20
  10. package/client/components/create-user.ts +8 -16
  11. package/client/components/credential-manager.ts +64 -0
  12. package/client/components/delete-user-popup.ts +1 -1
  13. package/client/components/domain-switch.ts +7 -4
  14. package/client/components/invite-customer.ts +7 -14
  15. package/client/components/invite-user.ts +2 -7
  16. package/client/components/ownership-transfer-popup.ts +3 -3
  17. package/client/components/partner-role-editor.ts +10 -16
  18. package/client/components/profile-component.ts +124 -7
  19. package/client/components/role-privilege-editor.ts +10 -17
  20. package/client/components/user-role-editor.ts +27 -38
  21. package/client/entries/auth/activate.ts +17 -17
  22. package/client/entries/auth/checkin.ts +15 -19
  23. package/client/entries/auth/forgot-password.ts +8 -6
  24. package/client/entries/auth/result.ts +13 -12
  25. package/client/entries/auth/signup.ts +20 -24
  26. package/client/entries/oauth2/oauth2-decision-error-page.ts +2 -2
  27. package/client/entries/oauth2/oauth2-decision-page.ts +60 -55
  28. package/client/entries/public/home.ts +40 -18
  29. package/client/index.ts +93 -66
  30. package/client/pages/app-binding/app-binding.ts +5 -9
  31. package/client/pages/app-binding/app-bindings.ts +2 -2
  32. package/client/pages/appliance/appliance.ts +6 -9
  33. package/client/pages/appliance/home.ts +3 -3
  34. package/client/pages/appliance/register.ts +1 -1
  35. package/client/pages/application/application.ts +29 -50
  36. package/client/pages/application/applications.ts +4 -12
  37. package/client/pages/application/register.ts +1 -1
  38. package/client/pages/attribute/attribute-set-item-list.ts +8 -22
  39. package/client/pages/attribute/attribute-set-management.ts +14 -20
  40. package/client/pages/auth-provider/auth-provider-management.ts +10 -16
  41. package/client/pages/domain/domain-management.ts +4 -9
  42. package/client/pages/user/user-management.ts +5 -5
  43. package/dist-client/auth-style-sign.js +29 -18
  44. package/dist-client/auth-style-sign.js.map +1 -1
  45. package/dist-client/bootstrap.d.ts +1 -1
  46. package/dist-client/bootstrap.js +5 -5
  47. package/dist-client/bootstrap.js.map +1 -1
  48. package/dist-client/components/abstract-auth-page.d.ts +4 -4
  49. package/dist-client/components/abstract-auth-page.js +40 -22
  50. package/dist-client/components/abstract-auth-page.js.map +1 -1
  51. package/dist-client/components/abstract-password-reset.d.ts +3 -2
  52. package/dist-client/components/abstract-password-reset.js +10 -9
  53. package/dist-client/components/abstract-password-reset.js.map +1 -1
  54. package/dist-client/components/abstract-sign.d.ts +3 -0
  55. package/dist-client/components/abstract-sign.js +110 -0
  56. package/dist-client/components/abstract-sign.js.map +1 -1
  57. package/dist-client/components/change-password.js +3 -3
  58. package/dist-client/components/change-password.js.map +1 -1
  59. package/dist-client/components/contact-us.d.ts +4 -4
  60. package/dist-client/components/contact-us.js +18 -23
  61. package/dist-client/components/contact-us.js.map +1 -1
  62. package/dist-client/components/create-domain-popup.d.ts +1 -1
  63. package/dist-client/components/create-domain-popup.js +11 -7
  64. package/dist-client/components/create-domain-popup.js.map +1 -1
  65. package/dist-client/components/create-role.d.ts +1 -1
  66. package/dist-client/components/create-role.js +7 -19
  67. package/dist-client/components/create-role.js.map +1 -1
  68. package/dist-client/components/create-user.js +6 -14
  69. package/dist-client/components/create-user.js.map +1 -1
  70. package/dist-client/components/credential-manager.d.ts +11 -0
  71. package/dist-client/components/credential-manager.js +64 -0
  72. package/dist-client/components/credential-manager.js.map +1 -0
  73. package/dist-client/components/delete-user-popup.js +1 -1
  74. package/dist-client/components/delete-user-popup.js.map +1 -1
  75. package/dist-client/components/domain-switch.d.ts +2 -0
  76. package/dist-client/components/domain-switch.js +9 -4
  77. package/dist-client/components/domain-switch.js.map +1 -1
  78. package/dist-client/components/invite-customer.d.ts +1 -1
  79. package/dist-client/components/invite-customer.js +5 -8
  80. package/dist-client/components/invite-customer.js.map +1 -1
  81. package/dist-client/components/invite-user.js +2 -7
  82. package/dist-client/components/invite-user.js.map +1 -1
  83. package/dist-client/components/ownership-transfer-popup.d.ts +1 -1
  84. package/dist-client/components/ownership-transfer-popup.js +3 -3
  85. package/dist-client/components/ownership-transfer-popup.js.map +1 -1
  86. package/dist-client/components/partner-role-editor.d.ts +1 -1
  87. package/dist-client/components/partner-role-editor.js +10 -16
  88. package/dist-client/components/partner-role-editor.js.map +1 -1
  89. package/dist-client/components/profile-component.d.ts +6 -0
  90. package/dist-client/components/profile-component.js +111 -7
  91. package/dist-client/components/profile-component.js.map +1 -1
  92. package/dist-client/components/role-privilege-editor.js +10 -17
  93. package/dist-client/components/role-privilege-editor.js.map +1 -1
  94. package/dist-client/components/user-role-editor.d.ts +2 -0
  95. package/dist-client/components/user-role-editor.js +26 -37
  96. package/dist-client/components/user-role-editor.js.map +1 -1
  97. package/dist-client/entries/auth/activate.d.ts +2 -1
  98. package/dist-client/entries/auth/activate.js +16 -17
  99. package/dist-client/entries/auth/activate.js.map +1 -1
  100. package/dist-client/entries/auth/checkin.d.ts +2 -2
  101. package/dist-client/entries/auth/checkin.js +13 -16
  102. package/dist-client/entries/auth/checkin.js.map +1 -1
  103. package/dist-client/entries/auth/forgot-password.d.ts +2 -1
  104. package/dist-client/entries/auth/forgot-password.js +7 -6
  105. package/dist-client/entries/auth/forgot-password.js.map +1 -1
  106. package/dist-client/entries/auth/result.d.ts +2 -2
  107. package/dist-client/entries/auth/result.js +12 -12
  108. package/dist-client/entries/auth/result.js.map +1 -1
  109. package/dist-client/entries/auth/signup.js +19 -24
  110. package/dist-client/entries/auth/signup.js.map +1 -1
  111. package/dist-client/entries/oauth2/oauth2-decision-error-page.d.ts +1 -1
  112. package/dist-client/entries/oauth2/oauth2-decision-error-page.js +2 -2
  113. package/dist-client/entries/oauth2/oauth2-decision-error-page.js.map +1 -1
  114. package/dist-client/entries/oauth2/oauth2-decision-page.d.ts +1 -1
  115. package/dist-client/entries/oauth2/oauth2-decision-page.js +59 -54
  116. package/dist-client/entries/oauth2/oauth2-decision-page.js.map +1 -1
  117. package/dist-client/entries/public/home.d.ts +3 -2
  118. package/dist-client/entries/public/home.js +40 -18
  119. package/dist-client/entries/public/home.js.map +1 -1
  120. package/dist-client/index.d.ts +11 -2
  121. package/dist-client/index.js +74 -66
  122. package/dist-client/index.js.map +1 -1
  123. package/dist-client/pages/app-binding/app-binding.d.ts +1 -1
  124. package/dist-client/pages/app-binding/app-binding.js +4 -9
  125. package/dist-client/pages/app-binding/app-binding.js.map +1 -1
  126. package/dist-client/pages/app-binding/app-bindings.js +2 -2
  127. package/dist-client/pages/app-binding/app-bindings.js.map +1 -1
  128. package/dist-client/pages/appliance/appliance.d.ts +1 -1
  129. package/dist-client/pages/appliance/appliance.js +5 -9
  130. package/dist-client/pages/appliance/appliance.js.map +1 -1
  131. package/dist-client/pages/appliance/home.js +3 -3
  132. package/dist-client/pages/appliance/home.js.map +1 -1
  133. package/dist-client/pages/appliance/register.js +1 -1
  134. package/dist-client/pages/appliance/register.js.map +1 -1
  135. package/dist-client/pages/application/application.d.ts +1 -1
  136. package/dist-client/pages/application/application.js +28 -50
  137. package/dist-client/pages/application/application.js.map +1 -1
  138. package/dist-client/pages/application/applications.js +4 -12
  139. package/dist-client/pages/application/applications.js.map +1 -1
  140. package/dist-client/pages/application/register.js +1 -1
  141. package/dist-client/pages/application/register.js.map +1 -1
  142. package/dist-client/pages/attribute/attribute-set-item-list.d.ts +1 -1
  143. package/dist-client/pages/attribute/attribute-set-item-list.js +6 -22
  144. package/dist-client/pages/attribute/attribute-set-item-list.js.map +1 -1
  145. package/dist-client/pages/attribute/attribute-set-management.d.ts +11 -2
  146. package/dist-client/pages/attribute/attribute-set-management.js +7 -11
  147. package/dist-client/pages/attribute/attribute-set-management.js.map +1 -1
  148. package/dist-client/pages/auth-provider/auth-provider-management.d.ts +11 -2
  149. package/dist-client/pages/auth-provider/auth-provider-management.js +8 -11
  150. package/dist-client/pages/auth-provider/auth-provider-management.js.map +1 -1
  151. package/dist-client/pages/domain/domain-management.d.ts +1 -0
  152. package/dist-client/pages/domain/domain-management.js +3 -7
  153. package/dist-client/pages/domain/domain-management.js.map +1 -1
  154. package/dist-client/pages/user/user-management.d.ts +1 -0
  155. package/dist-client/pages/user/user-management.js +4 -5
  156. package/dist-client/pages/user/user-management.js.map +1 -1
  157. package/dist-client/tsconfig.tsbuildinfo +1 -1
  158. package/dist-server/tsconfig.tsbuildinfo +1 -1
  159. package/package.json +8 -12
  160. package/translations/en.json +1 -0
  161. package/translations/ja.json +1 -0
  162. package/translations/ko.json +1 -0
  163. package/translations/ms.json +1 -0
  164. package/translations/zh.json +1 -0
  165. package/views/auth-page.html +3 -2
  166. package/views/oauth2-page.html +3 -2
@@ -1,4 +1,4 @@
1
- import '@material/mwc-textfield'
1
+ import '@material/web/textfield/filled-text-field.js'
2
2
 
3
3
  import gql from 'graphql-tag'
4
4
  import { css, html, LitElement } from 'lit'
@@ -12,7 +12,7 @@ import { OxPrompt } from '@operato/popup/ox-prompt.js'
12
12
  class CreateRole extends localize(i18next)(LitElement) {
13
13
  static styles = css`
14
14
  :host {
15
- --mdc-text-field-fill-color: var(--theme-white-color);
15
+ --md-text-field-fill-color: var(--theme-white-color);
16
16
  background-color: var(--theme-white-color);
17
17
  margin: var(--margin-wide) 0;
18
18
  padding: var(--padding-wide);
@@ -28,7 +28,7 @@ class CreateRole extends localize(i18next)(LitElement) {
28
28
  align-items: center;
29
29
  }
30
30
 
31
- mwc-button {
31
+ md-outlined-button {
32
32
  margin: var(--input-margin);
33
33
  }
34
34
 
@@ -37,7 +37,7 @@ class CreateRole extends localize(i18next)(LitElement) {
37
37
  grid-template-columns: 1fr 1fr;
38
38
  }
39
39
 
40
- mwc-button {
40
+ md-outlined-button {
41
41
  grid-column: span 2;
42
42
 
43
43
  margin: var(--input-margin);
@@ -50,22 +50,10 @@ class CreateRole extends localize(i18next)(LitElement) {
50
50
 
51
51
  render() {
52
52
  return html`
53
- <mwc-textfield
54
- type="text"
55
- name="name"
56
- label=${String(i18next.t('label.x name', { x: i18next.t('label.role') }))}
57
- ></mwc-textfield>
58
- <mwc-textfield
59
- type="text"
60
- name="description"
61
- label=${String(i18next.t('label.x description', { x: i18next.t('label.role') }))}
62
- ></mwc-textfield>
63
-
64
- <mwc-button
65
- @click=${this.onCreateRole.bind(this)}
66
- outlined
67
- label=${String(i18next.t('button.create'))}
68
- ></mwc-button>
53
+ <md-filled-text-field type="text" name="name" label=${String(i18next.t('label.x name', { x: i18next.t('label.role') }))}></md-filled-text-field>
54
+ <md-filled-text-field type="text" name="description" label=${String(i18next.t('label.x description', { x: i18next.t('label.role') }))}></md-filled-text-field>
55
+
56
+ <md-outlined-button @click=${this.onCreateRole.bind(this)}>${String(i18next.t('button.create'))}</md-outlined-button>
69
57
  `
70
58
  }
71
59
 
@@ -7,7 +7,7 @@ import { i18next } from '@operato/i18n'
7
7
  class CreateUser extends LitElement {
8
8
  static styles = css`
9
9
  :host {
10
- --mdc-text-field-fill-color: var(--theme-white-color);
10
+ --md-text-field-fill-color: var(--theme-white-color);
11
11
  background-color: var(--theme-white-color);
12
12
  margin: var(--margin-wide) 0;
13
13
  padding: var(--padding-wide);
@@ -23,7 +23,7 @@ class CreateUser extends LitElement {
23
23
  align-items: center;
24
24
  }
25
25
 
26
- mwc-button {
26
+ md-outlined-button {
27
27
  margin: var(--input-margin);
28
28
  }
29
29
 
@@ -32,7 +32,7 @@ class CreateUser extends LitElement {
32
32
  grid-template-columns: 1fr 1fr;
33
33
  }
34
34
 
35
- mwc-button {
35
+ md-outlined-button {
36
36
  grid-column: span 2;
37
37
 
38
38
  margin: var(--input-margin);
@@ -45,19 +45,11 @@ class CreateUser extends LitElement {
45
45
 
46
46
  render() {
47
47
  return html`
48
- <mwc-textfield
49
- type="text"
50
- name="name"
51
- label=${String(i18next.t('label.x name', { x: i18next.t('label.user') }))}
52
- ></mwc-textfield>
53
-
54
- <mwc-textfield type="email" name="email" label=${String(i18next.t('field.email'))}></mwc-textfield>
55
-
56
- <mwc-button
57
- @click=${this.onCreateUser.bind(this)}
58
- outlined
59
- label=${String(i18next.t('button.create'))}
60
- ></mwc-button>
48
+ <md-outlined-text-field type="text" name="name" label=${String(i18next.t('label.x name', { x: i18next.t('label.user') }))}></md-outlined-text-field>
49
+
50
+ <md-outlined-text-field type="email" name="email" label=${String(i18next.t('field.email'))}></md-outlined-text-field>
51
+
52
+ <md-outlined-button @click=${this.onCreateUser.bind(this)}>${String(i18next.t('button.create'))}</md-outlined-button>
61
53
  `
62
54
  }
63
55
 
@@ -0,0 +1,64 @@
1
+ import gql from 'graphql-tag'
2
+ import { LitElement, html, css } from 'lit'
3
+ import { customElement, property } from 'lit/decorators.js'
4
+
5
+ import { client } from '@operato/graphql'
6
+
7
+ @customElement('credential-manager')
8
+ export class CredentialManager extends LitElement {
9
+ @property({ type: Array }) credentials: {
10
+ credentialId: string
11
+ }[] = []
12
+
13
+ static styles = css`
14
+ div {
15
+ margin: 20px;
16
+ }
17
+ button {
18
+ margin: 5px;
19
+ }
20
+ `
21
+
22
+ connectedCallback() {
23
+ super.connectedCallback()
24
+ this.fetchCredentials()
25
+ }
26
+
27
+ async fetchCredentials() {
28
+ const response = await fetch('/auth/credentials', {
29
+ method: 'GET',
30
+ credentials: 'include'
31
+ })
32
+ this.credentials = await response.json()
33
+ }
34
+
35
+ async deleteCredential(credentialId: string) {
36
+ const response = await fetch(`/credentials/${credentialId}`, {
37
+ method: 'DELETE',
38
+ credentials: 'include'
39
+ })
40
+ if (response.ok) {
41
+ this.fetchCredentials()
42
+ } else {
43
+ console.error('Failed to delete credential')
44
+ }
45
+ }
46
+
47
+ render() {
48
+ return html`
49
+ <div>
50
+ <h2>Manage Your WebAuthn Credentials</h2>
51
+ <ul>
52
+ ${this.credentials.map(
53
+ credential => html`
54
+ <li>
55
+ ${credential.credentialId}
56
+ <button @click=${() => this.deleteCredential(credential.credentialId)}>Delete</button>
57
+ </li>
58
+ `
59
+ )}
60
+ </ul>
61
+ </div>
62
+ `
63
+ }
64
+ }
@@ -5,7 +5,7 @@ import { customElement } from 'lit/decorators.js'
5
5
 
6
6
  import { i18next, localize } from '@operato/i18n'
7
7
  import { notify } from '@operato/layout'
8
- import { auth } from '@things-factory/auth-base/dist-client'
8
+ import { auth } from '@things-factory/auth-base/dist-client/auth.js'
9
9
 
10
10
  @customElement('delete-user-popup')
11
11
  export class DeleteUserPopup extends localize(i18next)(LitElement) {
@@ -1,4 +1,6 @@
1
- import { css, html, LitElement } from 'lit'
1
+ import '@material/web/icon/icon.js'
2
+
3
+ import { css, html, LitElement, nothing } from 'lit'
2
4
  import { customElement, property } from 'lit/decorators.js'
3
5
  import { connect } from 'pwa-helpers/connect-mixin'
4
6
 
@@ -28,7 +30,7 @@ export class DomainSwitch extends connect(store)(LitElement) {
28
30
  padding: var(--padding-default);
29
31
  }
30
32
 
31
- mwc-icon {
33
+ md-icon {
32
34
  background-color: rgba(var(--primary-color-rgb), 0.8);
33
35
  margin-right: var(--margin-narrow);
34
36
  padding: 2px;
@@ -56,7 +58,7 @@ export class DomainSwitch extends connect(store)(LitElement) {
56
58
  padding: 0 !important;
57
59
  border-bottom: none;
58
60
  }
59
- :host([dark]) mwc-icon {
61
+ :host([dark]) md-icon {
60
62
  background-color: var(--secondary-text-color);
61
63
  margin: 1px 4px 0px 0px;
62
64
  padding: 1px 2px;
@@ -90,6 +92,7 @@ export class DomainSwitch extends connect(store)(LitElement) {
90
92
  @property({ type: Array }) domains: any[] = []
91
93
  @property({ type: Object }) domain: any
92
94
  @property({ type: String, attribute: true }) attrname: string = 'name'
95
+ @property({ type: String, attribute: true }) icon?: string
93
96
 
94
97
  render() {
95
98
  const domains = this.domains || []
@@ -98,7 +101,7 @@ export class DomainSwitch extends connect(store)(LitElement) {
98
101
 
99
102
  return html`
100
103
  <div>
101
- <mwc-icon>outlined_flag</mwc-icon>
104
+ ${this.icon ? html`<md-icon>${this.icon}</md-icon>` : nothing}
102
105
  ${domains.length <= 1
103
106
  ? html` <span>${domains[0]?.[attrname] || domain.name}</span> `
104
107
  : html`
@@ -1,5 +1,3 @@
1
- import '@things-factory/auth-ui'
2
-
3
1
  import gql from 'graphql-tag'
4
2
  import { css, html, LitElement } from 'lit'
5
3
  import { customElement, property, query } from 'lit/decorators.js'
@@ -20,7 +18,7 @@ class InviteCustomer extends localize(i18next)(LitElement) {
20
18
  min-width: 250px;
21
19
  font: var(--input-font);
22
20
  }
23
- mwc-button {
21
+ md-outlined-button {
24
22
  margin: var(--input-margin);
25
23
  }
26
24
  @media screen and (max-width: 480px) {
@@ -39,25 +37,20 @@ class InviteCustomer extends localize(i18next)(LitElement) {
39
37
  return html`
40
38
  <div>
41
39
  <input id="customer-name" required />
42
- <mwc-button
43
- @click=${this.invite.bind(this)}
44
- outlined
45
- icon="group_add"
46
- label=${String(i18next.t('label.invite customer'))}
47
- ></mwc-button>
40
+ <md-outlined-button @click=${this.invite.bind(this)}>
41
+ <md-icon slot="icon">group_add</md-icon>
42
+ ${String(i18next.t('label.invite customer'))}
43
+ </md-outlined-button>
48
44
  </div>
49
45
  `
50
46
  }
51
47
 
52
48
  async invite() {
53
49
  try {
54
- if (!this.customerNameInput.value)
55
- throw new Error(i18next.t('error.value is empty', { value: i18next.t('field.name') }))
50
+ if (!this.customerNameInput.value) throw new Error(i18next.t('error.value is empty', { value: i18next.t('field.name') }))
56
51
 
57
52
  if (this.customers.find(c => c.name?.toLowerCase() === this.customerNameInput.value.toLowerCase())) {
58
- throw new Error(
59
- i18next.t('error.x already exists in y', { x: this.customerNameInput.value, y: i18next.t('field.customer') })
60
- )
53
+ throw new Error(i18next.t('error.x already exists in y', { x: this.customerNameInput.value, y: i18next.t('field.customer') }))
61
54
  }
62
55
 
63
56
  if (
@@ -26,7 +26,7 @@ class InviteUser extends localize(i18next)(LitElement) {
26
26
  font: var(--input-font);
27
27
  }
28
28
 
29
- mwc-button {
29
+ md-outlined-button {
30
30
  margin: var(--input-margin);
31
31
  }
32
32
  `
@@ -36,12 +36,7 @@ class InviteUser extends localize(i18next)(LitElement) {
36
36
  render() {
37
37
  return html`
38
38
  <input name="email" type="email" required name="invite-email" autocapitalize="off" />
39
- <mwc-button
40
- @click=${this.invite.bind(this)}
41
- outlined
42
- icon="group_add"
43
- label=${String(i18next.t('label.invite user'))}
44
- ></mwc-button>
39
+ <md-outlined-button @click=${this.invite.bind(this)}> <md-icon slot="icon">group_add</md-icon>${String(i18next.t('label.invite user'))} </md-outlined-button>
45
40
  `
46
41
  }
47
42
 
@@ -1,4 +1,4 @@
1
- import '@material/mwc-button'
1
+ import '@material/web/button/elevated-button.js'
2
2
 
3
3
  import gql from 'graphql-tag'
4
4
  import { css, html, LitElement } from 'lit'
@@ -36,7 +36,7 @@ class OwnershipTransferPopup extends localize(i18next)(LitElement) {
36
36
  margin: auto;
37
37
  display: flex;
38
38
  }
39
- .input-container mwc-button {
39
+ .input-container md-elevated-button {
40
40
  margin: auto 0px auto var(--padding-wide);
41
41
  }
42
42
  `
@@ -52,7 +52,7 @@ class OwnershipTransferPopup extends localize(i18next)(LitElement) {
52
52
  <div>${i18next.t('text.please enter the email of the user you want to transfer owner')}</div>
53
53
  <div class="input-container">
54
54
  <input name="email" />
55
- <mwc-button raised @click=${this.transferOwnership}>${i18next.t('button.confirm')}</mwc-button>
55
+ <md-elevated-button @click=${this.transferOwnership}>${i18next.t('button.confirm')}</md-elevated-button>
56
56
  </div>
57
57
  </div>
58
58
  `
@@ -1,5 +1,5 @@
1
1
  import '@material/mwc-icon'
2
- import '@things-factory/auth-ui'
2
+ import './role-selector.js'
3
3
 
4
4
  import gql from 'graphql-tag'
5
5
  import { css, html, LitElement } from 'lit'
@@ -8,12 +8,14 @@ import { customElement, property, query } from 'lit/decorators.js'
8
8
  import { client, gqlContext } from '@operato/graphql'
9
9
  import { i18next, localize } from '@operato/i18n'
10
10
  import { OxPrompt } from '@operato/popup/ox-prompt.js'
11
+ import { ButtonContainerStyles } from '@operato/styles'
11
12
 
12
13
  import { RoleSelector } from './role-selector'
13
14
 
14
15
  @customElement('partner-role-editor')
15
16
  class PartnerRoleEditor extends localize(i18next)(LitElement) {
16
17
  static styles = [
18
+ ButtonContainerStyles,
17
19
  css`
18
20
  :host {
19
21
  display: flex;
@@ -24,16 +26,13 @@ class PartnerRoleEditor extends localize(i18next)(LitElement) {
24
26
  font: normal 15px var(--theme-font);
25
27
  color: var(--secondary-color);
26
28
  }
27
- [buttons] {
28
- margin: 0;
29
- padding: var(--padding-default);
30
- background-color: rgba(var(--primary-color-rgb), 0.2);
31
- }
32
- mwc-button {
29
+
30
+ md-elevated-button {
33
31
  margin-right: var(--padding-narrow);
34
32
  }
33
+
35
34
  [danger] {
36
- --mdc-theme-primary: var(--mdc-danger-button-primary-color);
35
+ --md-theme-primary: var(--md-danger-button-primary-color);
37
36
  }
38
37
  `
39
38
  ]
@@ -51,14 +50,9 @@ class PartnerRoleEditor extends localize(i18next)(LitElement) {
51
50
  return html`
52
51
  <role-selector .roles="${roles}" .userRoles="${grantingRoles}"></role-selector>
53
52
 
54
- <div buttons>
55
- <mwc-button @click=${this.onSave} raised label="${i18next.t('button.save')}"></mwc-button>
56
- <mwc-button
57
- @click=${this.onTerminateContract}
58
- raised
59
- danger
60
- label="${i18next.t('button.terminate contract')}"
61
- ></mwc-button>
53
+ <div class="button-container">
54
+ <md-elevated-button @click=${this.onSave}>${i18next.t('button.save')}</md-elevated-button>
55
+ <md-elevated-button @click=${this.onTerminateContract} danger>${i18next.t('button.terminate contract')}</md-elevated-button>
62
56
  </div>
63
57
  `
64
58
  }
@@ -4,13 +4,15 @@ import './change-password'
4
4
  import './delete-user-popup'
5
5
  import './my-login-history'
6
6
 
7
- import { css, html, LitElement } from 'lit'
7
+ import base64url from 'base64url'
8
+ import { css, html, LitElement, nothing } from 'lit'
8
9
  import { customElement, property, query, state } from 'lit/decorators.js'
9
10
 
10
11
  import { i18next, localize } from '@operato/i18n'
11
12
  import { notify, openPopup } from '@operato/layout'
12
13
  import { auth, getLanguages } from '@things-factory/auth-base/dist-client'
13
14
 
15
+ const isAvailableWebauthn = 'PublicKeyCredential' in window
14
16
  @customElement('profile-component')
15
17
  export class ProfileComponent extends localize(i18next)(LitElement) {
16
18
  static styles = [
@@ -36,12 +38,12 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
36
38
  }
37
39
 
38
40
  input {
39
- margin: var(--change-password-field-margin);
40
- border: var(--input-field-border);
41
- padding: var(--input-padding);
41
+ margin: var(--margin-narrow) 0;
42
+ border: 1px solid rgba(0, 0, 0, 0.2);
43
+ padding: 9px;
42
44
  border-radius: var(--border-radius);
43
- font: var(--input-font);
44
- width: var(--change-password-field-width);
45
+ font: var(--auth-input-field-font);
46
+ width: var(--auth-input-field-width);
45
47
  }
46
48
  input:focus {
47
49
  border: 1px solid var(--focus-background-color);
@@ -85,7 +87,7 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
85
87
  }
86
88
 
87
89
  [danger] {
88
- --mdc-theme-primary: var(--mdc-danger-button-primary-color);
90
+ --md-theme-primary: var(--md-danger-button-primary-color);
89
91
  }
90
92
 
91
93
  footer {
@@ -160,6 +162,14 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
160
162
 
161
163
  <change-password id="change-password"></change-password>
162
164
 
165
+ ${isAvailableWebauthn
166
+ ? html`
167
+ <md-text-button @click=${() => this.registerWebAuthn()}
168
+ >${i18next.t('button.security-key registration')}</md-text-button
169
+ >
170
+ `
171
+ : nothing}
172
+
163
173
  <footer>
164
174
  <p>
165
175
  ${i18next.t('text.click login history')}
@@ -233,4 +243,111 @@ export class ProfileComponent extends localize(i18next)(LitElement) {
233
243
  title: i18next.t('label.delete account')
234
244
  })
235
245
  }
246
+
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({ email: this.email }),
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
+ /* options.user.id must be the user's email */
276
+ id: Uint8Array.from(base64url.toBuffer(options.user.id)).buffer,
277
+ name: options.user.name,
278
+ displayName: options.user.displayName
279
+ },
280
+ challenge: Uint8Array.from(atob(options.challenge), c => c.charCodeAt(0)),
281
+ pubKeyCredParams: [
282
+ {
283
+ type: 'public-key',
284
+ alg: -7 // ES256
285
+ },
286
+ {
287
+ type: 'public-key',
288
+ alg: -257 // RS256
289
+ }
290
+ ],
291
+ authenticatorSelection: {
292
+ userVerification: 'preferred'
293
+ }
294
+ }
295
+ })) as PublicKeyCredential
296
+
297
+ if (!credential) {
298
+ notify({
299
+ level: 'error',
300
+ message: 'can not get user credential'
301
+ })
302
+
303
+ return
304
+ }
305
+
306
+ const response = credential.response as AuthenticatorAttestationResponse
307
+
308
+ var body = {
309
+ response: {
310
+ clientDataJSON: Buffer.from(response.clientDataJSON).toString('base64'),
311
+ attestationObject: Buffer.from(response.attestationObject).toString('base64')
312
+ } as any
313
+ }
314
+
315
+ if (response.getTransports) {
316
+ body.response.transports = response.getTransports()
317
+ }
318
+
319
+ const signinResponse = await fetch('/auth/signin-webauthn', {
320
+ method: 'POST',
321
+ headers: {
322
+ 'Content-Type': 'application/json',
323
+ Accept: 'application/json'
324
+ },
325
+ body: JSON.stringify(body),
326
+ credentials: 'include'
327
+ })
328
+
329
+ if (!signinResponse.ok) {
330
+ notify({
331
+ level: 'error',
332
+ message: await signinResponse.text()
333
+ })
334
+ } else {
335
+ notify({
336
+ level: 'info',
337
+ message: i18next.t('text.user credential registered successfully')
338
+ })
339
+ }
340
+ }
341
+
342
+ get applicationMeta() {
343
+ var iconLink: HTMLLinkElement | null = document.querySelector('link[rel="application-icon"]')
344
+ var titleMeta: HTMLMetaElement | null = document.querySelector('meta[name="application-name"]')
345
+ var descriptionMeta: HTMLMetaElement | null = document.querySelector('meta[name="application-description"]')
346
+
347
+ return {
348
+ icon: iconLink?.href || '',
349
+ title: titleMeta?.content || 'Things Factory',
350
+ description: descriptionMeta?.content || 'Reimagining Software'
351
+ }
352
+ }
236
353
  }
@@ -5,10 +5,12 @@ import { customElement, property } from 'lit/decorators.js'
5
5
  import { client, gqlContext } from '@operato/graphql'
6
6
  import { i18next, localize } from '@operato/i18n'
7
7
  import { OxPrompt } from '@operato/popup/ox-prompt.js'
8
+ import { ButtonContainerStyles } from '@operato/styles'
8
9
 
9
10
  @customElement('role-privilege-editor')
10
11
  class RolePrivilegeEditor extends localize(i18next)(LitElement) {
11
12
  static styles = [
13
+ ButtonContainerStyles,
12
14
  css`
13
15
  :host {
14
16
  display: flex;
@@ -49,21 +51,15 @@ class RolePrivilegeEditor extends localize(i18next)(LitElement) {
49
51
  font-weight: bold;
50
52
  }
51
53
 
52
- [buttons] {
53
- margin: 0;
54
- padding: 5px;
55
- background-color: rgba(var(--primary-color-rgb), 0.2);
56
- }
57
-
58
- mwc-button {
54
+ md-elevated-button {
59
55
  margin: 5px;
60
56
  background-color: var(--theme-white-color);
61
57
  }
62
58
 
63
59
  [danger] {
64
- --mdc-theme-primary: var(--mdc-danger-button-primary-color);
60
+ --md-theme-primary: var(--md-danger-button-primary-color);
65
61
  }
66
- [outlined] {
62
+ md-outlined-button {
67
63
  background-color: var(--theme-white-color);
68
64
  }
69
65
 
@@ -106,14 +102,11 @@ class RolePrivilegeEditor extends localize(i18next)(LitElement) {
106
102
  )}
107
103
  </ul>
108
104
 
109
- <div buttons>
110
- <mwc-button @click=${e => this.onSave()} outlined label=${String(i18next.t('button.save'))}></mwc-button>
111
- <mwc-button
112
- @click=${e => this.onDeleteRole()}
113
- raised
114
- danger
115
- label=${String(i18next.t('button.delete role'))}
116
- ></mwc-button>
105
+ <div class="button-container">
106
+ <md-elevated-button @click=${e => this.onSave()}>${String(i18next.t('button.save'))}</md-elevated-button>
107
+ <md-elevated-button @click=${e => this.onDeleteRole()} danger
108
+ >${String(i18next.t('button.delete role'))}</md-elevated-button
109
+ >
117
110
  </div>
118
111
  `
119
112
  }