payfrit-api/api/hub/vcgateway/visitor/auth.php
Mike cd373dd616 Add VC Gateway endpoints for invite links, visitor auth, DM, and rate limiting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 22:34:52 +00:00

113 lines
3.2 KiB
PHP

<?php
/**
* POST /api/hub/vcgateway/visitor/auth.php
*
* Authenticate as a visitor using an invite link token.
* Creates a visitor session and returns a VisitorToken for subsequent API calls.
* No user account or Sprinter agent required.
*
* Body:
* InviteToken string required The invite link token from the URL
* DisplayName string optional Visitor's display name (default: "Visitor")
*
* Response:
* OK, VisitorToken, VisitorID, DisplayName, AllowedChannels[], HostAddress
*/
require_once __DIR__ . '/../helpers.php';
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
jsonResponse(['OK' => false, 'ERROR' => 'method_not_allowed'], 405);
}
$body = readJsonBody();
$inviteToken = trim($body['InviteToken'] ?? '');
if (empty($inviteToken)) {
jsonResponse(['OK' => false, 'ERROR' => 'invite_token_required'], 400);
}
// Look up the invite link
$link = queryOne(
"SELECT * FROM Hub_InviteLinks WHERE Token = ? LIMIT 1",
[$inviteToken]
);
if (!$link) {
jsonResponse(['OK' => false, 'ERROR' => 'invalid_invite_token'], 404);
}
// Check if revoked
if ($link['IsRevoked']) {
jsonResponse(['OK' => false, 'ERROR' => 'invite_revoked'], 403);
}
// Check expiration
if ($link['ExpiresAt'] && strtotime($link['ExpiresAt']) < time()) {
jsonResponse(['OK' => false, 'ERROR' => 'invite_expired'], 403);
}
// Check max uses
if ($link['MaxUses'] > 0 && $link['UseCount'] >= $link['MaxUses']) {
jsonResponse(['OK' => false, 'ERROR' => 'invite_exhausted'], 403);
}
// Sanitize display name
$displayName = trim($body['DisplayName'] ?? '');
if (empty($displayName)) {
$displayName = 'Visitor';
}
$displayName = substr($displayName, 0, 100);
// Strip any HTML/script
$displayName = htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8');
// Generate visitor token
$visitorToken = bin2hex(random_bytes(24));
// Create the visitor session
queryTimed(
"INSERT INTO Hub_Visitors (InviteLinkID, VisitorToken, DisplayName)
VALUES (?, ?, ?)",
[(int)$link['ID'], $visitorToken, $displayName]
);
$visitorId = (int)lastInsertId();
// Increment use count on the invite link
queryTimed(
"UPDATE Hub_InviteLinks SET UseCount = UseCount + 1 WHERE ID = ?",
[(int)$link['ID']]
);
// Return the session info
$allowedChannels = json_decode($link['AllowedChannels'], true);
// Fetch channel details for the allowed channels
$channelDetails = [];
if (!empty($allowedChannels)) {
$placeholders = implode(',', array_fill(0, count($allowedChannels), '?'));
$channels = queryTimed(
"SELECT ID, Name, DisplayName, Purpose, ChannelType
FROM Hub_Channels
WHERE ID IN ($placeholders) AND IsArchived = 0",
array_map('intval', $allowedChannels)
);
foreach ($channels as $ch) {
$channelDetails[] = [
'ID' => (int)$ch['ID'],
'Name' => $ch['Name'],
'DisplayName' => $ch['DisplayName'],
'Purpose' => $ch['Purpose'],
'ChannelType' => $ch['ChannelType'],
];
}
}
jsonResponse([
'OK' => true,
'VisitorToken' => $visitorToken,
'VisitorID' => $visitorId,
'DisplayName' => $displayName,
'AllowedChannels' => $channelDetails,
'HostAddress' => $link['HostAddress'],
]);