feat: agregar contexto de operación y mejorar la restauración de sesión en el proveedor de autenticación
This commit is contained in:
parent
1a8d63373d
commit
ce9f9029e3
@ -71,7 +71,7 @@ export type IdentityInternalDeps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const buildIdentityDependencies = (params: ModuleParams): IdentityInternalDeps => {
|
export const buildIdentityDependencies = (params: ModuleParams): IdentityInternalDeps => {
|
||||||
const { database, config, transactionContext } = params;
|
const { database, config, operationContext, transactionContext } = params;
|
||||||
|
|
||||||
const transactionManager = buildTransactionManager(database, transactionContext);
|
const transactionManager = buildTransactionManager(database, transactionContext);
|
||||||
|
|
||||||
@ -120,6 +120,7 @@ export const buildIdentityDependencies = (params: ModuleParams): IdentityInterna
|
|||||||
authenticateRequest: identityAuthenticateRequest({
|
authenticateRequest: identityAuthenticateRequest({
|
||||||
accessTokenVerifier,
|
accessTokenVerifier,
|
||||||
accountRepository,
|
accountRepository,
|
||||||
|
operationContext,
|
||||||
}),
|
}),
|
||||||
useCases: {
|
useCases: {
|
||||||
loginWithPassword: () =>
|
loginWithPassword: () =>
|
||||||
@ -152,6 +153,7 @@ export const buildIdentityDependencies = (params: ModuleParams): IdentityInterna
|
|||||||
authenticateRequest: identityAuthenticateRequest({
|
authenticateRequest: identityAuthenticateRequest({
|
||||||
accessTokenVerifier,
|
accessTokenVerifier,
|
||||||
accountRepository,
|
accountRepository,
|
||||||
|
operationContext,
|
||||||
}),
|
}),
|
||||||
useCases: {
|
useCases: {
|
||||||
getCurrentAccount: () =>
|
getCurrentAccount: () =>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { useDataSource } from "@erp/core/hooks";
|
import { isApiClientError } from "@erp/core/client";
|
||||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
import { useDataSource } from "@erp/core/hooks";
|
||||||
|
import type { CurrentSessionResponseDTO } from "@erp/identity/common";
|
||||||
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
import type { LoginForm } from "../login/entities";
|
import type { LoginForm } from "../login/entities";
|
||||||
import {
|
import {
|
||||||
@ -16,7 +18,7 @@ import {
|
|||||||
useRefreshSessionMutation,
|
useRefreshSessionMutation,
|
||||||
} from "../shared";
|
} from "../shared";
|
||||||
import { getCurrentSession } from "../shared/api";
|
import { getCurrentSession } from "../shared/api";
|
||||||
import type { ActiveCompany, AuthSession, CompanySession } from "../shared/entities";
|
import type { ActiveCompany, AuthSession, AuthTokens, CompanySession } from "../shared/entities";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
resetIdentityAuthSessionRuntimeHandlers,
|
resetIdentityAuthSessionRuntimeHandlers,
|
||||||
@ -61,20 +63,25 @@ const IdentityAuthSessionContext = createContext<IdentityAuthSessionContextValue
|
|||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
const EMPTY_COMPANY_SESSION: CompanySession = {
|
const EMPTY_COMPANY_SESSION: CompanySession = {
|
||||||
availableCompanies: [],
|
availableCompanies: [],
|
||||||
activeCompany: null,
|
activeCompany: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isUnauthorizedError = (error: unknown) =>
|
||||||
|
isApiClientError(error) && error.status === 401;
|
||||||
|
|
||||||
export function IdentityAuthSessionProvider({ children }: { children: React.ReactNode }) {
|
export function IdentityAuthSessionProvider({ children }: { children: React.ReactNode }) {
|
||||||
const dataSource = useDataSource();
|
const dataSource = useDataSource();
|
||||||
const loginMutation = useLoginMutation();
|
const loginMutation = useLoginMutation();
|
||||||
const logoutMutation = useLogoutMutation();
|
const logoutMutation = useLogoutMutation();
|
||||||
const refreshSessionMutation = useRefreshSessionMutation();
|
const refreshSessionMutation = useRefreshSessionMutation();
|
||||||
|
const refreshSessionMutationAsync = refreshSessionMutation.mutateAsync;
|
||||||
|
|
||||||
const [session, setSession] = useState<AuthSession | null>(null);
|
const [session, setSession] = useState<AuthSession | null>(null);
|
||||||
const [companySession, setCompanySession] = useState<CompanySession>(EMPTY_COMPANY_SESSION);
|
const [companySession, setCompanySession] = useState<CompanySession>(EMPTY_COMPANY_SESSION);
|
||||||
const [isRestoringSession, setIsRestoringSession] = useState(authTokenStorage.hasAccessToken());
|
const [isRestoringSession, setIsRestoringSession] = useState(authTokenStorage.hasAccessToken());
|
||||||
|
const restoreInFlightRef = useRef<Promise<AuthSession | null> | null>(null);
|
||||||
|
|
||||||
const clearSession = useCallback(() => {
|
const clearSession = useCallback(() => {
|
||||||
authTokenStorage.clear();
|
authTokenStorage.clear();
|
||||||
@ -111,35 +118,67 @@ export function IdentityAuthSessionProvider({ children }: { children: React.Reac
|
|||||||
[dataSource]
|
[dataSource]
|
||||||
);
|
);
|
||||||
|
|
||||||
const restoreSession = useCallback(async () => {
|
const restoreSession = useCallback(() => {
|
||||||
const tokens = authTokenStorage.getTokens();
|
if (restoreInFlightRef.current) {
|
||||||
|
return restoreInFlightRef.current;
|
||||||
if (!tokens.accessToken) {
|
}
|
||||||
clearSession();
|
|
||||||
setIsRestoringSession(false);
|
const restorePromise = Promise.resolve().then(async () => {
|
||||||
return null;
|
const tokens = authTokenStorage.getTokens();
|
||||||
}
|
|
||||||
|
if (!tokens.accessToken) {
|
||||||
setIsRestoringSession(true);
|
clearSession();
|
||||||
|
setIsRestoringSession(false);
|
||||||
try {
|
return null;
|
||||||
const response = await getCurrentSession(dataSource, tokens.accessToken);
|
}
|
||||||
const restoredSession = mapCurrentSessionToAuthSession(response, {
|
|
||||||
accessToken: tokens.accessToken,
|
setIsRestoringSession(true);
|
||||||
refreshToken: tokens.refreshToken ?? "",
|
|
||||||
});
|
try {
|
||||||
|
let activeTokens: AuthTokens = {
|
||||||
await hydrateCompanySession(tokens.accessToken);
|
accessToken: tokens.accessToken,
|
||||||
setSession(restoredSession);
|
refreshToken: tokens.refreshToken ?? "",
|
||||||
|
};
|
||||||
return restoredSession;
|
let response: CurrentSessionResponseDTO;
|
||||||
} catch {
|
|
||||||
clearSession();
|
try {
|
||||||
return null;
|
response = await getCurrentSession(dataSource, activeTokens.accessToken);
|
||||||
} finally {
|
} catch (error) {
|
||||||
setIsRestoringSession(false);
|
if (!isUnauthorizedError(error) || !activeTokens.refreshToken) {
|
||||||
}
|
throw error;
|
||||||
}, [clearSession, dataSource, hydrateCompanySession]);
|
}
|
||||||
|
|
||||||
|
const refreshedTokens = await refreshSessionMutationAsync({
|
||||||
|
refreshToken: activeTokens.refreshToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
authTokenStorage.setTokens(refreshedTokens);
|
||||||
|
activeTokens = refreshedTokens;
|
||||||
|
response = await getCurrentSession(dataSource, activeTokens.accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
const restoredSession = mapCurrentSessionToAuthSession(response, {
|
||||||
|
accessToken: activeTokens.accessToken,
|
||||||
|
refreshToken: activeTokens.refreshToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
await hydrateCompanySession(activeTokens.accessToken);
|
||||||
|
setSession(restoredSession);
|
||||||
|
|
||||||
|
return restoredSession;
|
||||||
|
} catch {
|
||||||
|
clearSession();
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
restoreInFlightRef.current = null;
|
||||||
|
setIsRestoringSession(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
restoreInFlightRef.current = restorePromise;
|
||||||
|
|
||||||
|
return restorePromise;
|
||||||
|
}, [clearSession, dataSource, hydrateCompanySession, refreshSessionMutationAsync]);
|
||||||
|
|
||||||
const login = useCallback(
|
const login = useCallback(
|
||||||
async (params: LoginForm) => {
|
async (params: LoginForm) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user