1
0
mirror of git://f0xx.org/ac/ac-ms-identity synced 2026-07-30 03:38:12 +03:00

fix(2fa): use shortener QR PNG URL and otpauth fallback

AuthTwoFactorPage was setting img src to the short link URL (HTML), not
the /api/v1/qr/*.png endpoint. Always expose a PNG via qr_url.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Anton Afanasyeu
2026-07-05 19:12:53 +02:00
parent bb03237963
commit 635cc36dd5

View File

@@ -9,7 +9,7 @@ final class AuthTwoFactorPage {
* Returns an array with: * Returns an array with:
* long_url approval URL (fallback if shortener unavailable) * long_url approval URL (fallback if shortener unavailable)
* short_url shortened URL (s.f0xx.org) * short_url shortened URL (s.f0xx.org)
* qr_url URL of the QR PNG image (short_url + ?src=qr) * qr_url PNG image URL (url-shortener /api/v1/qr/… or otpauth QR fallback)
* raw_token the approval token to store in session for polling * raw_token the approval token to store in session for polling
* *
* @return array{long_url:string,short_url?:string,qr_url?:string,raw_token:string}|null * @return array{long_url:string,short_url?:string,qr_url?:string,raw_token:string}|null
@@ -28,29 +28,35 @@ final class AuthTwoFactorPage {
$approveUrl = 'https://apps.f0xx.org' . $project . '/two-factor/approve?token=' . urlencode($rawToken); $approveUrl = 'https://apps.f0xx.org' . $project . '/two-factor/approve?token=' . urlencode($rawToken);
$payload = [
'long_url' => $approveUrl,
'raw_token' => $rawToken,
'qr_url' => AuthTotp::qrImageUrl($approveUrl),
];
if (!UrlShortenerDatabase::enabled() || !UrlShortenerDatabase::ping()) { if (!UrlShortenerDatabase::enabled() || !UrlShortenerDatabase::ping()) {
return ['long_url' => $approveUrl, 'raw_token' => $rawToken]; return $payload;
} }
$bearers = ShortLinksRepository::listBearers(); $bearers = ShortLinksRepository::listBearers();
if ($bearers === []) { if ($bearers === []) {
return ['long_url' => $approveUrl, 'raw_token' => $rawToken]; return $payload;
} }
$bearerId = (int) ($bearers[0]['id'] ?? 0); $bearerId = (int) ($bearers[0]['id'] ?? 0);
if ($bearerId <= 0) { if ($bearerId <= 0) {
return ['long_url' => $approveUrl, 'raw_token' => $rawToken]; return $payload;
} }
$mint = ShortLinksRepository::createLink($bearerId, $approveUrl, 900); $mint = ShortLinksRepository::createLink($bearerId, $approveUrl, 900);
if (empty($mint['ok'])) { if (empty($mint['ok'])) {
return ['long_url' => $approveUrl, 'raw_token' => $rawToken]; return $payload;
} }
$shortUrl = (string) ($mint['short_url'] ?? ''); $shortUrl = (string) ($mint['short_url'] ?? '');
// QR image encodes the short URL with ?src=qr so mobile scans are tracked $qrUrl = (string) ($mint['qr_url'] ?? '');
$qrUrl = $shortUrl . (str_contains($shortUrl, '?') ? '&src=qr' : '?src=qr'); if ($qrUrl !== '') {
return [ $payload['qr_url'] = $qrUrl;
'long_url' => $approveUrl, }
'short_url' => $shortUrl, if ($shortUrl !== '') {
'qr_url' => $qrUrl, $payload['short_url'] = $shortUrl;
'raw_token' => $rawToken, }
]; return $payload;
} }
} }