基于项目创建Laravel Passport令牌及项目权限校验方案咨询
Got it, let's work through this problem where users need separate Laravel Passport tokens for each of their projects, and we need to validate their access to the specific project on every API request. Here's a practical, step-by-step solution:
First, we need to connect Passport's access tokens to projects. The default oauth_access_tokens table doesn't track project associations, so let's add that with a migration:
php artisan make:migration add_project_id_to_oauth_access_tokens_table
Open the generated migration file and add the project foreign key:
public function up() { Schema::table('oauth_access_tokens', function (Blueprint $table) { $table->unsignedBigInteger('project_id')->nullable(); $table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade'); }); } public function down() { Schema::table('oauth_access_tokens', function (Blueprint $table) { $table->dropForeign(['project_id']); $table->dropColumn('project_id'); }); }
Run the migration with php artisan migrate once you're done. Also, make sure your projects table already exists with a user_id field linking to the project owner.
Next, we need to extend Passport's token model to support the project_id field and add relationship checks. First, publish Passport's models:
php artisan vendor:publish --tag=passport-models
Open app/Models/Passport/AccessToken.php and update it like this:
use App\Models\Project; class AccessToken extends \Laravel\Passport\Token { protected $fillable = [ 'name', 'scopes', 'revoked', 'project_id', // Add this to allow filling ]; // Link token to its associated project public function project() { return $this->belongsTo(Project::class); } // Check if the token's owner has access to the linked project public function userHasAccess() { // Adjust this if you have project members (not just owners) return $this->project->user_id === $this->user_id; } }
Don't forget to update your config/passport.php to use this custom model:
'models' => [ 'access_token' => App\Models\Passport\AccessToken::class, ],
Now let's create an endpoint to generate project-specific tokens. Add this method to an API controller (e.g., AuthController):
use App\Models\Project; use Illuminate\Http\Request; use Laravel\Passport\Client; public function generateProjectToken(Request $request) { $request->validate([ 'project_id' => 'required|exists:projects,id', 'name' => 'required|string', // e.g., "My Project - Mobile App" ]); // First, confirm the user owns the requested project $project = Project::where('id', $request->project_id) ->where('user_id', auth()->id()) ->firstOrFail(); // Grab your password grant client (create one via `php artisan passport:client --password` if missing) $client = Client::where('password_client', true)->first(); // Create the token and attach the project ID $token = auth()->user()->createToken( $request->name, [], // Add scopes here if you need granular permissions $project->id ); return response()->json([ 'access_token' => $token->accessToken, 'token_type' => 'Bearer', 'expires_at' => $token->token->expires_at, 'project_id' => $project->id, ]); }
This is the core part—we need to check that the token used for the request is tied to the project being accessed. Create the middleware:
php artisan make:middleware ValidateProjectAccess
Open app/Http/Middleware/ValidateProjectAccess.php and add the validation logic:
use Closure; use Illuminate\Http\Request; use Laravel\Passport\TokenRepository; class ValidateProjectAccess { protected $tokenRepository; public function __construct(TokenRepository $tokenRepository) { $this->tokenRepository = $tokenRepository; } public function handle(Request $request, Closure $next) { // Get the project ID from the route (adjust this if you get it from request body instead) $projectId = $request->route('project'); if (!$projectId) { return response()->json(['message' => 'Project ID is required'], 400); } // Retrieve the token from the request $token = $this->tokenRepository->findById($request->bearerToken()); if (!$token) { return response()->json(['message' => 'Unauthenticated'], 401); } // Check if the token's project matches the requested project AND the user has access if ($token->project_id != $projectId || !$token->userHasAccess()) { return response()->json(['message' => 'You do not have access to this project'], 403); } // Attach the project instance to the request for easy use in controllers $request->merge(['project' => $token->project]); return $next($request); } }
Register the middleware in app/Http/Kernel.php under $routeMiddleware:
protected $routeMiddleware = [ // ... other middleware 'project.access' => \App\Http\Middleware\ValidateProjectAccess::class, ];
Finally, wrap your project-specific API routes with both auth:api and project.access middleware:
use Illuminate\Support\Facades\Route; Route::middleware(['auth:api', 'project.access'])->group(function () { // Example routes Route::get('/projects/{project}/tasks', [TaskController::class, 'index']); Route::post('/projects/{project}/tasks', [TaskController::class, 'store']); Route::put('/projects/{project}', [ProjectController::class, 'update']); });
- If your project has multiple roles (like members, editors, admins), replace the
userHasAccessmethod with a check against aproject_userpivot table that tracks roles. - Add scopes to tokens (e.g.,
['project:read', 'project:write']) and extend the middleware to validate scopes for specific actions if you need finer-grained control.
内容的提问来源于stack exchange,提问作者Kalyanakannan padivasu

