Laravel 圣所 | 删除当前用户令牌不起作用

Tar*_*nis 8 laravel vue.js laravel-sanctum

我确实需要帮助来完成我尝试做的一件小事。我尝试在我的 laravel / Vue 设置中为一个用户使用并发的 individual_access_tokens 。一切工作正常,除了一件事之外,我想在用户注销时通过其 id 删除一个令牌。通过一个用户的登录,我为他们创建一个personal_access_token。注销后,应删除该特定的 access_token。

现在在我的注销方法中,我删除了所有令牌。这工作正常,但是当删除一个特定的令牌(应该有效)时,我总是收到此方法不存在的错误:

public function logout(Request $request){
    
   Auth::guard('web')->logout();
    
   // First try: auth()->user()->currentAccessToken()->delete();
   // Second try: $request->user()->token()->revoke();
   auth()->user()->tokens()->delete();
}
Run Code Online (Sandbox Code Playgroud)

错误:

LOG.error:调用未定义的方法 Laravel\Sanctum\TransientToken::delete() {"userId":18,"exception":{}}

api.php

Route::group(['middleware' => 'auth:sanctum'], function () {
    Route::post('/logout', [AuthController::class, 'logout']);
});
Run Code Online (Sandbox Code Playgroud)

我尝试了以下内容:

https://laracasts.com/discuss/channels/laravel/passport-how-can-i-manually-revoke-access-token (护照)https://laracasts.com/discuss/channels/laravel/deleting-users-注销时的护照令牌 https://divinglaravel.com/authentication-and-laravel-airlock https://laracasts.com/discuss/channels/laravel/spa-and-mobile-logout?page=1&replyId=698040

在所有这些线程中,使用的方法应该有效,但不适合我。我是否忽略了什么?

我感谢每一个帮助!

Dan*_*iro 16

关于实施的一些注意事项:

  • 不要在同一代码中使用delete()and ;logout()
  • 切勿对web基于令牌的请求使用防护;
  • 不要用于attempt($credentials)基于令牌的登录;

简而言之,此异常是混合 cookie 和 token 身份验证代码的指示器。

不要在同一代码中使用delete()andlogout()

Sanctum 有两种身份验证方式:cookie 和 token。

每种类型的身份验证都需要完全不同的实现。它们不兼容。

  • Cookie 注销:auth('web')->logout();
  • 令牌注销:auth('sanctum')->user()->currentAccessToken()->delete();

当执行路由时,Sanctum Guard 会检测身份验证的类型:cookie 或令牌。如果是 cookie,则返回TransientTokenon currentAccessToken()。如果它是一个令牌,则返回一个PersonalAccessToken

因为这个决定会影响之后的一切,所以你不能混合 cookie 和令牌代码。如果您想同时接受这两种类型,则必须为每种类型创建单独的代码。

这意味着您将 cookie 身份验证路由放入 中web.php,将令牌身份验证路由放入 中api.php

如果混合使用它们,您将获得delete method not found基于 cookie 的注销和logout method not found基于令牌的注销。

切勿对web基于令牌的请求使用防护

守卫web是 的别名SessionGuard。顾名思义,它基于会话和cookie。

Laravel 自动加载路由的会话和 cookies 中间件web.php。这就是为什么您可以对路线使用“网络”守卫web.php

Laravel 不会为api.php路由加载这些中间件。因此,我们不能webapi.php路线中使用守卫。

因此,请确保您不在web任何api.php路线上使用防护装置。

对于圣所,你可以使用sanctum守卫来代替。

另请注意,web未指定时这是默认的防护。为了安全起见,请为每个身份验证调用显式设置防护:

  • Auth::guard('sanctum')->...;
  • auth('sanctum')->...;
  • auth()->guard('sanctum')->...;

不要用于attempt($credentials)基于令牌的登录

许多人使用 来实现 Sanctum 登录attempt($credentials)。这对于基于令牌的身份验证来说是错误的。

应该如何:

  • Cookie 登录:auth('web')->attempt($credentials);
  • Token登录:手动(即获取用户+检查密码+返回Token);

如果您使用auth()->attempt($credentials),则使用“网络”防护。Web 防护使用 cookie,它在路由中不起作用api.php,也不适合基于令牌的身份验证。

如果您使用它,您可能不会收到错误,并且身份验证甚至可能有效,但它是错误的,也是您收到异常的主要原因。

不幸的是,没有auth('sanctum')->attempt($credentials),所以你必须手动实现它。

Sanctum官方文档有一个实现的片段。您只需复制并粘贴(并根据需要进行调整):

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
 
Route::post('/sanctum/token', function (Request $request) {
    $request->validate([
        'email' => 'required|email',
        'password' => 'required',
        'device_name' => 'required',
    ]);
 
    $user = User::where('email', $request->email)->first();
 
    if (! $user || ! Hash::check($request->password, $user->password)) {
        throw ValidationException::withMessages([
            'email' => ['The provided credentials are incorrect.'],
        ]);
    }
 
    return $user->createToken($request->device_name)->plainTextToken;
});
Run Code Online (Sandbox Code Playgroud)


Jan*_*ker 6

我有同样的问题,调用auth()->user()->currentAccessToken()->delete()给出错误Call to undefined method Laravel\\Sanctum\\TransientToken::delete()

我发现这是因为 Sanctum 身份验证守卫的工作原理。当 Laravel 会话已经就位时,通过会话 cookie,守卫会将 TransientToken 放在用户身上,然后通过currentAccessToken(). 但这TransientToken不是真实的PersonalAccessToken,也没有delete()方法。

在此处的源代码中找到了这一点。

我的解决方法是将注销路由不放在 Web 组中,而是放在 api 组中,该组不包含会话的中间件。


Kal*_*kat 1

对于特定用户:

Auth::user()->tokens()->where('id', $id)->delete();
Run Code Online (Sandbox Code Playgroud)

对于想要注销的请求用户

$user = request()->user();
$user->tokens()->where('id', $user->currentAccessToken()->id)->delete();
Run Code Online (Sandbox Code Playgroud)