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

Laravel Passport:如何实现Token有效期动态延长?

Laravel Passport: Dynamic Token Lifecycle Management

Got it, let's tackle this dynamic token lifecycle requirement in Laravel Passport. The goal is to have tokens expire after 1 hour of inactivity, but auto-extend their validity whenever the user makes a request. Here's a practical, step-by-step implementation:

1. Add a last_active_at Field to Track User Activity

First, we need to track when the token was last used. Let's add a timestamp column to Passport's oauth_access_tokens table:

Create a migration:

php artisan make:migration add_last_active_at_to_oauth_access_tokens_table

Update the migration file:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up()
    {
        Schema::table('oauth_access_tokens', function (Blueprint $table) {
            $table->timestamp('last_active_at')->nullable();
        });
    }

    public function down()
    {
        Schema::table('oauth_access_tokens', function (Blueprint $table) {
            $table->dropColumn('last_active_at');
        });
    }
};

Run the migration:

php artisan migrate

2. Initialize Token Metadata on Login

When a user logs in, generate their token and set the initial last_active_at timestamp. We'll also set a far-future expires_at value (since we'll use last_active_at to determine validity instead of the default expiration):

In your login controller (e.g., AuthController):

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;

class AuthController extends Controller
{
    public function login(Request $request)
    {
        $credentials = $request->validate([
            'email' => 'required|email',
            'password' => 'required'
        ]);

        if (!Auth::attempt($credentials)) {
            return response()->json(['message' => 'Invalid credentials'], 401);
        }

        $user = Auth::user();
        $tokenResult = $user->createToken('API Access Token');
        $token = $tokenResult->token;

        // Set a far-future expiration (we'll use last_active_at for actual validity)
        $token->expires_at = Carbon::now()->addYear();
        // Record initial active time
        $token->last_active_at = Carbon::now();
        $token->save();

        return response()->json([
            'access_token' => $tokenResult->accessToken,
            'token_type' => 'Bearer',
            'expires_at' => Carbon::parse($token->expires_at)->toDateTimeString()
        ]);
    }
}

3. Custom Token Validation for Inactivity Checks

We'll override Passport's default token validator to check if the token has been inactive for over 1 hour, and update the last_active_at timestamp on every valid request.

Create a custom validator class:

<?php

namespace App\Validation;

use League\OAuth2\Server\Validation\AccessTokenValidatorInterface;
use League\OAuth2\Server\Exception\OAuthServerException;
use Psr\Http\Message\ServerRequestInterface;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

class CustomAccessTokenValidator implements AccessTokenValidatorInterface
{
    public function validateAuthorization(ServerRequestInterface $request)
    {
        // Retrieve the authenticated token from the request
        $token = $request->getAttribute('oauth_access_token');

        if (!$token) {
            throw OAuthServerException::accessDenied('Invalid access token');
        }

        // Fetch the token record from the database
        $tokenRecord = DB::table('oauth_access_tokens')
            ->where('id', $token->getIdentifier())
            ->first();

        // Check if token exists or has been inactive for >1 hour
        if (!$tokenRecord || Carbon::parse($tokenRecord->last_active_at)->addHour()->isPast()) {
            // Revoke the token to prevent future use
            DB::table('oauth_access_tokens')
                ->where('id', $token->getIdentifier())
                ->update(['revoked' => true]);

            throw OAuthServerException::accessDenied('Token expired due to inactivity');
        }

        // Update last active timestamp for valid requests
        DB::table('oauth_access_tokens')
            ->where('id', $token->getIdentifier())
            ->update(['last_active_at' => Carbon::now()]);

        return $request;
    }
}

4. Register the Custom Validator

Tell Laravel to use our custom validator instead of the default one. Update AuthServiceProvider:

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport;
use League\OAuth2\Server\Validation\AccessTokenValidatorInterface;
use App\Validation\CustomAccessTokenValidator;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        // 'App\Models\Model' => 'App\Policies\ModelPolicy',
    ];

    public function boot()
    {
        $this->registerPolicies();

        Passport::routes();

        // Bind our custom token validator
        $this->app->bind(AccessTokenValidatorInterface::class, CustomAccessTokenValidator::class);
    }
}

5. Test the Flow

  • When a user logs in, they get a token with last_active_at set to now.
  • Every subsequent request will update last_active_at to the current time, effectively extending the token's validity by another hour.
  • If the user doesn't make any requests for 1 hour, the next request will be rejected with a 401 error, and the token will be revoked.

Bonus: Clean Up Expired Tokens

To keep your database clean, you can add a scheduled task to revoke tokens that have been inactive for over 1 hour. Add this to app/Console/Kernel.php:

protected function schedule(Schedule $schedule)
{
    $schedule->call(function () {
        DB::table('oauth_access_tokens')
            ->where('last_active_at', '<=', Carbon::now()->subHour())
            ->update(['revoked' => true]);
    })->hourly();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 20:37:51