Etsy PHP OAuth问题:调用getAccessToken无法获取Access Token
Etsy OAuth 401 Error When Fetching Access Token: Fixes & Debugging
Let's break down why you're hitting that 401 "Invalid auth/bad request" error when calling getAccessToken, and walk through fixing your code step by step.
Common Issues in Your Current Code
First, let's identify the key problems causing the auth failure:
- Redundant OAuth Instance Creation: Inside
getToken, you're re-reading your consumer key/secret and creating a newOAuthinstance ($oauth1) instead of reusing the existing one. This is unnecessary and can introduce subtle errors if the key/secret reading fails. - Inconsistent Callback URL: Etsy requires the callback URL used in
getRequestTokento exactly match the one registered in your Etsy app settings. Your code useshttp://localhost/ksec.php—double-check this matches what's in your Etsy app dashboard. - Poor Newline Handling: Using
trim(fgets($ksecrFile), "\n")doesn't account for Windows-style\r\nnewlines, which could leave hidden carriage returns in your consumer key/secret, breaking authentication. - Flow Logic Gaps: When handling the callback (after user authorization), you're not properly persisting the request token/secret between the initial redirect and the callback. Right now, you're relying on the
$req_tokenvariable from the same request, but that won't exist when Etsy redirects back to your callback URL.
Fixed Code with Explanations
Here's a cleaned-up version of your code that addresses these issues, plus proper credential storage:
<?php header('Content-type: text/plain'); ini_set('max_execution_time', 600); // Helper function to read key/secret correctly (handles all newline types) function readCredentials($filePath) { $lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); if (count($lines) < 2) { die("Invalid credentials file: must contain key on line 1, secret on line 2"); } return [ 'key' => $lines[0], 'secret' => $lines[1] ]; } // Initialize credentials $credentials = readCredentials("key_secret.txt"); $consumerKey = $credentials['key']; $consumerSecret = $credentials['secret']; // Initialize OAuth client $oauth = new OAuth($consumerKey, $consumerSecret); $oauth->disableSSLChecks(); // Only use this for local testing! Remove in production. // Step 1: Check if we're handling the OAuth callback (have verifier) if (isset($_GET['oauth_verifier'])) { session_start(); // Start session to retrieve stored request token // Retrieve the request token/secret we stored in the session if (!isset($_SESSION['oauth_request_token'], $_SESSION['oauth_request_token_secret'])) { die("Request token not found - please start the auth flow again"); } $requestToken = $_SESSION['oauth_request_token']; $requestTokenSecret = $_SESSION['oauth_request_token_secret']; $verifier = $_GET['oauth_verifier']; // Set the request token before fetching access token $oauth->setToken($requestToken, $requestTokenSecret); try { // Fetch access token $accessToken = $oauth->getAccessToken( "https://openapi.etsy.com/v2/oauth/access_token", null, $verifier ); // Success! Store credentials to file first, then database $tokenData = implode("\n", [ $verifier, $accessToken['oauth_token'], $accessToken['oauth_token_secret'] ]); file_put_contents("token.txt", $tokenData); echo "Access token retrieved successfully!\n"; print_r($accessToken); // Now you can insert $accessToken into your database // Example: $pdo->execute("INSERT INTO etsy_tokens (token, secret, verifier) VALUES (?, ?, ?)", [$accessToken['oauth_token'], $accessToken['oauth_token_secret'], $verifier]); } catch (OAuthException $e) { echo "Error fetching access token:\n"; echo $e->getMessage() . "\n"; echo "bad"; } } // Step 2: Check if we already have stored tokens (for testing) elseif (file_exists("token.txt")) { $tokenLines = file("token.txt", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); echo "Stored credentials found:\n"; echo "Verifier: " . $tokenLines[0] . "\n"; echo "Access Token: " . $tokenLines[1] . "\n"; echo "Access Token Secret: " . $tokenLines[2] . "\n"; } // Step 3: Start the OAuth flow (no callback, no stored tokens) else { session_start(); // Start session to store request token temporarily try { // Fetch request token with correct scope and callback $requestToken = $oauth->getRequestToken( "https://openapi.etsy.com/v2/oauth/request_token?scope=email_r%20listings_r", "http://localhost/ksec.php" // Must match Etsy app callback URL exactly ); // Store request token in session for callback use $_SESSION['oauth_request_token'] = $requestToken['oauth_token']; $_SESSION['oauth_request_token_secret'] = $requestToken['oauth_token_secret']; // Redirect user to Etsy authorization page $loginUrl = sprintf( "%s?oauth_consumer_key=%s&oauth_token=%s", $requestToken['login_url'], $consumerKey, $requestToken['oauth_token'] ); header("Location: " . $loginUrl); exit; } catch (OAuthException $e) { echo "Error fetching request token:\n"; echo $e->getMessage() . "\n"; die("Failed to start auth flow"); } } ?>
Key Fixes Explained
- Session-Based Request Token Storage: We use PHP sessions to persist the request token/secret between the initial redirect and the callback. This solves the problem of losing the request token data when Etsy redirects back to your app.
- Proper Credential Reading: The
readCredentialsfunction usesFILE_IGNORE_NEW_LINESto automatically strip all newline characters, avoiding hidden whitespace in your key/secret. - Simplified OAuth Flow: The code is split into clear steps: handle callback, check for stored tokens, or start the auth flow. No redundant OAuth instances.
- Callback URL Consistency: Double-check that the callback URL in
getRequestTokenmatches exactly what's set in your Etsy app settings (including http/https, port if needed).
Additional Debugging Tips
- Enable OAuth Debugging: Add
$oauth->enableDebug();right after initializing the OAuth instance to get detailed logs of the requests/responses, which can help pinpoint auth issues. - Check Etsy App Settings: Ensure your consumer key/secret are correct, and the callback URL is an exact match (no trailing slashes, correct protocol).
- Remove
disableSSLChecks()in Production: This is only for local testing—never use it in live environments, as it disables SSL validation.
内容的提问来源于stack exchange,提问作者Pioryby Clowcadia
相关产品推荐
相关产品推荐

