Laravel Passport 无效范围提供了异常

Posted

技术标签:

【中文标题】Laravel Passport 无效范围提供了异常【英文标题】:Laravel Passport invalid scope(s) provided exception 【发布时间】:2020-03-08 09:23:23 【问题描述】: Laravel Passport 8.0.0 版 Laravel 版本:6.2

我在AuthServiceProvider.php 文件的引导方法中使用Passport::tokensCan 方法定义了我的API 的范围:

Passport::routes();

Passport::tokensCan([
    'view-posts' => 'View posts',
    'create-posts' => 'Create posts'
]);

app/Http/Kernel.php 文件的$routeMiddleware 属性中添加了以下中间件:

protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    /*
    . . .
    */
    'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
    'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
];

然后,我在routes/api.php 中保护了一个API 路由:

Route::post('/posts', [
    'uses' => 'PostController@store',
    'middleware' => ['auth:api', 'scope:create-posts']
]);

在客户端应用中,我的授权路线是 (routes/web.php):

Route::middleware(['auth'])->group(function () 
    Route::get('/auth/blogger', 'BloggerAuthController@redirect');
    Route::get('/auth/blogger/callback', 'BloggerAuthController@callback');
);

BloggerAuthController控制器是这样的:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use GuzzleHttp\Client as Guzzle;

class BloggerAuthController extends Controller

    protected $client;

    public function __construct(Guzzle $client)
    
        $this->client = $client;
    

    public function redirect()
    
        $query = http_build_query([
            'client_id' => '3',
            'redirect_uri' => 'http://localhost:8001/auth/blogger/callback',
            'response_type' => 'code',
            'scope' => 'view-posts'
        ]);

        return redirect('http://localhost:8000/oauth/authorize?' . $query);
    

    public function callback(Request $request)
    
        $response = $this->client->post('http://localhost:8000/oauth/token', [
            'form_params' => [
                'grant_type' => 'authorization_code',
                'client_id' => '3',
                'client_secret' => 'gG5HcVn1JlGhzO0RfgTfWuqP8IVro1Qhu9g2q0Dq',
                'redirect_uri' => 'http://localhost:8001/auth/blogger/callback',
                'code' => $request->code
            ]
        ]);

        $response = json_decode($response->getBody());

        $request->user()->token()->delete();

        $request->user()->token()->create([
            'access_token' => $response->access_token
        ]);

        return redirect('/home');
    

当我 (POST) 从我的客户端应用程序向 /api/posts 请求时, 我得到一个例外:


    "message": "Invalid scope(s) provided.",
    "exception": "Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException",
    "file": "C:\\Users\\nbayramberdiyev\\Desktop\\fresh\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Exceptions\\Handler.php",
    "line": 206,
    /*
    . . .
    */

但预期的结果是:


    "message": "Unauthenticated."

状态为 401。

为什么会这样?我错过了docs 中的任何内容吗?

【问题讨论】:

你找到解决办法了吗? 【参考方案1】:

你只需要覆盖 MissingScopeException 异常来返回你想要的任何消息,并且仍然在你的路由文件中作用域中间件

在渲染函数的Handler文件中的Exception文件夹中

if ($exception instanceof MissingScopeException && $request->wantsJson())
            return response()->json([
                'error' => 'Unauthenticated',
            ], 403);
        

【讨论】:

【参考方案2】:

您请求view-posts 范围,但随后尝试创建需要create-posts 范围的帖子。这就是问题所在。

【讨论】:

是的,完全正确。但在我的情况下,它应该返回带有消息 Unauthenticated. 的 401 响应,而不是抛出异常。【参考方案3】:

创建access_token时,添加范围:create-posts

 $request->user()->token()->create([
            'access_token' => $response->access_token
        ], ['create-posts']);

【讨论】:

【参考方案4】:

我通过添加中间件和验证范围来解决这个问题,而不是在路由中使用 'scopes:some_scope'。这是我的解决方法。

    创建一个中间件并在这样的中间件中验证范围。

    public function handle($request, Closure $next)
    if (!$request->user()->tokenCan('user')) 
        return response()->json([
            'message' => "Unauthenticated",
        ]);
    
    
    return $next($request);
    

    request-&gt;user()-&gt;tokenCan('user') 验证范围。

    你可以返回任何你想要的错误信息。

    return response()->json([
        'message' => "Unauthenticated",
    ]);
    

    使用中间件。我一直在寻找那个答案,这是我能想到的最好的解决方案,它仍然处于边界并且没有太多的工作。如果您确实解决了更好的方法,请也分享给我。我也想知道。

【讨论】:

以上是关于Laravel Passport 无效范围提供了异常的主要内容,如果未能解决你的问题,请参考以下文章

使用 Laravel Passport 范围进行单元测试

如何使用 Laravel Passport (5.3) 记录身份验证尝试

Laravel 5.1 oAuth 2 服务器“无效范围”错误

身份验证用户提供程序 [passport] 未使用 laravel 护照定义

Laravel passport认证

laravel Passport - 创建 REST API 用户认证以及Dingo/Api v2.0+Passport实现api认证