AddOn/Calender_Google_User/exampelV2/GraphCalendarUserAuth.php hinzugefügt
This commit is contained in:
parent
00128ecde8
commit
cd8c5d6ac7
|
|
@ -0,0 +1,308 @@
|
|||
<?php
|
||||
|
||||
class GraphCalendarUserAuth {
|
||||
private $clientId;
|
||||
private $clientSecret;
|
||||
private $tenantId;
|
||||
private $redirectUri;
|
||||
private $accessToken;
|
||||
|
||||
public function __construct($clientId, $clientSecret, $tenantId, $redirectUri) {
|
||||
$this->clientId = $clientId;
|
||||
$this->clientSecret = $clientSecret;
|
||||
$this->tenantId = $tenantId;
|
||||
$this->redirectUri = $redirectUri;
|
||||
}
|
||||
|
||||
public function getLoginUrl() {
|
||||
$params = http_build_query([
|
||||
'client_id' => $this->clientId,
|
||||
'response_type' => 'code',
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'response_mode' => 'query',
|
||||
'scope' => SCOPES,
|
||||
'prompt' => 'consent', // erzwingt Benutzer-Zustimmung beim ersten Mal
|
||||
]);
|
||||
return "https://login.microsoftonline.com/{$this->tenantId}/oauth2/v2.0/authorize?$params";
|
||||
}
|
||||
|
||||
public function handleCallback($code) {
|
||||
$tokenUrl = "https://login.microsoftonline.com/{$this->tenantId}/oauth2/v2.0/token";
|
||||
|
||||
$data = [
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $code,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'client_id' => $this->clientId,
|
||||
'client_secret' => $this->clientSecret,
|
||||
'scope' => SCOPES,
|
||||
];
|
||||
|
||||
$response = $this->post($tokenUrl, $data);
|
||||
$tokens = json_decode($response, true);
|
||||
|
||||
// === Fehlerbehandlung ===
|
||||
if (!isset($tokens['access_token'])) {
|
||||
echo "<pre><strong>Fehler bei Token-Antwort:</strong>\n";
|
||||
print_r($tokens);
|
||||
echo "\nResponse RAW:\n" . htmlspecialchars($response);
|
||||
echo "</pre>";
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->accessToken = $tokens['access_token'];
|
||||
$user = $this->getUserInfo();
|
||||
|
||||
if (!isset($user['id'])) {
|
||||
echo "<pre><strong>Fehler: Benutzerinformationen konnten nicht abgerufen werden.</strong>\n";
|
||||
print_r($user);
|
||||
echo "</pre>";
|
||||
exit;
|
||||
}
|
||||
|
||||
// === E-Mail auslesen ===
|
||||
$email = $user['mail'] ?? $user['userPrincipalName'] ?? 'unknown@example.com';
|
||||
|
||||
// === Tokens + Zeit + E-Mail speichern ===
|
||||
$tokens['created_at'] = time();
|
||||
$this->saveTokensToFile($user['id'], $tokens, $email);
|
||||
|
||||
// === User zurückgeben ===
|
||||
$user['email'] = $email;
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function refreshAccessTokenFromFile($userId) {
|
||||
$tokenFile = TOKEN_DIR . "/$userId.json";
|
||||
if (!file_exists($tokenFile)) throw new Exception("Token nicht gefunden");
|
||||
|
||||
$stored = json_decode(file_get_contents($tokenFile), true);
|
||||
|
||||
$data = [
|
||||
'grant_type' => 'refresh_token',
|
||||
'refresh_token' => $stored['refresh_token'],
|
||||
'client_id' => $this->clientId,
|
||||
'client_secret' => $this->clientSecret,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'scope' => SCOPES,
|
||||
];
|
||||
|
||||
$tokenUrl = "https://login.microsoftonline.com/{$this->tenantId}/oauth2/v2.0/token";
|
||||
$response = $this->post($tokenUrl, $data);
|
||||
$tokens = json_decode($response, true);
|
||||
|
||||
if (!isset($tokens['access_token'])) {
|
||||
echo "<pre><strong>Fehler beim Refresh-Token:</strong>\n";
|
||||
print_r($tokens);
|
||||
echo "\nResponse RAW:\n" . htmlspecialchars($response);
|
||||
echo "</pre>";
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->accessToken = $tokens['access_token'];
|
||||
$tokens['created_at'] = time();
|
||||
$email = $stored['email'] ?? null;
|
||||
$this->saveTokensToFile($userId, $tokens, $email);
|
||||
}
|
||||
|
||||
// ===========================================
|
||||
// NEU: Automatischer Refresh / Offline Access
|
||||
// ===========================================
|
||||
public function getValidAccessToken($userId) {
|
||||
$tokenFile = TOKEN_DIR . "/$userId.json";
|
||||
if (!file_exists($tokenFile)) {
|
||||
throw new Exception("Token-Datei für Benutzer $userId nicht gefunden");
|
||||
}
|
||||
|
||||
$stored = json_decode(file_get_contents($tokenFile), true);
|
||||
|
||||
// Prüfen, ob Access Token noch gültig ist (mit 2-Minuten-Puffer)
|
||||
$expiresAt = ($stored['created_at'] ?? 0) + ($stored['expires_in'] ?? 0) - 120;
|
||||
if (time() < $expiresAt && isset($stored['access_token'])) {
|
||||
$this->accessToken = $stored['access_token'];
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
// Token ist abgelaufen → refresh
|
||||
$data = [
|
||||
'grant_type' => 'refresh_token',
|
||||
'refresh_token' => $stored['refresh_token'],
|
||||
'client_id' => $this->clientId,
|
||||
'client_secret' => $this->clientSecret,
|
||||
'redirect_uri' => $this->redirectUri,
|
||||
'scope' => SCOPES,
|
||||
];
|
||||
|
||||
$tokenUrl = "https://login.microsoftonline.com/{$this->tenantId}/oauth2/v2.0/token";
|
||||
$response = $this->post($tokenUrl, $data);
|
||||
$tokens = json_decode($response, true);
|
||||
|
||||
if (!isset($tokens['access_token'])) {
|
||||
echo "<pre><strong>Fehler beim automatischen Refresh:</strong>\n";
|
||||
print_r($tokens);
|
||||
echo "\nResponse RAW:\n" . htmlspecialchars($response);
|
||||
echo "</pre>";
|
||||
throw new Exception("Access Token konnte nicht erneuert werden.");
|
||||
}
|
||||
|
||||
$tokens['created_at'] = time();
|
||||
$email = $stored['email'] ?? null;
|
||||
$this->saveTokensToFile($userId, $tokens, $email);
|
||||
$this->accessToken = $tokens['access_token'];
|
||||
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
// ===========================================
|
||||
// Hilfsfunktionen
|
||||
// ===========================================
|
||||
|
||||
private function saveTokensToFile($userId, $tokens, $email = null) {
|
||||
$tokens['email'] = $email;
|
||||
file_put_contents(TOKEN_DIR . "/$userId.json", json_encode($tokens, JSON_PRETTY_PRINT));
|
||||
}
|
||||
|
||||
public function getUserInfo() {
|
||||
$headers = [
|
||||
"Authorization: Bearer {$this->accessToken}",
|
||||
"Content-Type: application/json"
|
||||
];
|
||||
|
||||
$ch = curl_init("https://graph.microsoft.com/v1.0/me");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => $headers
|
||||
]);
|
||||
$res = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return json_decode($res, true);
|
||||
}
|
||||
|
||||
public function getAccessToken() {
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
private function post($url, $data) {
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_HTTPHEADER => ["Content-Type: application/x-www-form-urlencoded"],
|
||||
CURLOPT_POSTFIELDS => http_build_query($data),
|
||||
]);
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
// =======================
|
||||
// Kalender-Funktionen
|
||||
// =======================
|
||||
|
||||
/**
|
||||
* Holt die nächsten X Termine des Benutzers
|
||||
* @param int $top Anzahl der Termine, default 10
|
||||
* @return array Termine
|
||||
*/
|
||||
public function getCalendarEvents($top = 10) {
|
||||
// Sicherstellen, dass Access Token gültig ist
|
||||
$token = $this->accessToken ?? null;
|
||||
if (!$token) throw new Exception("Kein Access Token vorhanden.");
|
||||
|
||||
$headers = [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Content-Type: application/json"
|
||||
];
|
||||
|
||||
$url = "https://graph.microsoft.com/v1.0/me/events?\$top={$top}&\$orderby=start/dateTime";
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => $headers
|
||||
]);
|
||||
|
||||
$res = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
$data = json_decode($res, true);
|
||||
return $data['value'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt einen neuen Termin
|
||||
* @param string $subject Betreff
|
||||
* @param string $start ISO8601 Startzeit, z.B. 2025-11-11T10:00:00
|
||||
* @param string $end ISO8601 Endzeit
|
||||
* @param string $body Textinhalt des Termins
|
||||
* @return array Microsoft Graph Antwort
|
||||
*/
|
||||
public function createCalendarEvent($subject, $start, $end, $body = '') {
|
||||
$token = $this->accessToken ?? null;
|
||||
if (!$token) throw new Exception("Kein Access Token vorhanden.");
|
||||
|
||||
$headers = [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Content-Type: application/json"
|
||||
];
|
||||
|
||||
$data = [
|
||||
'subject' => $subject,
|
||||
'start' => [
|
||||
'dateTime' => $start,
|
||||
'timeZone' => 'UTC'
|
||||
],
|
||||
'end' => [
|
||||
'dateTime' => $end,
|
||||
'timeZone' => 'UTC'
|
||||
],
|
||||
'body' => [
|
||||
'contentType' => 'Text',
|
||||
'content' => $body
|
||||
]
|
||||
];
|
||||
|
||||
$ch = curl_init("https://graph.microsoft.com/v1.0/me/events");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_HTTPHEADER => $headers,
|
||||
CURLOPT_POSTFIELDS => json_encode($data),
|
||||
]);
|
||||
|
||||
$res = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return json_decode($res, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Löscht einen Termin anhand der Event-ID
|
||||
* @param string $eventId ID des Termins
|
||||
* @return bool Erfolg
|
||||
*/
|
||||
public function deleteCalendarEvent($eventId) {
|
||||
$token = $this->accessToken ?? null;
|
||||
if (!$token) throw new Exception("Kein Access Token vorhanden.");
|
||||
|
||||
$headers = [
|
||||
"Authorization: Bearer {$token}"
|
||||
];
|
||||
|
||||
$ch = curl_init("https://graph.microsoft.com/v1.0/me/events/{$eventId}");
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_CUSTOMREQUEST => "DELETE",
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => $headers
|
||||
]);
|
||||
|
||||
curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
return $httpCode === 204; // 204 = gelöscht erfolgreich
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
Loading…
Reference in New Issue