@acorex/modules 20.6.3 → 20.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{acorex-modules-auth-acorex-modules-auth-CAGlVzjD.mjs → acorex-modules-auth-acorex-modules-auth-D2RpP22p.mjs} +10 -10
- package/fesm2022/{acorex-modules-auth-acorex-modules-auth-CAGlVzjD.mjs.map → acorex-modules-auth-acorex-modules-auth-D2RpP22p.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-app-chooser.component-BAoN8dn6.mjs → acorex-modules-auth-app-chooser.component-BI3wED24.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-app-chooser.component-BAoN8dn6.mjs.map → acorex-modules-auth-app-chooser.component-BI3wED24.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-login.module-CToi7k63.mjs → acorex-modules-auth-login.module-CceKXOOB.mjs} +4 -4
- package/fesm2022/{acorex-modules-auth-login.module-CToi7k63.mjs.map → acorex-modules-auth-login.module-CceKXOOB.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-master.layout-YZfKfaoC.mjs → acorex-modules-auth-master.layout-knGLhLSY.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-master.layout-YZfKfaoC.mjs.map → acorex-modules-auth-master.layout-knGLhLSY.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-oauth-callback.component-CKQh5Mpa.mjs → acorex-modules-auth-oauth-callback.component-BmxjkoNA.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-oauth-callback.component-CKQh5Mpa.mjs.map → acorex-modules-auth-oauth-callback.component-BmxjkoNA.mjs.map} +1 -1
- package/fesm2022/acorex-modules-auth-password.component-BMLK4drm.mjs +563 -0
- package/fesm2022/acorex-modules-auth-password.component-BMLK4drm.mjs.map +1 -0
- package/fesm2022/{acorex-modules-auth-password.component-Jskc80kr.mjs → acorex-modules-auth-password.component-BhPfqSA_.mjs} +6 -6
- package/fesm2022/{acorex-modules-auth-password.component-Jskc80kr.mjs.map → acorex-modules-auth-password.component-BhPfqSA_.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-routes-BOofdLjr.mjs → acorex-modules-auth-routes-BtenIM-p.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-routes-BOofdLjr.mjs.map → acorex-modules-auth-routes-BtenIM-p.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-two-factor-code.component-BmlUjtYC.mjs → acorex-modules-auth-two-factor-code.component-Bk0jHCv_.mjs} +3 -3
- package/fesm2022/{acorex-modules-auth-two-factor-code.component-BmlUjtYC.mjs.map → acorex-modules-auth-two-factor-code.component-Bk0jHCv_.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-two-factor.module-Bc39IBha.mjs → acorex-modules-auth-two-factor.module-BUSxDCwt.mjs} +3 -3
- package/fesm2022/{acorex-modules-auth-two-factor.module-Bc39IBha.mjs.map → acorex-modules-auth-two-factor.module-BUSxDCwt.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-user-sessions.component-BBX97AST.mjs → acorex-modules-auth-user-sessions.component-CbMB_YWS.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-user-sessions.component-BBX97AST.mjs.map → acorex-modules-auth-user-sessions.component-CbMB_YWS.mjs.map} +1 -1
- package/fesm2022/acorex-modules-auth.mjs +1 -1
- package/package.json +2 -2
- package/fesm2022/acorex-modules-auth-password.component-Dqb6GBes.mjs +0 -226
- package/fesm2022/acorex-modules-auth-password.component-Dqb6GBes.mjs.map +0 -1
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
import * as i2 from '@acorex/components/button';
|
|
2
|
+
import { AXButtonModule } from '@acorex/components/button';
|
|
3
|
+
import * as i9 from '@acorex/components/check-box';
|
|
4
|
+
import { AXCheckBoxModule } from '@acorex/components/check-box';
|
|
5
|
+
import * as i1$1 from '@acorex/components/decorators';
|
|
6
|
+
import { AXDecoratorModule } from '@acorex/components/decorators';
|
|
7
|
+
import { AXDialogService } from '@acorex/components/dialog';
|
|
8
|
+
import * as i1 from '@acorex/components/form';
|
|
9
|
+
import { AXFormModule, AXFormComponent } from '@acorex/components/form';
|
|
10
|
+
import * as i3 from '@acorex/components/label';
|
|
11
|
+
import { AXLabelModule } from '@acorex/components/label';
|
|
12
|
+
import * as i2$2 from '@acorex/components/loading';
|
|
13
|
+
import { AXLoadingModule } from '@acorex/components/loading';
|
|
14
|
+
import * as i5$1 from '@acorex/components/password-box';
|
|
15
|
+
import { AXPasswordBoxModule } from '@acorex/components/password-box';
|
|
16
|
+
import * as i2$1 from '@acorex/components/text-box';
|
|
17
|
+
import { AXTextBoxModule } from '@acorex/components/text-box';
|
|
18
|
+
import * as i5 from '@acorex/core/translation';
|
|
19
|
+
import { AXTranslationModule } from '@acorex/core/translation';
|
|
20
|
+
import * as i1$2 from '@angular/common';
|
|
21
|
+
import { CommonModule } from '@angular/common';
|
|
22
|
+
import * as i0 from '@angular/core';
|
|
23
|
+
import { signal, Component, inject, Injector, effect, ViewContainerRef, ViewChild } from '@angular/core';
|
|
24
|
+
import { RouterModule, ActivatedRoute, Router } from '@angular/router';
|
|
25
|
+
import { AXToastService } from '@acorex/components/toast';
|
|
26
|
+
import { AXValidationModule } from '@acorex/core/validation';
|
|
27
|
+
import { AXPLoginChallengeComponentBase, AXPSessionService, AXP_LOGIN_CHALLENGE_PROVIDER } from '@acorex/platform/auth';
|
|
28
|
+
import { AXPHomePageService } from '@acorex/platform/common';
|
|
29
|
+
import * as i2$3 from '@angular/forms';
|
|
30
|
+
import { FormsModule } from '@angular/forms';
|
|
31
|
+
import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
|
32
|
+
import { A as AXM_AUTH_CONFIG_TOKEN, a as AXMAuthenticationTypes } from './acorex-modules-auth-acorex-modules-auth-D2RpP22p.mjs';
|
|
33
|
+
|
|
34
|
+
//#region ---- Default Challenge Component ----
|
|
35
|
+
/**
|
|
36
|
+
* Default built-in challenge component for login
|
|
37
|
+
* Shows challenge image/content with an input field and refresh button
|
|
38
|
+
*/
|
|
39
|
+
class AXPDefaultChallengeComponent extends AXPLoginChallengeComponentBase {
|
|
40
|
+
constructor() {
|
|
41
|
+
super(...arguments);
|
|
42
|
+
//#region ---- Local State ----
|
|
43
|
+
/** Local state for the response input */
|
|
44
|
+
this.response = signal('', ...(ngDevMode ? [{ debugName: "response" }] : []));
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region ---- Event Handlers ----
|
|
48
|
+
/**
|
|
49
|
+
* Handles response input change
|
|
50
|
+
*/
|
|
51
|
+
onResponseChange(value) {
|
|
52
|
+
this.response.set(value);
|
|
53
|
+
this.responseChange.emit(value);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Handles refresh button click
|
|
57
|
+
*/
|
|
58
|
+
onRefreshClick() {
|
|
59
|
+
this.refreshRequest.emit();
|
|
60
|
+
}
|
|
61
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPDefaultChallengeComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
62
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPDefaultChallengeComponent, isStandalone: true, selector: "axp-default-challenge", usesInheritance: true, ngImport: i0, template: `
|
|
63
|
+
<div class="ax-flex ax-flex-col ax-gap-3 ax-p-4 ax-border ax-rounded-md ax-border-warning-300 ax-bg-warning-50">
|
|
64
|
+
<!-- Challenge Image -->
|
|
65
|
+
<div class="ax-flex ax-items-center ax-justify-center ax-gap-2">
|
|
66
|
+
@if (challengeData(); as challenge) {
|
|
67
|
+
@if (challenge.contentType === 'image-base64') {
|
|
68
|
+
<img
|
|
69
|
+
[src]="'data:image/png;base64,' + challenge.content"
|
|
70
|
+
alt="Security Challenge"
|
|
71
|
+
class="ax-h-12 ax-border ax-rounded"
|
|
72
|
+
/>
|
|
73
|
+
} @else if (challenge.contentType === 'image-url') {
|
|
74
|
+
<img [src]="challenge.content" alt="Security Challenge" class="ax-h-12 ax-border ax-rounded" />
|
|
75
|
+
} @else {
|
|
76
|
+
<span class="ax-font-mono ax-text-lg ax-p-2 ax-border ax-rounded ax-bg-white">
|
|
77
|
+
{{ challenge.content }}
|
|
78
|
+
</span>
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
<!-- Refresh Challenge Button -->
|
|
82
|
+
<ax-button
|
|
83
|
+
look="blank"
|
|
84
|
+
color="primary"
|
|
85
|
+
class="ax-sm"
|
|
86
|
+
id="refresh-challenge"
|
|
87
|
+
(onClick)="onRefreshClick()"
|
|
88
|
+
[disabled]="isLoading()"
|
|
89
|
+
>
|
|
90
|
+
@if (isLoading()) {
|
|
91
|
+
<ax-loading></ax-loading>
|
|
92
|
+
} @else {
|
|
93
|
+
<ax-icon><i class="fa-solid fa-rotate"></i></ax-icon>
|
|
94
|
+
}
|
|
95
|
+
</ax-button>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<!-- Challenge Response Input -->
|
|
99
|
+
<ax-form-field>
|
|
100
|
+
<ax-label>{{ '@auth:challenge.enter-code' | translate | async }}</ax-label>
|
|
101
|
+
<ax-text-box
|
|
102
|
+
[name]="'challengeResponse'"
|
|
103
|
+
id="challenge-response"
|
|
104
|
+
[placeholder]="'@auth:challenge.placeholder' | translate | async"
|
|
105
|
+
[value]="response()"
|
|
106
|
+
(valueChange)="onResponseChange($event)"
|
|
107
|
+
>
|
|
108
|
+
<ax-validation-rule rule="required"></ax-validation-rule>
|
|
109
|
+
</ax-text-box>
|
|
110
|
+
</ax-form-field>
|
|
111
|
+
</div>
|
|
112
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXTextBoxModule }, { kind: "component", type: i2$1.AXTextBoxComponent, selector: "ax-text-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "maxLength", "allowNull", "type", "autoComplete", "look", "mask-options", "class"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXLabelModule }, { kind: "component", type: i3.AXLabelComponent, selector: "ax-label", inputs: ["required", "for"], outputs: ["requiredChange"] }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i1.AXFormFieldComponent, selector: "ax-form-field", inputs: ["labelMode"] }, { kind: "directive", type: i1.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i2$2.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXValidationModule }, { kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }] }); }
|
|
113
|
+
}
|
|
114
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPDefaultChallengeComponent, decorators: [{
|
|
115
|
+
type: Component,
|
|
116
|
+
args: [{
|
|
117
|
+
selector: 'axp-default-challenge',
|
|
118
|
+
standalone: true,
|
|
119
|
+
imports: [
|
|
120
|
+
AXButtonModule,
|
|
121
|
+
AXTextBoxModule,
|
|
122
|
+
AXLabelModule,
|
|
123
|
+
AXFormModule,
|
|
124
|
+
AXDecoratorModule,
|
|
125
|
+
AXLoadingModule,
|
|
126
|
+
AXTranslationModule,
|
|
127
|
+
AXValidationModule,
|
|
128
|
+
CommonModule,
|
|
129
|
+
],
|
|
130
|
+
template: `
|
|
131
|
+
<div class="ax-flex ax-flex-col ax-gap-3 ax-p-4 ax-border ax-rounded-md ax-border-warning-300 ax-bg-warning-50">
|
|
132
|
+
<!-- Challenge Image -->
|
|
133
|
+
<div class="ax-flex ax-items-center ax-justify-center ax-gap-2">
|
|
134
|
+
@if (challengeData(); as challenge) {
|
|
135
|
+
@if (challenge.contentType === 'image-base64') {
|
|
136
|
+
<img
|
|
137
|
+
[src]="'data:image/png;base64,' + challenge.content"
|
|
138
|
+
alt="Security Challenge"
|
|
139
|
+
class="ax-h-12 ax-border ax-rounded"
|
|
140
|
+
/>
|
|
141
|
+
} @else if (challenge.contentType === 'image-url') {
|
|
142
|
+
<img [src]="challenge.content" alt="Security Challenge" class="ax-h-12 ax-border ax-rounded" />
|
|
143
|
+
} @else {
|
|
144
|
+
<span class="ax-font-mono ax-text-lg ax-p-2 ax-border ax-rounded ax-bg-white">
|
|
145
|
+
{{ challenge.content }}
|
|
146
|
+
</span>
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
<!-- Refresh Challenge Button -->
|
|
150
|
+
<ax-button
|
|
151
|
+
look="blank"
|
|
152
|
+
color="primary"
|
|
153
|
+
class="ax-sm"
|
|
154
|
+
id="refresh-challenge"
|
|
155
|
+
(onClick)="onRefreshClick()"
|
|
156
|
+
[disabled]="isLoading()"
|
|
157
|
+
>
|
|
158
|
+
@if (isLoading()) {
|
|
159
|
+
<ax-loading></ax-loading>
|
|
160
|
+
} @else {
|
|
161
|
+
<ax-icon><i class="fa-solid fa-rotate"></i></ax-icon>
|
|
162
|
+
}
|
|
163
|
+
</ax-button>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<!-- Challenge Response Input -->
|
|
167
|
+
<ax-form-field>
|
|
168
|
+
<ax-label>{{ '@auth:challenge.enter-code' | translate | async }}</ax-label>
|
|
169
|
+
<ax-text-box
|
|
170
|
+
[name]="'challengeResponse'"
|
|
171
|
+
id="challenge-response"
|
|
172
|
+
[placeholder]="'@auth:challenge.placeholder' | translate | async"
|
|
173
|
+
[value]="response()"
|
|
174
|
+
(valueChange)="onResponseChange($event)"
|
|
175
|
+
>
|
|
176
|
+
<ax-validation-rule rule="required"></ax-validation-rule>
|
|
177
|
+
</ax-text-box>
|
|
178
|
+
</ax-form-field>
|
|
179
|
+
</div>
|
|
180
|
+
`,
|
|
181
|
+
}]
|
|
182
|
+
}] });
|
|
183
|
+
|
|
184
|
+
//#region ---- Module Imports ----
|
|
185
|
+
const MODULES = [
|
|
186
|
+
CommonModule,
|
|
187
|
+
FormsModule,
|
|
188
|
+
AXFormModule,
|
|
189
|
+
AXTextBoxModule,
|
|
190
|
+
AXPasswordBoxModule,
|
|
191
|
+
AXButtonModule,
|
|
192
|
+
AXLabelModule,
|
|
193
|
+
AXDecoratorModule,
|
|
194
|
+
AXCheckBoxModule,
|
|
195
|
+
AXTranslationModule,
|
|
196
|
+
AXLoadingModule,
|
|
197
|
+
RouterModule,
|
|
198
|
+
AXValidationModule,
|
|
199
|
+
];
|
|
200
|
+
//#endregion
|
|
201
|
+
class AXPLoginPasswordComponent {
|
|
202
|
+
constructor() {
|
|
203
|
+
//#region ---- Services & Dependencies ----
|
|
204
|
+
this.configs = inject(AXM_AUTH_CONFIG_TOKEN);
|
|
205
|
+
this.dialogService = inject(AXDialogService);
|
|
206
|
+
this.sessionService = inject(AXPSessionService);
|
|
207
|
+
this.activateRoute = inject(ActivatedRoute);
|
|
208
|
+
this.homePageService = inject(AXPHomePageService);
|
|
209
|
+
this.toastService = inject(AXToastService);
|
|
210
|
+
this.router = inject(Router);
|
|
211
|
+
this.injector = inject(Injector);
|
|
212
|
+
/** Optional challenge provider - injected only if configured */
|
|
213
|
+
this.challengeProvider = inject(AXP_LOGIN_CHALLENGE_PROVIDER, { optional: true });
|
|
214
|
+
/** Value of the first input field (username/email) */
|
|
215
|
+
this.v1 = '';
|
|
216
|
+
/** Value of the second input field (password) */
|
|
217
|
+
this.v2 = '';
|
|
218
|
+
/** Whether to keep the user logged in */
|
|
219
|
+
this.isKeepLogin = false;
|
|
220
|
+
/** Current challenge data from the server */
|
|
221
|
+
this.challengeData = signal(null, ...(ngDevMode ? [{ debugName: "challengeData" }] : []));
|
|
222
|
+
/** User's response to the challenge (e.g., CAPTCHA text) */
|
|
223
|
+
this.challengeResponse = signal('', ...(ngDevMode ? [{ debugName: "challengeResponse" }] : []));
|
|
224
|
+
/** Loading state for challenge refresh */
|
|
225
|
+
this.isChallengeLoading = signal(false, ...(ngDevMode ? [{ debugName: "isChallengeLoading" }] : []));
|
|
226
|
+
/**
|
|
227
|
+
* Effect to render the challenge component when challenge data changes
|
|
228
|
+
*/
|
|
229
|
+
this.challengeRenderEffect = effect(() => {
|
|
230
|
+
const data = this.challengeData();
|
|
231
|
+
const isLoading = this.isChallengeLoading();
|
|
232
|
+
// Wait for next tick to ensure ViewContainerRef is available
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
if (data && this.challengeContainerRef) {
|
|
235
|
+
this.renderChallengeComponent(data, isLoading);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
this.destroyChallengeComponent();
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}, ...(ngDevMode ? [{ debugName: "challengeRenderEffect" }] : []));
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region ---- Loading & Status State ----
|
|
244
|
+
this.isLoading$ = new BehaviorSubject(false);
|
|
245
|
+
this.isExternalLoading$ = new BehaviorSubject(false);
|
|
246
|
+
this.submitText = 'signin.submit';
|
|
247
|
+
}
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region ---- Computed Properties ----
|
|
250
|
+
/** Check if external providers (Google, Apple, etc.) are configured */
|
|
251
|
+
get hasExternalProviders() {
|
|
252
|
+
return !!(this.configs?.externalProviders && Object.keys(this.configs.externalProviders).length > 0);
|
|
253
|
+
}
|
|
254
|
+
/** Check if a challenge is currently active and should be displayed */
|
|
255
|
+
get hasChallengeActive() {
|
|
256
|
+
return this.challengeData() !== null;
|
|
257
|
+
}
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region ---- Lifecycle Methods ----
|
|
260
|
+
ngOnInit() {
|
|
261
|
+
this.l2 = 'password';
|
|
262
|
+
switch (this.configs.type) {
|
|
263
|
+
case AXMAuthenticationTypes.EmailPassword:
|
|
264
|
+
this.l1 = 'email';
|
|
265
|
+
break;
|
|
266
|
+
case AXMAuthenticationTypes.MobilePassword:
|
|
267
|
+
this.l1 = 'mobile';
|
|
268
|
+
break;
|
|
269
|
+
case AXMAuthenticationTypes.UsernameEmailPassword:
|
|
270
|
+
this.l1 = 'username-email';
|
|
271
|
+
break;
|
|
272
|
+
case AXMAuthenticationTypes.UsernamePassword:
|
|
273
|
+
default:
|
|
274
|
+
this.l1 = 'username';
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region ---- Navigation Handlers ----
|
|
279
|
+
handleForgotOnClick() {
|
|
280
|
+
this.router.navigate(['/auth/forgot']);
|
|
281
|
+
}
|
|
282
|
+
handleRegisterOnClick() {
|
|
283
|
+
this.router.navigate([this.configs.signup?.url || '/auth/register']);
|
|
284
|
+
}
|
|
285
|
+
//#endregion
|
|
286
|
+
//#region ---- Login Handlers ----
|
|
287
|
+
handleLoginClick() {
|
|
288
|
+
this.sessionService.setStrategy(this.configs.defaultStrategyName || 'user-pass');
|
|
289
|
+
const redirectUrl = this.activateRoute.snapshot.queryParams['redirectUrl'];
|
|
290
|
+
this.form.validate().then(async (form) => {
|
|
291
|
+
if (form.result) {
|
|
292
|
+
this.isLoading$.next(true);
|
|
293
|
+
this.submitText = 'processing';
|
|
294
|
+
try {
|
|
295
|
+
const strategy = this.sessionService.getSessionData()?.strategy;
|
|
296
|
+
if (!strategy) {
|
|
297
|
+
throw new Error('Strategy not found');
|
|
298
|
+
}
|
|
299
|
+
// Build credentials object with challenge data if active
|
|
300
|
+
const currentChallenge = this.challengeData();
|
|
301
|
+
const credentials = {
|
|
302
|
+
strategy: strategy,
|
|
303
|
+
username: this.v1,
|
|
304
|
+
password: this.v2,
|
|
305
|
+
...(currentChallenge && {
|
|
306
|
+
challengeId: currentChallenge.id,
|
|
307
|
+
challengeResponse: this.challengeResponse(),
|
|
308
|
+
}),
|
|
309
|
+
};
|
|
310
|
+
await this.sessionService.signin(credentials);
|
|
311
|
+
await this.sessionService.signInComplete();
|
|
312
|
+
// Check authentication status before navigation
|
|
313
|
+
const isAuthenticated = await firstValueFrom(this.sessionService.isAuthenticated$);
|
|
314
|
+
if (!isAuthenticated) {
|
|
315
|
+
throw new Error('Authentication failed after signin');
|
|
316
|
+
}
|
|
317
|
+
// Clear challenge state on successful login
|
|
318
|
+
this.clearChallengeState();
|
|
319
|
+
const currentApplication = await firstValueFrom(this.sessionService.application$);
|
|
320
|
+
if (!currentApplication) {
|
|
321
|
+
if (this.sessionService.tenant?.id == null) {
|
|
322
|
+
this.router.navigate(['/auth/account/tenant-chooser']);
|
|
323
|
+
}
|
|
324
|
+
else if (this.sessionService.application?.id == null) {
|
|
325
|
+
this.router.navigate(['/auth/account/app-chooser']);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
else if (redirectUrl) {
|
|
329
|
+
this.router.navigate([redirectUrl]);
|
|
330
|
+
}
|
|
331
|
+
else {
|
|
332
|
+
await this.homePageService.navigateTo();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
await this.handleLoginError(error);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Handles login errors and checks if a challenge is required
|
|
343
|
+
*/
|
|
344
|
+
async handleLoginError(error) {
|
|
345
|
+
// Check if challenge provider wants to handle this error
|
|
346
|
+
if (this.challengeProvider) {
|
|
347
|
+
const checkResult = this.challengeProvider.checkResponse(error);
|
|
348
|
+
if (checkResult?.required) {
|
|
349
|
+
try {
|
|
350
|
+
// Fetch challenge from server
|
|
351
|
+
const challenge = await this.challengeProvider.getChallenge(checkResult.serverData);
|
|
352
|
+
this.challengeData.set(challenge);
|
|
353
|
+
this.challengeResponse.set('');
|
|
354
|
+
// Show challenge message if provided
|
|
355
|
+
if (checkResult.message) {
|
|
356
|
+
this.toastService.show({
|
|
357
|
+
color: 'warning',
|
|
358
|
+
content: checkResult.message,
|
|
359
|
+
timeOut: 5000,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
this.submitText = 'signin.submit';
|
|
363
|
+
this.isLoading$.next(false);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
catch (challengeError) {
|
|
367
|
+
console.error('Failed to fetch challenge:', challengeError);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Default error handling
|
|
372
|
+
this.showLoginError(error);
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Displays the login error message to the user
|
|
376
|
+
*/
|
|
377
|
+
showLoginError(error) {
|
|
378
|
+
let errorMsg = 'An error occurred during sign in.';
|
|
379
|
+
if (error && typeof error === 'object') {
|
|
380
|
+
const err = error;
|
|
381
|
+
const errObj = err['error'];
|
|
382
|
+
errorMsg =
|
|
383
|
+
errObj?.['error_description'] ||
|
|
384
|
+
errObj?.['error'] ||
|
|
385
|
+
err['message'] ||
|
|
386
|
+
errorMsg;
|
|
387
|
+
if (typeof errorMsg === 'object') {
|
|
388
|
+
const msgObj = errorMsg;
|
|
389
|
+
errorMsg = msgObj['message'] || msgObj['error_description'] || JSON.stringify(errorMsg);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
this.toastService.show({
|
|
393
|
+
color: 'danger',
|
|
394
|
+
content: errorMsg,
|
|
395
|
+
timeOut: 5000,
|
|
396
|
+
});
|
|
397
|
+
console.error('Login error:', error);
|
|
398
|
+
this.submitText = 'signin.submit';
|
|
399
|
+
this.v2 = null;
|
|
400
|
+
this.isLoading$.next(false);
|
|
401
|
+
}
|
|
402
|
+
//#endregion
|
|
403
|
+
//#region ---- Challenge Handlers ----
|
|
404
|
+
/**
|
|
405
|
+
* Gets the challenge component type from provider or uses default
|
|
406
|
+
*/
|
|
407
|
+
getChallengeComponentType() {
|
|
408
|
+
return this.challengeProvider?.getChallengeComponent() ?? AXPDefaultChallengeComponent;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Renders the challenge component dynamically
|
|
412
|
+
*/
|
|
413
|
+
renderChallengeComponent(data, isLoading) {
|
|
414
|
+
if (!this.challengeContainerRef)
|
|
415
|
+
return;
|
|
416
|
+
// If component already exists, just update inputs
|
|
417
|
+
if (this.challengeComponentRef) {
|
|
418
|
+
this.updateChallengeComponentInputs(data, isLoading);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
// Clear container and create new component
|
|
422
|
+
this.challengeContainerRef.clear();
|
|
423
|
+
const componentType = this.getChallengeComponentType();
|
|
424
|
+
this.challengeComponentRef = this.challengeContainerRef.createComponent(componentType);
|
|
425
|
+
// Set inputs
|
|
426
|
+
this.updateChallengeComponentInputs(data, isLoading);
|
|
427
|
+
// Subscribe to outputs
|
|
428
|
+
const instance = this.challengeComponentRef.instance;
|
|
429
|
+
instance.responseChange.subscribe((response) => {
|
|
430
|
+
this.challengeResponse.set(response);
|
|
431
|
+
});
|
|
432
|
+
instance.refreshRequest.subscribe(() => {
|
|
433
|
+
this.handleRefreshChallenge();
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Updates the inputs of the challenge component
|
|
438
|
+
*/
|
|
439
|
+
updateChallengeComponentInputs(data, isLoading) {
|
|
440
|
+
if (!this.challengeComponentRef)
|
|
441
|
+
return;
|
|
442
|
+
this.challengeComponentRef.setInput('challengeData', data);
|
|
443
|
+
this.challengeComponentRef.setInput('isLoading', isLoading);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Destroys the challenge component
|
|
447
|
+
*/
|
|
448
|
+
destroyChallengeComponent() {
|
|
449
|
+
if (this.challengeComponentRef) {
|
|
450
|
+
this.challengeComponentRef.destroy();
|
|
451
|
+
this.challengeComponentRef = undefined;
|
|
452
|
+
}
|
|
453
|
+
if (this.challengeContainerRef) {
|
|
454
|
+
this.challengeContainerRef.clear();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Refreshes the challenge (e.g., user clicks "new image" button)
|
|
459
|
+
*/
|
|
460
|
+
async handleRefreshChallenge() {
|
|
461
|
+
if (!this.challengeProvider)
|
|
462
|
+
return;
|
|
463
|
+
this.isChallengeLoading.set(true);
|
|
464
|
+
try {
|
|
465
|
+
const newChallenge = await this.challengeProvider.refreshChallenge();
|
|
466
|
+
this.challengeData.set(newChallenge);
|
|
467
|
+
this.challengeResponse.set('');
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
console.error('Failed to refresh challenge:', error);
|
|
471
|
+
this.toastService.show({
|
|
472
|
+
color: 'danger',
|
|
473
|
+
content: 'Failed to load new challenge. Please try again.',
|
|
474
|
+
timeOut: 3000,
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
finally {
|
|
478
|
+
this.isChallengeLoading.set(false);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Clears the challenge state (after successful login or manual clear)
|
|
483
|
+
*/
|
|
484
|
+
clearChallengeState() {
|
|
485
|
+
this.challengeData.set(null);
|
|
486
|
+
this.challengeResponse.set('');
|
|
487
|
+
this.destroyChallengeComponent();
|
|
488
|
+
}
|
|
489
|
+
//#endregion
|
|
490
|
+
//#region ---- External Sign-In Handlers ----
|
|
491
|
+
async handleExternalSignIn(provider) {
|
|
492
|
+
this.sessionService.setStrategy(provider);
|
|
493
|
+
try {
|
|
494
|
+
this.isExternalLoading$.next(true);
|
|
495
|
+
// Get provider configuration (optional, for validation)
|
|
496
|
+
const providerConfig = this.configs?.externalProviders?.[provider];
|
|
497
|
+
if (!providerConfig) {
|
|
498
|
+
throw new Error(`Provider '${provider}' is not configured`);
|
|
499
|
+
}
|
|
500
|
+
await this.sessionService.signin({ strategy: provider });
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
console.error(`${provider} sign-in error:`, error);
|
|
504
|
+
}
|
|
505
|
+
finally {
|
|
506
|
+
this.isExternalLoading$.next(false);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
//#endregion
|
|
510
|
+
//#region ---- PKCE Utilities ----
|
|
511
|
+
/**
|
|
512
|
+
* Generates a code challenge for PKCE (Proof Key for Code Exchange)
|
|
513
|
+
*/
|
|
514
|
+
async generateCodeChallenge(codeVerifier) {
|
|
515
|
+
const hash = await this.sha256(codeVerifier);
|
|
516
|
+
return this.base64UrlEncode(hash);
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Generates a random string for code verifier
|
|
520
|
+
*/
|
|
521
|
+
generateRandomString(length) {
|
|
522
|
+
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
|
|
523
|
+
let text = '';
|
|
524
|
+
for (let i = 0; i < length; i++) {
|
|
525
|
+
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
526
|
+
}
|
|
527
|
+
return text;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Creates SHA256 hash of the input string using Web Crypto API
|
|
531
|
+
*/
|
|
532
|
+
async sha256(str) {
|
|
533
|
+
const encoder = new TextEncoder();
|
|
534
|
+
const data = encoder.encode(str);
|
|
535
|
+
return await crypto.subtle.digest('SHA-256', data);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Base64url encodes an ArrayBuffer
|
|
539
|
+
*/
|
|
540
|
+
base64UrlEncode(buffer) {
|
|
541
|
+
const bytes = new Uint8Array(buffer);
|
|
542
|
+
let binary = '';
|
|
543
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
544
|
+
binary += String.fromCharCode(bytes[i]);
|
|
545
|
+
}
|
|
546
|
+
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
547
|
+
}
|
|
548
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLoginPasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
549
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPLoginPasswordComponent, isStandalone: true, selector: "ng-component", viewQueries: [{ propertyName: "form", first: true, predicate: AXFormComponent, descendants: true }, { propertyName: "challengeContainerRef", first: true, predicate: ["challengeContainer"], descendants: true, read: ViewContainerRef }], ngImport: i0, template: "<ng-container *translate=\"let t\">\n <div class=\"ax-text-center ax-mb-4\">\n <h1 class=\"ax-font-bold ax-text-xl ax-mb-2\">\n {{ '@auth:signin.title' | translate | async }}\n </h1>\n <p class=\"ax-text-base ax-text-neutral-600\">\n {{ '@auth:signin.hint' | translate | async }}\n </p>\n </div>\n <div class=\"ax-w-80 lg:ax-w-96\">\n <ax-form>\n <div class=\"ax-flex ax-flex-col ax-gap-6\">\n <ax-form-field>\n <ax-label>{{ '@auth:username-email' | translate | async }}</ax-label>\n <ax-text-box [name]=\"'username'\" id=\"username\" [autoComplete]=\"'username'\" [(value)]=\"v1\">\n <ax-validation-rule rule=\"required\"></ax-validation-rule>\n </ax-text-box>\n </ax-form-field>\n <ax-form-field>\n <ax-label>{{ '@auth:password' | translate | async }}</ax-label>\n <ax-password-box [name]=\"'password'\" id=\"password\" [autoComplete]=\"'current-password'\" [(value)]=\"v2\">\n <ax-validation-rule rule=\"required\"></ax-validation-rule>\n </ax-password-box>\n </ax-form-field>\n\n <!-- Challenge Section (CAPTCHA) - rendered via ViewContainerRef -->\n @if (hasChallengeActive) {\n <ng-container #challengeContainer></ng-container>\n }\n\n <div class=\"ax-flex ax-justify-between ax-items-center\">\n <ax-check-box [(ngModel)]=\"isKeepLogin\">\n <ax-label class=\"ax-mb-0\">{{ '@auth:signin.remember' | translate | async }}</ax-label>\n </ax-check-box>\n <ax-button\n color=\"primary\"\n class=\"ax-sm\"\n id=\"forgot-password\"\n [text]=\"'@auth:forgot.password' | translate | async\"\n look=\"blank\"\n (onClick)=\"handleForgotOnClick()\"\n >\n </ax-button>\n </div>\n </div>\n <ax-button\n color=\"primary\"\n class=\"ax-mt-6 ax-w-full\"\n id=\"submit-login\"\n [text]=\"'@auth:signin.submit' | translate | async\"\n (onClick)=\"handleLoginClick()\"\n [disabled]=\"isLoading$ | async\"\n [type]=\"'submit'\"\n >\n @if (isLoading$ | async) {\n <ax-loading></ax-loading>\n }\n </ax-button>\n @if (hasExternalProviders || configs.signup?.enable) {\n <div class=\"ax-heading ax-heading-center !ax-my-8\">\n <span>{{ '@auth:signin.or' | translate | async }}</span>\n </div>\n }\n <div class=\"ax-flex ax-flex-col ax-gap-2 lg:ax-flex-row ax-justify-between ax-mt-6\">\n @if (configs.externalProviders?.['google']) {\n <ax-button\n class=\"ax-w-full ax-mt-2\"\n text=\"{{ '@auth:authentication.with-google' | translate | async }}\"\n [color]=\"'default'\"\n id=\"google\"\n (onClick)=\"handleExternalSignIn('google')\"\n [disabled]=\"isExternalLoading$ | async\"\n >\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-brands fa-google fa-lg ax-text-danger-500\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-loading *ngIf=\"isExternalLoading$ | async\"></ax-loading>\n </ax-button>\n }\n @if (configs.externalProviders?.['apple']) {\n <ax-button\n text=\"{{ '@auth:authentication.with-apple' | translate | async }}\"\n [color]=\"'default'\"\n id=\"apple\"\n (onClick)=\"handleExternalSignIn('apple')\"\n [disabled]=\"isExternalLoading$ | async\"\n >\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-brands fa-apple fa-xl\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-loading *ngIf=\"isExternalLoading$ | async\"></ax-loading>\n </ax-button>\n }\n @if (configs.externalProviders?.['microsoft']) {\n <ax-button\n text=\"{{ '@auth:authentication.with-microsoft' | translate | async }}\"\n [color]=\"'default'\"\n id=\"microsoft\"\n (onClick)=\"handleExternalSignIn('microsoft')\"\n [disabled]=\"isExternalLoading$ | async\"\n >\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-brands fa-microsoft fa-lg text-blue-500\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-loading *ngIf=\"isExternalLoading$ | async\"></ax-loading>\n </ax-button>\n }\n </div>\n </ax-form>\n @if (configs.signup?.enable) {\n <ax-button\n class=\"ax-w-full ax-mt-2\"\n id=\"register\"\n (onClick)=\"handleRegisterOnClick()\"\n text=\"{{ '@auth:register' | translate | async }}\"\n [color]=\"'default'\"\n >\n </ax-button>\n }\n </div>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i1.AXFormFieldComponent, selector: "ax-form-field", inputs: ["labelMode"] }, { kind: "component", type: i1.AXFormComponent, selector: "ax-form", inputs: ["disabled", "readonly", "labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }, { kind: "directive", type: i1.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: AXTextBoxModule }, { kind: "component", type: i2$1.AXTextBoxComponent, selector: "ax-text-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "maxLength", "allowNull", "type", "autoComplete", "look", "mask-options", "class"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXPasswordBoxModule }, { kind: "component", type: i5$1.AXPasswordBoxComponent, selector: "ax-password-box", inputs: ["readonly", "disabled", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "autoComplete", "showToggleButton", "class"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXLabelModule }, { kind: "component", type: i3.AXLabelComponent, selector: "ax-label", inputs: ["required", "for"], outputs: ["requiredChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i1$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i1$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXCheckBoxModule }, { kind: "component", type: i9.AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i5.AXTranslatorDirective, selector: "[translate]" }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i2$2.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: AXValidationModule }, { kind: "pipe", type: i1$2.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }] }); }
|
|
550
|
+
}
|
|
551
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLoginPasswordComponent, decorators: [{
|
|
552
|
+
type: Component,
|
|
553
|
+
args: [{ imports: [...MODULES], template: "<ng-container *translate=\"let t\">\n <div class=\"ax-text-center ax-mb-4\">\n <h1 class=\"ax-font-bold ax-text-xl ax-mb-2\">\n {{ '@auth:signin.title' | translate | async }}\n </h1>\n <p class=\"ax-text-base ax-text-neutral-600\">\n {{ '@auth:signin.hint' | translate | async }}\n </p>\n </div>\n <div class=\"ax-w-80 lg:ax-w-96\">\n <ax-form>\n <div class=\"ax-flex ax-flex-col ax-gap-6\">\n <ax-form-field>\n <ax-label>{{ '@auth:username-email' | translate | async }}</ax-label>\n <ax-text-box [name]=\"'username'\" id=\"username\" [autoComplete]=\"'username'\" [(value)]=\"v1\">\n <ax-validation-rule rule=\"required\"></ax-validation-rule>\n </ax-text-box>\n </ax-form-field>\n <ax-form-field>\n <ax-label>{{ '@auth:password' | translate | async }}</ax-label>\n <ax-password-box [name]=\"'password'\" id=\"password\" [autoComplete]=\"'current-password'\" [(value)]=\"v2\">\n <ax-validation-rule rule=\"required\"></ax-validation-rule>\n </ax-password-box>\n </ax-form-field>\n\n <!-- Challenge Section (CAPTCHA) - rendered via ViewContainerRef -->\n @if (hasChallengeActive) {\n <ng-container #challengeContainer></ng-container>\n }\n\n <div class=\"ax-flex ax-justify-between ax-items-center\">\n <ax-check-box [(ngModel)]=\"isKeepLogin\">\n <ax-label class=\"ax-mb-0\">{{ '@auth:signin.remember' | translate | async }}</ax-label>\n </ax-check-box>\n <ax-button\n color=\"primary\"\n class=\"ax-sm\"\n id=\"forgot-password\"\n [text]=\"'@auth:forgot.password' | translate | async\"\n look=\"blank\"\n (onClick)=\"handleForgotOnClick()\"\n >\n </ax-button>\n </div>\n </div>\n <ax-button\n color=\"primary\"\n class=\"ax-mt-6 ax-w-full\"\n id=\"submit-login\"\n [text]=\"'@auth:signin.submit' | translate | async\"\n (onClick)=\"handleLoginClick()\"\n [disabled]=\"isLoading$ | async\"\n [type]=\"'submit'\"\n >\n @if (isLoading$ | async) {\n <ax-loading></ax-loading>\n }\n </ax-button>\n @if (hasExternalProviders || configs.signup?.enable) {\n <div class=\"ax-heading ax-heading-center !ax-my-8\">\n <span>{{ '@auth:signin.or' | translate | async }}</span>\n </div>\n }\n <div class=\"ax-flex ax-flex-col ax-gap-2 lg:ax-flex-row ax-justify-between ax-mt-6\">\n @if (configs.externalProviders?.['google']) {\n <ax-button\n class=\"ax-w-full ax-mt-2\"\n text=\"{{ '@auth:authentication.with-google' | translate | async }}\"\n [color]=\"'default'\"\n id=\"google\"\n (onClick)=\"handleExternalSignIn('google')\"\n [disabled]=\"isExternalLoading$ | async\"\n >\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-brands fa-google fa-lg ax-text-danger-500\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-loading *ngIf=\"isExternalLoading$ | async\"></ax-loading>\n </ax-button>\n }\n @if (configs.externalProviders?.['apple']) {\n <ax-button\n text=\"{{ '@auth:authentication.with-apple' | translate | async }}\"\n [color]=\"'default'\"\n id=\"apple\"\n (onClick)=\"handleExternalSignIn('apple')\"\n [disabled]=\"isExternalLoading$ | async\"\n >\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-brands fa-apple fa-xl\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-loading *ngIf=\"isExternalLoading$ | async\"></ax-loading>\n </ax-button>\n }\n @if (configs.externalProviders?.['microsoft']) {\n <ax-button\n text=\"{{ '@auth:authentication.with-microsoft' | translate | async }}\"\n [color]=\"'default'\"\n id=\"microsoft\"\n (onClick)=\"handleExternalSignIn('microsoft')\"\n [disabled]=\"isExternalLoading$ | async\"\n >\n <ax-prefix>\n <ax-icon>\n <i class=\"fa-brands fa-microsoft fa-lg text-blue-500\"></i>\n </ax-icon>\n </ax-prefix>\n <ax-loading *ngIf=\"isExternalLoading$ | async\"></ax-loading>\n </ax-button>\n }\n </div>\n </ax-form>\n @if (configs.signup?.enable) {\n <ax-button\n class=\"ax-w-full ax-mt-2\"\n id=\"register\"\n (onClick)=\"handleRegisterOnClick()\"\n text=\"{{ '@auth:register' | translate | async }}\"\n [color]=\"'default'\"\n >\n </ax-button>\n }\n </div>\n</ng-container>\n" }]
|
|
554
|
+
}], propDecorators: { form: [{
|
|
555
|
+
type: ViewChild,
|
|
556
|
+
args: [AXFormComponent]
|
|
557
|
+
}], challengeContainerRef: [{
|
|
558
|
+
type: ViewChild,
|
|
559
|
+
args: ['challengeContainer', { read: ViewContainerRef }]
|
|
560
|
+
}] } });
|
|
561
|
+
|
|
562
|
+
export { AXPLoginPasswordComponent };
|
|
563
|
+
//# sourceMappingURL=acorex-modules-auth-password.component-BMLK4drm.mjs.map
|