Login Único entre Múltiplos Subdomínios: Uma Explicação Simplificada

Imagine um cenário com vários sistemas web interconectados (A, B, C, D, E, F) que compartilham o mesmo domínio raiz (por exemplo, .meudominio.com). O objetivo é implementar um sistema de login único onde, após o usuário se autenticar em qualquer um desses sistemas, ele possa acessar todos os outros sem a necessidade de fazer login novamente. O token de autenticação tem uma validade de 12 horas e é renovado a cada acesso.

A solução para este problema se baseia no uso estratégico dos Cookies do navegador.

Quando um usuário faz login com sucesso, o frontend do sistema de autenticação armazena o token em um cookie. Vamos enalisar o comportamento:

O nome da chave do cookie utilizado é _micro.common-info.

O valor associado a essa chave é uma estrutura de dados definida pelo frontend, que geralmente combina informações do ambiente (como ENV) e o token de autneticação.

O campo Domain do cookie é crucial aqui. Ele permite que o cookie seja acessível por diferentes subdomínios dentro de um mesmo domínio raiz.

As funções principais para gerenciar esses cookies são setCookie e getCookie. Segue um exemplo de implementação em JavaScript:


import Cookies from 'js-cookie';

const TOKEN_KEY = 'sessionToken';
const COOKIE_STORE_KEY = '_auth_session';
const CURRENT_ENV = import.meta.env.VITE_APP_ENV; // Exemplo: 'production', 'staging'
const COOKIE_DOMAIN_MAIN = 'meudominio.com';
const DOMAIN_PATTERN = /meudominio\.com$/;

export function retrieveToken() {
 const allStoredCookies = loadAllCookies();
 const envCookies = allStoredCookies[CURRENT_ENV];
 return envCookies ? envCookies[TOKEN_KEY] : null;
}

export function persistToken(token) {
 const storedCookies = loadAllCookies();
 if (!storedCookies[CURRENT_ENV]) {
   storedCookies[CURRENT_ENV] = {};
 }
 storedCookies[CURRENT_ENV][TOKEN_KEY] = token;

 return new Promise((resolve, reject) => {
   try {
     Cookies.set(COOKIE_STORE_KEY, JSON.stringify(storedCookies), {
       domain: determineCookieDomain(),
       expires: 365, // Exemplo: expira em 1 ano para persistência mais longa
       secure: true, // Recomendado em produção
       sameSite: 'lax' // Recomendado
     });
     resolve();
   } catch (error) {
     console.error("Erro ao salvar cookie:", error);
     reject(error);
   }
 });
}

export function clearToken() {
 // Limpa o token definindo-o como uma string vazia
 return persistToken('');
}

function determineCookieDomain() {
 const currentHost = window.location.hostname;
 const localHosts = ['localhost', '127.0.0.1', '::1']; // Adicionar outros hosts locais se necessário

 if (localHosts.includes(currentHost)) {
   return currentHost; // Usa o host local para testes
 } else if (DOMAIN_PATTERN.test(currentHost)) {
   return COOKIE_DOMAIN_MAIN; // Usa o domínio principal para subdomínios
 } else {
   return currentHost; // Usa o domínio atual se não corresponder
 }
}

function loadAllCookies() {
 const cookieString = Cookies.get(COOKIE_STORE_KEY, {
   domain: determineCookieDomain(),
 });

 if (cookieString && typeof cookieString === 'string') {
   try {
     const parsedCookie = JSON.parse(cookieString);
     if (parsedCookie && typeof parsedCookie === 'object') {
       return parsedCookie;
     }
   } catch (e) {
     console.error("Erro ao parsear cookie JSON:", e);
   }
 }
 return {}; // Retorna um objeto vazio se o cookie não for encontrado ou for inválido
}
 

A lógica dentro da função determineCookieDomain é particularmente importante:


function determineCookieDomain() {
 const currentHost = window.location.hostname;
 const localHosts = ['localhost', '127.0.0.1', '::1'];

 if (localHosts.includes(currentHost)) {
   return currentHost;
 } else if (DOMAIN_PATTERN.test(currentHost)) { // DOMAIN_PATTERN = /meudominio\.com$/
   return COOKIE_DOMAIN_MAIN; // COOKIE_DOMAIN_MAIN = 'meudominio.com'
 } else {
   return currentHost;
 }
}
 

Esta função determina qual domínio deve ser usado para o cookie. Se o domínio atual for um host local como 'localhost', ele usa o próprio host. Caso contrário, verifica se o domínio atual termina com 'meudominio.com' usando a expressão regular /meudominio\.com$/. Se corresponder, o cookie será associado ao domínio principal 'meudominio.com'; caso contrário, o cookie será associado ao domínio atual.

Dessa forma, quando os sistemas A, B, C, D, E e F são acessados, eles primeiro consultam o cookie _auth_session no domínio 'meudominio.com' para obter o token. Como todos os sistemas estão configurados para usar o mesmo domínio de cookie, o token persistido em um subdomínio se torna acessível aos outros, permitindo o login único e a autenticação transparente entre eles.

Tags: cookies autenticacao single sign-on sso javascript

Publicado em 6-1 01:30 por Thomas