valtech-components 2.0.463 → 2.0.465
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/esm2022/lib/services/auth/auth.service.mjs +17 -12
- package/esm2022/lib/services/auth/interceptor.mjs +24 -11
- package/esm2022/lib/services/firebase/firebase.service.mjs +18 -9
- package/fesm2022/valtech-components.mjs +56 -29
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/services/firebase/firebase.service.d.ts +8 -4
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@ import * as i1$2 from '@angular/platform-browser';
|
|
|
13
13
|
import QRCodeStyling from 'qr-code-styling';
|
|
14
14
|
import * as i1$3 from '@angular/forms';
|
|
15
15
|
import { ReactiveFormsModule, FormsModule, FormControl, Validators } from '@angular/forms';
|
|
16
|
-
import { BehaviorSubject, filter, map, distinctUntilChanged, Subject, firstValueFrom, throwError, of } from 'rxjs';
|
|
16
|
+
import { BehaviorSubject, filter, map, distinctUntilChanged, Subject, firstValueFrom, throwError, of, from } from 'rxjs';
|
|
17
17
|
import * as i1$4 from 'ng-otp-input';
|
|
18
18
|
import { NgOtpInputComponent, NgOtpInputModule } from 'ng-otp-input';
|
|
19
19
|
import * as i2 from '@ionic/angular';
|
|
@@ -38,7 +38,7 @@ import * as i1$7 from '@angular/fire/storage';
|
|
|
38
38
|
import { provideStorage, getStorage, connectStorageEmulator, ref, uploadBytesResumable, getDownloadURL, getMetadata, deleteObject, listAll } from '@angular/fire/storage';
|
|
39
39
|
import * as i1$9 from '@angular/common/http';
|
|
40
40
|
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
|
41
|
-
import { tap, catchError, switchMap, finalize, filter as filter$1, take } from 'rxjs/operators';
|
|
41
|
+
import { tap, catchError, switchMap, map as map$1, finalize, filter as filter$1, take } from 'rxjs/operators';
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* val-avatar
|
|
@@ -21480,6 +21480,11 @@ class FirebaseService {
|
|
|
21480
21480
|
async signInWithCustomToken(token) {
|
|
21481
21481
|
try {
|
|
21482
21482
|
const credential = await signInWithCustomToken(this.auth, token);
|
|
21483
|
+
// Forzar refresh del token para asegurar que los claims estén actualizados
|
|
21484
|
+
// Esto es necesario porque getIdTokenResult() cachea el resultado
|
|
21485
|
+
if (credential.user) {
|
|
21486
|
+
await credential.user.getIdToken(true);
|
|
21487
|
+
}
|
|
21483
21488
|
return credential;
|
|
21484
21489
|
}
|
|
21485
21490
|
catch (error) {
|
|
@@ -21557,14 +21562,15 @@ class FirebaseService {
|
|
|
21557
21562
|
* Obtiene los claims personalizados del token del usuario.
|
|
21558
21563
|
* Los claims son establecidos por tu backend al crear el Custom Token.
|
|
21559
21564
|
*
|
|
21565
|
+
* @param forceRefresh - Si true, fuerza la renovación del token para obtener claims actualizados
|
|
21560
21566
|
* @returns Objeto con los claims o vacío si no hay usuario
|
|
21561
21567
|
*/
|
|
21562
|
-
async getClaims() {
|
|
21568
|
+
async getClaims(forceRefresh = false) {
|
|
21563
21569
|
const user = this.auth.currentUser;
|
|
21564
21570
|
if (!user)
|
|
21565
21571
|
return {};
|
|
21566
21572
|
try {
|
|
21567
|
-
const result = await user.getIdTokenResult();
|
|
21573
|
+
const result = await user.getIdTokenResult(forceRefresh);
|
|
21568
21574
|
return result.claims;
|
|
21569
21575
|
}
|
|
21570
21576
|
catch {
|
|
@@ -21589,6 +21595,7 @@ class FirebaseService {
|
|
|
21589
21595
|
* Obtiene las memberships (organizaciones) del usuario.
|
|
21590
21596
|
* Cada membership contiene el rol y permisos en esa organización.
|
|
21591
21597
|
*
|
|
21598
|
+
* @param forceRefresh - Si true, fuerza la renovación del token
|
|
21592
21599
|
* @returns Mapa de orgId → MembershipInfo
|
|
21593
21600
|
*
|
|
21594
21601
|
* @example
|
|
@@ -21597,27 +21604,29 @@ class FirebaseService {
|
|
|
21597
21604
|
* // { 'org_abc': { roleId: 'admin', roleName: 'admin', permissions: ['users:*', ...] } }
|
|
21598
21605
|
* ```
|
|
21599
21606
|
*/
|
|
21600
|
-
async getMemberships() {
|
|
21601
|
-
const claims = await this.getClaims();
|
|
21607
|
+
async getMemberships(forceRefresh = false) {
|
|
21608
|
+
const claims = await this.getClaims(forceRefresh);
|
|
21602
21609
|
return claims['memberships'] || {};
|
|
21603
21610
|
}
|
|
21604
21611
|
/**
|
|
21605
21612
|
* Obtiene la organización activa del usuario.
|
|
21606
21613
|
* La organización activa se establece al hacer login o al cambiar de org.
|
|
21607
21614
|
*
|
|
21615
|
+
* @param forceRefresh - Si true, fuerza la renovación del token
|
|
21608
21616
|
* @returns ID de la organización activa o null si no hay ninguna
|
|
21609
21617
|
*/
|
|
21610
|
-
async getActiveOrg() {
|
|
21611
|
-
const claims = await this.getClaims();
|
|
21618
|
+
async getActiveOrg(forceRefresh = false) {
|
|
21619
|
+
const claims = await this.getClaims(forceRefresh);
|
|
21612
21620
|
return claims['activeOrg'] || null;
|
|
21613
21621
|
}
|
|
21614
21622
|
/**
|
|
21615
21623
|
* Obtiene información de todas las organizaciones del usuario.
|
|
21616
21624
|
*
|
|
21625
|
+
* @param forceRefresh - Si true, fuerza la renovación del token
|
|
21617
21626
|
* @returns Array con información de cada organización
|
|
21618
21627
|
*/
|
|
21619
|
-
async getOrganizations() {
|
|
21620
|
-
const memberships = await this.getMemberships();
|
|
21628
|
+
async getOrganizations(forceRefresh = false) {
|
|
21629
|
+
const memberships = await this.getMemberships(forceRefresh);
|
|
21621
21630
|
return Object.entries(memberships).map(([id, info]) => ({
|
|
21622
21631
|
id,
|
|
21623
21632
|
roleId: info.roleId,
|
|
@@ -24938,16 +24947,21 @@ class AuthService {
|
|
|
24938
24947
|
switchOrg(organizationId) {
|
|
24939
24948
|
return this.http
|
|
24940
24949
|
.post(`${this.baseUrl}/switch-org`, { organizationId })
|
|
24941
|
-
.pipe(
|
|
24950
|
+
.pipe(switchMap(response => {
|
|
24942
24951
|
// Re-autenticar en Firebase con el nuevo token
|
|
24943
|
-
|
|
24944
|
-
|
|
24945
|
-
|
|
24946
|
-
|
|
24947
|
-
|
|
24948
|
-
|
|
24949
|
-
|
|
24950
|
-
|
|
24952
|
+
// Usar switchMap + from para esperar a que Firebase complete
|
|
24953
|
+
const firebaseAuth$ = response.firebaseToken
|
|
24954
|
+
? from(this.signInWithFirebase(response.firebaseToken))
|
|
24955
|
+
: of(undefined);
|
|
24956
|
+
return firebaseAuth$.pipe(tap(() => {
|
|
24957
|
+
// Notificar cambio a otras pestañas
|
|
24958
|
+
this.syncService.broadcast({
|
|
24959
|
+
type: 'ORG_SWITCH',
|
|
24960
|
+
payload: { activeOrg: response.activeOrg },
|
|
24961
|
+
});
|
|
24962
|
+
}),
|
|
24963
|
+
// Retornar response original después de que Firebase complete
|
|
24964
|
+
map$1(() => response));
|
|
24951
24965
|
}), catchError(error => this.handleAuthError(error)));
|
|
24952
24966
|
}
|
|
24953
24967
|
// =============================================
|
|
@@ -25216,8 +25230,8 @@ const authInterceptor = (request, next) => {
|
|
|
25216
25230
|
if (!isApiRequest(request, config.apiUrl)) {
|
|
25217
25231
|
return next(request);
|
|
25218
25232
|
}
|
|
25219
|
-
// Omitir endpoints
|
|
25220
|
-
if (
|
|
25233
|
+
// Omitir endpoints públicos que no necesitan token
|
|
25234
|
+
if (isPublicEndpoint(request, config.authPrefix)) {
|
|
25221
25235
|
return next(request);
|
|
25222
25236
|
}
|
|
25223
25237
|
const accessToken = authService.accessToken();
|
|
@@ -25226,7 +25240,7 @@ const authInterceptor = (request, next) => {
|
|
|
25226
25240
|
request = addAuthHeader(request, accessToken);
|
|
25227
25241
|
}
|
|
25228
25242
|
return next(request).pipe(catchError((error) => {
|
|
25229
|
-
if (error.status === 401 && !
|
|
25243
|
+
if (error.status === 401 && !isNoRefreshEndpoint(request, config.authPrefix)) {
|
|
25230
25244
|
return handle401Error(request, next, authService);
|
|
25231
25245
|
}
|
|
25232
25246
|
if (error.status === 403) {
|
|
@@ -25252,22 +25266,35 @@ function isApiRequest(request, apiUrl) {
|
|
|
25252
25266
|
return request.url.startsWith(apiUrl) || request.url.includes('/v2/auth');
|
|
25253
25267
|
}
|
|
25254
25268
|
/**
|
|
25255
|
-
* Verifica si la request es a un endpoint
|
|
25269
|
+
* Verifica si la request es a un endpoint público que NO requiere token.
|
|
25256
25270
|
*/
|
|
25257
|
-
function
|
|
25258
|
-
|
|
25271
|
+
function isPublicEndpoint(request, authPrefix) {
|
|
25272
|
+
const publicEndpoints = [
|
|
25273
|
+
'/signin',
|
|
25274
|
+
'/signup',
|
|
25275
|
+
'/refresh',
|
|
25276
|
+
'/logout',
|
|
25277
|
+
'/mfa/verify', // Solo durante login
|
|
25278
|
+
];
|
|
25279
|
+
return publicEndpoints.some((endpoint) => request.url.includes(`${authPrefix}${endpoint}`));
|
|
25280
|
+
}
|
|
25281
|
+
/**
|
|
25282
|
+
* Verifica si la request es a un endpoint que NO debe reintentar refresh en 401.
|
|
25283
|
+
*/
|
|
25284
|
+
function isNoRefreshEndpoint(request, authPrefix) {
|
|
25285
|
+
// Endpoints que no deben intentar refresh en 401:
|
|
25259
25286
|
// - Públicos: signin, signup, refresh, logout
|
|
25260
|
-
// - MFA: verify (durante login),
|
|
25261
|
-
const
|
|
25287
|
+
// - MFA: verify (durante login), confirm (401 = código incorrecto), disable (401 = contraseña incorrecta)
|
|
25288
|
+
const noRefreshEndpoints = [
|
|
25262
25289
|
'/signin',
|
|
25263
25290
|
'/signup',
|
|
25264
25291
|
'/refresh',
|
|
25265
25292
|
'/logout',
|
|
25266
25293
|
'/mfa/verify',
|
|
25267
|
-
'/mfa/disable',
|
|
25268
25294
|
'/mfa/confirm',
|
|
25295
|
+
'/mfa/disable',
|
|
25269
25296
|
];
|
|
25270
|
-
return
|
|
25297
|
+
return noRefreshEndpoints.some((endpoint) => request.url.includes(`${authPrefix}${endpoint}`));
|
|
25271
25298
|
}
|
|
25272
25299
|
/**
|
|
25273
25300
|
* Maneja errores 401 refrescando el token.
|