126 lines
4.0 KiB
PHP
126 lines
4.0 KiB
PHP
<?php
|
|
// oauth/token.php
|
|
|
|
require_once __DIR__ . '/../config.php';
|
|
require_once __DIR__ . '/../classes/OAuth.php';
|
|
|
|
header('Content-Type: application/json');
|
|
header('Cache-Control: no-store');
|
|
header('Pragma: no-cache');
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
|
http_response_code(405);
|
|
echo json_encode(['error' => 'invalid_request', 'error_description' => 'POST required.']);
|
|
exit();
|
|
}
|
|
|
|
function respond_with_error(string $error, string $description, int $status = 400): void {
|
|
http_response_code($status);
|
|
echo json_encode(['error' => $error, 'error_description' => $description], JSON_UNESCAPED_SLASHES);
|
|
exit();
|
|
}
|
|
|
|
function extract_client_credentials(): array {
|
|
$clientId = null;
|
|
$clientSecret = null;
|
|
|
|
if (!empty($_SERVER['HTTP_AUTHORIZATION']) && stripos($_SERVER['HTTP_AUTHORIZATION'], 'basic ') === 0) {
|
|
$encoded = substr($_SERVER['HTTP_AUTHORIZATION'], 6);
|
|
$decoded = base64_decode($encoded, true);
|
|
if ($decoded !== false && strpos($decoded, ':') !== false) {
|
|
[$clientId, $clientSecret] = explode(':', $decoded, 2);
|
|
}
|
|
}
|
|
|
|
if ($clientId === null && isset($_POST['client_id'])) {
|
|
$clientId = $_POST['client_id'];
|
|
$clientSecret = $_POST['client_secret'] ?? '';
|
|
}
|
|
|
|
return [trim((string) $clientId), (string) $clientSecret];
|
|
}
|
|
|
|
[$clientId, $clientSecret] = extract_client_credentials();
|
|
$grantType = $_POST['grant_type'] ?? '';
|
|
|
|
if ($clientId === '' || $grantType === '') {
|
|
respond_with_error('invalid_request', 'client_id and grant_type are required.');
|
|
}
|
|
|
|
$oauthService = new OAuthService($pdo);
|
|
$client = $oauthService->getClient($clientId);
|
|
|
|
if (!$client) {
|
|
respond_with_error('unauthorized_client', 'Unknown or revoked client.', 401);
|
|
}
|
|
|
|
$requiresSecret = (int) $client['is_confidential'] === 1;
|
|
if ($requiresSecret && $clientSecret === '') {
|
|
respond_with_error('invalid_client', 'Client credentials required.', 401);
|
|
}
|
|
|
|
if ($requiresSecret && !$oauthService->verifyClientSecret($client, $clientSecret)) {
|
|
respond_with_error('invalid_client', 'Client authentication failed.', 401);
|
|
}
|
|
|
|
switch ($grantType) {
|
|
case 'authorization_code':
|
|
$code = $_POST['code'] ?? '';
|
|
$redirectUri = $_POST['redirect_uri'] ?? '';
|
|
|
|
if ($code === '') {
|
|
respond_with_error('invalid_request', 'Authorization code is required.');
|
|
}
|
|
|
|
$authRecord = $oauthService->consumeAuthorizationCode($code, $clientId);
|
|
if (!$authRecord) {
|
|
respond_with_error('invalid_grant', 'Authorization code is invalid or expired.');
|
|
}
|
|
|
|
if ($redirectUri !== '' && !hash_equals($authRecord['redirect_uri'], $redirectUri)) {
|
|
respond_with_error('invalid_grant', 'redirect_uri mismatch.');
|
|
}
|
|
|
|
$tokens = $oauthService->issueTokens(
|
|
$clientId,
|
|
(int) $authRecord['person_id'],
|
|
$authRecord['scope'] ?? null,
|
|
true
|
|
);
|
|
break;
|
|
|
|
case 'refresh_token':
|
|
$refreshToken = $_POST['refresh_token'] ?? '';
|
|
if ($refreshToken === '') {
|
|
respond_with_error('invalid_request', 'refresh_token is required.');
|
|
}
|
|
|
|
$tokens = $oauthService->exchangeRefreshToken($clientId, $refreshToken);
|
|
if (!$tokens) {
|
|
respond_with_error('invalid_grant', 'Refresh token is invalid or expired.');
|
|
}
|
|
break;
|
|
|
|
default:
|
|
respond_with_error('unsupported_grant_type', 'The grant_type is not supported.');
|
|
}
|
|
|
|
$response = [
|
|
'access_token' => $tokens['access_token'],
|
|
'token_type' => $tokens['token_type'],
|
|
'expires_in' => max(0, $tokens['access_expires_at'] - time()),
|
|
];
|
|
|
|
if (!empty($tokens['scope'])) {
|
|
$response['scope'] = $tokens['scope'];
|
|
}
|
|
|
|
if (!empty($tokens['refresh_token'])) {
|
|
$response['refresh_token'] = $tokens['refresh_token'];
|
|
if (!empty($tokens['refresh_expires_at'])) {
|
|
$response['refresh_expires_in'] = max(0, $tokens['refresh_expires_at'] - time());
|
|
}
|
|
}
|
|
|
|
echo json_encode($response, JSON_UNESCAPED_SLASHES);
|