viabiliza/routes/authRoutes.js

129 lines
4.2 KiB
JavaScript

const express = require('express');
const router = express.Router();
const dotenv = require('dotenv');
dotenv.config();
/**
* Página simples de login
*/
router.get('/login', (req, res) => {
// Redirect straight to Microsoft OAuth start to avoid an extra click/page
return res.redirect('/auth/microsoft');
});
/**
* Inicia login Microsoft
*/
router.get('/auth/microsoft', (req, res) => {
const params = new URLSearchParams({
client_id: process.env.OAUTH_CLIENT_ID,
response_type: 'code',
redirect_uri: process.env.OAUTH_REDIRECT_URI,
response_mode: 'query',
scope: 'openid profile email',
});
res.redirect(
`https://login.microsoftonline.com/${process.env.OAUTH_TENANT_ID}/oauth2/v2.0/authorize?${params}`
);
});
/**
* Callback do Azure
*/
// shared handler so we accept both /auth/microsoft/callback and /auth/callback
async function oauthCallbackHandler(req, res) {
const code = req.query.code;
// Verbose logging for debugging the OAuth callback flow
console.log('[OAuth callback] incoming query:', {
code: req.query.code ? '[present]' : '[absent]',
state: req.query.state,
error: req.query.error,
error_description: req.query.error_description ? '[present]' : undefined,
});
if (!code) {
console.warn('[OAuth callback] no authorization code present, redirecting to /login');
return res.redirect('/login');
}
try {
console.log('[OAuth callback] exchanging code for tokens (will not log secrets)');
const tokenRespRaw = await fetch(
`https://login.microsoftonline.com/${process.env.OAUTH_TENANT_ID}/oauth2/v2.0/token`,
{
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.OAUTH_CLIENT_ID,
client_secret: process.env.OAUTH_CLIENT_SECRET,
grant_type: 'authorization_code',
code,
redirect_uri: process.env.OAUTH_REDIRECT_URI,
}),
}
);
console.log('[OAuth callback] token endpoint HTTP status:', tokenRespRaw.status);
const tokenResp = await tokenRespRaw.json();
// Log token response keys but never print tokens or client secret
console.log('[OAuth callback] token response keys:', Object.keys(tokenResp));
if (tokenResp.error) {
console.error('[OAuth callback] token endpoint returned error:', tokenResp.error, tokenResp.error_description || '');
return res.redirect('/login');
}
if (!tokenResp.id_token) {
console.warn('[OAuth callback] No id_token received — token response:', Object.keys(tokenResp));
}
// decodificar id_token (JWT) com cuidado — não logar o token inteiro
let payload = {};
try {
const parts = (tokenResp.id_token || '').split('.');
if (parts.length >= 2) {
payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
} else {
console.warn('[OAuth callback] id_token malformed');
}
} catch (e) {
console.error('[OAuth callback] failed to decode id_token payload:', e && (e.message || e));
}
// Log a few safe user-identifying fields
console.log('[OAuth callback] id_token payload (safe fields):', {
name: payload.name,
preferred_username: payload.preferred_username || payload.upn || payload.email,
oid: payload.oid,
});
// salva sessão
req.session.user = {
authenticated: true,
email: payload.preferred_username,
name: payload.name,
oid: payload.oid,
};
console.log('[OAuth callback] session created for user:', req.session.user && { email: req.session.user.email, name: req.session.user.name });
res.redirect('/');
} catch (err) {
console.error('[OAuth callback] unexpected error during token exchange or session creation:', err && (err.stack || err.message || err));
res.redirect('/login');
}
}
router.get('/auth/microsoft/callback', oauthCallbackHandler);
// some Azure app registrations (or tooling like ngrok) may use /auth/callback — accept that too
router.get('/auth/callback', oauthCallbackHandler);
router.get('/logout', (req, res) => {
req.session.destroy(() => res.redirect('/login'));
});
module.exports = router;