You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Socialite无状态模式Facebook授权码重复使用报错求助

Laravel Socialite无状态认证回调报错:授权码已使用

问题描述

在Laravel后端(REST API)+Angular前端架构中实现Laravel Socialite无状态认证,可正常获取重定向URL,但输入Facebook凭据授权后,回调时出现以下错误:

Client error: `POST https://graph.facebook.com/v3.3/oauth/access_token` resulted in a `400 Bad Request` response: {"error":{"message":"This authorization code has been used.","type":"OAuthException","code":100,"error_subcode":36009,"f (truncated...)

相关代码

1. api.php路由

Route::get('/auth/redirect/{provider}', [AuthController::class, 'redirectToProvider'])
    ->where('provider', '[A-Za-z]+');
Route::get('/auth/{provider}/callback', [AuthController::class, 'handleProviderCallback'])
    ->where('provider', '[A-Za-z]+');

2. AuthController.php控制器方法

/**
     * @param $provider
     * @return JsonResponse
     */
    public function redirectToProvider($provider): JsonResponse
    {
        $response = $this->authService->redirectToProvider($provider);
        return response()->json($response);
    }

    /**
     * @param $provider
     * @param Request $request
     * @return JsonResponse
     * @throws \App\Exceptions\Custom\CustomValidationException
     */
    public function handleProviderCallback($provider, Request $request): JsonResponse
    {
        ValidationUtils::validate($request->all(), [
            'code' => 'required',
        ]);

        $response = $this->authService->handleProviderCallback($provider);
        return response()->json($response);
    }

3. AuthServiceImpl.php实现类方法

/**
     * @param $provider
     * @return array[]
     */
    public function redirectToProvider($provider): array
    {
        if (!in_array($provider, self::PROVIDERS)) {
            throw new CustomNotFoundException(trans('errors.not_found.provider'));
        }

        $success['provider_redirect'] = Socialite::driver($provider)->stateless()->redirect()->getTargetUrl();

        return [
            'data' => $success
        ];
    }

    /**
     * @param $provider
     * @return array[]|void
     */
    public function handleProviderCallback($provider)
    {
        if (!in_array($provider, self::PROVIDERS)) {
            throw new CustomNotFoundException(trans('errors.not_found.provider'));
        }

        try {
            $providerUser = Socialite::driver($provider)->stateless()->user();

            if ($providerUser) {
                $user = $this->socialAccountsService->findOrCreate($providerUser, $provider);
                $user->markEmailAsVerified();
                $token = $user->createToken(env('API_AUTH_TOKEN_PASSPORT_SOCIAL'))->accessToken;

                return [
                    'data' => [
                        'tokens' => [
                            'token_type' => 'Bearer',
                            'expires_in' => 5400,
                            'access_token' => $token
                        ],
                        'user' => new UserResource($user)
                    ],
                    'message' => trans('auth.login')
                ];
            }
        } catch (\Exception $e) {
            throw new CustomUnauthorizedException($e->getMessage());
        }
    }

问题分析与修复方案

核心原因

错误提示授权码已被使用,结合代码来看,服务层的回调方法没有正确接收并使用前端传来的授权码code。在无状态模式下,Socialite无法自动获取请求中的code,必须手动传递。原代码中直接调用user()方法,会导致内部尝试使用无效/重复的code。

修复步骤

  1. 修改控制器,传递code到服务层
    在AuthController.php的handleProviderCallback方法中,将请求中的code参数传给服务层:
$response = $this->authService->handleProviderCallback($provider, $request->code);
  1. 修改服务层方法,使用userFromCode获取用户
    在AuthServiceImpl.php的handleProviderCallback方法中,接收code参数,并调用userFromCode方法替代user():
/**
 * @param $provider
 * @param string $code
 * @return array[]|void
 */
public function handleProviderCallback($provider, string $code)
{
    if (!in_array($provider, self::PROVIDERS)) {
        throw new CustomNotFoundException(trans('errors.not_found.provider'));
    }

    try {
        // 用userFromCode替代user(),手动传入授权码
        $providerUser = Socialite::driver($provider)->stateless()->userFromCode($code);

        if ($providerUser) {
            $user = $this->socialAccountsService->findOrCreate($providerUser, $provider);
            $user->markEmailAsVerified();
            $token = $user->createToken(env('API_AUTH_TOKEN_PASSPORT_SOCIAL'))->accessToken;

            return [
                'data' => [
                    'tokens' => [
                        'token_type' => 'Bearer',
                        'expires_in' => 5400,
                        'access_token' => $token
                    ],
                    'user' => new UserResource($user)
                ],
                'message' => trans('auth.login')
            ];
        }
    } catch (\Exception $e) {
        throw new CustomUnauthorizedException($e->getMessage());
    }
}
  1. 额外检查项
  • 前端回调逻辑:确保Facebook授权完成后,仅调用一次后端回调接口,避免重复提交同一个code
  • Facebook应用配置:确认回调URL与后端设置完全一致,同时升级Graph API版本(当前v3.3过旧,建议使用v18.0等较新版本)

内容的提问来源于stack exchange,提问作者user13289818

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 18:48:38