@haxen/auth
v1.0.21
Published
Uma biblioteca de autenticação da Haxen, projetada para acelerar e facilitar a construção de interfaces consistentes e eficientes nas aplicações.
Readme
@haxen/auth
Biblioteca de autenticacao para padronizar sessao, refresh token, persistencia e integracao React entre projetos web e mobile.
Objetivo
A lib existe para centralizar a responsabilidade de autenticacao. O consumidor integra endpoints, mapeia payloads e conecta efeitos secundarios; a lib controla sessao, persistencia e expiracao.
Responsabilidade da lib no web:
- persistir a sessao em
localStorage - manter cookie(s) de autenticacao sincronizados
- propagar mudancas de sessao para React
- executar refresh token e logout
Responsabilidade da lib no mobile:
- usar somente o
StorageAdapterinformado pelo app - nunca depender de
window,document,localStorageou cookie
Componentes principais
AuthManager: nucleo da autenticacao e da sessaoStorageAdapter: contrato de persistenciaWebStorageAdapter: adapter web comlocalStoragee cookiesAuthProvider: integracao ReactuseAuth: leitura da sessao e acesso ao managerAuthMapper: helper opcional para montarAuthSession
Modelo de sessao
type AuthSession = {
accessToken: string;
refreshToken: string;
login: string;
user: UsuarioAutenticado;
empresas: Empresa[];
empresaSelecionada: Empresa | null;
filiais?: any[];
expiresAt: number;
};Campos importantes:
accessToken: token bearer usado nas chamadas autenticadasrefreshToken: token de refreshlogin: identificador do usuariouser: payload do usuario autenticadoempresaSelecionada: contexto ativofiliais: dados complementares enriquecidos pelo projetoexpiresAt: timestamp absoluto em milissegundos para expiracao
Fluxo de vida da sessao
- O projeto instancia um
AuthManager. - O React
AuthProviderchamamanager.init(). - A sessao persistida e restaurada.
- A lib rearma o timer de expiracao.
- Quando ocorre
signIn()oureauthenticate(), a sessao e persistida novamente. - No web, os cookies configurados pelo
WebStorageAdaptersao sincronizados. - Quando a sessao expira, a lib chama
onExpired.
Referencias de integracao
mobile-inventario
Padrao coerente com a lib:
- usa
AuthManagercomAsyncStorageAdapter - deixa a sessao concentrada no manager
- usa
onPostAuthpara headers e enriquecimento - chama
restoreSession()no bootstrap e ao voltar para foreground - nao depende de cookie
front-painelvendas
Padrao correto apos alinhamento:
- usa
AuthManagercomWebStorageAdapter - a lib escreve
Auth.TokeneUser.Session - o front usa
useAuth().sessioncomo fonte do React proxy.tsusa somenteAuth.Tokenpara guard SSR/middleware- o modal de reautenticacao usa
authManager.getSession()apos refresh, nunca cookie manual
Padrao incorreto que causa regressao:
- gravar
Auth.TokeneUser.Sessionmanualmente fora da lib - usar cookie como fallback para montar o estado React
- reautenticar com token lido de cookie antigo em vez da sessao atual do manager
API publica
init()
Restaura a sessao do storage e marca o manager como inicializado.
Use quando:
- a app estiver subindo
- voce precisa evitar expiracao precoce antes do bootstrap
subscribe(listener)
Assina mudancas de sessao.
O listener e chamado quando ocorre:
signInreauthenticateupdateSessionlogoutinit/restoreSessioncom sessao existente
restoreSession()
Rele a sessao do adapter configurado e a coloca em memoria.
Use quando:
- o app voltar ao foreground no mobile
- o projeto quiser forcar uma reidratacao manual
signIn(credentials)
Executa autenticacao inicial.
Modos suportados:
ssocredentials
reauthenticate()
Executa refresh token. A lib serializa chamadas concorrentes via reauthInFlight, evitando refresh duplicado.
updateSession(patch)
Atualiza a sessao atual sem novo login.
Aceita:
Partial<AuthSession>- funcao
(current) => next
logout()
Limpa sessao em memoria, storage e cookies gerenciados pela lib.
getSession()
Retorna a sessao atual em memoria.
Use quando:
- o projeto precisa ler o estado mais recente dentro de callback assicrona
- um handler de expiracao quer consultar a sessao apos
reauthenticate() - um interceptor quer acessar o token atual sem depender de render React
onPostAuth(hook)
Registra efeitos secundarios apos login e refresh.
Casos tipicos:
- configurar header
Authorization - carregar menu
- buscar filiais
- preencher analytics/telemetria
onExpired(handler)
Registra o fluxo de expiracao.
A lib nao impõe UX. O projeto decide se abre modal, renova automaticamente ou faz logout.
Contratos principais
StorageAdapter
abstract class StorageAdapter {
abstract get<T>(key: string): Promise<T | null>;
abstract set<T>(key: string, value: T): Promise<void>;
abstract remove(key: string): Promise<void>;
}AuthConfig
type AuthConfig<TMode extends 'sso' | 'credentials', TRawResponse> = {
storage: StorageAdapter;
httpClient: {
post<T>(
url: string,
body?: unknown,
config?: { headers?: Record<string, string> },
): Promise<{ data: T }>;
get<T>(
url: string,
config?: { headers?: Record<string, string>; signal?: AbortSignal },
): Promise<{ data: T }>;
};
mode: TMode;
endpoints: {
signIn?: string;
sso?: string;
refresh: string;
authenticatedUser?: string;
};
sessionMapper: (raw: TRawResponse) => AuthSession;
refreshBodyMapper?: (session: AuthSession) => unknown;
refreshMerger?: (current: AuthSession, raw: RefreshResponse) => AuthSession;
};O que faz cada campo
storage: onde a sessao sera persistidahttpClient: cliente HTTP usado pela libmode: define fluxossooucredentialsendpoints.signIn: login por credenciaisendpoints.sso: login via token SSO/magic tokenendpoints.refresh: refresh tokenendpoints.authenticatedUser:GETcomplementar para buscar usuario autenticado emcredentialssessionMapper: converte payload bruto da API paraAuthSessionrefreshBodyMapper: customiza o body do refreshrefreshMerger: customiza como a resposta de refresh atualiza a sessao atual
WebStorageAdapter
O WebStorageAdapter implementa o comportamento esperado para projetos browser.
Comportamento padrão:
- salva a sessao em
localStorage - grava cookie
Auth.Token - por padrao, tambem grava
User.Sessioncom snapshot completo da sessao sem duplicar os tokens - remove cookies gerenciados pela lib no logout
Configuracao suportada:
type WebStorageAdapterOptions = {
tokenCookieKey?: string; // padrao: 'Auth.Token'
sessionCookieKey?: string; // padrao: 'User.Session'
cookiePath?: string; // padrao: '/'
cookieDomain?: string;
sameSite?: 'Lax' | 'Strict' | 'None'; // padrao: 'Lax'
secure?: boolean; // por padrao, detecta https
};Regra operacional
tokenCookieKeydeve ser usado quando a aplicacao precisa que middleware/proxy/SSR leiam o token- por padrao, a lib tambem grava
User.Session sessionCookieKeypermite customizar o nome desse cookie quando o projeto precisar- o snapshot de sessao inclui
login,expiresAt,usercompleto,empresas,empresaSelecionadaefiliais - como o cookie carrega
usercompleto, valide o tamanho final do payload; navegadores aplicam limite por cookie e payloads muito grandes podem ser descartados - a fonte principal da sessao no React continua sendo
useAuth().session, nunca cookie
Fluxo sso
Use quando o projeto recebe um token SSO via URL, deep link ou portal.
Comportamento:
POSTemendpoints.ssosessionMapper- persistencia
- hooks de
onPostAuth
Exemplo:
const authManager = new AuthManager<'sso', any>({
storage: new WebStorageAdapter(),
httpClient: api,
mode: 'sso',
endpoints: {
sso: '/Auth/v1/Autenticacao/authenticate',
refresh: '/Auth/v1/Autenticacao/refresh-token',
},
sessionMapper: data => ({
accessToken: data.autenticacao.accessToken,
refreshToken: data.autenticacao.refreshToken,
login: data.usuario.login,
user: data.usuario,
empresas: data.usuario.empresas,
empresaSelecionada: data.usuario.empresas?.[0] ?? null,
filiais: [],
expiresAt: data.usuario.expiraEm
? new Date(data.usuario.expiraEm).getTime()
: Date.now() + (data.autenticacao.expiresIn ?? 3600) * 1000,
}),
refreshBodyMapper: session => ({
login: session.login,
refreshToken: session.refreshToken,
codigoCliente: session.empresaSelecionada?.codigoCliente,
codigoEmpresa: session.empresaSelecionada?.codigoEmpresa,
}),
});Fluxo credentials
Use quando o projeto autentica com login e senha.
Comportamento:
POSTemendpoints.signIn- opcionalmente
GETemendpoints.authenticatedUser - merge do payload
sessionMapper- persistencia
Exemplo:
const authManager = new AuthManager<'credentials', any>({
storage: new WebStorageAdapter(),
httpClient: api,
mode: 'credentials',
endpoints: {
signIn: '/Auth/v1/Autenticacao/login',
authenticatedUser: '/Admin/v1/Usuario/Autenticado',
refresh: '/Auth/v1/Autenticacao/refresh-token',
},
sessionMapper: data => ({
accessToken: data.autenticacao.accessToken,
refreshToken: data.autenticacao.refreshToken,
login: data.usuario.login,
user: data.usuario,
empresas: data.usuario.empresas,
empresaSelecionada: data.usuario.empresas?.[0] ?? null,
filiais: data.usuario.filiais ?? [],
expiresAt: new Date(data.usuario.expiraEm).getTime(),
}),
});Como integrar efeitos secundarios
Header de autenticacao
authManager.onPostAuth(async session => {
api.defaults.headers.common.Authorization = `Bearer ${session.accessToken}`;
});Enriquecer a sessao com dados adicionais
authManager.onPostAuth(async session => {
const filiais = await getUserBranchs(
session.accessToken,
session.user.codigoUsuario,
);
await authManager.updateSession({ filiais });
});Modal de expiracao
authManager.onExpired(({ reauthenticate, logout }) => {
openModal({
onConfirm: () => reauthenticate(),
onCancel: () => logout(),
});
});Boa pratica:
- dentro do callback de expiracao, leia
authManager.getSession()quando precisar do estado atual - nao use cookie como fonte de verdade da sessao do componente
Integracao React
Provider
<AuthProvider manager={authManager}>
<App />
</AuthProvider>O AuthProvider da lib:
- chama
manager.init() - restaura a sessao
- assina
subscribe() - atualiza automaticamente
useAuth().session
Hook
const { session, manager } = useAuth();Use:
sessionpara renderizacaomanagerpara comandos e callbacks
Implementacao recomendada no web
- Instancie o manager com
new WebStorageAdapter(). - O comportamento padrao ja grava
Auth.TokeneUser.Session. - Use
sessionCookieKeyapenas quando precisar customizar o nome do cookie de snapshot da sessao. - Use
useAuth().sessioncomo fonte principal no React. - Deixe a lib ser dona do cookie
Auth.Token. - Deixe a lib ser dona tambem do cookie
User.Sessionou do nome customizado configurado. - Use cookie apenas onde Next/middleware realmente precisar.
Contrato de persistencia no web
localStorage:- sempre recebe a
AuthSessioncompleta sob@haxen/auth/session
- sempre recebe a
- cookie
Auth.Token:- contem somente o bearer token
- existe para middleware, proxy, SSR e integracoes externas ao React
- cookie
User.Sessionou configurado emsessionCookieKey:- contem snapshot da sessao sem duplicar
accessTokenourefreshToken - inclui
login,expiresAt,usercompleto,empresas,empresaSelecionadaefiliais
- contem snapshot da sessao sem duplicar
Leitura correta no web
- componente React:
useAuth().session - callback fora do React:
authManager.getSession() - middleware/proxy Next: cookie
Auth.Token - necessidades excepcionais de SSR com dados do usuario: cookie
User.Sessionou equivalente customizado
Exemplo:
export const authManager = new AuthManager<'sso', any>({
storage: new WebStorageAdapter({
tokenCookieKey: 'Auth.Token',
sessionCookieKey: 'User.Session',
}),
httpClient: api,
mode: 'sso',
endpoints: {
sso: '/Auth/v1/Autenticacao/authenticate',
refresh: '/Auth/v1/Autenticacao/refresh-token',
},
sessionMapper,
refreshBodyMapper,
refreshMerger,
});O que o front web nao deve fazer
- gravar
Auth.Tokenmanualmente - gravar
User.Sessionmanualmente - usar cookie para montar o estado React da sessao
- manter copia paralela da sessao apenas para contornar falta de subscribe
Implementacao recomendada no mobile
- Implemente um adapter mobile para
StorageAdapter. - Passe esse adapter ao
AuthManager. - Chame
authManager.init()no bootstrap. - Configure headers em
onPostAuth. - Se fizer sentido, reexecute
restoreSession()ao voltar ao foreground.
Exemplo de adapter:
import AsyncStorage from '@react-native-async-storage/async-storage';
import type { StorageAdapter } from '@haxen/auth';
export class AsyncStorageAdapter implements StorageAdapter {
async get<T>(key: string): Promise<T | null> {
const value = await AsyncStorage.getItem(key);
return value ? JSON.parse(value) : null;
}
async set<T>(key: string, value: T): Promise<void> {
await AsyncStorage.setItem(key, JSON.stringify(value));
}
async remove(key: string): Promise<void> {
await AsyncStorage.removeItem(key);
}
}Bootstrap:
useEffect(() => {
(async () => {
await authManager.init();
const restored = await authManager.restoreSession();
setSession(restored);
})();
}, []);Contrato de persistencia no mobile
- a lib nao cria cookie
- a lib nao acessa
window,documentoulocalStorage - toda persistencia depende exclusivamente do adapter informado pelo app
- se o app precisar trocar
AsyncStoragepor secure storage, a troca acontece no adapter sem mudar oAuthManager
Diferença entre web e mobile
- Web:
WebStorageAdapterlocalStorage- cookie
Auth.Token - cookie
User.Sessionpor padrao
- Mobile:
- adapter customizado
- storage informado pelo app
- sem cookie
Erros comuns
- nao chamar
init()no bootstrap - mapear
expiresAtincorretamente - depender de cookie dentro do React
- regravar
Auth.Tokenfora da lib - regravar
User.Sessionfora da lib - esquecer de atualizar headers do cliente HTTP em
onPostAuth - nao remover o
ssoda URL depois do login web
Troubleshooting
Modal de reautenticacao abrindo repetidamente no web
Causas mais comuns:
- cookie manual ficou diferente da sessao real do manager
onExpiredabriu modal lendo estado stale- o front refez header com token lido do cookie em vez do token atualizado por
reauthenticate() - havia mais de uma fonte de verdade para sessao
Correcao recomendada:
- manter
WebStorageAdaptercomo unico dono deAuth.TokeneUser.Session - usar
useAuth().sessionouauthManager.getSession() - apos
reauthenticate(), reler a sessao do manager e atualizar headers com ela - remover qualquer
setCookie/destroyCookiemanual ligado a auth
Cookie de sessao nao contem dados suficientes
Se o projeto precisar de dados completos do usuario fora do React:
- use o cookie padrao
User.Sessionou configuresessionCookieKey - deixe a lib sincronizar esse cookie
- consuma esse cookie apenas em middleware/SSR/integracao, nunca como fonte principal de renderizacao
Checklist de integracao
Web
AuthManagerconfigurado comWebStorageAdaptersessionMapperimplementadorefreshBodyMapperimplementado quando a API exigirAuthProviderenvolvendo a aplicacaouseAuth().sessioncomo fonte principal- nenhum
setCookie('Auth.Token')manual no front - nenhum
setCookie('User.Session')manual no front
Mobile
- adapter implementando
StorageAdapter authManager.init()chamado no bootstrap- headers configurados em
onPostAuth - sessao restaurada ao iniciar e quando necessario
Resumo
- A lib e dona da sessao.
- No web, a lib e dona dos cookies de autenticacao configurados.
- O consumidor integra UX e efeitos secundarios, mas nao reimplementa persistencia auth.
useAuth().sessiondeve ser a fonte principal para componentes React.
