/oauth/token 返回500或SQLSTATE[42S02]的根本原因是数据库表缺失或密钥不可读:未运行php artisan migrate导致oauth_clients表不存在,或storage/oauth-private.key权限不足(如www-data无读取权),或旧迁移未回滚就重装。

Passport 不是装完 php artisan passport:install 就能直接调通 OAuth 接口的——漏掉迁移、权限、模型继承或客户端配置中的任意一环,/oauth/token 都会返回 500 或 401,且错误信息极其模糊。
为什么 /oauth/token 总是 500 或 SQLSTATE[42S02]?
这是最常卡住开发者的起点。根本原因几乎总是数据库表缺失或密钥不可读。
- 没运行
php artisan migrate就执行了php artisan passport:install:命令看似成功,但oauth_clients表根本不存在,后续所有 token 请求都会触发SQLSTATE[42S02]: Base table or view not found - 已存在旧迁移但未回滚就重装:用
php artisan migrate:fresh会清空全部表(含用户数据),应改用php artisan migrate:rollback --step=1回滚 Passport 相关迁移,再migrate -
storage/oauth-private.key权限不对:Web 服务器用户(如www-data)无法读取该文件时,Passport::tokenResponse()内部调用file_get_contents()直接报Permission denied;检查命令:ls -l storage/oauth-*.key,确保是-rw-r--r--级别
密码授权模式(Password Grant)必须手动用 client_id/client_secret
很多人以为 passport:install 后就能直接 POST 用户名密码拿 token,其实不行——它只生成了「个人访问客户端」和「密码授权客户端」两条记录,但你得知道用哪条、怎么传。
- 密码授权必须用
client_id对应的那条oauth_clients记录,其password_client字段为1;passport:install输出的第二组 ID/secret 就是它 - 请求体必须包含:
grant_type=password、client_id、client_secret、username、password、scope(哪怕为空);漏掉client_secret或填错,直接 401 - 不要在前端硬编码
client_secret:它是服务端敏感凭据,只应在后端(如登录接口)中使用;前端调用/oauth/token时,应由你的 API 控制器代为转发并注入 secret
User 模型继承 Authenticatable 是 createToken() 能用的前提
$user->createToken() 报 Call to undefined method,90% 是因为模型没继承基础认证类,而不仅仅是漏了 trait。
- 确认
app/Models/User.php中的类声明是:class User extends Authenticatable,不是extends Model或自定义基类 -
use HasApiTokens和use Authenticatable必须同时存在;仅加 trait 不生效,因为createToken()依赖Authenticatable提供的$this->id、$this->getAuthIdentifierName()等底层能力 - 若用了多模型认证(如
Admin也需要 API 登录),每个模型都得单独配一遍:继承Authenticatable+use HasApiTokens+ 在config/auth.php中为对应 guard 指定 provider
auth:api 中间件不生效?检查 config/auth.php 的 guards.api 配置
路由加了 middleware => ['auth:api'] 却仍能未登录访问,问题一定出在守卫驱动没切到 passport。
- 打开
config/auth.php,定位到'guards' => ['api' => [...]],确认'driver' => 'passport'已设置;写成'driver' => 'token'或遗漏这一行,中间件就完全绕过 Passport 验证逻辑 - 别混淆
auth:api和auth:sanctum:两者不能混用;如果项目里同时装了 Sanctum 和 Passport,务必确保 API 路由只绑定其中一个中间件 - 测试时用 curl 带 Bearer 头验证:
curl -H "Authorization: Bearer xxx" http://localhost/api/user;如果返回 401 但日志里没报密钥读取失败,大概率就是 guard 驱动配置错误
真正容易被忽略的是密钥文件的部署时机——本地开发时 passport:install 生成的 oauth-private.key 在 Git 中被忽略,但上线后若构建脚本没自动执行该命令,或容器启动时没 chown 到 Web 用户,接口就会静默失败。每次部署后,务必手动验证 storage/oauth-private.key 是否存在、可读、属主正确。


















