使用 API 调用登录 Laravel 用户以推进 Web 应用程序

Logging Laravel user in to progress web app using API call

我已经设置了 Passport 路由以通过 API 为移动应用程序登录用户。但是,移动应用程序的一部分使用 webview 来显示门控内容,而其中一部分从 API 中提取其他内容。

用户使用 API 登录应用后,我需要他们同时登录 Web 内容。

但是,API 没有创建会话。我怎样才能通过 API 以一种在显示 webview 时会保留的方式登录和注销用户?

我已经尝试在 API\LoginController.php 上使用它来执行登录:

protected function sendLoginResponse(Request $request)
{
    if ($request->hasSession()) {
        $request->session()->regenerate();
    } else {
        // Login user for PWA pages.
        \Session::start();
        \Auth::login($this->guard()->user());
    }
    $this->clearLoginAttempts($request);
    return $this->authenticated($request, $this->guard()->user());
}

protected function authenticated(Request $request, User $user): ?JsonResponse
{
    return response()->json(['token' => $user->createToken(config('app.name'))->accessToken], 200);
}

这扩展了基础 Laravel 默认值 LoginController.php 但覆盖了这些方法以支持 JSON 响应。

相关路线:

Route::post('login')->name('api.auth.login')->uses('API\Auth\LoginController@login');

通过 API 调用时登录工作正常,但不会将会话持久保存到 Web 视图中。

在这个 GitHub 问题中使用 Hypnopompia 的解决方案解决了这个问题: https://github.com/laravel/passport/issues/135

提取他的代码并将其简单地插入到 web 中间件中。

namespace App\Http\Middleware;

use Auth;
use Closure;
use DB;
use Laravel\Passport\Passport;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\ValidationData;

class ApiTokenWebLogin
{
    /**
     * @param string $tokenId
     *
     * @return mixed
     */
    private function isAccessTokenRevoked($tokenId)
    {
        return DB::table('oauth_access_tokens')
            ->where('id', $tokenId)
            ->where('revoked', 1)
            ->exists();
    }

    /**
     * @param string $jwt
     *
     * @return array|bool
     */
    private function validateToken($jwt)
    {
        try {
            $token = (new Parser())->parse($jwt);

            if ($token->verify(new Sha256(), file_get_contents(Passport::keyPath('oauth-public.key'))) === false) {
                return false;
            }

            // Ensure access token hasn't expired.
            $data = new ValidationData();
            $data->setCurrentTime(time());

            if ($token->validate($data) === false) {
                return false;
            }

            // Check if token has been revoked.
            if ($this->isAccessTokenRevoked($token->getClaim('jti'))) {
                return false;
            }

            return [
                'user_id' => $token->getClaim('sub'),
            ];
        } catch (\Exception $e) {
            return false; // Decoder error.
        }
    }

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Closure $next
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $token = $request->bearerToken();

        // If user passed a valid Passport token, then login to the webview.
        if (!empty($token) && $request->hasSession() && !Auth::check() && $user_id = $this->validateToken($token)) {
            \Auth::loginUsingId($user_id);
        }

        return $next($request);
    }
}