Laravel Passport调用/oauth/clients返回404的前后端对接问题
核心问题定位
你遇到404报错的直接原因是路径写错:Passport自带的客户端管理JSON API默认注册在web中间件组下,路由前缀没有/api,正确路径是/oauth/clients而非/api/oauth/clients。
这套接口本身的设计定位是给同域下的自有系统后台使用,要求调用方必须持有Laravel会话Cookie、通过CSRF校验和登录认证,本身就不支持外部跨域前端直接调用,就算改对路径也会返回419/401错误。
客户端创建的正确方式
OAuth2体系中的客户端代表被授权访问资源的应用实体,创建逻辑必须放在Laravel后端侧,绝对不能写在外部前端项目中,常规创建方式有两种:
- 命令行创建(开发/固定客户端场景首选):直接在Laravel项目根目录执行
php artisan passport:client命令,按照交互提示输入客户端名称、授权回调地址即可,执行完成后命令行会直接返回生成的client_id和client_secret,全程无权限问题,是最稳妥的创建方式。如果是纯前端、移动端这类无法存密钥的公开客户端,加--public参数创建即可,这类客户端不会生成client_secret。 - 自有后台调用(开放平台场景):如果你需要让登录你主站的用户自行创建OAuth应用,就把官方文档给出的
axios.post('/oauth/clients')调用逻辑写在Laravel项目自带的同域前端页面中(比如Blade渲染的用户后台页),这类页面本身在web路由下,用户登录后自动携带会话凭证和CSRF令牌,可以正常调用接口。
注意:
client_secret属于高敏感凭证,绝对不能暴露在外部前端代码、移动端安装包中,这类公开场景必须使用PKCE模式的授权码流程,禁止使用需要传递client_secret的授权模式。
纯原生JS前端对接完整流程
不需要依赖Laravel Breeze做模拟客户端,纯原生JS完全可以完成对接,走标准OAuth2 PKCE授权码流程即可,步骤如下:
- 后端提前创建客户端:执行
php artisan passport:client --public,回调地址填写你前端项目的回调页地址(比如前端本地跑在5173端口就填http://127.0.0.1:5173/callback),记录返回的client_id。 - 配置跨域:修改Laravel项目
config/cors.php配置,在paths数组中加入oauth/*,allowed_origins数组中加入你前端项目的访问地址,避免跨域拦截。 - 前端实现PKCE参数生成:用户点击登录时,前端原生生成随机长度43位以上的字符串作为
code_verifier存在sessionStorage中,对该字符串做SHA256哈希后转base64url格式得到code_challenge。 - 跳转授权页:将用户重定向到Laravel授权地址,示例:
http://127.0.0.1:8000/oauth/authorize?client_id=刚才记录的客户端ID&redirect_uri=http://127.0.0.1:5173/callback&response_type=code&scope=*&code_challenge=上一步生成的挑战码&code_challenge_method=S256
- 换取访问令牌:用户在Laravel授权页点击同意后,会自动跳回你填写的前端回调地址,URL查询参数中会携带
code(授权码)。在回调页拿到授权码后,发POST请求到http://127.0.0.1:8000/oauth/token,请求参数示例:
const searchParams = new URLSearchParams(); searchParams.append('grant_type', 'authorization_code'); searchParams.append('client_id', 你的客户端ID); searchParams.append('redirect_uri', 'http://127.0.0.1:5173/callback'); searchParams.append('code_verifier', sessionStorage.getItem('code_verifier')); searchParams.append('code', 从URL拿到的授权码); const tokenRes = await axios.post('http://127.0.0.1:8000/oauth/token', searchParams); // tokenRes.data 中包含 access_token、refresh_token、过期时间等字段
- 后续访问业务API时,把拿到的
access_token放在请求头Authorization: Bearer ${access_token}中即可通过Passport认证。
额外说明:你当前
routes/api.php里默认的auth:sanctum路由和Passport授权流程无关,不需要在该文件中额外注册OAuth相关路由,Passport已经自动完成路由注册。
内容的提问来源于stack exchange,提问作者Fear4Haunt
相关产品推荐
相关产品推荐

