使用Facebook账号登录后刷新页面出现This authorization code has been used错误求助
问题概述
使用Facebook账号完成登录操作后刷新页面,触发「This authorization code has been used」错误,相关代码如下,请求协助排查。
相关代码
Providers\FacebookRepository.php
class FacebookRepository { protected $facebook; public function __construct() { $this->facebook = new Facebook([ 'app_id' => config('providers.facebook.app_id'), 'app_secret' => config('providers.facebook.app_secret'), 'default_graph_version' => 'v11.0' ]); } public function redirectTo() { $helper = $this->facebook->getRedirectLoginHelper(); $permissions = [ 'pages_manage_posts', 'pages_read_engagement' ]; $redirectUri = config('app.url') . '/home/facebookPage'; return $helper->getLoginUrl($redirectUri, $permissions); } public function handleCallback() { $helper = $this->facebook->getRedirectLoginHelper(); if (request('state')) { $helper->getPersistentDataHandler()->set('state', request('state')); } try { $accessToken = $helper->getAccessToken(); } catch(FacebookResponseException $e) { throw new Exception("Graph returned an error: {$e->getMessage()}"); } catch(FacebookSDKException $e) { throw new Exception("Facebook SDK returned an error: {$e->getMessage()}"); } //$accessToken = Api::where('app_id', 107003655273452)->pluck('access_token')->first(); $accessToken = $helper->getAccessToken(); if (!isset($accessToken)) { throw new Exception('Access token error'); } if (!$accessToken->isLongLived()) { try { $oAuth2Client = $this->facebook->getOAuth2Client(); $accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken); } catch (FacebookSDKException $e) { throw new Exception("Error getting a long-lived access token: {$e->getMessage()}"); } } return $accessToken->getValue(); } public function getPages($accessToken) { $pages = $this->facebook->get('/me/accounts', $accessToken); $pages = $pages->getGraphEdge()->asArray(); return array_map(function ($item) { return [ 'access_token' => $item['access_token'], 'id' => $item['id'], 'name' => $item['name'], 'image' => "https://graph.facebook.com/{$item['id']}/picture?type=large" ]; }, $pages); } }
Controller\SocialController.php
class SocialController extends Controller { protected $facebook; public function __construct() { $this->facebook = new FacebookRepository(); } public function redirectToProvider() { return redirect($this->facebook->redirectTo()); } public function handleProviderCallback() { if (request('error') == 'access_denied') { //handle error dd(request('error')); } $accessToken = $this->facebook->handleCallback(); //dd($accessToken); //use token to get facebook pages $page = $this->facebook->getPages($accessToken); //store acceess token in databese and use it to get pages //dd($accessToken); return redirect(route('facebook.login.callback'))->with('page', $page); } }
问题根源分析
- 重复调用授权码兑换逻辑:在
FacebookRepository.php的handleCallback方法中,连续两次调用$helper->getAccessToken()。第一次调用已经消耗了Facebook返回的授权码,第二次调用时授权码已失效,直接触发「This authorization code has been used」错误。 - 回调页面无防刷新处理:用户刷新回调页面时,浏览器会重复发送包含已使用授权码的请求,此时未做判断就重新执行授权流程,必然触发错误。
修复方案
1. 移除重复的授权码兑换调用
修改FacebookRepository.php的handleCallback方法,删除第二次多余的getAccessToken()调用:
public function handleCallback() { $helper = $this->facebook->getRedirectLoginHelper(); if (request('state')) { $helper->getPersistentDataHandler()->set('state', request('state')); } try { $accessToken = $helper->getAccessToken(); // 仅保留这一次调用 } catch(FacebookResponseException $e) { throw new Exception("Graph returned an error: {$e->getMessage()}"); } catch(FacebookSDKException $e) { throw new Exception("Facebook SDK returned an error: {$e->getMessage()}"); } if (!isset($accessToken)) { throw new Exception('Access token error'); } if (!$accessToken->isLongLived()) { try { $oAuth2Client = $this->facebook->getOAuth2Client(); $accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken); } catch (FacebookSDKException $e) { throw new Exception("Error getting a long-lived access token: {$e->getMessage()}"); } } return $accessToken->getValue(); }
2. 添加回调页面防刷新逻辑
在SocialController.php的handleProviderCallback方法中,缓存授权结果,避免刷新时重复执行授权流程:
public function handleProviderCallback() { if (request('error') == 'access_denied') { dd(request('error')); } // 检查Session中是否已有缓存数据,避免重复授权 if(session()->has('facebook_page_data')) { return redirect(route('facebook.login.callback'))->with('page', session('facebook_page_data')); } try { $accessToken = $this->facebook->handleCallback(); $page = $this->facebook->getPages($accessToken); // 将数据存入Session,关联当前用户的话也可存入数据库 session(['facebook_page_data' => $page, 'facebook_access_token' => $accessToken]); return redirect(route('facebook.login.callback'))->with('page', $page); } catch(Exception $e) { // 错误处理:跳转到登录页并提示错误 return redirect(route('facebook.login'))->with('error', $e->getMessage()); } }
3. 额外优化建议
- 确保
state参数验证逻辑正常运行,防止CSRF攻击(当前代码已包含该逻辑,无需修改) - 长期令牌获取后建议持久化到数据库,避免用户每次登录都重新授权
内容的提问来源于stack exchange,提问作者iyed ghodhbane
相关产品推荐
相关产品推荐

