@acontplus/ng-auth 1.1.0 → 1.1.2
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/README.md +66 -47
- package/fesm2022/acontplus-ng-auth.mjs +56 -87
- package/fesm2022/acontplus-ng-auth.mjs.map +1 -1
- package/index.d.ts +29 -50
- package/package.json +3 -3
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { UserRepository, BaseUseCase } from '@acontplus/ng-infrastructure';
|
|
1
|
+
import { UserRepository, BaseUseCase, LoggingService, TOKEN_PROVIDER } from '@acontplus/ng-infrastructure';
|
|
2
2
|
export { TOKEN_PROVIDER } from '@acontplus/ng-infrastructure';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { inject, PLATFORM_ID, Injectable, signal, input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
4
|
+
import { inject, PLATFORM_ID, Injectable, NgZone, signal, input, computed, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
5
5
|
import { Router } from '@angular/router';
|
|
6
6
|
import * as i1 from '@angular/common';
|
|
7
7
|
import { isPlatformBrowser, DOCUMENT, CommonModule } from '@angular/common';
|
|
8
8
|
import { jwtDecode } from 'jwt-decode';
|
|
9
9
|
import { ENVIRONMENT, AUTH_API } from '@acontplus/ng-config';
|
|
10
10
|
import { catchError, switchMap } from 'rxjs/operators';
|
|
11
|
-
import { throwError,
|
|
11
|
+
import { throwError, firstValueFrom, map, from, of, tap, catchError as catchError$1 } from 'rxjs';
|
|
12
12
|
import { HttpClient } from '@angular/common/http';
|
|
13
|
-
import { AuthTokens } from '@acontplus/core';
|
|
14
13
|
import * as i2 from '@angular/forms';
|
|
15
14
|
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
16
15
|
import { MatCard, MatCardHeader, MatCardTitle, MatCardContent, MatCardFooter } from '@angular/material/card';
|
|
@@ -29,7 +28,7 @@ class TokenRepository {
|
|
|
29
28
|
this.setRefreshToken(tokens.refreshToken, rememberMe);
|
|
30
29
|
}
|
|
31
30
|
}
|
|
32
|
-
|
|
31
|
+
getToken() {
|
|
33
32
|
if (!isPlatformBrowser(this.platformId)) {
|
|
34
33
|
return null;
|
|
35
34
|
}
|
|
@@ -75,7 +74,7 @@ class TokenRepository {
|
|
|
75
74
|
sessionStorage.removeItem(this.environment.refreshTokenKey);
|
|
76
75
|
}
|
|
77
76
|
isAuthenticated() {
|
|
78
|
-
const accessToken = this.
|
|
77
|
+
const accessToken = this.getToken();
|
|
79
78
|
if (!accessToken) {
|
|
80
79
|
return false;
|
|
81
80
|
}
|
|
@@ -90,7 +89,7 @@ class TokenRepository {
|
|
|
90
89
|
}
|
|
91
90
|
}
|
|
92
91
|
needsRefresh() {
|
|
93
|
-
const accessToken = this.
|
|
92
|
+
const accessToken = this.getToken();
|
|
94
93
|
if (!accessToken) {
|
|
95
94
|
return false;
|
|
96
95
|
}
|
|
@@ -106,13 +105,13 @@ class TokenRepository {
|
|
|
106
105
|
}
|
|
107
106
|
}
|
|
108
107
|
getTokenPayload() {
|
|
109
|
-
const token = this.
|
|
108
|
+
const token = this.getToken();
|
|
110
109
|
if (!token)
|
|
111
110
|
return null;
|
|
112
111
|
try {
|
|
113
112
|
return jwtDecode(token);
|
|
114
113
|
}
|
|
115
|
-
catch
|
|
114
|
+
catch {
|
|
116
115
|
return null;
|
|
117
116
|
}
|
|
118
117
|
}
|
|
@@ -138,25 +137,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
138
137
|
}]
|
|
139
138
|
}] });
|
|
140
139
|
|
|
141
|
-
class AuthTokenService {
|
|
142
|
-
tokenRepository;
|
|
143
|
-
constructor(tokenRepository) {
|
|
144
|
-
this.tokenRepository = tokenRepository;
|
|
145
|
-
}
|
|
146
|
-
getToken() {
|
|
147
|
-
return this.tokenRepository.getAccessToken();
|
|
148
|
-
}
|
|
149
|
-
isAuthenticated() {
|
|
150
|
-
return this.tokenRepository.isAuthenticated();
|
|
151
|
-
}
|
|
152
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthTokenService, deps: [{ token: TokenRepository }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
153
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthTokenService, providedIn: 'root' });
|
|
154
|
-
}
|
|
155
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: AuthTokenService, decorators: [{
|
|
156
|
-
type: Injectable,
|
|
157
|
-
args: [{ providedIn: 'root' }]
|
|
158
|
-
}], ctorParameters: () => [{ type: TokenRepository }] });
|
|
159
|
-
|
|
160
140
|
/**
|
|
161
141
|
* Service to manage URL redirection after authentication
|
|
162
142
|
* Stores the intended URL when session is lost and redirects to it after successful login
|
|
@@ -164,7 +144,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
164
144
|
*/
|
|
165
145
|
class UrlRedirectService {
|
|
166
146
|
REDIRECT_URL_KEY = 'acp_redirect_url';
|
|
167
|
-
EXCLUDED_ROUTES = [
|
|
147
|
+
EXCLUDED_ROUTES = [
|
|
148
|
+
'/login',
|
|
149
|
+
'/auth',
|
|
150
|
+
'/register',
|
|
151
|
+
'/forgot-password',
|
|
152
|
+
'/reset-password',
|
|
153
|
+
];
|
|
168
154
|
router = inject(Router);
|
|
169
155
|
platformId = inject(PLATFORM_ID);
|
|
170
156
|
document = inject(DOCUMENT);
|
|
@@ -271,11 +257,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
271
257
|
}] });
|
|
272
258
|
|
|
273
259
|
const authGuard = (_route, state) => {
|
|
274
|
-
const
|
|
260
|
+
const tokenRepository = inject(TokenRepository);
|
|
275
261
|
const router = inject(Router);
|
|
276
262
|
const urlRedirectService = inject(UrlRedirectService);
|
|
277
263
|
const environment = inject(ENVIRONMENT);
|
|
278
|
-
if (
|
|
264
|
+
if (tokenRepository.isAuthenticated()) {
|
|
279
265
|
return true;
|
|
280
266
|
}
|
|
281
267
|
// Store the current URL for redirection after login
|
|
@@ -292,14 +278,14 @@ const authGuard = (_route, state) => {
|
|
|
292
278
|
const authRedirectInterceptor = (req, next) => {
|
|
293
279
|
const router = inject(Router);
|
|
294
280
|
const urlRedirectService = inject(UrlRedirectService);
|
|
295
|
-
const
|
|
281
|
+
const tokenRepository = inject(TokenRepository);
|
|
296
282
|
const environment = inject(ENVIRONMENT);
|
|
297
283
|
return next(req).pipe(catchError((error) => {
|
|
298
284
|
// Handle 401 Unauthorized errors
|
|
299
285
|
if (error.status === 401) {
|
|
300
286
|
// Only store and redirect if user was previously authenticated
|
|
301
287
|
// This prevents redirect loops and handles session expiry scenarios
|
|
302
|
-
if (
|
|
288
|
+
if (tokenRepository.isAuthenticated()) {
|
|
303
289
|
// Store the current URL for redirection after re-authentication
|
|
304
290
|
urlRedirectService.storeCurrentUrlIfAllowed();
|
|
305
291
|
// Navigate to login page
|
|
@@ -311,31 +297,7 @@ const authRedirectInterceptor = (req, next) => {
|
|
|
311
297
|
}));
|
|
312
298
|
};
|
|
313
299
|
|
|
314
|
-
|
|
315
|
-
id;
|
|
316
|
-
email;
|
|
317
|
-
displayName;
|
|
318
|
-
_refreshToken;
|
|
319
|
-
constructor(id, email, displayName, _refreshToken) {
|
|
320
|
-
this.id = id;
|
|
321
|
-
this.email = email;
|
|
322
|
-
this.displayName = displayName;
|
|
323
|
-
this._refreshToken = _refreshToken;
|
|
324
|
-
}
|
|
325
|
-
get refreshToken() {
|
|
326
|
-
return this._refreshToken;
|
|
327
|
-
}
|
|
328
|
-
updateRefreshToken(token) {
|
|
329
|
-
this._refreshToken = token;
|
|
330
|
-
}
|
|
331
|
-
clearRefreshToken() {
|
|
332
|
-
this._refreshToken = undefined;
|
|
333
|
-
}
|
|
334
|
-
static create(email, displayName, _password) {
|
|
335
|
-
const id = Date.now(); // Generate a numeric ID
|
|
336
|
-
return new User(id, email, displayName);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
300
|
+
// src/lib/domain/models/auth.ts
|
|
339
301
|
|
|
340
302
|
// src/lib/domain/models/index.ts
|
|
341
303
|
|
|
@@ -349,7 +311,6 @@ class AuthRepository {
|
|
|
349
311
|
// src/lib/services/csrf.service.ts
|
|
350
312
|
class CsrfService {
|
|
351
313
|
http = inject(HttpClient);
|
|
352
|
-
environment = inject(ENVIRONMENT);
|
|
353
314
|
csrfToken = null;
|
|
354
315
|
/**
|
|
355
316
|
* Get CSRF token, fetching it if not available
|
|
@@ -359,10 +320,9 @@ class CsrfService {
|
|
|
359
320
|
return this.csrfToken;
|
|
360
321
|
}
|
|
361
322
|
try {
|
|
362
|
-
|
|
363
|
-
.get(
|
|
364
|
-
.
|
|
365
|
-
this.csrfToken = response?.csrfToken || null;
|
|
323
|
+
this.csrfToken = await firstValueFrom(this.http
|
|
324
|
+
.get('/csrf-token')
|
|
325
|
+
.pipe(map(response => response.csrfToken)));
|
|
366
326
|
return this.csrfToken || '';
|
|
367
327
|
}
|
|
368
328
|
catch {
|
|
@@ -405,26 +365,22 @@ class AuthHttpRepository extends AuthRepository {
|
|
|
405
365
|
})));
|
|
406
366
|
}
|
|
407
367
|
register(request) {
|
|
408
|
-
return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http
|
|
409
|
-
.post(`${this.URL}register`, request, {
|
|
368
|
+
return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http.post(`${this.URL}register`, request, {
|
|
410
369
|
headers: {
|
|
411
370
|
'Device-Info': getDeviceInfo(),
|
|
412
371
|
'X-CSRF-Token': csrfToken,
|
|
413
372
|
},
|
|
414
373
|
withCredentials: true,
|
|
415
|
-
})
|
|
416
|
-
.pipe(map(response => new AuthTokens(response.token, response.refreshToken)))));
|
|
374
|
+
})));
|
|
417
375
|
}
|
|
418
376
|
refreshToken(request) {
|
|
419
|
-
return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http
|
|
420
|
-
.post(`${this.URL}refresh`, request, {
|
|
377
|
+
return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http.post(`${this.URL}refresh`, request, {
|
|
421
378
|
headers: {
|
|
422
379
|
'Device-Info': getDeviceInfo(),
|
|
423
380
|
'X-CSRF-Token': csrfToken,
|
|
424
381
|
},
|
|
425
382
|
withCredentials: true,
|
|
426
|
-
})
|
|
427
|
-
.pipe(map(response => new AuthTokens(response.token, response.refreshToken)))));
|
|
383
|
+
})));
|
|
428
384
|
}
|
|
429
385
|
logout(email, refreshToken) {
|
|
430
386
|
return from(this.csrfService.getCsrfToken()).pipe(switchMap(csrfToken => this.http.post(`${this.URL}logout`, { email, refreshToken: refreshToken || undefined }, {
|
|
@@ -452,6 +408,7 @@ class AuthStore {
|
|
|
452
408
|
tokenRepository = inject(TokenRepository);
|
|
453
409
|
userRepository = inject(UserRepository);
|
|
454
410
|
router = inject(Router);
|
|
411
|
+
ngZone = inject(NgZone);
|
|
455
412
|
// Authentication state signals
|
|
456
413
|
_isAuthenticated = signal(false, ...(ngDevMode ? [{ debugName: "_isAuthenticated" }] : []));
|
|
457
414
|
_isLoading = signal(false, ...(ngDevMode ? [{ debugName: "_isLoading" }] : []));
|
|
@@ -491,7 +448,7 @@ class AuthStore {
|
|
|
491
448
|
* Schedule token refresh based on actual expiration time
|
|
492
449
|
*/
|
|
493
450
|
scheduleTokenRefresh() {
|
|
494
|
-
const accessToken = this.tokenRepository.
|
|
451
|
+
const accessToken = this.tokenRepository.getToken();
|
|
495
452
|
if (!accessToken) {
|
|
496
453
|
return;
|
|
497
454
|
}
|
|
@@ -505,12 +462,16 @@ class AuthStore {
|
|
|
505
462
|
if (this.refreshTokenTimeout) {
|
|
506
463
|
clearTimeout(this.refreshTokenTimeout);
|
|
507
464
|
}
|
|
508
|
-
this.
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
465
|
+
this.ngZone.runOutsideAngular(() => {
|
|
466
|
+
this.refreshTokenTimeout = window.setTimeout(() => {
|
|
467
|
+
this.ngZone.run(() => {
|
|
468
|
+
// Check if refresh is still needed before executing
|
|
469
|
+
if (this.tokenRepository.needsRefresh()) {
|
|
470
|
+
this.refreshToken().subscribe();
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
}, refreshTime);
|
|
474
|
+
});
|
|
514
475
|
}
|
|
515
476
|
catch {
|
|
516
477
|
// Silent fail - token might be invalid
|
|
@@ -555,7 +516,7 @@ class AuthStore {
|
|
|
555
516
|
},
|
|
556
517
|
error: () => {
|
|
557
518
|
this.refreshInProgress$ = undefined;
|
|
558
|
-
}
|
|
519
|
+
},
|
|
559
520
|
}));
|
|
560
521
|
return this.refreshInProgress$;
|
|
561
522
|
}
|
|
@@ -584,7 +545,7 @@ class AuthStore {
|
|
|
584
545
|
error: () => {
|
|
585
546
|
// Server logout failed, still clear client-side data for security
|
|
586
547
|
this.performClientLogout();
|
|
587
|
-
}
|
|
548
|
+
},
|
|
588
549
|
});
|
|
589
550
|
}
|
|
590
551
|
else {
|
|
@@ -744,7 +705,9 @@ class LogoutUseCase extends BaseUseCase {
|
|
|
744
705
|
const userData = this.userRepository.getCurrentUser();
|
|
745
706
|
const refreshToken = this.tokenRepository.getRefreshToken();
|
|
746
707
|
if (userData?.email && refreshToken && refreshToken.length > 0) {
|
|
747
|
-
return this.authRepository
|
|
708
|
+
return this.authRepository
|
|
709
|
+
.logout(userData.email, refreshToken)
|
|
710
|
+
.pipe(tap(() => this.cleanup()));
|
|
748
711
|
}
|
|
749
712
|
this.cleanup();
|
|
750
713
|
return of(void 0);
|
|
@@ -785,8 +748,10 @@ class LoginComponent {
|
|
|
785
748
|
// Computed signal to check if footer content exists
|
|
786
749
|
hasFooterContent = computed(() => this.footerContent() !== null, ...(ngDevMode ? [{ debugName: "hasFooterContent" }] : []));
|
|
787
750
|
fb = inject(FormBuilder);
|
|
751
|
+
authStore = inject(AuthStore);
|
|
788
752
|
loginUseCase = inject(LoginUseCase);
|
|
789
753
|
registerUseCase = inject(RegisterUseCase);
|
|
754
|
+
loggingService = inject(LoggingService);
|
|
790
755
|
// Angular 20+ signals
|
|
791
756
|
isLoginMode = signal(true, ...(ngDevMode ? [{ debugName: "isLoginMode" }] : []));
|
|
792
757
|
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
@@ -832,7 +797,7 @@ class LoginComponent {
|
|
|
832
797
|
const loginRequest = {
|
|
833
798
|
...this.signinForm.value,
|
|
834
799
|
// If showRememberMe is false, default rememberMe to false
|
|
835
|
-
rememberMe: this.showRememberMe() ? (this.signinForm.value.rememberMe ?? false) : false
|
|
800
|
+
rememberMe: this.showRememberMe() ? (this.signinForm.value.rememberMe ?? false) : false,
|
|
836
801
|
};
|
|
837
802
|
this.loginUseCase.execute(loginRequest).subscribe({
|
|
838
803
|
next: () => {
|
|
@@ -841,7 +806,7 @@ class LoginComponent {
|
|
|
841
806
|
error: error => {
|
|
842
807
|
this.isLoading.set(false);
|
|
843
808
|
this.errorMessage.set('Error al iniciar sesión. Verifique sus credenciales.');
|
|
844
|
-
|
|
809
|
+
this.loggingService.error('Login failed', { error });
|
|
845
810
|
},
|
|
846
811
|
});
|
|
847
812
|
}
|
|
@@ -859,13 +824,13 @@ class LoginComponent {
|
|
|
859
824
|
error: error => {
|
|
860
825
|
this.isLoading.set(false);
|
|
861
826
|
this.errorMessage.set('Error al registrar usuario. Intente nuevamente.');
|
|
862
|
-
|
|
827
|
+
this.loggingService.error('Register error', { error });
|
|
863
828
|
},
|
|
864
829
|
});
|
|
865
830
|
}
|
|
866
831
|
}
|
|
867
832
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
868
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: LoginComponent, isStandalone: true, selector: "acp-login", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, showRegisterButton: { classPropertyName: "showRegisterButton", publicName: "showRegisterButton", isSignal: true, isRequired: false, transformFunction: null }, showRememberMe: { classPropertyName: "showRememberMe", publicName: "showRememberMe", isSignal: true, isRequired: false, transformFunction: null }, additionalSigninControls: { classPropertyName: "additionalSigninControls", publicName: "additionalSigninControls", isSignal: true, isRequired: false, transformFunction: null }, additionalSignupControls: { classPropertyName: "additionalSignupControls", publicName: "additionalSignupControls", isSignal: true, isRequired: false, transformFunction: null }, additionalSigninFields: { classPropertyName: "additionalSigninFields", publicName: "additionalSigninFields", isSignal: true, isRequired: false, transformFunction: null }, additionalSignupFields: { classPropertyName: "additionalSignupFields", publicName: "additionalSignupFields", isSignal: true, isRequired: false, transformFunction: null }, footerContent: { classPropertyName: "footerContent", publicName: "footerContent", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<section id=\"wrapper\" class=\"d-flex justify-content-center align-items-center\">\n <mat-card class=\"mat-elevation-z8 p-4 rounded\">\n <mat-card-header>\n <mat-card-title class=\"text-center\">{{ title() }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (isLoginMode()) {\n <form [formGroup]=\"signinForm\" (ngSubmit)=\"signIn()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Usuario</mat-label>\n <input matInput type=\"text\" placeholder=\"Ingrese su usuario\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input matInput type=\"password\" placeholder=\"Ingrese su contrase\u00F1a\" formControlName=\"password\" />\n </mat-form-field>\n\n <!-- Remember Me checkbox - conditional -->\n @if (showRememberMe()) {\n <div class=\"d-flex align-items-center mt-2\">\n <mat-checkbox formControlName=\"rememberMe\">\n Recordarme\n </mat-checkbox>\n </div>\n }\n\n <!-- Additional signin fields -->\n <ng-container *ngTemplateOutlet=\"additionalSigninFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button mat-raised-button color=\"primary\" [disabled]=\"!signinForm.valid || isLoading()\" type=\"submit\"\n class=\"w-100\">\n @if (isLoading()) {\n Ingresando...\n } @else {\n <ng-container>Ingresar <mat-icon>login</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n @if (showRegisterButton()) {\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFNo tienes cuenta? Reg\u00EDstrate\n </button>\n }\n </div>\n </form>\n } @else {\n <form [formGroup]=\"signupForm\" (ngSubmit)=\"registerUser()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Nombre</mat-label>\n <input matInput type=\"text\" placeholder=\"Ingrese su nombre\" formControlName=\"displayName\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Email</mat-label>\n <input matInput type=\"email\" placeholder=\"Ingrese su email\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input matInput type=\"password\" placeholder=\"Ingrese su contrase\u00F1a\" formControlName=\"password\" />\n </mat-form-field>\n\n <!-- Additional signup fields -->\n <ng-container *ngTemplateOutlet=\"additionalSignupFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button mat-raised-button color=\"primary\" [disabled]=\"!signupForm.valid || isLoading()\" type=\"submit\"\n class=\"w-100\">\n @if (isLoading()) {\n Registrando...\n } @else {\n <ng-container>Registrarse <mat-icon>person_add</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n <button mat-button type=\"button\" (click)=\"switchMode()\">\u00BFYa tienes cuenta? Inicia sesi\u00F3n</button>\n </div>\n </form>\n }\n @if (errorMessage()) {\n <div class=\"alert alert-danger mt-3\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n }\n </mat-card-content>\n @if (hasFooterContent()) {\n <mat-card-footer>\n <ng-container *ngTemplateOutlet=\"footerContent()\"></ng-container>\n </mat-card-footer>\n }\n </mat-card>\n</section>\n", styles: ["#wrapper{min-height:100vh;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,var(--mdc-theme-primary, #5c5f5c) 0%,var(--mdc-theme-secondary, #79747e) 100%);padding:20px}mat-card{width:100%;max-width:400px;border-radius:12px;box-shadow:0 8px 32px #0000001a}mat-card-header{text-align:center;margin-bottom:20px}mat-card-title{font-size:24px;font-weight:600;color:var(--mdc-theme-on-surface, #1c1b1f)}mat-form-field{width:100%}.w-100{width:100%}.d-flex{display:flex}.flex-column{flex-direction:column}.gap-3{gap:12px}.justify-content-center{justify-content:center}.align-items-center{align-items:center}.mt-3{margin-top:12px}.mt-2{margin-top:8px}.text-center{text-align:center}.p-4{padding:16px}.rounded{border-radius:12px}.alert-danger{background-color:var(--mdc-theme-error-container, #ffdad6);border-color:var(--mdc-theme-error, #ba1a1a);color:var(--mdc-theme-on-error-container, #410002);padding:12px;border-radius:4px;border:1px solid transparent}.row{display:flex;flex-wrap:wrap;margin:0 -15px}.col-xs-12,.col-sm-12,.col-md-12{flex:0 0 100%;max-width:100%;padding:0 15px}.m-t-10{margin-top:10px}.social{display:flex;justify-content:center}mat-button{border-radius:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "directive", type: MatCardContent, selector: "mat-card-content" }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatCardFooter, selector: "mat-card-footer" }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
833
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.2", type: LoginComponent, isStandalone: true, selector: "acp-login", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, showRegisterButton: { classPropertyName: "showRegisterButton", publicName: "showRegisterButton", isSignal: true, isRequired: false, transformFunction: null }, showRememberMe: { classPropertyName: "showRememberMe", publicName: "showRememberMe", isSignal: true, isRequired: false, transformFunction: null }, additionalSigninControls: { classPropertyName: "additionalSigninControls", publicName: "additionalSigninControls", isSignal: true, isRequired: false, transformFunction: null }, additionalSignupControls: { classPropertyName: "additionalSignupControls", publicName: "additionalSignupControls", isSignal: true, isRequired: false, transformFunction: null }, additionalSigninFields: { classPropertyName: "additionalSigninFields", publicName: "additionalSigninFields", isSignal: true, isRequired: false, transformFunction: null }, additionalSignupFields: { classPropertyName: "additionalSignupFields", publicName: "additionalSignupFields", isSignal: true, isRequired: false, transformFunction: null }, footerContent: { classPropertyName: "footerContent", publicName: "footerContent", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<mat-card class=\"mat-elevation-z8 p-4 rounded\">\n <mat-card-header>\n <mat-card-title class=\"text-center\">{{ title() }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (isLoginMode()) {\n <form [formGroup]=\"signinForm\" (ngSubmit)=\"signIn()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Usuario</mat-label>\n <input matInput type=\"text\" placeholder=\"Ingrese su usuario\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input\n matInput\n type=\"password\"\n placeholder=\"Ingrese su contrase\u00F1a\"\n formControlName=\"password\"\n autocomplete=\"current-password\"\n />\n </mat-form-field>\n\n <!-- Remember Me checkbox - conditional -->\n @if (showRememberMe()) {\n <div class=\"d-flex align-items-center mt-2\">\n <mat-checkbox formControlName=\"rememberMe\"> Recordarme </mat-checkbox>\n </div>\n }\n\n <!-- Additional signin fields -->\n <ng-container *ngTemplateOutlet=\"additionalSigninFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button\n mat-raised-button\n color=\"primary\"\n [disabled]=\"!signinForm.valid || isLoading()\"\n type=\"submit\"\n class=\"w-100\"\n >\n @if (isLoading()) {\n Ingresando...\n } @else {\n <ng-container>Ingresar <mat-icon>login</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n @if (showRegisterButton()) {\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFNo tienes cuenta? Reg\u00EDstrate\n </button>\n }\n </div>\n </form>\n } @else {\n <form [formGroup]=\"signupForm\" (ngSubmit)=\"registerUser()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Nombre</mat-label>\n <input\n matInput\n type=\"text\"\n placeholder=\"Ingrese su nombre\"\n formControlName=\"displayName\"\n />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Email</mat-label>\n <input matInput type=\"email\" placeholder=\"Ingrese su email\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input\n matInput\n type=\"password\"\n placeholder=\"Ingrese su contrase\u00F1a\"\n formControlName=\"password\"\n autocomplete=\"new-password\"\n />\n </mat-form-field>\n\n <!-- Additional signup fields -->\n <ng-container *ngTemplateOutlet=\"additionalSignupFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button\n mat-raised-button\n color=\"primary\"\n [disabled]=\"!signupForm.valid || isLoading()\"\n type=\"submit\"\n class=\"w-100\"\n >\n @if (isLoading()) {\n Registrando...\n } @else {\n <ng-container>Registrarse <mat-icon>person_add</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFYa tienes cuenta? Inicia sesi\u00F3n\n </button>\n </div>\n </form>\n }\n @if (errorMessage()) {\n <div class=\"alert alert-danger mt-3\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n }\n </mat-card-content>\n @if (hasFooterContent()) {\n <mat-card-footer>\n <ng-container *ngTemplateOutlet=\"footerContent()\"></ng-container>\n </mat-card-footer>\n }\n</mat-card>\n", styles: [":host{display:flex;justify-content:center;align-items:center;min-height:100vh;width:100%;padding:16px;box-sizing:border-box}mat-card{width:100%;max-width:400px;border-radius:12px;box-shadow:0 8px 32px #0000001a}mat-card-header{text-align:center;margin-bottom:20px}mat-card-title{font-size:24px;font-weight:600;color:var(--mat-sys-on-surface, #1c1b1f)}mat-form-field{width:100%}.w-100{width:100%}.d-flex{display:flex}.flex-column{flex-direction:column}.gap-3{gap:12px}.justify-content-center{justify-content:center}.align-items-center{align-items:center}.mt-3{margin-top:12px}.mt-2{margin-top:8px}.text-center{text-align:center}.p-4{padding:16px}.rounded{border-radius:12px}.alert-danger{background-color:var(--mat-sys-error-container, #ffdad6);border-color:var(--mat-sys-error, #ba1a1a);color:var(--mat-sys-on-error-container, #410002);padding:12px;border-radius:4px;border:1px solid transparent}.row{display:flex;flex-wrap:wrap;margin:0 -15px}.col-xs-12,.col-sm-12,.col-md-12{flex:0 0 100%;max-width:100%;padding:0 15px}.m-t-10{margin-top:10px}.social{display:flex;justify-content:center}mat-button{border-radius:8px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "directive", type: MatCardContent, selector: "mat-card-content" }, { kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatCardFooter, selector: "mat-card-footer" }, { kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
869
834
|
}
|
|
870
835
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImport: i0, type: LoginComponent, decorators: [{
|
|
871
836
|
type: Component,
|
|
@@ -884,7 +849,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.2", ngImpor
|
|
|
884
849
|
MatCardFooter,
|
|
885
850
|
MatAnchor,
|
|
886
851
|
MatCheckbox,
|
|
887
|
-
], changeDetection: ChangeDetectionStrategy.OnPush,
|
|
852
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<mat-card class=\"mat-elevation-z8 p-4 rounded\">\n <mat-card-header>\n <mat-card-title class=\"text-center\">{{ title() }}</mat-card-title>\n </mat-card-header>\n <mat-card-content>\n @if (isLoginMode()) {\n <form [formGroup]=\"signinForm\" (ngSubmit)=\"signIn()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Usuario</mat-label>\n <input matInput type=\"text\" placeholder=\"Ingrese su usuario\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input\n matInput\n type=\"password\"\n placeholder=\"Ingrese su contrase\u00F1a\"\n formControlName=\"password\"\n autocomplete=\"current-password\"\n />\n </mat-form-field>\n\n <!-- Remember Me checkbox - conditional -->\n @if (showRememberMe()) {\n <div class=\"d-flex align-items-center mt-2\">\n <mat-checkbox formControlName=\"rememberMe\"> Recordarme </mat-checkbox>\n </div>\n }\n\n <!-- Additional signin fields -->\n <ng-container *ngTemplateOutlet=\"additionalSigninFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button\n mat-raised-button\n color=\"primary\"\n [disabled]=\"!signinForm.valid || isLoading()\"\n type=\"submit\"\n class=\"w-100\"\n >\n @if (isLoading()) {\n Ingresando...\n } @else {\n <ng-container>Ingresar <mat-icon>login</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n @if (showRegisterButton()) {\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFNo tienes cuenta? Reg\u00EDstrate\n </button>\n }\n </div>\n </form>\n } @else {\n <form [formGroup]=\"signupForm\" (ngSubmit)=\"registerUser()\" class=\"d-flex flex-column gap-3\">\n <mat-form-field class=\"w-100\">\n <mat-label>Nombre</mat-label>\n <input\n matInput\n type=\"text\"\n placeholder=\"Ingrese su nombre\"\n formControlName=\"displayName\"\n />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Email</mat-label>\n <input matInput type=\"email\" placeholder=\"Ingrese su email\" formControlName=\"email\" />\n </mat-form-field>\n\n <mat-form-field class=\"w-100\">\n <mat-label>Contrase\u00F1a</mat-label>\n <input\n matInput\n type=\"password\"\n placeholder=\"Ingrese su contrase\u00F1a\"\n formControlName=\"password\"\n autocomplete=\"new-password\"\n />\n </mat-form-field>\n\n <!-- Additional signup fields -->\n <ng-container *ngTemplateOutlet=\"additionalSignupFields()\"></ng-container>\n\n <div class=\"d-flex justify-content-center mt-3\">\n <button\n mat-raised-button\n color=\"primary\"\n [disabled]=\"!signupForm.valid || isLoading()\"\n type=\"submit\"\n class=\"w-100\"\n >\n @if (isLoading()) {\n Registrando...\n } @else {\n <ng-container>Registrarse <mat-icon>person_add</mat-icon></ng-container>\n }\n </button>\n </div>\n\n <div class=\"text-center mt-2\">\n <button mat-button type=\"button\" (click)=\"switchMode()\">\n \u00BFYa tienes cuenta? Inicia sesi\u00F3n\n </button>\n </div>\n </form>\n }\n @if (errorMessage()) {\n <div class=\"alert alert-danger mt-3\" role=\"alert\">\n {{ errorMessage() }}\n </div>\n }\n </mat-card-content>\n @if (hasFooterContent()) {\n <mat-card-footer>\n <ng-container *ngTemplateOutlet=\"footerContent()\"></ng-container>\n </mat-card-footer>\n }\n</mat-card>\n", styles: [":host{display:flex;justify-content:center;align-items:center;min-height:100vh;width:100%;padding:16px;box-sizing:border-box}mat-card{width:100%;max-width:400px;border-radius:12px;box-shadow:0 8px 32px #0000001a}mat-card-header{text-align:center;margin-bottom:20px}mat-card-title{font-size:24px;font-weight:600;color:var(--mat-sys-on-surface, #1c1b1f)}mat-form-field{width:100%}.w-100{width:100%}.d-flex{display:flex}.flex-column{flex-direction:column}.gap-3{gap:12px}.justify-content-center{justify-content:center}.align-items-center{align-items:center}.mt-3{margin-top:12px}.mt-2{margin-top:8px}.text-center{text-align:center}.p-4{padding:16px}.rounded{border-radius:12px}.alert-danger{background-color:var(--mat-sys-error-container, #ffdad6);border-color:var(--mat-sys-error, #ba1a1a);color:var(--mat-sys-on-error-container, #410002);padding:12px;border-radius:4px;border:1px solid transparent}.row{display:flex;flex-wrap:wrap;margin:0 -15px}.col-xs-12,.col-sm-12,.col-md-12{flex:0 0 100%;max-width:100%;padding:0 15px}.m-t-10{margin-top:10px}.social{display:flex;justify-content:center}mat-button{border-radius:8px}\n"] }]
|
|
888
853
|
}], ctorParameters: () => [] });
|
|
889
854
|
|
|
890
855
|
// src/lib/presentation/components/index.ts
|
|
@@ -896,6 +861,10 @@ const authProviders = [
|
|
|
896
861
|
provide: AuthRepository,
|
|
897
862
|
useClass: AuthHttpRepository,
|
|
898
863
|
},
|
|
864
|
+
{
|
|
865
|
+
provide: TOKEN_PROVIDER,
|
|
866
|
+
useClass: TokenRepository,
|
|
867
|
+
},
|
|
899
868
|
];
|
|
900
869
|
|
|
901
870
|
// src/lib/providers/index.ts
|
|
@@ -904,5 +873,5 @@ const authProviders = [
|
|
|
904
873
|
* Generated bundle index. Do not edit.
|
|
905
874
|
*/
|
|
906
875
|
|
|
907
|
-
export { AuthHttpRepository, AuthRepository, AuthStore,
|
|
876
|
+
export { AuthHttpRepository, AuthRepository, AuthStore, LoginComponent, LoginUseCase, LogoutUseCase, RefreshTokenUseCase, RegisterUseCase, TokenRepository, UrlRedirectService, authGuard, authProviders, authRedirectInterceptor };
|
|
908
877
|
//# sourceMappingURL=acontplus-ng-auth.mjs.map
|