Sessões e tokens
O que mantém alguém autenticado, o que encerra isso, e as configurações que decidem os dois.
Três coisas são fáceis de confundir, e quase todo bug de sessão vem de tratá-las como uma só:
a sessãoregistro no servidorUm período autenticado, num dispositivo. Tem id, impressão digital e expiração. É o que você revoga.
o access tokencurta duraçãoO que viaja em cada requisição. Rotaciona o tempo todo e não vale rastrear.
o refresh tokenlonga duraçãoO que compra o próximo access token. Vinculado a uma sessão — não ao usuário.
A sessão dura o que o refresh token durar
É esta a regra que vale internalizar, porque é onde as configurações encontram a realidade.
Com refresh tokens habilitados, a sessão expira quando o refresh token expira. O access token expirar no meio do caminho é um não-evento: o SDK troca ele e o usuário não percebe nada.
Com eles desabilitados, a sessão expira quando o access token expira. Não há com o que renovar, então "fique conectado por 30 dias" vira "fique conectado por uma hora" — a mesma configuração, um produto completamente diferente.
Elas ficam no console, em Autenticações.
Expiração do token de acessoPor quanto tempo um access token vale. Curto é mais seguro: um token vazado tem uma janela menor.
Habilitar token de atualizaçãoSe as sessões podem sobreviver a um access token.
Tempo limite do token de atualizaçãoA resposta real para "por quanto tempo as pessoas ficam conectadas".
A renovação é automática, e não vale brigar com ela
O cliente de navegador renova por dois gatilhos, e você não dirige nenhum. Ele renova sob demanda com uma margem de 90 segundos — peça um token que expira em menos que isso e ele é renovado antes de ser entregue — e renova por tempo, verificando a cada 30 segundos, para que uma sessão que ninguém está usando também nunca vença.
const token = await nulvless.auth.getAccessToken();É essa a API inteira. O erro a evitar é guardar a string:
// Errado — este token vai acabar sendo rejeitado.
const token = await nulvless.auth.getAccessToken();
setInterval(() => fetch('/api/things', { headers: auth(token) }), 60_000);Peça de novo a cada requisição. É leitura de memória quando o token está fresco, e a margem existe justamente para ele nunca ser entregue prestes a expirar.
Para qualquer coisa que segure o token fora do SDK — um websocket, um worker — assine em vez de consultar:
nulvless.auth.on('token-refreshed', (session) => {
socket.updateAuth(session.access_token);
});Saber quando realmente acabou
Um 401 de uma chamada não significa que a sessão acabou. Normalmente significa
que o access token expirou, e o SDK renova e repete.
A sessão acabou quando a renovação falha, e existe um sinal só para isso:
nulvless.auth.on('unauthorized', () => {
clearAppState();
router.push('/sign-in');
});Não fique consultando getSession() para detectar isso. É mais lento, custa uma
requisição por ciclo, e mesmo assim não diz nada entre um ciclo e outro. O evento
dispara no instante em que o SDK desiste.
Encerrando sessões
Quatro formas, e elas não são intercambiáveis:
signOut()este dispositivoO caso comum. Limpa o armazenamento local só depois de o servidor confirmar.
signOut('others')todos os outrosMantém o usuário onde ele está. É o que "desconectar meus outros dispositivos" deve chamar.
signOut('global')todos, inclusive esteCombine com uma troca de senha depois de suspeita de comprometimento.
sessions.revoke({ id })uma sessão específicaA partir da lista de "onde você está conectado". Protegido por sudo.
Como o refresh token é vinculado a uma sessão, revogar a sessão mata o refresh token junto. Não há como uma sessão revogada se renovar de volta à existência.
A tela de "onde você está conectado"
const { data: sessions } = await nulvless.auth.sessions.list();Tudo que a tela precisa está em cada entrada: finger_print para a linha de
dispositivo e localização, amr para como entrou, created_at para quando, e
is_current_session para o selo de "este dispositivo".
O finger_print só é preenchido quando o client foi criado com
trackUser: true — e, no servidor, só quando você repassa os headers do usuário
com forwardUserHeaders. Sem um dos dois, toda sessão
aparece vindo da sua infraestrutura, o que deixa a tela pior que inútil: parece
que a conta foi acessada de um data center.
Claims customizadas
Um ambiente pode injetar claims próprias no access token, a partir de um
template. Se a sua própria API lê o JWT direto em vez de chamar
verify, é assim que ela recebe os seus campos sem uma
segunda consulta.
Ainda assim prefira o verify quando a pergunta é sobre permissão — uma claim é
uma fotografia de quando o token foi emitido, e um grant revogado desde então
continua lá dentro.