Update: 2026-05-08 04:58:23
This commit is contained in:
@@ -158,17 +158,87 @@ class NotificationService
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OAuth2 Access Token for Firebase (Cache this in production!)
|
||||
* Note: This requires a JWT library or manual signing.
|
||||
* For simplicity, we assume the user might use a Google Auth library.
|
||||
* But since we avoid extra deps, I will provide a minimal implementation or suggestion.
|
||||
* Get OAuth2 Access Token for Firebase using Service Account JWT
|
||||
* Self-contained: no external libraries needed.
|
||||
*/
|
||||
private function getAccessToken(): ?string
|
||||
{
|
||||
// This is a complex part that usually requires 'google/auth' library.
|
||||
// For now, I will return null and tell the user they need to install google/auth via composer
|
||||
// OR I can write a minimal JWT signer for Google Auth if they don't want composer.
|
||||
error_log("[NotificationService] OAuth2 Token generation needs google/auth library.");
|
||||
return null;
|
||||
// Check cache first (token is valid for 1 hour, we cache for 50 min)
|
||||
$cacheFile = STORAGE_PATH . '/cache/fcm_token.json';
|
||||
if (file_exists($cacheFile)) {
|
||||
$cached = json_decode(file_get_contents($cacheFile), true);
|
||||
if ($cached && ($cached['expires_at'] ?? 0) > time()) {
|
||||
return $cached['access_token'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!file_exists($this->serviceAccountPath)) {
|
||||
error_log("[NotificationService] Firebase service account file missing");
|
||||
return null;
|
||||
}
|
||||
|
||||
$sa = json_decode(file_get_contents($this->serviceAccountPath), true);
|
||||
if (!$sa || empty($sa['private_key']) || empty($sa['client_email'])) {
|
||||
error_log("[NotificationService] Invalid service account JSON");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Build JWT
|
||||
$now = time();
|
||||
$header = json_encode(['alg' => 'RS256', 'typ' => 'JWT']);
|
||||
$payload = json_encode([
|
||||
'iss' => $sa['client_email'],
|
||||
'scope' => 'https://www.googleapis.com/auth/firebase.messaging',
|
||||
'aud' => 'https://oauth2.googleapis.com/token',
|
||||
'iat' => $now,
|
||||
'exp' => $now + 3600,
|
||||
]);
|
||||
|
||||
$b64Header = rtrim(strtr(base64_encode($header), '+/', '-_'), '=');
|
||||
$b64Payload = rtrim(strtr(base64_encode($payload), '+/', '-_'), '=');
|
||||
$signingInput = $b64Header . '.' . $b64Payload;
|
||||
|
||||
$privateKey = openssl_pkey_get_private($sa['private_key']);
|
||||
if (!$privateKey) {
|
||||
error_log("[NotificationService] Failed to parse private key");
|
||||
return null;
|
||||
}
|
||||
|
||||
openssl_sign($signingInput, $signature, $privateKey, OPENSSL_ALGO_SHA256);
|
||||
$b64Signature = rtrim(strtr(base64_encode($signature), '+/', '-_'), '=');
|
||||
$jwt = $signingInput . '.' . $b64Signature;
|
||||
|
||||
// Exchange JWT for access token
|
||||
$ch = curl_init('https://oauth2.googleapis.com/token');
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query([
|
||||
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
||||
'assertion' => $jwt,
|
||||
]),
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode !== 200) {
|
||||
error_log("[NotificationService] Token exchange failed ($httpCode): $response");
|
||||
return null;
|
||||
}
|
||||
|
||||
$tokenData = json_decode($response, true);
|
||||
$accessToken = $tokenData['access_token'] ?? null;
|
||||
|
||||
if ($accessToken) {
|
||||
// Cache for 50 minutes
|
||||
@file_put_contents($cacheFile, json_encode([
|
||||
'access_token' => $accessToken,
|
||||
'expires_at' => $now + 3000,
|
||||
]));
|
||||
}
|
||||
|
||||
return $accessToken;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user