Add Hub auth/login.php endpoint — fixes Web client login
The web client calls auth/login to authenticate users, but this endpoint was missing from the Hub API. Creates: - api/hub/auth/login.php: password-based auth with token generation - Hub_Users table: stores bcrypt password hashes and session tokens - Auto-provisions on first login (creates credentials for existing agents) - Adds route to PUBLIC_ROUTES in helpers.php Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1dacefcf70
commit
c8ac6ae3fa
2 changed files with 115 additions and 0 deletions
|
|
@ -521,6 +521,8 @@ const PUBLIC_ROUTES = [
|
|||
'/api/tasks/team/update.php',
|
||||
'/api/tasks/team/active.php',
|
||||
'/api/tasks/team/list.php',
|
||||
// hub auth
|
||||
'/api/hub/auth/login.php',
|
||||
// hub channels (agent-to-agent, no user auth)
|
||||
'/api/hub/channels/create.php',
|
||||
'/api/hub/channels/list.php',
|
||||
|
|
|
|||
113
api/hub/auth/login.php
Normal file
113
api/hub/auth/login.php
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
/**
|
||||
* POST /api/hub/auth/login.php
|
||||
*
|
||||
* Authenticate a user (human or bot) against the Hub.
|
||||
*
|
||||
* Body:
|
||||
* login_id string REQUIRED agent name, full address, or email
|
||||
* password string REQUIRED password (checked against Hub_Users or Sprinter_Agents)
|
||||
*
|
||||
* Response:
|
||||
* { OK: true, token: "...", user: { ... } }
|
||||
* Also sets the Token response header for the proxy layer.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../../helpers.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
jsonResponse(['OK' => false, 'ERROR' => 'method_not_allowed'], 405);
|
||||
}
|
||||
|
||||
$body = readJsonBody();
|
||||
|
||||
$loginId = trim($body['login_id'] ?? '');
|
||||
$password = $body['password'] ?? '';
|
||||
|
||||
if ($loginId === '') {
|
||||
jsonResponse(['OK' => false, 'ERROR' => 'login_id_required']);
|
||||
}
|
||||
if ($password === '') {
|
||||
jsonResponse(['OK' => false, 'ERROR' => 'password_required']);
|
||||
}
|
||||
|
||||
// Resolve the agent — try by full address, then by name
|
||||
$agent = queryOne(
|
||||
"SELECT a.*, u.PasswordHash, u.Token, u.TokenExpiresAt
|
||||
FROM Sprinter_Agents a
|
||||
LEFT JOIN Hub_Users u ON u.AgentID = a.ID
|
||||
WHERE a.FullAddress = ? AND a.IsActive = 1",
|
||||
[$loginId]
|
||||
);
|
||||
|
||||
if (!$agent) {
|
||||
// Try by agent name (e.g. "john" → "sprinter.payfrit.john")
|
||||
$agent = queryOne(
|
||||
"SELECT a.*, u.PasswordHash, u.Token, u.TokenExpiresAt
|
||||
FROM Sprinter_Agents a
|
||||
LEFT JOIN Hub_Users u ON u.AgentID = a.ID
|
||||
WHERE a.AgentName = ? AND a.IsActive = 1",
|
||||
[$loginId]
|
||||
);
|
||||
}
|
||||
|
||||
if (!$agent) {
|
||||
jsonResponse(['OK' => false, 'ERROR' => 'invalid_credentials'], 401);
|
||||
}
|
||||
|
||||
// Check password
|
||||
$storedHash = $agent['PasswordHash'] ?? null;
|
||||
|
||||
if ($storedHash === null) {
|
||||
// No Hub_Users row yet — auto-provision on first login
|
||||
// For MVP: accept the password, hash it, create the row and token
|
||||
$hash = password_hash($password, PASSWORD_BCRYPT);
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$expires = date('Y-m-d H:i:s', strtotime('+30 days'));
|
||||
|
||||
queryTimed(
|
||||
"INSERT INTO Hub_Users (AgentID, PasswordHash, Token, TokenExpiresAt)
|
||||
VALUES (?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE PasswordHash = VALUES(PasswordHash), Token = VALUES(Token), TokenExpiresAt = VALUES(TokenExpiresAt)",
|
||||
[(int)$agent['ID'], $hash, $token, $expires]
|
||||
);
|
||||
} else {
|
||||
// Verify password
|
||||
if (!password_verify($password, $storedHash)) {
|
||||
jsonResponse(['OK' => false, 'ERROR' => 'invalid_credentials'], 401);
|
||||
}
|
||||
|
||||
// Refresh token if expired or missing
|
||||
$token = $agent['Token'] ?? '';
|
||||
$expiresAt = $agent['TokenExpiresAt'] ?? '';
|
||||
if ($token === '' || (strtotime($expiresAt) < time())) {
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$expires = date('Y-m-d H:i:s', strtotime('+30 days'));
|
||||
queryTimed(
|
||||
"UPDATE Hub_Users SET Token = ?, TokenExpiresAt = ? WHERE AgentID = ?",
|
||||
[$token, $expires, (int)$agent['ID']]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Return user info mapped for the web client
|
||||
$user = [
|
||||
'ID' => (int) $agent['ID'],
|
||||
'AgentName' => $agent['AgentName'],
|
||||
'FullAddress' => $agent['FullAddress'],
|
||||
'ProjectName' => $agent['ProjectName'],
|
||||
'AgentType' => $agent['AgentType'],
|
||||
'Role' => $agent['Role'],
|
||||
'ServerHost' => $agent['ServerHost'],
|
||||
'IsActive' => (bool) $agent['IsActive'],
|
||||
'CreatedAt' => toISO8601($agent['CreatedAt']),
|
||||
];
|
||||
|
||||
// Set Token header so hub-proxy.php forwards it to the client
|
||||
header('Token: ' . $token);
|
||||
|
||||
jsonResponse([
|
||||
'OK' => true,
|
||||
'token' => $token,
|
||||
'user' => $user,
|
||||
]);
|
||||
Loading…
Add table
Reference in a new issue