consumerKey = $consumerKey; $this->consumerSecret = $consumerSecret; $this->tokenFile = __DIR__ . '/../data/oauth_token.json'; // Load saved tokens if exist $this->loadTokens(); } /** * Check if we have valid access tokens */ public function isAuthorized() { return !empty($this->oauthToken) && !empty($this->oauthTokenSecret); } /** * Get stored OAuth token */ public function getOAuthToken() { return $this->oauthToken; } /** * Get stored OAuth token secret */ public function getOAuthTokenSecret() { return $this->oauthTokenSecret; } /** * Step 1: Get request token and return authorization URL */ public function getAuthorizationUrl($callbackUrl) { $params = [ 'oauth_callback' => $callbackUrl, 'oauth_consumer_key' => $this->consumerKey, 'oauth_nonce' => $this->generateNonce(), 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_timestamp' => time(), 'oauth_version' => '1.0', ]; $params['oauth_signature'] = $this->generateSignature('GET', $this->requestTokenUrl, $params); $url = $this->requestTokenUrl . '?' . http_build_query($params); $response = $this->httpRequest($url); if (!$response) { throw new Exception('Failed to get request token'); } parse_str($response, $data); if (!isset($data['oauth_token']) || !isset($data['oauth_token_secret'])) { throw new Exception('Invalid request token response: ' . $response); } // Store request token temporarily (needed for step 2) $_SESSION['flickr_request_token'] = $data['oauth_token']; $_SESSION['flickr_request_token_secret'] = $data['oauth_token_secret']; // Return URL for user to authorize return $this->authorizeUrl . '?oauth_token=' . $data['oauth_token'] . '&perms=read'; } /** * Step 2: Exchange verifier for access token */ public function handleCallback($oauthToken, $oauthVerifier) { if (!isset($_SESSION['flickr_request_token_secret'])) { throw new Exception('Request token secret not found in session'); } $requestTokenSecret = $_SESSION['flickr_request_token_secret']; $params = [ 'oauth_consumer_key' => $this->consumerKey, 'oauth_token' => $oauthToken, 'oauth_verifier' => $oauthVerifier, 'oauth_nonce' => $this->generateNonce(), 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_timestamp' => time(), 'oauth_version' => '1.0', ]; $params['oauth_signature'] = $this->generateSignature( 'GET', $this->accessTokenUrl, $params, $requestTokenSecret ); $url = $this->accessTokenUrl . '?' . http_build_query($params); $response = $this->httpRequest($url); if (!$response) { throw new Exception('Failed to get access token'); } parse_str($response, $data); if (!isset($data['oauth_token']) || !isset($data['oauth_token_secret'])) { throw new Exception('Invalid access token response: ' . $response); } // Save access tokens $this->oauthToken = $data['oauth_token']; $this->oauthTokenSecret = $data['oauth_token_secret']; $this->saveTokens($data); // Clean up session unset($_SESSION['flickr_request_token']); unset($_SESSION['flickr_request_token_secret']); return [ 'oauth_token' => $this->oauthToken, 'user_nsid' => $data['user_nsid'] ?? null, 'username' => $data['username'] ?? null, 'fullname' => $data['fullname'] ?? null, ]; } /** * Sign an API request with OAuth */ public function signRequest($method, $url, $params = []) { $oauthParams = [ 'oauth_consumer_key' => $this->consumerKey, 'oauth_token' => $this->oauthToken, 'oauth_nonce' => $this->generateNonce(), 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_timestamp' => time(), 'oauth_version' => '1.0', ]; $allParams = array_merge($params, $oauthParams); $oauthParams['oauth_signature'] = $this->generateSignature( $method, $url, $allParams, $this->oauthTokenSecret ); return $oauthParams; } /** * Generate OAuth signature */ private function generateSignature($method, $url, $params, $tokenSecret = '') { ksort($params); $paramString = http_build_query($params, '', '&', PHP_QUERY_RFC3986); $baseString = strtoupper($method) . '&' . rawurlencode($url) . '&' . rawurlencode($paramString); $signingKey = rawurlencode($this->consumerSecret) . '&' . rawurlencode($tokenSecret); return base64_encode(hash_hmac('sha1', $baseString, $signingKey, true)); } /** * Generate random nonce */ private function generateNonce() { return md5(uniqid(mt_rand(), true)); } /** * Make HTTP request */ private function httpRequest($url) { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => true, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { return false; } return $response; } /** * Save tokens to file */ private function saveTokens($data) { $dir = dirname($this->tokenFile); if (!is_dir($dir)) { mkdir($dir, 0700, true); } $tokenData = [ 'oauth_token' => $data['oauth_token'], 'oauth_token_secret' => $data['oauth_token_secret'], 'user_nsid' => $data['user_nsid'] ?? null, 'username' => $data['username'] ?? null, 'created_at' => date('Y-m-d H:i:s'), ]; file_put_contents($this->tokenFile, json_encode($tokenData, JSON_PRETTY_PRINT)); chmod($this->tokenFile, 0600); } /** * Load tokens from file */ private function loadTokens() { if (file_exists($this->tokenFile)) { $data = json_decode(file_get_contents($this->tokenFile), true); if ($data) { $this->oauthToken = $data['oauth_token'] ?? null; $this->oauthTokenSecret = $data['oauth_token_secret'] ?? null; } } } /** * Clear saved tokens (logout) */ public function clearTokens() { $this->oauthToken = null; $this->oauthTokenSecret = null; if (file_exists($this->tokenFile)) { unlink($this->tokenFile); } } }