@mintplayer/ng-spark-auth 0.1.0 → 0.3.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 (40) hide show
  1. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs +36 -0
  2. package/fesm2022/mintplayer-ng-spark-auth-auth-bar.mjs.map +1 -0
  3. package/fesm2022/mintplayer-ng-spark-auth-core.mjs +106 -0
  4. package/fesm2022/mintplayer-ng-spark-auth-core.mjs.map +1 -0
  5. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs +58 -0
  6. package/fesm2022/mintplayer-ng-spark-auth-forgot-password.mjs.map +1 -0
  7. package/fesm2022/mintplayer-ng-spark-auth-guards.mjs +23 -0
  8. package/fesm2022/mintplayer-ng-spark-auth-guards.mjs.map +1 -0
  9. package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs +26 -0
  10. package/fesm2022/mintplayer-ng-spark-auth-interceptors.mjs.map +1 -0
  11. package/fesm2022/mintplayer-ng-spark-auth-login.mjs +72 -0
  12. package/fesm2022/mintplayer-ng-spark-auth-login.mjs.map +1 -0
  13. package/fesm2022/mintplayer-ng-spark-auth-models.mjs +17 -0
  14. package/fesm2022/mintplayer-ng-spark-auth-models.mjs.map +1 -0
  15. package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs +23 -0
  16. package/fesm2022/mintplayer-ng-spark-auth-pipes.mjs.map +1 -0
  17. package/fesm2022/mintplayer-ng-spark-auth-register.mjs +86 -0
  18. package/fesm2022/mintplayer-ng-spark-auth-register.mjs.map +1 -0
  19. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs +90 -0
  20. package/fesm2022/mintplayer-ng-spark-auth-reset-password.mjs.map +1 -0
  21. package/fesm2022/mintplayer-ng-spark-auth-routes.mjs +52 -0
  22. package/fesm2022/mintplayer-ng-spark-auth-routes.mjs.map +1 -0
  23. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs +73 -0
  24. package/fesm2022/mintplayer-ng-spark-auth-two-factor.mjs.map +1 -0
  25. package/fesm2022/mintplayer-ng-spark-auth.mjs +16 -521
  26. package/fesm2022/mintplayer-ng-spark-auth.mjs.map +1 -1
  27. package/package.json +49 -1
  28. package/types/mintplayer-ng-spark-auth-auth-bar.d.ts +14 -0
  29. package/types/mintplayer-ng-spark-auth-core.d.ts +33 -0
  30. package/types/mintplayer-ng-spark-auth-forgot-password.d.ts +23 -0
  31. package/types/mintplayer-ng-spark-auth-guards.d.ts +5 -0
  32. package/types/mintplayer-ng-spark-auth-interceptors.d.ts +5 -0
  33. package/types/mintplayer-ng-spark-auth-login.d.ts +27 -0
  34. package/types/mintplayer-ng-spark-auth-models.d.ts +33 -0
  35. package/types/mintplayer-ng-spark-auth-pipes.d.ts +11 -0
  36. package/types/mintplayer-ng-spark-auth-register.d.ts +25 -0
  37. package/types/mintplayer-ng-spark-auth-reset-password.d.ts +30 -0
  38. package/types/mintplayer-ng-spark-auth-routes.d.ts +5 -0
  39. package/types/mintplayer-ng-spark-auth-two-factor.d.ts +28 -0
  40. package/types/mintplayer-ng-spark-auth.d.ts +5 -191
@@ -1,159 +1,8 @@
1
- import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, signal, computed, Injectable, Pipe, makeEnvironmentProviders, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { HttpClient, withInterceptors, withXsrfConfiguration, HttpErrorResponse } from '@angular/common/http';
4
- import { firstValueFrom, tap } from 'rxjs';
5
- import { Router, RouterLink, ActivatedRoute } from '@angular/router';
6
- import * as i1 from '@angular/forms';
7
- import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
8
- import { Color } from '@mintplayer/ng-bootstrap';
9
- import { BsAlertComponent } from '@mintplayer/ng-bootstrap/alert';
10
- import { BsCardComponent, BsCardHeaderComponent } from '@mintplayer/ng-bootstrap/card';
11
- import { BsFormComponent, BsFormControlDirective } from '@mintplayer/ng-bootstrap/form';
12
- import { BsToggleButtonComponent } from '@mintplayer/ng-bootstrap/toggle-button';
13
- import { BsSpinnerComponent } from '@mintplayer/ng-bootstrap/spinner';
14
-
15
- const SPARK_AUTH_CONFIG = new InjectionToken('SPARK_AUTH_CONFIG');
16
- const defaultSparkAuthConfig = {
17
- apiBasePath: '/spark/auth',
18
- defaultRedirectUrl: '/',
19
- loginUrl: '/login',
20
- };
21
-
22
- const SPARK_AUTH_ROUTE_PATHS = new InjectionToken('SPARK_AUTH_ROUTE_PATHS');
23
-
24
- class SparkAuthService {
25
- http = inject(HttpClient);
26
- config = inject(SPARK_AUTH_CONFIG);
27
- currentUser = signal(null, ...(ngDevMode ? [{ debugName: "currentUser" }] : []));
28
- user = this.currentUser.asReadonly();
29
- isAuthenticated = computed(() => this.currentUser()?.isAuthenticated === true, ...(ngDevMode ? [{ debugName: "isAuthenticated" }] : []));
30
- constructor() {
31
- this.checkAuth();
32
- }
33
- async login(email, password) {
34
- await firstValueFrom(this.http.post(`${this.config.apiBasePath}/login?useCookies=true`, { email, password }));
35
- await this.csrfRefresh();
36
- await this.checkAuth();
37
- }
38
- async loginTwoFactor(twoFactorCode, twoFactorRecoveryCode) {
39
- await firstValueFrom(this.http.post(`${this.config.apiBasePath}/login?useCookies=true`, {
40
- twoFactorCode,
41
- twoFactorRecoveryCode,
42
- }));
43
- await this.csrfRefresh();
44
- await this.checkAuth();
45
- }
46
- async register(email, password) {
47
- await firstValueFrom(this.http.post(`${this.config.apiBasePath}/register`, { email, password }));
48
- }
49
- async logout() {
50
- await firstValueFrom(this.http.post(`${this.config.apiBasePath}/logout`, {}));
51
- await this.csrfRefresh();
52
- this.currentUser.set(null);
53
- }
54
- async csrfRefresh() {
55
- await firstValueFrom(this.http.post(`${this.config.apiBasePath}/csrf-refresh`, {}));
56
- }
57
- async checkAuth() {
58
- try {
59
- const user = await firstValueFrom(this.http.get(`${this.config.apiBasePath}/me`));
60
- this.currentUser.set(user);
61
- return user;
62
- }
63
- catch {
64
- this.currentUser.set(null);
65
- return null;
66
- }
67
- }
68
- async forgotPassword(email) {
69
- await firstValueFrom(this.http.post(`${this.config.apiBasePath}/forgotPassword`, { email }));
70
- }
71
- async resetPassword(email, resetCode, newPassword) {
72
- await firstValueFrom(this.http.post(`${this.config.apiBasePath}/resetPassword`, {
73
- email,
74
- resetCode,
75
- newPassword,
76
- }));
77
- }
78
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
79
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthService, providedIn: 'root' });
80
- }
81
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthService, decorators: [{
82
- type: Injectable,
83
- args: [{ providedIn: 'root' }]
84
- }], ctorParameters: () => [] });
85
-
86
- /** Read the global language signal set by @mintplayer/ng-spark */
87
- const currentLanguage = globalThis.__sparkCurrentLanguage ?? signal('en');
88
- class SparkAuthTranslationService {
89
- http = inject(HttpClient);
90
- translationsMap = signal({}, ...(ngDevMode ? [{ debugName: "translationsMap" }] : []));
91
- constructor() {
92
- this.loadTranslations();
93
- }
94
- async loadTranslations() {
95
- try {
96
- const t = await firstValueFrom(this.http.get('/spark/translations'));
97
- this.translationsMap.set(t);
98
- }
99
- catch {
100
- // Translations failed to load; keys will be shown as-is
101
- }
102
- }
103
- t(key) {
104
- const ts = this.translationsMap()[key];
105
- if (!ts)
106
- return key;
107
- const lang = currentLanguage();
108
- return ts[lang] ?? ts['en'] ?? Object.values(ts)[0] ?? key;
109
- }
110
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthTranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
111
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthTranslationService, providedIn: 'root' });
112
- }
113
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthTranslationService, decorators: [{
114
- type: Injectable,
115
- args: [{ providedIn: 'root' }]
116
- }], ctorParameters: () => [] });
117
-
118
- class TranslateKeyPipe {
119
- translation = inject(SparkAuthTranslationService);
120
- transform(key) {
121
- return this.translation.t(key);
122
- }
123
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslateKeyPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
124
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: TranslateKeyPipe, isStandalone: true, name: "t", pure: false });
125
- }
126
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TranslateKeyPipe, decorators: [{
127
- type: Pipe,
128
- args: [{ name: 't', pure: false, standalone: true }]
129
- }] });
130
-
131
- const sparkAuthInterceptor = (req, next) => {
132
- const config = inject(SPARK_AUTH_CONFIG);
133
- const router = inject(Router);
134
- return next(req).pipe(tap({
135
- error: (error) => {
136
- if (error.status === 401 &&
137
- !req.url.startsWith(config.apiBasePath)) {
138
- router.navigate([config.loginUrl], {
139
- queryParams: { returnUrl: router.url },
140
- });
141
- }
142
- },
143
- }));
144
- };
145
-
146
- const sparkAuthGuard = (route, state) => {
147
- const authService = inject(SparkAuthService);
148
- const router = inject(Router);
149
- const config = inject(SPARK_AUTH_CONFIG);
150
- if (authService.isAuthenticated()) {
151
- return true;
152
- }
153
- return router.createUrlTree([config.loginUrl], {
154
- queryParams: { returnUrl: state.url },
155
- });
156
- };
1
+ import { defaultSparkAuthConfig, SPARK_AUTH_CONFIG } from '@mintplayer/ng-spark-auth/models';
2
+ export { SPARK_AUTH_CONFIG, defaultSparkAuthConfig } from '@mintplayer/ng-spark-auth/models';
3
+ import { makeEnvironmentProviders } from '@angular/core';
4
+ import { withInterceptors, withXsrfConfiguration } from '@angular/common/http';
5
+ import { sparkAuthInterceptor } from '@mintplayer/ng-spark-auth/interceptors';
157
6
 
158
7
  function provideSparkAuth(config) {
159
8
  return makeEnvironmentProviders([
@@ -170,374 +19,20 @@ function withSparkAuth() {
170
19
  ];
171
20
  }
172
21
 
173
- function resolveEntry(entry, defaultPath, defaultLoader) {
174
- if (entry === undefined || typeof entry === 'string') {
175
- return {
176
- path: typeof entry === 'string' ? entry : defaultPath,
177
- loadComponent: defaultLoader,
178
- };
179
- }
180
- return {
181
- path: entry.path,
182
- loadComponent: entry.component
183
- ? () => Promise.resolve(entry.component)
184
- : defaultLoader,
185
- };
186
- }
187
- function sparkAuthRoutes(config) {
188
- const login = resolveEntry(config?.login, 'login', () => Promise.resolve().then(function () { return sparkLogin_component; }).then(m => m.SparkLoginComponent));
189
- const twoFactor = resolveEntry(config?.twoFactor, 'login/two-factor', () => Promise.resolve().then(function () { return sparkTwoFactor_component; }).then(m => m.SparkTwoFactorComponent));
190
- const register = resolveEntry(config?.register, 'register', () => Promise.resolve().then(function () { return sparkRegister_component; }).then(m => m.SparkRegisterComponent));
191
- const forgotPassword = resolveEntry(config?.forgotPassword, 'forgot-password', () => Promise.resolve().then(function () { return sparkForgotPassword_component; }).then(m => m.SparkForgotPasswordComponent));
192
- const resetPassword = resolveEntry(config?.resetPassword, 'reset-password', () => Promise.resolve().then(function () { return sparkResetPassword_component; }).then(m => m.SparkResetPasswordComponent));
193
- const paths = {
194
- login: '/' + login.path,
195
- twoFactor: '/' + twoFactor.path,
196
- register: '/' + register.path,
197
- forgotPassword: '/' + forgotPassword.path,
198
- resetPassword: '/' + resetPassword.path,
199
- };
200
- return [
201
- {
202
- path: '',
203
- providers: [
204
- { provide: SPARK_AUTH_ROUTE_PATHS, useValue: paths },
205
- ],
206
- children: [
207
- { path: login.path, loadComponent: login.loadComponent },
208
- { path: twoFactor.path, loadComponent: twoFactor.loadComponent },
209
- { path: register.path, loadComponent: register.loadComponent },
210
- { path: forgotPassword.path, loadComponent: forgotPassword.loadComponent },
211
- { path: resetPassword.path, loadComponent: resetPassword.loadComponent },
212
- ],
213
- },
214
- ];
215
- }
216
-
217
- class SparkAuthBarComponent {
218
- authService = inject(SparkAuthService);
219
- config = inject(SPARK_AUTH_CONFIG);
220
- router = inject(Router);
221
- async onLogout() {
222
- await this.authService.logout();
223
- this.router.navigateByUrl('/');
224
- }
225
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
226
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkAuthBarComponent, isStandalone: true, selector: "spark-auth-bar", ngImport: i0, template: "@if (authService.isAuthenticated()) {\n <span class=\"me-2\">{{ authService.user()?.userName }}</span>\n <button class=\"btn btn-outline-light btn-sm\" (click)=\"onLogout()\">{{ 'authLogout' | t }}</button>\n} @else {\n <a class=\"btn btn-outline-light btn-sm\" [routerLink]=\"config.loginUrl\">{{ 'authLogin' | t }}</a>\n}\n", dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
227
- }
228
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkAuthBarComponent, decorators: [{
229
- type: Component,
230
- args: [{ selector: 'spark-auth-bar', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [RouterLink, TranslateKeyPipe], template: "@if (authService.isAuthenticated()) {\n <span class=\"me-2\">{{ authService.user()?.userName }}</span>\n <button class=\"btn btn-outline-light btn-sm\" (click)=\"onLogout()\">{{ 'authLogout' | t }}</button>\n} @else {\n <a class=\"btn btn-outline-light btn-sm\" [routerLink]=\"config.loginUrl\">{{ 'authLogin' | t }}</a>\n}\n" }]
231
- }] });
232
-
233
- class SparkLoginComponent {
234
- fb = inject(FormBuilder);
235
- authService = inject(SparkAuthService);
236
- router = inject(Router);
237
- route = inject(ActivatedRoute);
238
- config = inject(SPARK_AUTH_CONFIG);
239
- translation = inject(SparkAuthTranslationService);
240
- routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
241
- colors = Color;
242
- loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
243
- errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
244
- form = this.fb.group({
245
- email: ['', Validators.required],
246
- password: ['', Validators.required],
247
- rememberMe: [false],
248
- });
249
- async onSubmit() {
250
- if (this.form.invalid)
251
- return;
252
- this.loading.set(true);
253
- this.errorMessage.set('');
254
- const { email, password } = this.form.value;
255
- try {
256
- await this.authService.login(email, password);
257
- const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');
258
- this.router.navigateByUrl(returnUrl || this.config.defaultRedirectUrl);
259
- }
260
- catch (err) {
261
- if (err instanceof HttpErrorResponse && err.status === 401 && err.error?.detail === 'RequiresTwoFactor') {
262
- const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');
263
- this.router.navigate([this.routePaths.twoFactor], {
264
- queryParams: returnUrl ? { returnUrl } : undefined,
265
- });
266
- }
267
- else {
268
- this.errorMessage.set(this.translation.t('authInvalidCredentials'));
269
- }
270
- }
271
- finally {
272
- this.loading.set(false);
273
- }
274
- }
275
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkLoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
276
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkLoginComponent, isStandalone: true, selector: "spark-login", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authLogin' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"text\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"username\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"password\" class=\"form-label\">{{ 'authPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n autocomplete=\"current-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <bs-toggle-button [type]=\"'checkbox'\" formControlName=\"rememberMe\" [name]=\"'rememberMe'\">{{ 'authRememberMe' | t }}</bs-toggle-button>\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authLogin' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.register\">{{ 'authCreateAccount' | t }}</a>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.forgotPassword\">{{ 'authForgotPassword' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["rounded"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["noPadding"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsToggleButtonComponent, selector: "bs-toggle-button", inputs: ["type", "isToggled", "name", "value", "group"], outputs: ["isToggledChange"] }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
277
- }
278
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkLoginComponent, decorators: [{
279
- type: Component,
280
- args: [{ selector: 'spark-login', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsToggleButtonComponent, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authLogin' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"text\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"username\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"password\" class=\"form-label\">{{ 'authPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n autocomplete=\"current-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <bs-toggle-button [type]=\"'checkbox'\" formControlName=\"rememberMe\" [name]=\"'rememberMe'\">{{ 'authRememberMe' | t }}</bs-toggle-button>\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authLogin' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.register\">{{ 'authCreateAccount' | t }}</a>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.forgotPassword\">{{ 'authForgotPassword' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n" }]
281
- }] });
282
-
283
- var sparkLogin_component = /*#__PURE__*/Object.freeze({
284
- __proto__: null,
285
- SparkLoginComponent: SparkLoginComponent,
286
- default: SparkLoginComponent
287
- });
288
-
289
- class SparkTwoFactorComponent {
290
- fb = inject(FormBuilder);
291
- authService = inject(SparkAuthService);
292
- router = inject(Router);
293
- route = inject(ActivatedRoute);
294
- config = inject(SPARK_AUTH_CONFIG);
295
- translation = inject(SparkAuthTranslationService);
296
- routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
297
- colors = Color;
298
- loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
299
- errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
300
- useRecoveryCode = signal(false, ...(ngDevMode ? [{ debugName: "useRecoveryCode" }] : []));
301
- form = this.fb.group({
302
- code: [''],
303
- recoveryCode: [''],
304
- });
305
- toggleRecoveryCode() {
306
- this.useRecoveryCode.update(v => !v);
307
- this.errorMessage.set('');
308
- }
309
- async onSubmit() {
310
- const isRecovery = this.useRecoveryCode();
311
- const code = isRecovery ? this.form.value.recoveryCode : this.form.value.code;
312
- if (!code?.trim()) {
313
- this.errorMessage.set(isRecovery
314
- ? this.translation.t('authEnterRecoveryCodeError')
315
- : this.translation.t('authEnterCodeError'));
316
- return;
317
- }
318
- this.loading.set(true);
319
- this.errorMessage.set('');
320
- const twoFactorCode = isRecovery ? undefined : code;
321
- const twoFactorRecoveryCode = isRecovery ? code : undefined;
322
- try {
323
- await this.authService.loginTwoFactor(twoFactorCode ?? '', twoFactorRecoveryCode);
324
- const returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');
325
- this.router.navigateByUrl(returnUrl || this.config.defaultRedirectUrl);
326
- }
327
- catch {
328
- this.errorMessage.set(this.translation.t('authInvalidCode'));
329
- }
330
- finally {
331
- this.loading.set(false);
332
- }
333
- }
334
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkTwoFactorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
335
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkTwoFactorComponent, isStandalone: true, selector: "spark-two-factor", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authTwoFactorTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n @if (!useRecoveryCode()) {\n <div class=\"mb-3\">\n <label for=\"code\" class=\"form-label\">{{ 'authCode' | t }}</label>\n <input\n type=\"text\"\n id=\"code\"\n formControlName=\"code\"\n autocomplete=\"one-time-code\"\n maxlength=\"6\"\n [placeholder]=\"'authEnterCode' | t\"\n />\n </div>\n } @else {\n <div class=\"mb-3\">\n <label for=\"recoveryCode\" class=\"form-label\">{{ 'authRecoveryCode' | t }}</label>\n <input\n type=\"text\"\n id=\"recoveryCode\"\n formControlName=\"recoveryCode\"\n autocomplete=\"off\"\n [placeholder]=\"'authEnterRecoveryCode' | t\"\n />\n </div>\n }\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authVerify' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <button class=\"btn btn-link\" (click)=\"toggleRecoveryCode()\">\n @if (useRecoveryCode()) {\n {{ 'authUseAuthCode' | t }}\n } @else {\n {{ 'authUseRecoveryCode' | t }}\n }\n </button>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["rounded"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["noPadding"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
336
- }
337
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkTwoFactorComponent, decorators: [{
338
- type: Component,
339
- args: [{ selector: 'spark-two-factor', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authTwoFactorTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n @if (!useRecoveryCode()) {\n <div class=\"mb-3\">\n <label for=\"code\" class=\"form-label\">{{ 'authCode' | t }}</label>\n <input\n type=\"text\"\n id=\"code\"\n formControlName=\"code\"\n autocomplete=\"one-time-code\"\n maxlength=\"6\"\n [placeholder]=\"'authEnterCode' | t\"\n />\n </div>\n } @else {\n <div class=\"mb-3\">\n <label for=\"recoveryCode\" class=\"form-label\">{{ 'authRecoveryCode' | t }}</label>\n <input\n type=\"text\"\n id=\"recoveryCode\"\n formControlName=\"recoveryCode\"\n autocomplete=\"off\"\n [placeholder]=\"'authEnterRecoveryCode' | t\"\n />\n </div>\n }\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authVerify' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <button class=\"btn btn-link\" (click)=\"toggleRecoveryCode()\">\n @if (useRecoveryCode()) {\n {{ 'authUseAuthCode' | t }}\n } @else {\n {{ 'authUseRecoveryCode' | t }}\n }\n </button>\n </div>\n <div class=\"mt-2 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n" }]
340
- }] });
341
-
342
- var sparkTwoFactor_component = /*#__PURE__*/Object.freeze({
343
- __proto__: null,
344
- SparkTwoFactorComponent: SparkTwoFactorComponent,
345
- default: SparkTwoFactorComponent
346
- });
347
-
348
- function passwordMatchValidator$1(control) {
349
- const password = control.get('password');
350
- const confirmPassword = control.get('confirmPassword');
351
- if (password && confirmPassword && password.value !== confirmPassword.value) {
352
- return { passwordMismatch: true };
353
- }
354
- return null;
355
- }
356
- class SparkRegisterComponent {
357
- fb = inject(FormBuilder);
358
- authService = inject(SparkAuthService);
359
- router = inject(Router);
360
- translation = inject(SparkAuthTranslationService);
361
- routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
362
- colors = Color;
363
- loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
364
- errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
365
- form = this.fb.group({
366
- email: ['', [Validators.required, Validators.email]],
367
- password: ['', Validators.required],
368
- confirmPassword: ['', Validators.required],
369
- }, { validators: passwordMatchValidator$1 });
370
- async onSubmit() {
371
- if (this.form.invalid) {
372
- this.form.markAllAsTouched();
373
- return;
374
- }
375
- this.loading.set(true);
376
- this.errorMessage.set('');
377
- const { email, password } = this.form.value;
378
- try {
379
- await this.authService.register(email, password);
380
- this.router.navigate([this.routePaths.login], {
381
- queryParams: { registered: 'true' },
382
- });
383
- }
384
- catch (err) {
385
- if (err instanceof HttpErrorResponse) {
386
- if (err.status === 400 && err.error?.errors) {
387
- const messages = [].concat(...Object.values(err.error.errors));
388
- this.errorMessage.set(messages.join(' '));
389
- }
390
- else if (err.error?.detail) {
391
- this.errorMessage.set(err.error.detail);
392
- }
393
- else {
394
- this.errorMessage.set(this.translation.t('authRegistrationFailed'));
395
- }
396
- }
397
- else {
398
- this.errorMessage.set(this.translation.t('authRegistrationFailed'));
399
- }
400
- }
401
- finally {
402
- this.loading.set(false);
403
- }
404
- }
405
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkRegisterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
406
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkRegisterComponent, isStandalone: true, selector: "spark-register", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authRegister' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"email\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"email\"\n />\n @if (form.get('email')?.touched && form.get('email')?.hasError('email')) {\n <div class=\"text-danger mt-1\">{{ 'authInvalidEmail' | t }}</div>\n }\n </div>\n\n <div class=\"mb-3\">\n <label for=\"password\" class=\"form-label\">{{ 'authPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'authConfirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'authPasswordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authRegister' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <span>{{ 'authAlreadyHaveAccount' | t }} </span>\n <a [routerLink]=\"routePaths.login\">{{ 'authLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["rounded"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["noPadding"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
407
- }
408
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkRegisterComponent, decorators: [{
409
- type: Component,
410
- args: [{ selector: 'spark-register', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authRegister' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"email\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"email\"\n />\n @if (form.get('email')?.touched && form.get('email')?.hasError('email')) {\n <div class=\"text-danger mt-1\">{{ 'authInvalidEmail' | t }}</div>\n }\n </div>\n\n <div class=\"mb-3\">\n <label for=\"password\" class=\"form-label\">{{ 'authPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"password\"\n formControlName=\"password\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'authConfirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'authPasswordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authRegister' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <span>{{ 'authAlreadyHaveAccount' | t }} </span>\n <a [routerLink]=\"routePaths.login\">{{ 'authLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n" }]
411
- }] });
412
-
413
- var sparkRegister_component = /*#__PURE__*/Object.freeze({
414
- __proto__: null,
415
- SparkRegisterComponent: SparkRegisterComponent,
416
- default: SparkRegisterComponent
417
- });
418
-
419
- class SparkForgotPasswordComponent {
420
- fb = inject(FormBuilder);
421
- authService = inject(SparkAuthService);
422
- translation = inject(SparkAuthTranslationService);
423
- routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
424
- colors = Color;
425
- loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
426
- errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
427
- successMessage = signal('', ...(ngDevMode ? [{ debugName: "successMessage" }] : []));
428
- form = this.fb.group({
429
- email: ['', [Validators.required, Validators.email]],
430
- });
431
- async onSubmit() {
432
- if (this.form.invalid) {
433
- this.form.markAllAsTouched();
434
- return;
435
- }
436
- this.loading.set(true);
437
- this.errorMessage.set('');
438
- this.successMessage.set('');
439
- const { email } = this.form.value;
440
- try {
441
- await this.authService.forgotPassword(email);
442
- }
443
- catch {
444
- // Don't reveal whether the email exists
445
- }
446
- this.successMessage.set(this.translation.t('authForgotPasswordSuccess'));
447
- this.loading.set(false);
448
- }
449
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkForgotPasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
450
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkForgotPasswordComponent, isStandalone: true, selector: "spark-forgot-password", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authForgotPasswordTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">{{ successMessage() }}</bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <p class=\"text-muted mb-3\">\n {{ 'authForgotPasswordDescription' | t }}\n </p>\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"email\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"email\"\n />\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authSendResetLink' | t }}\n </button>\n </form>\n </bs-form>\n }\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["rounded"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["noPadding"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
451
- }
452
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkForgotPasswordComponent, decorators: [{
453
- type: Component,
454
- args: [{ selector: 'spark-forgot-password', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authForgotPasswordTitle' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">{{ successMessage() }}</bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <p class=\"text-muted mb-3\">\n {{ 'authForgotPasswordDescription' | t }}\n </p>\n\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"email\" class=\"form-label\">{{ 'authEmail' | t }}</label>\n <input\n type=\"email\"\n id=\"email\"\n formControlName=\"email\"\n autocomplete=\"email\"\n />\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authSendResetLink' | t }}\n </button>\n </form>\n </bs-form>\n }\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n </div>\n </bs-card>\n</div>\n" }]
455
- }] });
456
-
457
- var sparkForgotPassword_component = /*#__PURE__*/Object.freeze({
458
- __proto__: null,
459
- SparkForgotPasswordComponent: SparkForgotPasswordComponent,
460
- default: SparkForgotPasswordComponent
461
- });
462
-
463
- function passwordMatchValidator(control) {
464
- const password = control.get('newPassword');
465
- const confirmPassword = control.get('confirmPassword');
466
- if (password && confirmPassword && password.value !== confirmPassword.value) {
467
- return { passwordMismatch: true };
468
- }
469
- return null;
470
- }
471
- class SparkResetPasswordComponent {
472
- fb = inject(FormBuilder);
473
- authService = inject(SparkAuthService);
474
- router = inject(Router);
475
- route = inject(ActivatedRoute);
476
- translation = inject(SparkAuthTranslationService);
477
- routePaths = inject(SPARK_AUTH_ROUTE_PATHS);
478
- colors = Color;
479
- loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
480
- errorMessage = signal('', ...(ngDevMode ? [{ debugName: "errorMessage" }] : []));
481
- successMessage = signal('', ...(ngDevMode ? [{ debugName: "successMessage" }] : []));
482
- email = '';
483
- code = '';
484
- form = this.fb.group({
485
- newPassword: ['', Validators.required],
486
- confirmPassword: ['', Validators.required],
487
- }, { validators: passwordMatchValidator });
488
- ngOnInit() {
489
- this.email = this.route.snapshot.queryParamMap.get('email') ?? '';
490
- this.code = this.route.snapshot.queryParamMap.get('code') ?? '';
491
- if (!this.email || !this.code) {
492
- this.errorMessage.set(this.translation.t('authInvalidResetLink'));
493
- }
494
- }
495
- async onSubmit() {
496
- if (this.form.invalid) {
497
- this.form.markAllAsTouched();
498
- return;
499
- }
500
- if (!this.email || !this.code) {
501
- this.errorMessage.set(this.translation.t('authInvalidResetLink'));
502
- return;
503
- }
504
- this.loading.set(true);
505
- this.errorMessage.set('');
506
- this.successMessage.set('');
507
- const { newPassword } = this.form.value;
508
- try {
509
- await this.authService.resetPassword(this.email, this.code, newPassword);
510
- this.successMessage.set(this.translation.t('authResetSuccess'));
511
- }
512
- catch (err) {
513
- if (err instanceof HttpErrorResponse && err.error?.detail) {
514
- this.errorMessage.set(err.error.detail);
515
- }
516
- else {
517
- this.errorMessage.set(this.translation.t('authResetFailed'));
518
- }
519
- }
520
- finally {
521
- this.loading.set(false);
522
- }
523
- }
524
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkResetPasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
525
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: SparkResetPasswordComponent, isStandalone: true, selector: "spark-reset-password", ngImport: i0, template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authResetPassword' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">\n {{ successMessage() }}\n <div class=\"mt-2\">\n <a [routerLink]=\"routePaths.login\">{{ 'authGoToLogin' | t }}</a>\n </div>\n </bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"newPassword\" class=\"form-label\">{{ 'authNewPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"newPassword\"\n formControlName=\"newPassword\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'authConfirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'authPasswordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authResetPassword' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n }\n </div>\n </bs-card>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: BsAlertComponent, selector: "bs-alert", inputs: ["type", "isVisible"], outputs: ["isVisibleChange", "afterOpenedOrClosed"] }, { kind: "component", type: BsCardComponent, selector: "bs-card", inputs: ["rounded"] }, { kind: "component", type: BsCardHeaderComponent, selector: "bs-card-header", inputs: ["noPadding"] }, { kind: "component", type: BsFormComponent, selector: "bs-form", inputs: ["action", "method"], outputs: ["submitted"] }, { kind: "directive", type: BsFormControlDirective, selector: "bs-form input:not(.no-form-control), bs-form textarea:not(.no-form-control)" }, { kind: "component", type: BsSpinnerComponent, selector: "bs-spinner", inputs: ["type", "color"] }, { kind: "pipe", type: TranslateKeyPipe, name: "t" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
526
- }
527
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SparkResetPasswordComponent, decorators: [{
528
- type: Component,
529
- args: [{ selector: 'spark-reset-password', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, RouterLink, BsAlertComponent, BsCardComponent, BsCardHeaderComponent, BsFormComponent, BsFormControlDirective, BsSpinnerComponent, TranslateKeyPipe], template: "<div class=\"d-flex justify-content-center\">\n <bs-card style=\"width: 100%; max-width: 400px;\">\n <bs-card-header>\n <h3 class=\"mb-0 text-center\">{{ 'authResetPassword' | t }}</h3>\n </bs-card-header>\n <div class=\"p-4\">\n @if (successMessage()) {\n <bs-alert [type]=\"colors.success\" class=\"mb-3\">\n {{ successMessage() }}\n <div class=\"mt-2\">\n <a [routerLink]=\"routePaths.login\">{{ 'authGoToLogin' | t }}</a>\n </div>\n </bs-alert>\n }\n\n @if (errorMessage()) {\n <bs-alert [type]=\"colors.danger\" class=\"mb-3\">{{ errorMessage() }}</bs-alert>\n }\n\n @if (!successMessage()) {\n <bs-form>\n <form [formGroup]=\"form\" (ngSubmit)=\"onSubmit()\">\n <div class=\"mb-3\">\n <label for=\"newPassword\" class=\"form-label\">{{ 'authNewPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"newPassword\"\n formControlName=\"newPassword\"\n autocomplete=\"new-password\"\n />\n </div>\n\n <div class=\"mb-3\">\n <label for=\"confirmPassword\" class=\"form-label\">{{ 'authConfirmPassword' | t }}</label>\n <input\n type=\"password\"\n id=\"confirmPassword\"\n formControlName=\"confirmPassword\"\n autocomplete=\"new-password\"\n />\n @if (form.touched && form.hasError('passwordMismatch')) {\n <div class=\"text-danger mt-1\">{{ 'authPasswordMismatch' | t }}</div>\n }\n </div>\n\n <button\n type=\"submit\"\n class=\"btn btn-primary w-100\"\n [disabled]=\"loading()\"\n >\n @if (loading()) {\n <bs-spinner class=\"me-1\" />\n }\n {{ 'authResetPassword' | t }}\n </button>\n </form>\n </bs-form>\n\n <div class=\"mt-3 text-center\">\n <a [routerLink]=\"routePaths.login\">{{ 'authBackToLogin' | t }}</a>\n </div>\n }\n </div>\n </bs-card>\n</div>\n" }]
530
- }] });
531
-
532
- var sparkResetPassword_component = /*#__PURE__*/Object.freeze({
533
- __proto__: null,
534
- SparkResetPasswordComponent: SparkResetPasswordComponent,
535
- default: SparkResetPasswordComponent
536
- });
22
+ // Root entry point bootstrap API only.
23
+ // For other members import from sub-paths:
24
+ // @mintplayer/ng-spark-auth/core (SparkAuthService, SparkAuthTranslationService)
25
+ // @mintplayer/ng-spark-auth/models (types + SPARK_AUTH_ROUTE_PATHS)
26
+ // @mintplayer/ng-spark-auth/guards (sparkAuthGuard)
27
+ // @mintplayer/ng-spark-auth/interceptors (sparkAuthInterceptor)
28
+ // @mintplayer/ng-spark-auth/pipes (TranslateKeyPipe)
29
+ // @mintplayer/ng-spark-auth/routes (sparkAuthRoutes)
30
+ // @mintplayer/ng-spark-auth/auth-bar (SparkAuthBarComponent)
31
+ // @mintplayer/ng-spark-auth/{login,two-factor,register,forgot-password,reset-password}
537
32
 
538
33
  /**
539
34
  * Generated bundle index. Do not edit.
540
35
  */
541
36
 
542
- export { SPARK_AUTH_CONFIG, SPARK_AUTH_ROUTE_PATHS, SparkAuthBarComponent, SparkAuthService, SparkAuthTranslationService, SparkForgotPasswordComponent, SparkLoginComponent, SparkRegisterComponent, SparkResetPasswordComponent, SparkTwoFactorComponent, TranslateKeyPipe, defaultSparkAuthConfig, provideSparkAuth, sparkAuthGuard, sparkAuthInterceptor, sparkAuthRoutes, withSparkAuth };
37
+ export { provideSparkAuth, withSparkAuth };
543
38
  //# sourceMappingURL=mintplayer-ng-spark-auth.mjs.map