SessionManager
O ciclo de vida do token que o client de navegador roda por você, e como conduzi-lo direto.
O client de navegador mantém uma sessão viva sem você fazer nada: ele guarda os tokens, percebe quando o access token está perto de expirar, troca o refresh token por um par novo, e levanta um evento quando não consegue.
Você nunca chama isso diretamente — o client de navegador é dono dele. O
auth.getAccessToken() e o
auth.refreshSession() são as portas públicas e os dois
passam por aqui. Esta página é para quando você precisa saber o que acontece por
baixo.
Só no navegador. O createServerClient não tem session manager, porque um
servidor não guarda a sessão de um usuário específico para manter viva.
O que fica guardado
access_tokenstringRequiredO que vai na requisição.
refresh_tokenstringOptionalO que compra o próximo. Sem ele uma sessão não sobrevive ao vencimento.
expires_atnumberRequiredUm timestamp epoch absoluto, calculado na gravação como now + expires_in. O
padrão é uma hora quando o servidor não informa.
userUserOptionalCarregado para o client conseguir responder "quem está autenticado" sem requisição.
Repare que ele guarda um vencimento absoluto, não o expires_in relativo
que o servidor devolve. Uma sessão guardada continua correta depois de a aba
ficar uma hora dormindo, o que não aconteceria com uma contagem regressiva.
getValidToken
getValidToken() → string | null
Devolve um token utilizável, renovando antes se ele estiver perto de expirar.
const token = await nulvless.auth.getAccessToken(); // passa por aquiA regra é uma margem de 90 segundos: se o token expira em menos que isso, ele é renovado antes de ser entregue. Essa margem existe para um token nunca ser entregue e depois rejeitado no meio do caminho — a requisição teria falhado entre a sua checagem e a do servidor.
Devolve null em três casos, e vale distingui-los:
sem sessãonullNada guardado. Ninguém autenticado.
sem refresh tokennullExiste sessão, mas ela não consegue se renovar. Levanta unauthorized.
renovação recusadanullO servidor recusou o refresh token — um 401, ou um token desconhecido, já
usado, emitido em outro lugar ou de um usuário banido. A sessão guardada é
apagada e unauthorized é levantado.
Uma renovação que apenas não conseguiu terminar — sem rede, timeout, 429,
5xx — é tentada de novo até três vezes, com backoff. Se ainda falhar, volta
null, mas a sessão é mantida, então uma conexão instável nunca desautentica
ninguém.
É por isso que segurar a string do token anula o mecanismo. Peça a cada requisição:
await fetch('/api/projects', {
headers: { Authorization: `Bearer ${await nulvless.auth.getAccessToken()}` },
});refreshSession
auth.refreshSession() → string | null
Renova o access token agora, independente de quanta vida ele ainda tenha, e
compartilha a tentativa com qualquer renovação já em andamento. Devolve o token
novo, ou null quando a sessão não pôde ser recuperada.
const token = await nulvless.auth.refreshSession();Use depois que algo mudou no servidor e o token atual ainda não carrega — scopes novos depois de aceitar um convite, por exemplo.
forceRefresh
forceRefresh({ deauthOnFailure }) → string | null
O que o refreshSession chama, e o que o próprio client chama depois de um
401 ou de um 403 forbidden_resource_access. Renova independente de quanta
vida o token atual ainda tenha — a não ser que outra aba já o tenha substituído,
caso em que esse token é devolvido sem uma segunda troca.
deauthOnFailurebooleanOptionaldefault trueO que acontece quando a própria renovação falha.
trueApaga a sessão guardada e levanta unauthorized. É o comportamento certo
depois de um 401 — a sessão realmente acabou.
falseDeixa a sessão intacta e apenas devolve null. Para uma renovação
suave, quando a falha original era sobre permissão e não sobre
identidade.
A distinção importa. Um 403 forbidden_resource_access significa que a pessoa
está autenticada e não tem permissão — desautenticá-la ali seria derrubar
alguém por ter clicado no botão errado. Passe deauthOnFailure: false para quem
chamou conseguir mostrar o erro real.
Renovação agendada
Com autoRefresh ligado (o padrão), o client de navegador não espera uma
requisição encontrar o token vencido. A cada 30 segundos — e assim que uma aba
oculta volta a ficar visível — ele confere a sessão guardada e a renova quando
ela entra na margem de 90 segundos.
- Abas ocultas pulam a conferência. Uma aba em segundo plano não mantém aquecida uma sessão que ninguém está olhando.
- Uma aba renova por vez. Abas do mesmo projeto se coordenam pela Web Locks API onde ela existe; uma aba que encontra o lock ocupado pula aquele ciclo.
- Toda aba fica sabendo. O token novo é transmitido por
BroadcastChannel, e cada aba emitetoken-refreshed.
Desligue com createClient(url, key, { autoRefresh: false }) quando o host
controla a renovação. A renovação no uso continua acontecendo.
Chame auth.dispose() quando descartar um client — testes, hot reload, um
micro-frontend sendo desmontado. Ele para o timer e fecha o canal entre abas.
Callbacks
Três hooks conduzem tudo acima. O client de navegador liga eles aos eventos que você assina, então assinar normalmente basta.
setRefreshFn(refresh_token) => Promise<Result>OptionalComo trocar um refresh token. Definido pelo client na construção — é injetado depois em vez de passado, para evitar uma dependência circular entre o manager e o client HTTP.
setOnTokenRefreshed(auth) => voidOptionalDispara depois de cada renovação bem-sucedida. Vira o evento token-refreshed.
setOnUnauthorized(failed) => voidOptionalDispara quando a sessão não consegue ser renovada. Vira o evento
unauthorized, e carrega a sessão que falhou para uma aplicação multi-conta
remover a entrada certa.
save, get, clear
save(auth) · get() · clear()
As primitivas de armazenamento. O save calcula o expires_at e escreve pelo
adaptador de storage configurado; o clear remove a
sessão ativa e nada mais — o armazenamento de contas fica intacto, e é isso que
permite ao deselect largar uma sessão e
ainda voltar para ela.
O signOut chama o clear por você, e só depois que o servidor confirma — então
um logout que falha não deixa o client desautenticado localmente e vivo
remotamente.