ngx-sp-auth 4.23.15 → 4.23.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/fesm2022/ngx-sp-auth.mjs
CHANGED
|
@@ -821,14 +821,20 @@ class AuthStorageService {
|
|
|
821
821
|
});
|
|
822
822
|
}
|
|
823
823
|
}
|
|
824
|
+
/**
|
|
825
|
+
* Verifica a sessão atual no servidor (`/verify`).
|
|
826
|
+
* Chamadas simultâneas compartilham a mesma requisição em andamento.
|
|
827
|
+
* @returns Status da sessão e, quando válida, a identidade (domínio/usuário) informada pelo servidor.
|
|
828
|
+
*/
|
|
824
829
|
checkAuthentication() {
|
|
825
830
|
if (!this.__authenticationCheck$) {
|
|
826
|
-
let errorMessage = '';
|
|
827
831
|
this.__authenticationCheck$ = this.verifyToken()
|
|
828
|
-
.pipe(map((response) => {
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
+
.pipe(map((response) => ({
|
|
833
|
+
error: response.ErrorMessage,
|
|
834
|
+
isLoggedIn: !response.Error,
|
|
835
|
+
dominio: response.Dominio,
|
|
836
|
+
usuario: response.Usuario
|
|
837
|
+
})), catchError((err) => {
|
|
832
838
|
this.isLoggedInSub.next(false);
|
|
833
839
|
return of({ error: err.error.ErrorMessage, isLoggedIn: false });
|
|
834
840
|
}), finalize(() => this.__authenticationCheck$ = null), shareReplay({ bufferSize: 1, refCount: true }));
|
|
@@ -2353,6 +2359,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.25", ngImpo
|
|
|
2353
2359
|
}], ctorParameters: () => [] });
|
|
2354
2360
|
|
|
2355
2361
|
class LoginOSComponent {
|
|
2362
|
+
/**
|
|
2363
|
+
* Nome do lock (Web Locks API) usado para garantir que apenas um login integrado OS
|
|
2364
|
+
* seja processado por vez no navegador, mesmo com várias abas abertas simultaneamente.
|
|
2365
|
+
*/
|
|
2366
|
+
static { this.LOGIN_OS_LOCK_NAME = 'ngx-sp-auth:login-os'; }
|
|
2356
2367
|
// #endregion PUBLIC
|
|
2357
2368
|
// #endregion ==========> PROPERTIES <==========
|
|
2358
2369
|
// #region ==========> INITIALIZATION <==========
|
|
@@ -2379,65 +2390,105 @@ class LoginOSComponent {
|
|
|
2379
2390
|
}
|
|
2380
2391
|
});
|
|
2381
2392
|
this.getParams();
|
|
2382
|
-
this.logOn();
|
|
2393
|
+
this.runExclusive(() => this.logOn());
|
|
2383
2394
|
}
|
|
2384
2395
|
// #endregion ==========> INITIALIZATION <==========
|
|
2385
2396
|
// #region ==========> API METHODS <==========
|
|
2386
2397
|
// #region LOGIN
|
|
2387
|
-
|
|
2398
|
+
/**
|
|
2399
|
+
* Processa o login integrado OS → V6.
|
|
2400
|
+
*
|
|
2401
|
+
* O fluxo é totalmente sequencial para evitar que respostas concorrentes sobrescrevam/expirem os cookies de autenticação:
|
|
2402
|
+
* 1. Aguarda a verificação da sessão atual (`/verify`). Se já houver uma verificação em andamento
|
|
2403
|
+
* (ex.: disparada pelo construtor do `AuthStorageService`), ela é reaproveitada.
|
|
2404
|
+
* Assim, um eventual 401 (que expira os cookies) acontece ANTES do novo login gravar os cookies.
|
|
2405
|
+
* 2. Se a sessão do servidor já pertence ao mesmo domínio/usuário do payload, reaproveita a sessão (sem criar outra).
|
|
2406
|
+
* 3. Caso contrário, realiza o login do zero.
|
|
2407
|
+
*
|
|
2408
|
+
* Deve ser executado via `runExclusive()` para que apenas uma aba processe o login por vez.
|
|
2409
|
+
*/
|
|
2410
|
+
async logOn() {
|
|
2388
2411
|
this.loginStatus = "loading";
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
this._storageService.infraEstabNome = res.InfraEstabNome;
|
|
2401
|
-
this._storageService.infraEmpresaId = res.InfraEmpresaId;
|
|
2402
|
-
this._storageService.infraEmpresaNome = res.InfraEmpresaNome;
|
|
2403
|
-
// Após, redireciona para a URL de destino
|
|
2404
|
-
status = "keep";
|
|
2405
|
-
this.redirect();
|
|
2406
|
-
return;
|
|
2407
|
-
},
|
|
2408
|
-
error: error => {
|
|
2409
|
-
this.loginStatus = "error";
|
|
2410
|
-
this._authUtilService.showHTTPErrorOS(error);
|
|
2411
|
-
}
|
|
2412
|
-
});
|
|
2412
|
+
const hadPreviousUser = !!this._storageService.dominio && !!this._storageService.user;
|
|
2413
|
+
try {
|
|
2414
|
+
// (1) Serializa com qualquer /verify em andamento
|
|
2415
|
+
const check = await firstValueFrom(this._storageService.checkAuthentication());
|
|
2416
|
+
// (2) Sessão atual válida e do mesmo usuário do payload → reaproveita
|
|
2417
|
+
if (check.isLoggedIn && this.isSameUserSession(check.dominio, check.usuario)) {
|
|
2418
|
+
const kept = await this.keepCurrentSession();
|
|
2419
|
+
if (kept) {
|
|
2420
|
+
this.loginStatus = "updated";
|
|
2421
|
+
this.startTimer();
|
|
2422
|
+
return;
|
|
2413
2423
|
}
|
|
2414
|
-
status = (!currDominio || !currUsuario) ? "none" : "updated";
|
|
2415
|
-
this._authService.loginOS(this._parmsLogin, this._loginOSModel.serialV6).subscribe({
|
|
2416
|
-
next: () => {
|
|
2417
|
-
if (status === "none") {
|
|
2418
|
-
this.startTimer();
|
|
2419
|
-
this.loginStatus = "success";
|
|
2420
|
-
}
|
|
2421
|
-
else {
|
|
2422
|
-
this.startTimer();
|
|
2423
|
-
this.loginStatus = "updated";
|
|
2424
|
-
}
|
|
2425
|
-
},
|
|
2426
|
-
error: (error) => {
|
|
2427
|
-
this.loginStatus = "error";
|
|
2428
|
-
this._authUtilService.showHTTPErrorOS(error);
|
|
2429
|
-
},
|
|
2430
|
-
});
|
|
2431
|
-
},
|
|
2432
|
-
error: (error) => {
|
|
2433
|
-
this.loginStatus = "error";
|
|
2434
|
-
this._authUtilService.showHTTPErrorOS(error);
|
|
2435
2424
|
}
|
|
2436
|
-
|
|
2425
|
+
// (3) Login do zero
|
|
2426
|
+
await firstValueFrom(this._authService.getAuthentication(this._loginOSModel.dominio));
|
|
2427
|
+
await firstValueFrom(this._authService.loginOS(this._parmsLogin, this._loginOSModel.serialV6));
|
|
2428
|
+
this.loginStatus = hadPreviousUser ? "updated" : "success";
|
|
2429
|
+
this.startTimer();
|
|
2430
|
+
}
|
|
2431
|
+
catch (error) {
|
|
2432
|
+
this.loginStatus = "error";
|
|
2433
|
+
this._authUtilService.showHTTPErrorOS(error);
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Reaproveita a sessão atual, atualizando os dados do estabelecimento logado.
|
|
2438
|
+
* Só reaproveita se os dados locais (localStorage) também forem do mesmo usuário, pois as telas de destino dependem deles.
|
|
2439
|
+
* @returns `true` se a sessão foi reaproveitada; `false` se deve seguir com um login novo.
|
|
2440
|
+
*/
|
|
2441
|
+
async keepCurrentSession() {
|
|
2442
|
+
const localMatches = this.equalsIgnoreCase(this._storageService.dominio, this._loginOSModel.dominio)
|
|
2443
|
+
&& this.equalsIgnoreCase(this._storageService.user, this._loginOSModel.usuario)
|
|
2444
|
+
&& !!this._storageService.infraUsuarioId;
|
|
2445
|
+
if (!localMatches)
|
|
2446
|
+
return false;
|
|
2447
|
+
try {
|
|
2448
|
+
// Atualiza o último estabelecimento logado do usuário
|
|
2449
|
+
const res = await firstValueFrom(this._loginOSUtils.getEstabelecimentoSession(''));
|
|
2450
|
+
this._storageService.infraEstabId = res.InfraEstabId;
|
|
2451
|
+
this._storageService.infraEstabNome = res.InfraEstabNome;
|
|
2452
|
+
this._storageService.infraEmpresaId = res.InfraEmpresaId;
|
|
2453
|
+
this._storageService.infraEmpresaNome = res.InfraEmpresaNome;
|
|
2454
|
+
return true;
|
|
2455
|
+
}
|
|
2456
|
+
catch (error) {
|
|
2457
|
+
// Não foi possível confirmar os dados da sessão atual: segue com login novo
|
|
2458
|
+
console.warn('login-os: não foi possível reaproveitar a sessão atual, realizando novo login.', error);
|
|
2459
|
+
return false;
|
|
2460
|
+
}
|
|
2437
2461
|
}
|
|
2438
2462
|
// #endregion LOGIN
|
|
2439
2463
|
// #endregion ==========> API METHODS <==========
|
|
2440
2464
|
// #region ==========> UTILS <==========
|
|
2465
|
+
/**
|
|
2466
|
+
* Executa a função de forma exclusiva no navegador (entre abas da mesma origem) usando a Web Locks API.
|
|
2467
|
+
* Se a API não estiver disponível, executa diretamente.
|
|
2468
|
+
*/
|
|
2469
|
+
runExclusive(fn) {
|
|
2470
|
+
const locks = navigator.locks;
|
|
2471
|
+
if (locks?.request) {
|
|
2472
|
+
locks.request(LoginOSComponent.LOGIN_OS_LOCK_NAME, () => fn())
|
|
2473
|
+
.catch(error => {
|
|
2474
|
+
console.error('login-os: falha ao obter lock, executando sem exclusividade.', error);
|
|
2475
|
+
fn();
|
|
2476
|
+
});
|
|
2477
|
+
}
|
|
2478
|
+
else {
|
|
2479
|
+
fn();
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
/** Verifica se a sessão informada pelo servidor pertence ao mesmo domínio/usuário do payload. */
|
|
2483
|
+
isSameUserSession(dominio, usuario) {
|
|
2484
|
+
return this.equalsIgnoreCase(dominio, this._loginOSModel.dominio)
|
|
2485
|
+
&& this.equalsIgnoreCase(usuario, this._loginOSModel.usuario);
|
|
2486
|
+
}
|
|
2487
|
+
equalsIgnoreCase(a, b) {
|
|
2488
|
+
if (!a || !b)
|
|
2489
|
+
return false;
|
|
2490
|
+
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
2491
|
+
}
|
|
2441
2492
|
redirect() {
|
|
2442
2493
|
let hostname = window.location.host == "localhost" ? "v6siscandes.sispro.com.br" : window.location.host;
|
|
2443
2494
|
let baseURL = `https://${hostname}/SisproErpCloud`;
|