Era uma tarde comum de setembro quando recebemos um requisito aparentemente simples: "Precisamos implementar autenticação segura com Google e Microsoft no site da Cara Core". O que parecia ser uma tarefa de algumas horas se transformou em uma jornada épica de descobertas, frustrações e, principalmente, muito aprendizado.
Esta é a história real de como implementamos OpenID Connect (OIDC) do zero, os obstáculos que enfrentamos e as lições valiosas que aprendemos no caminho.
Como muitos desenvolvedores, nossa primeira reação foi: "OAuth? OIDC? Deve ser só adicionar uns botões e pronto!". Que ingênuos éramos.
Nossa primeira tentativa foi copiar um exemplo básico da internet. Resultado? Uma tela branca, erros no console e a descoberta de que autenticação é muito mais complexa do que imaginávamos.
// Nossa primeira tentativa (spoiler: não funcionou)
function login() {
window.location.href = "https://accounts.google.com/oauth/authorize?...";
// E agora? Como eu capturo a resposta?
}
Depois de algumas horas tentando "fazer funcionar na unha", percebemos que precisávamos entender o que realmente estava acontecendo. Foi aí que descobrimos conceitos como:
Cada termo era um novo universo a ser explorado.
Após pesquisar por horas, encontramos várias opções:
Nossa escolha: oidc-client-ts - uma biblioteca robusta que suportava ambos os provedores.
// O momento "eureka" - descobrindo o UserManager
import { UserManager } from 'oidc-client-ts';
const userManager = new UserManager({
authority: 'https://accounts.google.com',
client_id: 'nossa-client-id-super-secreta',
redirect_uri: 'http://localhost:8000/callback',
// ... mais configurações que não fazíamos ideia do que faziam
});
Depois de dois dias de configuração, conseguimos o primeiro login com Google! A sensação era de ter escalado o Everest. Claro, era só localhost e funcionava apenas uma vez em cada dez tentativas, mas era um progresso!
Se há um erro que todo desenvolvedor OIDC conhece, é este. E nós conhecemos muito bem - ele apareceu centenas de vezes durante nossa implementação.
O problema era simples de entender, mas complexo de resolver:
http://localhost:8000https://nossa-url-temporaria.comhttps://chmulato.github.io/cara-coreCada ambiente precisava de sua própria configuração no Google Cloud Console. Mas nós só descobrimos isso depois de muito sofrimento.
// Nossa solução "gambiarrada" inicial
const redirectUri = window.location.hostname === 'localhost'
? 'http://localhost:8000/secure/index.html'
: 'https://chmulato.github.io/cara-core/secure/index.html';
Até que tivemos uma ideia: configuração dinâmica baseada no ambiente!
// A solução elegante que desenvolvemos
function generateGoogleConfig() {
const baseUrl = window.location.origin;
return {
authority: "https://accounts.google.com",
client_id: "nossa-client-id",
redirect_uri: `${baseUrl}/secure/index.html`,
// Funciona em qualquer ambiente!
};
}
Quando algo dá errado em OIDC, descobrir o que aconteceu é como procurar uma agulha no palheiro... no escuro... com vendas nos olhos.
As ferramentas de desenvolvedor do navegador ajudavam, mas precisávamos de algo mais robusto para entender o fluxo completo:
Desenvolvemos um sistema de logging específico para OIDC que se tornou nosso melhor amigo:
class OIDCLogger {
constructor() {
this.logs = [];
this.sessionId = this.generateSessionId();
}
authEvent(event, data = {}) {
this.addLog('INFO', `Auth Event: ${event}`, {
event: event,
provider: this.getCurrentProvider(),
...data
});
}
authError(error, context = {}) {
this.addLog('ERROR', 'Authentication Error', {
error: error.message || error,
stack: error.stack,
provider: this.getCurrentProvider(),
context: context
});
}
}
Criamos até uma interface web para visualizar os logs em tempo real:
Isso nos permitiu debuggar problemas em produção que seriam impossíveis de rastrear de outra forma.
Quando chegou a hora de integrar o Microsoft Entra ID, pensamos: "Agora que entendemos OIDC, deve ser moleza!". Ledo engano.
O Microsoft tem suas próprias peculiaridades:
Para não duplicar código, criamos uma abstração que funcionasse com ambos os provedores:
class OIDCAuthManager {
async switchProvider(newProvider) {
if (this.currentProvider === newProvider) return;
// Logout do provedor atual
await this.logoutLocal();
// Inicializar com novo provedor
await this.initialize(newProvider);
console.log(`Provedor alterado para: ${newProvider}`);
}
}
Nosso primeiro deploy para produção foi... traumático. Tudo funcionava perfeitamente em desenvolvimento, mas em produção:
Desenvolvemos um sistema que detecta automaticamente o ambiente e se adapta:
function detectEnvironment() {
const hostname = window.location.hostname;
// GitHub Pages
if (hostname.includes('github.io')) {
return {
type: 'production',
baseUrl: `${protocol}//${hostname}`,
isGitHubPages: true
};
}
// Localhost
if (hostname === 'localhost') {
return {
type: 'development',
baseUrl: `${protocol}//${hostname}${port}`,
isLocalhost: true
};
}
// Custom domain
return {
type: 'production',
baseUrl: `${protocol}//${hostname}`,
isCustomDomain: true
};
}
Após semanas de desenvolvimento e refinamento, conseguimos criar um sistema que:
// O resultado final - elegante e funcional
window.OIDCAuth = {
async login(provider = 'google') {
try {
await this.switchProvider(provider);
await this.userManager.signinRedirect();
} catch (error) {
window.logOIDC.authError(error, { context: 'login' });
throw error;
}
},
async handleCallback() {
try {
const user = await this.userManager.signinRedirectCallback();
window.logOIDC.authEvent('user_authenticated', {
provider: this.currentProvider,
userId: user.profile?.sub
});
return user;
} catch (error) {
window.logOIDC.authError(error, { context: 'callback' });
throw error;
}
}
};
Durante nossa jornada, percebemos como a documentação inadequada pode tornar uma implementação simples em um pesadelo. Por isso, documentamos extensivamente nossa solução.
Utilizamos bibliotecas como oidc-client-ts que nos pouparam meses de desenvolvimento. Contribuir de volta para a comunidade é fundamental.
Implementar OIDC nos ensinou que segurança não é algo que você "adiciona" ao final - ela precisa ser pensada desde o início.
Por trás de toda a complexidade técnica, existe um usuário que só quer fazer login e usar o sistema. Manter isso em mente foi crucial para nossas decisões de design.
Implementar OIDC na Cara Core foi muito mais do que apenas "adicionar autenticação". Foi uma jornada de aprendizado que nos ensinou sobre protocolos de segurança modernos, arquitetura de sistemas distribuídos, debugging avançado, User Experience em sistemas seguros e desenvolvimento multi-ambiente.
Hoje, quando vemos aqueles dois botões simples - "Continuar com Google" e "Continuar com Microsoft" - sabemos que por trás deles existe uma arquitetura robusta, testada e monitorada que garante segurança e uma excelente experiência do usuário.
Se você está prestes a implementar OIDC, algumas dicas valiosas: