@things-factory/auth-ui 8.0.0-beta.8 → 8.0.0

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 (100) hide show
  1. package/client/auth-style-sign.ts +194 -0
  2. package/client/bootstrap.ts +51 -0
  3. package/client/components/abstract-auth-page.ts +301 -0
  4. package/client/components/abstract-password-reset.ts +168 -0
  5. package/client/components/abstract-sign.ts +127 -0
  6. package/client/components/change-password.ts +153 -0
  7. package/client/components/contact-us.ts +113 -0
  8. package/client/components/create-domain-popup.ts +141 -0
  9. package/client/components/create-role.ts +123 -0
  10. package/client/components/create-user.ts +95 -0
  11. package/client/components/credential-manager.ts +64 -0
  12. package/client/components/delete-user-popup.ts +117 -0
  13. package/client/components/domain-switch.ts +127 -0
  14. package/client/components/invite-customer.ts +104 -0
  15. package/client/components/invite-user.ts +96 -0
  16. package/client/components/my-login-history.ts +101 -0
  17. package/client/components/ownership-transfer-popup.ts +110 -0
  18. package/client/components/partner-info-card.ts +89 -0
  19. package/client/components/partner-role-editor.ts +153 -0
  20. package/client/components/profile-component.ts +332 -0
  21. package/client/components/role-edit-form.ts +92 -0
  22. package/client/components/role-privilege-editor.ts +267 -0
  23. package/client/components/role-selector.ts +102 -0
  24. package/client/components/user-role-editor.ts +499 -0
  25. package/client/constants/application.ts +9 -0
  26. package/client/constants/index.ts +1 -0
  27. package/client/entries/auth/activate.ts +272 -0
  28. package/client/entries/auth/checkin.ts +190 -0
  29. package/client/entries/auth/forgot-password.ts +103 -0
  30. package/client/entries/auth/reset-password.ts +22 -0
  31. package/client/entries/auth/result.ts +193 -0
  32. package/client/entries/auth/signin.ts +18 -0
  33. package/client/entries/auth/signup.ts +109 -0
  34. package/client/entries/auth/unlock-user.ts +22 -0
  35. package/client/entries/oauth2/oauth2-decision-error-page.ts +50 -0
  36. package/client/entries/oauth2/oauth2-decision-page.ts +196 -0
  37. package/client/entries/public/home.ts +246 -0
  38. package/client/index.ts +124 -0
  39. package/client/pages/app-binding/app-binding.ts +423 -0
  40. package/client/pages/app-binding/app-bindings.ts +171 -0
  41. package/client/pages/appliance/appliance.ts +452 -0
  42. package/client/pages/appliance/home.ts +177 -0
  43. package/client/pages/appliance/register.ts +183 -0
  44. package/client/pages/application/application.ts +428 -0
  45. package/client/pages/application/applications.ts +182 -0
  46. package/client/pages/application/register.ts +211 -0
  47. package/client/pages/attribute/attribute-set-item-list.ts +237 -0
  48. package/client/pages/attribute/attribute-set-management.ts +282 -0
  49. package/client/pages/auth-provider/auth-provider-management.ts +381 -0
  50. package/client/pages/domain/domain-management.ts +410 -0
  51. package/client/pages/partner/partner-management.ts +112 -0
  52. package/client/pages/profile.ts +32 -0
  53. package/client/pages/role/role-management.ts +134 -0
  54. package/client/pages/user/user-management.ts +224 -0
  55. package/client/route.ts +67 -0
  56. package/client/themes/auth-theme.css +65 -0
  57. package/client/utils/password-rule.ts +37 -0
  58. package/dist-client/components/abstract-auth-page.js +10 -10
  59. package/dist-client/components/abstract-auth-page.js.map +1 -1
  60. package/dist-client/components/abstract-password-reset.d.ts +2 -1
  61. package/dist-client/components/abstract-password-reset.js +14 -7
  62. package/dist-client/components/abstract-password-reset.js.map +1 -1
  63. package/dist-client/components/abstract-sign.js +11 -12
  64. package/dist-client/components/abstract-sign.js.map +1 -1
  65. package/dist-client/components/contact-us.d.ts +1 -1
  66. package/dist-client/components/contact-us.js +7 -10
  67. package/dist-client/components/contact-us.js.map +1 -1
  68. package/dist-client/components/create-user.js +5 -28
  69. package/dist-client/components/create-user.js.map +1 -1
  70. package/dist-client/components/invite-user.js +11 -19
  71. package/dist-client/components/invite-user.js.map +1 -1
  72. package/dist-client/components/ownership-transfer-popup.js +3 -3
  73. package/dist-client/components/ownership-transfer-popup.js.map +1 -1
  74. package/dist-client/components/profile-component.d.ts +1 -5
  75. package/dist-client/components/profile-component.js +4 -64
  76. package/dist-client/components/profile-component.js.map +1 -1
  77. package/dist-client/components/role-privilege-editor.js +1 -2
  78. package/dist-client/components/role-privilege-editor.js.map +1 -1
  79. package/dist-client/components/user-role-editor.js +18 -18
  80. package/dist-client/components/user-role-editor.js.map +1 -1
  81. package/dist-client/entries/auth/checkin.js +1 -1
  82. package/dist-client/entries/auth/checkin.js.map +1 -1
  83. package/dist-client/entries/auth/forgot-password.js +2 -11
  84. package/dist-client/entries/auth/forgot-password.js.map +1 -1
  85. package/dist-client/entries/auth/signup.js +7 -13
  86. package/dist-client/entries/auth/signup.js.map +1 -1
  87. package/dist-client/index.js +1 -1
  88. package/dist-client/index.js.map +1 -1
  89. package/dist-client/pages/user/user-management.d.ts +1 -5
  90. package/dist-client/pages/user/user-management.js +7 -6
  91. package/dist-client/pages/user/user-management.js.map +1 -1
  92. package/dist-client/tsconfig.tsbuildinfo +1 -1
  93. package/dist-server/tsconfig.tsbuildinfo +1 -1
  94. package/package.json +12 -12
  95. package/server/index.ts +0 -0
  96. package/translations/en.json +2 -6
  97. package/translations/ja.json +2 -6
  98. package/translations/ko.json +2 -6
  99. package/translations/ms.json +2 -6
  100. package/translations/zh.json +2 -6
@@ -0,0 +1,193 @@
1
+ import '@material/web/button/elevated-button.js'
2
+
3
+ import '@operato/lottie-player'
4
+ import '../../components/profile-component'
5
+
6
+ import { css, html, LitElement } from 'lit'
7
+ import { customElement, property } from 'lit/decorators.js'
8
+
9
+ import { i18next, localize } from '@operato/i18n'
10
+ import { isSafari } from '@operato/utils'
11
+
12
+ @customElement('auth-result')
13
+ export class AuthResult extends localize(i18next)(LitElement) {
14
+ static styles = [
15
+ css`
16
+ :host {
17
+ display: flex;
18
+ width: 100vw;
19
+ height: 100vh;
20
+ height: 100dvh;
21
+ background-color: var(--md-sys-color-primary);
22
+ color: var(--md-sys-color-on-primary);
23
+ }
24
+ .wrap {
25
+ display: block;
26
+ width: 450px;
27
+ min-width: 350px;
28
+ margin: 0 auto;
29
+ text-align: center;
30
+ }
31
+ .auth-brand {
32
+ color: #fff;
33
+ }
34
+ .auth-brand img {
35
+ margin: 15% auto 5px auto;
36
+ width: 100px;
37
+ border: 3px solid var(--md-sys-color-on-primary);
38
+ border-radius: 25px;
39
+ box-shadow: var(--box-shadow);
40
+ }
41
+ .name {
42
+ display: block;
43
+ font: var(--auth-brand-name);
44
+ text-shadow: var(--auth-brand-name-shadow);
45
+ }
46
+ h1 {
47
+ margin: 50px 0 0 0;
48
+ padding: 0;
49
+ font-size: 24px;
50
+ color: var(--auth-title-color, var(--md-sys-color-on-primary));
51
+ }
52
+ p {
53
+ margin: 0;
54
+ padding: var(--auth-description-padding);
55
+ font: var(--auth-description-font);
56
+ color: var(--auth-description-color, var(--md-sys-color-on-secondary));
57
+ }
58
+ :host *:focus {
59
+ outline: none;
60
+ }
61
+ .user {
62
+ background: url(/assets/images/icon-profile.png) center top no-repeat;
63
+ margin: var(--profile-icon-margin);
64
+ padding: 180px 20px 20px 20px;
65
+ color: var(--md-sys-color-secondary);
66
+ font: var(--header-bar-title);
67
+ text-align: center;
68
+ }
69
+
70
+ label {
71
+ font: bold 14px var(--theme-font);
72
+ color: var(--md-sys-color-primary);
73
+ }
74
+
75
+ #button-area {
76
+ border-top: 1px dashed rgba(0, 0, 0, 0.1);
77
+ padding-top: 10px;
78
+ }
79
+
80
+ md-elevated-button {
81
+ --md-button-horizontal-padding: var(--spacing-medium);
82
+ --md-button-ink-color: var(--md-sys-color-primary);
83
+ }
84
+
85
+ .lottie-container {
86
+ width: 100%;
87
+ height: 300px;
88
+ position: absolute;
89
+ left: 0;
90
+ bottom: 0;
91
+ pointer-events: none;
92
+ }
93
+ .lottie-container lottie-player {
94
+ position: absolute;
95
+ bottom: -6%;
96
+ width: 100%;
97
+ height: auto;
98
+ }
99
+
100
+ @media (max-width: 450px) {
101
+ .wrap {
102
+ width: 85%;
103
+ min-width: 320px;
104
+ padding-bottom: 100px;
105
+ }
106
+ .auth-form {
107
+ grid-template-columns: 1fr;
108
+ }
109
+ .auth-brand img {
110
+ margin: 12% auto 5px auto;
111
+ width: 75px;
112
+ }
113
+ .lottie-container {
114
+ overflow: hidden;
115
+ height: 200px;
116
+ pointer-events: none;
117
+ }
118
+ .lottie-container lottie-player {
119
+ width: 1200px;
120
+ left: -30%;
121
+ }
122
+ }
123
+ `
124
+ ]
125
+
126
+ @property({ type: Object }) data: any
127
+ @property({ type: String }) message?: string
128
+ @property({ type: String }) resultType?: string
129
+
130
+ private _applicationMeta?: { icon?: string; title?: string; description?: string }
131
+
132
+ updated(changed) {
133
+ if (changed.has('data')) {
134
+ this.message = this.data.message
135
+ this.resultType = this.data.resultType || 'congratulations'
136
+ }
137
+ }
138
+
139
+ render() {
140
+ var { icon, title, description } = this.applicationMeta
141
+
142
+ return html`
143
+ <div class="wrap ${this.resultType}">
144
+ <div class="auth-brand">
145
+ <img src=${icon} />
146
+ <strong class="name">${title}</strong>
147
+ <span class="welcome-msg">${description}</span>
148
+ </div>
149
+
150
+ <div id="message-area">
151
+ <h1>${this.message || ''}</h1>
152
+
153
+ <!--description message container-->
154
+ <p></p>
155
+ </div>
156
+ <div id="button-area">
157
+ <md-elevated-button
158
+ @click=${e => {
159
+ window.location.replace('/auth/signin')
160
+ }}
161
+ >
162
+ <md-icon slot="icon">home</md-icon>
163
+ ${i18next.t('button.go to home')}
164
+ </md-elevated-button>
165
+ </div>
166
+
167
+ ${isSafari()
168
+ ? html``
169
+ : html`
170
+ <div class="lottie-container">
171
+ <lottie-player autoplay loop src="../../assets/images/background-animation.json"></lottie-player>
172
+ </div>
173
+ `}
174
+ </div>
175
+ `
176
+ }
177
+
178
+ get applicationMeta() {
179
+ if (!this._applicationMeta) {
180
+ var iconLink: HTMLLinkElement | null = document.querySelector('link[rel="application-icon"]')
181
+ var titleMeta: HTMLMetaElement | null = document.querySelector('meta[name="application-name"]')
182
+ var descriptionMeta: HTMLMetaElement | null = document.querySelector('meta[name="application-description"]')
183
+
184
+ this._applicationMeta = {
185
+ icon: iconLink?.href,
186
+ title: titleMeta ? titleMeta.content : 'Things Factory',
187
+ description: descriptionMeta ? descriptionMeta.content : 'Reimagining Software'
188
+ }
189
+ }
190
+
191
+ return this._applicationMeta
192
+ }
193
+ }
@@ -0,0 +1,18 @@
1
+ import { customElement } from 'lit/decorators.js'
2
+
3
+ import { AbstractSign } from '../../components/abstract-sign'
4
+
5
+ @customElement('auth-signin')
6
+ export class AuthSignin extends AbstractSign {
7
+ get pageName() {
8
+ return 'sign in'
9
+ }
10
+
11
+ get actionUrl() {
12
+ return '/auth/signin'
13
+ }
14
+
15
+ get autocompletable() {
16
+ return true
17
+ }
18
+ }
@@ -0,0 +1,109 @@
1
+ import '@operato/i18n/ox-i18n.js'
2
+
3
+ import { html } from 'lit'
4
+ import { customElement, query } from 'lit/decorators.js'
5
+
6
+ import { i18next } from '@operato/i18n'
7
+
8
+ import { AbstractSign } from '../../components/abstract-sign'
9
+ import { generatePasswordPatternHelp, generatePasswordPatternRegExp } from '../../utils/password-rule'
10
+
11
+ @customElement('auth-signup')
12
+ export class AuthSignup extends AbstractSign {
13
+ private passwordPattern?: string
14
+ private passwordHelp?: string
15
+
16
+ get pageName() {
17
+ return 'sign up'
18
+ }
19
+
20
+ get actionUrl() {
21
+ return '/auth/signup'
22
+ }
23
+
24
+ @query('#confirm-password') confirmPass!: HTMLInputElement
25
+
26
+ get formfields() {
27
+ const { name = '', email = '' } = this.data || {}
28
+
29
+ // .validationMessage=${this.passwordHelp || ''}
30
+ // .validationMessage=${String(i18next.t('text.passwords do not match'))}
31
+
32
+ return html`
33
+ <div class="field">
34
+ <md-filled-text-field
35
+ name="name"
36
+ type="text"
37
+ label=${String(i18next.t('field.name'))}
38
+ .value=${name}
39
+ required
40
+ ></md-filled-text-field>
41
+ </div>
42
+ <div class="field">
43
+ <md-filled-text-field
44
+ name="email"
45
+ type="email"
46
+ label=${String(i18next.t('field.email'))}
47
+ required
48
+ .value=${email}
49
+ autocapitalize="off"
50
+ autocomplete="off"
51
+ @input=${(e: Event) => {
52
+ const target = e.target as HTMLInputElement
53
+ if (target.validity.typeMismatch) {
54
+ target.setCustomValidity(i18next.t('text.invalid-email'))
55
+ } else {
56
+ target.setCustomValidity('')
57
+ }
58
+ }}
59
+ ><md-icon slot="leading-icon">mail</md-icon></md-filled-text-field
60
+ >
61
+ </div>
62
+ <div class="field">
63
+ <md-filled-text-field
64
+ name="password"
65
+ type="password"
66
+ label=${String(i18next.t('field.password'))}
67
+ pattern=${this.passwordPattern || ''}
68
+ supporting-text=${this.passwordHelp || ''}
69
+ required
70
+ autocomplete="off"
71
+ @input=${e => {
72
+ var val = e.target.value
73
+ this.confirmPass.setAttribute('pattern', val.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '[$&]'))
74
+ }}
75
+ >
76
+ ></md-filled-text-field
77
+ >
78
+ </div>
79
+ <div class="field">
80
+ <md-filled-text-field
81
+ id="confirm-password"
82
+ name="confirm-password"
83
+ type="password"
84
+ autocomplete="off"
85
+ label=${String(i18next.t('field.confirm password'))}
86
+ required
87
+ >
88
+ ></md-filled-text-field
89
+ >
90
+ </div>
91
+ <md-elevated-button class="ui button" @click=${e => this._onSubmit(e)}>
92
+ <ox-i18n msgid="field.${this.pageName}"></ox-i18n>
93
+ </md-elevated-button>
94
+ `
95
+ }
96
+
97
+ languageUpdated() {
98
+ this.passwordPattern = generatePasswordPatternRegExp(this.data.passwordRule).source
99
+ this.passwordHelp = generatePasswordPatternHelp(this.data.passwordRule)
100
+ }
101
+
102
+ get links() {
103
+ return html`
104
+ <a class="link" href="/auth/signin">
105
+ <md-text-button><ox-i18n msgid="field.sign in"></ox-i18n></md-text-button>
106
+ </a>
107
+ `
108
+ }
109
+ }
@@ -0,0 +1,22 @@
1
+ import { customElement } from 'lit/decorators.js'
2
+
3
+ import { AbstractPasswordReset } from '../../components/abstract-password-reset'
4
+
5
+ @customElement('unlock-user')
6
+ export class UnlockUser extends AbstractPasswordReset {
7
+ get pageName() {
8
+ return 'unlock user'
9
+ }
10
+
11
+ get actionUrl() {
12
+ return '/auth/unlock-user'
13
+ }
14
+
15
+ get submitButtonLabel() {
16
+ return 'label.unlock account'
17
+ }
18
+
19
+ get title() {
20
+ return 'unlock account'
21
+ }
22
+ }
@@ -0,0 +1,50 @@
1
+ import '@material/web/button/elevated-button.js'
2
+
3
+ import { html, LitElement } from 'lit'
4
+ import { customElement, property } from 'lit/decorators.js'
5
+
6
+ @customElement('oauth2-decision-error')
7
+ class OAuth2DecisionErrorPage extends LitElement {
8
+ @property({ type: Object }) data: any = {}
9
+ @property({ type: Object }) user: any = {}
10
+ @property({ type: Object }) domain: any = {}
11
+ @property({ type: Object }) req: any = {}
12
+ @property({ type: String }) redirectURI: string = ''
13
+ @property({ type: String }) transactionID: string = ''
14
+
15
+ render() {
16
+ return html`
17
+ <h2>Application Binding Error</h2>
18
+
19
+ <p>Hi ${this.user.name}!</p>
20
+ <p><b>Application Not Found</b></p>
21
+ <p>You better check you give a correct client id (appKey)</p>
22
+
23
+ <p>Company: ${this.domain.subdomain}</p>
24
+ <p>Application Client ID : ${this.req.clientID}</p>
25
+ <p>Redirect URI : ${this.redirectURI}</p>
26
+ <p>State : ${this.req.state}</p>
27
+
28
+ <form>
29
+ <input name="transaction_id" value="${this.transactionID}" hidden />
30
+ <input name="subdomain" type="text" value="${this.domain.subdomain}" hidden />
31
+ <input name="appKey" value="${this.req.clientID}" hidden />
32
+ <input name="scope" value="${this.req.scope}" hidden />
33
+ <input name="state" type="text" value="${this.req.state}" hidden />
34
+ <input name="email" type="text" value="${this.user.email}" hidden />
35
+
36
+ <md-elevated-button raised @click="${() => history.back()}">Move back</md-elevated-button>
37
+ </form>
38
+ `
39
+ }
40
+
41
+ updated(changedProps) {
42
+ if (changedProps.has('data')) {
43
+ this.user = this.data?.oauth2?.user || this.user
44
+ this.domain = this.data?.domain || this.domain
45
+ this.req = this.data?.oauth2?.req || this.req
46
+ this.redirectURI = this.data?.oauth2?.redirectURI || this.redirectURI
47
+ this.transactionID = this.data?.oauth2?.transactionID || this.transactionID
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,196 @@
1
+ import '@material/web/button/elevated-button.js'
2
+ import '../../components/role-selector'
3
+
4
+ import gql from 'graphql-tag'
5
+ import { css, html, LitElement } from 'lit'
6
+ import { customElement, property, query } from 'lit/decorators.js'
7
+
8
+ import { client } from '@operato/graphql'
9
+ import { i18next } from '@operato/i18n'
10
+ import { OxPrompt } from '@operato/popup/ox-prompt.js'
11
+ import { ButtonContainerStyles } from '@operato/styles'
12
+
13
+ @customElement('oauth2-decision')
14
+ class OAuth2DecisionPage extends LitElement {
15
+ static styles = [
16
+ ButtonContainerStyles,
17
+ css`
18
+ :host {
19
+ display: flex;
20
+ flex-direction: column;
21
+
22
+ border: 1px solid var(--md-sys-color-primary);
23
+ margin: var(--spacing-medium);
24
+ padding: var(--spacing-medium);
25
+ font: normal 15px var(--theme-font);
26
+ color: var(--md-sys-color-secondary);
27
+ }
28
+
29
+ [field-2column] {
30
+ border-radius: var(--border-radius);
31
+ display: grid;
32
+ grid-template-columns: 1fr 1fr;
33
+ gap: 5px 15px;
34
+ clear: both;
35
+ max-width: var(--input-container-max-width);
36
+ }
37
+
38
+ [field] {
39
+ display: flex;
40
+ flex-direction: column;
41
+ padding-bottom: var(--spacing-medium);
42
+ }
43
+
44
+ input {
45
+ border: var(--border-dim-color);
46
+ border-radius: var(--border-radius);
47
+ margin: var(--input-margin);
48
+ padding: var(--input-padding);
49
+ min-width: 250px;
50
+ font: var(--input-font);
51
+ }
52
+
53
+ label {
54
+ display: flex;
55
+ flex-direction: column;
56
+ font: var(--label-font);
57
+ color: var(--label-color, var(--md-sys-color-on-surface));
58
+ text-transform: var(--label-text-transform);
59
+ }
60
+
61
+ md-elevated-button {
62
+ margin-right: var(--spacing-small);
63
+ }
64
+ `
65
+ ]
66
+
67
+ @property({ type: Object }) data: any = {}
68
+ @property({ type: Object }) user: any = {}
69
+ @property({ type: Object }) client: any = {}
70
+ @property({ type: Object }) domain: any = {}
71
+ @property({ type: Object }) req: any = {}
72
+ @property({ type: String }) redirectURI: string = ''
73
+ @property({ type: String }) transactionID: string = ''
74
+ @property({ type: Array }) roles: any[] = []
75
+
76
+ @query('role-selector') roleSelector!: HTMLElement & { selectedRoles: () => any[] }
77
+
78
+ render() {
79
+ return html`
80
+ <h2>Application Binding</h2>
81
+ <p>Hi ${this.user.name}!</p>
82
+ <p><b> ${this.client.name} </b> is requesting access to your account.</p>
83
+ <p>Do you approve?</p>
84
+
85
+ <form field-2column>
86
+ <div field>
87
+ <label
88
+ >${i18next.t('label.company')}
89
+ <input readonly value="${this.domain.subdomain}" />
90
+ </label>
91
+
92
+ <label
93
+ >${i18next.t('label.client-id')}
94
+ <input readonly value="${this.req.clientID}" />
95
+ </label>
96
+ </div>
97
+
98
+ <div field>
99
+ <label
100
+ >${i18next.t('label.redirect_uri')}
101
+ <input readonly value="${this.redirectURI}" />
102
+ </label>
103
+
104
+ <label
105
+ >${i18next.t('label.state')}
106
+ <input readonly value="${this.req.state}" />
107
+ </label>
108
+ </div>
109
+ </form>
110
+
111
+ <role-selector .roleCategory="${this.domain.name}" .roles="${this.roles}"></role-selector>
112
+
113
+ <div class="button-container">
114
+ <md-elevated-button @click="${() => this.decision(true)}" raised label="Allow"></md-elevated-button>
115
+ <md-elevated-button @click="${() => this.decision(false)}" raised danger label="Deny"></md-elevated-button>
116
+ </div>
117
+ `
118
+ }
119
+
120
+ async firstUpdated() {
121
+ this.fetchMyRoles()
122
+ }
123
+
124
+ updated(changedProps) {
125
+ if (changedProps.has('data')) {
126
+ this.user = this.data?.oauth2?.user || this.user
127
+ this.client = this.data?.oauth2?.client || this.client
128
+ this.domain = this.data?.domain || this.domain
129
+ this.req = this.data?.oauth2?.req || this.req
130
+ this.redirectURI = this.data?.oauth2?.redirectURI || this.redirectURI
131
+ this.transactionID = this.data?.oauth2?.transactionID || this.transactionID
132
+ }
133
+ }
134
+
135
+ async fetchMyRoles() {
136
+ const response = await client.query({
137
+ query: gql`
138
+ query myRoles {
139
+ myRoles {
140
+ id
141
+ name
142
+ description
143
+ }
144
+ }
145
+ `
146
+ })
147
+
148
+ if (!response.errors?.length) {
149
+ this.roles = response.data.myRoles
150
+ }
151
+ }
152
+
153
+ async decision(allowOrCancel) {
154
+ const selectedRoles = this.selectedRoles()
155
+ if (allowOrCancel && !selectedRoles?.length) {
156
+ await OxPrompt.open({
157
+ title: i18next.t('title.nothing selected'),
158
+ text: i18next.t('text.should select at least one of x', { x: i18next.t('label.role') }),
159
+ confirmButton: { text: i18next.t('button.confirm') }
160
+ })
161
+
162
+ return
163
+ }
164
+
165
+ let data: any = {
166
+ transaction_id: this.transactionID,
167
+ subdomain: this.domain.subdomain,
168
+ appKey: this.req.clientID,
169
+ state: this.req.state,
170
+ email: this.user.email,
171
+ scopes: selectedRoles
172
+ }
173
+ if (!allowOrCancel) {
174
+ data.cancel = 'Deny'
175
+ }
176
+
177
+ const response = await fetch('/oauth/decision', {
178
+ method: 'POST',
179
+ mode: 'cors',
180
+ credentials: 'include',
181
+ headers: {
182
+ 'Content-Type': 'application/json'
183
+ },
184
+ redirect: 'follow',
185
+ body: JSON.stringify(data)
186
+ })
187
+
188
+ if (response.redirected) {
189
+ location.href = response.url
190
+ }
191
+ }
192
+
193
+ selectedRoles() {
194
+ return this.roleSelector.selectedRoles()
195
+ }
196
+ }