移动端应用PHP场景下Cookie失效问题排查求助
我的移动端应用通过OAuth 2.0向第三方网站请求授权码,回调到auth.example.com/redirect.php换取access token后,需要将token关联用户UUID存入Firebase实时数据库。由于第三方不支持state参数,我用storeUuid.php接收编码后的UUID,存入Cookie和Session,但回调时redirect.php始终无法读取Cookie,抛出{error:UUID not found in the cookie},且print_r($_COOKIE)无输出。无UUID关联时token可正常存入Firebase。
storeUuid.php代码
<?php // storeUuid.php session_start(); // Function to return JSON response function jsonResponse($data) { header('Content-Type: application/json'); echo json_encode($data); } // Function to log errors function logError($message) { error_log("Error: $message"); } session_set_cookie_params([ 'lifetime' => 3600, 'path' => '/', 'domain' => 'auth.example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'None' ]); // Function to store UUID in session and cookie function storeUUID($encodedUUID) { $decodedUUID = base64_decode($encodedUUID); if ($decodedUUID) { $_SESSION['userUid'] = $decodedUUID; setcookie("decodedUUID", $decodedUUID, time()+3600, "/", "auth.example.com"); jsonResponse(["success" => "UUID stored in session and cookie.", "decodedUUID" => htmlspecialchars($decodedUUID), "sessionId" => session_id(), "cookie" => ($_COOKIE)]); } else { logError("No UUID provided or decoding failed."); jsonResponse(["error" => "No UUID provided or decoding failed.", "sessionId" => session_id()]); } } // Check if the request method is POST if ($_SERVER['REQUEST_METHOD'] === 'POST') { $data = json_decode(file_get_contents('php://input'), true); $encodedUUID = isset($data['encodedUserUid']) ? $data['encodedUserUid'] : null; if ($encodedUUID) { storeUUID($encodedUUID); } else { logError("No encoded UUID provided in POST request."); jsonResponse(["error" => "No encoded UUID provided in POST request.", "sessionId" => session_id()]); } } else { logError("Invalid request method."); jsonResponse(["error" => "Invalid request method.", "sessionId" => session_id()]); } ?>
redirect.php代码
<?php // redirect.php session_start(); require __DIR__.'/vendor/autoload.php'; use Kreait\Firebase\Factory; use Kreait\Firebase\Exception\FirebaseException; $serviceAccountPath = __DIR__.'/1234ABCD.json'; $databaseUrl = 'https://fundraiser-application-example.com'; function jsonResponse($data) { header('Content-Type: application/json'); echo json_encode($data); } // Function to log errors function logError($message) { error_log("Error: $message"); } // Retrieve the decoded UUID from the cookie if (isset($_COOKIE['decodedUUID'])) { $decodedUUID = $_COOKIE['decodedUUID']; } else { logError("UUID not found in the cookie"); jsonResponse(["error" => "UUID not found in the cookie"]); exit; } print_r($_COOKIE); if (!isset($_SESSION['userUid'])) { logError("User UID not found in session"); jsonResponse(["error" => "User UID not found in session"]); exit; } try { $firebase = (new Factory) ->withServiceAccount($serviceAccountPath) ->withDatabaseUri($databaseUrl) ->createDatabase(); } catch (FirebaseException $e) { logError("Firebase Error: " . $e->getMessage()); jsonResponse(["error" => "Firebase Error: " . $e->getMessage()]); exit; } catch (Exception $e) { logError("General Error: " . $e->getMessage()); jsonResponse(["error" => "General Error: " . $e->getMessage()]); exit; } $clientID = 'MY_CLIENT_ID'; $clientSecret = 'MY_SECRET_Key'; $tokenURL = 'https://auth.3rdparty.com/oauth/token'; if (isset($_GET['code'])) { $authorizationCode = $_GET['code']; $postData = http_build_query([ 'grant_type' => 'authorization_code', 'client_id' => $clientID, 'client_secret' => $clientSecret, 'code' => $authorizationCode, 'redirect_uri' => 'https://auth.example.com/redirect' ]); $context = stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => "Content-Type: application/x-www-form-urlencoded\r\n", 'content' => $postData ] ]); $response = file_get_contents($tokenURL, false, $context); if ($response === FALSE) { logError("Error in token request"); jsonResponse(["error" => "Error in token request"]); exit; } $responseData = json_decode($response, true); if (isset($responseData['access_token'])) { $accessToken = $responseData['access_token']; $userUid = $_SESSION['userUid']; $firebase->getReference('users/' . $userUid . '/example/access_token/')->set($accessToken); jsonResponse(["success" => "Access Token stored successfully for user", "userUid" => htmlspecialchars($userUid)]); $_SESSION = array(); session_destroy(); } else { logError("Access token not found in response or user UID not available in session"); jsonResponse(["error" => "Access token not found in response or user UID not available in session", "response" => $responseData]); } } else { logError("Authorization code not found"); jsonResponse(["error" => "Authorization code not found"]); } ?>
运行日志
LOG Initiating OAuth process... LOG Encoded user UID: ABCD1234 LOG Response from storeUuid.php: Array ( [decodedUUID] => BFM6OIHuCBe56s08cgcnmiEotff1 [PHPSESSID] => lgh2gphfbbolcimv0abkkmrere ) {"success":"UUID stored in session and cookie.","decodedUUID":"5678DCBA","sessionId":"my_session_id","cookie":{"decodedUUID":"5678DCBA","PHPSESSID":"my_session_id"}} LOG Authorization URL: https://auth.3rdparty.com/oauth/authorize?response_type=code&client_id=(MY_CLIENT_ID)&redirect_uri=https://auth.example.com/redirect&scope=read LOG OAuth URL opened successfully.
解决方案建议
修正Cookie参数:
storeUuid.php中setcookie需补充secure和httponly参数,与Session Cookie参数保持一致,确保HTTPS环境下浏览器正常存储:setcookie("decodedUUID", $decodedUUID, time()+3600, "/", "auth.example.com", true, true);调整Session初始化顺序:
session_set_cookie_params必须在session_start()之前调用,否则参数不生效。修改storeUuid.php的代码顺序:session_set_cookie_params([ 'lifetime' => 3600, 'path' => '/', 'domain' => 'auth.example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'None' ]); session_start();验证跨站Cookie策略:使用
SameSite=None时必须搭配secure=true,且站点必须为HTTPS环境,否则浏览器会拒绝存储Cookie。检查Session传递有效性:在
redirect.php开头添加Session Cookie检查,确认Session是否正常传递:if (!isset($_COOKIE['PHPSESSID'])) { logError("Session cookie not found"); jsonResponse(["error" => "Session cookie not found"]); exit; }确保域名一致性:确认
storeUuid.php和redirect.php的域名完全一致,避免子域名、大小写差异导致Cookie无法读取。
内容的提问来源于stack exchange,提问作者Yanhamu

