Files
fitme3070.pt/views/auth/login.php
T
andreandClaude Opus 4.8 9ad0c2b763 SSO: redeem one-time ticket on return from the hub
fitme3070.pt is off the .andmore.pt cookie scope, so am_sso never arrives
and login never provisioned the user. Redeem the ?sso_ticket=… the hub now
appends to the return URL instead.

- sso_redeem_ticket(): atomic single-use burn of sso_tickets in the central
  DB, bound to PLATFORM_SLUG, then provision the local row + FIT_SESS session.
- Extract _sso_provision_local() shared with the cookie path.
- views/auth/login.php redeems the ticket before the cookie fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 22:17:40 +01:00

77 lines
3.9 KiB
PHP

<?php
// SSO-only platform: there is no local login form. All credentials live on
// id.andmore.pt. This file exists solely so old links / bookmarks pointing
// at /views/auth/login.php still land the visitor on the central form.
//
// Also acts as the post-login landing for the id.andmore.pt return_to: the
// am_sso cookie is now in the browser, sso_resolve_session() picks it up,
// and we forward to the dashboard. The `?from=sso` flag prevents an infinite
// loop if resolution fails here — we render a clean error instead of
// bouncing back to id.andmore.pt (which would just send us back here again).
//
// Carries through the optional claim intent (?next=session&session_id=X)
// from the homepage "vagas disponíveis" cards so that after authenticating
// the user lands on their dashboard scrolled to the open session.
require __DIR__ . '/../../config/db.php';
require_once __DIR__ . '/../../config/functions.php';
// Establish the session on return from the hub. fitme3070.pt is off the
// .andmore.pt cookie scope, so the real handoff is the one-time ?sso_ticket=…
// the hub appended to this return URL — redeem it. (sso_resolve_session() is
// kept as a fallback for the rare case a same-site am_sso cookie is present.)
if (empty($_SESSION['user_id']) && identity_is_federated()) {
if (!empty($_GET['sso_ticket'])) {
sso_redeem_ticket($pdo, (string) $_GET['sso_ticket']);
}
if (empty($_SESSION['user_id'])) {
sso_resolve_session($pdo);
}
}
$next = $_GET['next'] ?? '';
$session_id = (int) ($_GET['session_id'] ?? 0);
$claim_intent = ($next === 'session' && $session_id > 0);
$from_sso = isset($_GET['from']) && $_GET['from'] === 'sso';
$dashboard_for = function (string $role) use ($claim_intent, $session_id): string {
if ($claim_intent && $role === 'client') {
return 'views/user/dashboard.php?focus_session=' . $session_id;
}
return post_login_destination($role);
};
if (!empty($_SESSION['user_id'])) {
header('Location: ' . base_url($dashboard_for($_SESSION['user']['role'] ?? '')));
exit;
}
// We came back from id.andmore.pt but sso_resolve_session() couldn't pick up
// a valid session. Bouncing to id.andmore.pt again would loop forever — show
// a clean error instead so the user can see what's wrong.
if ($from_sso) {
http_response_code(500);
$am_cookie_present = !empty($_COOKIE[SSO_COOKIE_NAME] ?? '');
?><!doctype html><html lang="pt"><head><meta charset="utf-8"><title>Erro de autenticação</title>
<style>body{font-family:system-ui,-apple-system,sans-serif;max-width:560px;margin:80px auto;padding:0 1.5rem;color:#1c1c1a;background:#f7f5f1;line-height:1.6}h1{font-weight:300;font-size:1.6rem;margin-bottom:.5rem}code{background:rgba(0,0,0,.05);padding:2px 6px;border-radius:3px;font-size:.9em}.muted{color:#6b6863;font-size:.9rem}a{color:#5a7a62}</style>
</head><body>
<h1>Não foi possível concluir o login</h1>
<p>O servidor Andmore ID autenticou-o, mas esta plataforma não conseguiu carregar a sua sessão.</p>
<p class="muted">
Cookie <code>am_sso</code>: <?= $am_cookie_present ? 'presente' : '<strong>em falta</strong>' ?>.<br>
Plataforma: <code><?= htmlspecialchars(PLATFORM_SLUG) ?></code> · Modo: <code><?= htmlspecialchars(IDENTITY_MODE) ?></code>.
</p>
<p><a href="<?= htmlspecialchars(base_url('index.php')) ?>">Voltar ao início</a> · <a href="<?= htmlspecialchars(base_url('views/auth/logout.php')) ?>">Tentar novamente</a></p>
</body></html><?php
exit;
}
// First arrival — bounce to id.andmore.pt for credentials, tagging the
// return URL with ?from=sso so the loop-guard above fires on the way back.
$return_to = base_url('views/auth/login.php?from=sso'
. ($claim_intent ? '&next=session&session_id=' . $session_id : ''));
$_login_qs = '?return_to=' . urlencode($return_to) . '&platform=' . urlencode(PLATFORM_SLUG);
header('Location: ' . ANDMORE_ID_URL . 'auth/login.php' . $_login_qs);
exit;