cratespace/sentinel

此包已被废弃且不再维护。作者建议使用 emberfuse/sentinel 包。

Laravel 应用程序的仅后端认证。

v3.1.6 2021-05-28 21:05 UTC

README

简介

Sentinel 是 Cratespace 的前端无关认证后端实现。Sentinel 注册了实现 Cratespace 所有认证功能所需的路由和控制器,包括登录、注册、密码重置、电子邮件验证等。

Sentinel 实质上采用了 Cratespace UI 的路由和控制器,并提供了一个不包含用户界面的包。这允许您仍然快速搭建应用程序认证层的后端实现,而无需绑定到任何特定的前端观点。

安装

要开始,请使用 Composer 包管理器安装 Sentinel

composer require cratespace/sentinel

接下来,使用 sentinel:install 命令发布 Sentinel 的资源

php artisan sentinel:install

此命令将通过覆盖它们或创建新的来将 Sentinel 的操作、模型、策略和服务提供者发布到您的 app 目录。此外,Sentinel 的配置文件和迁移也将被发布。

接下来,您应该迁移您的数据库

php artisan migrate:fresh

Sentinel 发布了一个修改过的 create_users_table 迁移。为了方便使用,迁移需要重新应用。

Sentinel 服务提供者

上面讨论的 vendor:publish 命令还将发布 app/Providers/SentinelServiceProvider 文件。您应确保此文件在您的 app 配置文件的 providers 数组中注册。

此服务提供者注册了 Sentinel 发布的操作,指示 Sentinel 在执行相应的任务时使用它们。

认证

为了开始,我们需要指导 Sentinel 如何返回我们的 login 视图。请记住,Sentinel 是一个无头认证库。

可以使用通过 Sentinel\Sentinel\View 类提供的适当方法自定义所有认证视图的渲染逻辑。通常,您应从 SentinelServiceProviderboot 方法调用此方法

use Cratespace\Sentinel\Sentinel\View;

View::login('auth.login');

Sentinel 将处理生成返回此视图的 /login 路由。您的 login 模板应包含一个表单,该表单向 /login 发送 POST 请求。/login 操作期望一个字符串电子邮件地址/用户名和一个 password。电子邮件/用户名字段的名称应与 sentinel 配置文件的 username 值匹配。

如果登录尝试成功,Sentinel 将将您重定向到通过 sentinel 配置文件中的 home 配置选项配置的 URI。如果登录请求是 XHR 请求,将返回 200 HTTP 响应。

如果请求未成功,用户将重定向回登录屏幕,并通过共享的 $errors Blade 模板变量提供验证错误。或者,在 XHR 请求的情况下,验证错误将与 422 HTTP 响应一起返回。

自定义用户认证

Sentinel 将根据提供的凭据和为您的应用程序配置的认证保护器自动检索和验证用户。但是,您有时可能希望完全自定义登录凭据的认证方式和用户检索方式。幸运的是,Sentinel 允许您通过使用 AuthenticateUser 类轻松实现这一点。

可以通过修改 App\Actions\Sentinel\AuthenticateUser 动作来自定义认证过程。

注册

要开始实现注册功能,我们需要指导 Sentinel 如何返回我们的 register 视图。

可以使用通过 Sentinel\Sentinel\View 类提供的适当方法自定义所有认证视图的渲染逻辑。通常,您应从 SentinelServiceProviderboot 方法调用此方法

use Cratespace\Sentinel\Sentinel\View;

View::register('auth.register');

Sentinel 将负责生成返回此视图的 /register 路由。您的 register 模板应包含一个表单,该表单向 /register 发送 POST 请求。/register 动作期望一个字符串 name,字符串电子邮件地址/用户名,passwordpassword_confirmation 字段。电子邮件/用户名字段的名称应与 sentinel 配置文件中的 username 值匹配。

如果注册尝试成功,Sentinel 将将您重定向到通过 sentinel 配置文件中的 home 配置选项配置的 URI。如果登录请求是 XHR 请求,将返回 200 HTTP 响应。

如果请求不成功,用户将被重定向回注册屏幕,并通过共享的 $errors Blade 模板变量提供验证错误。或者,在 XHR 请求的情况下,验证错误将随 422 HTTP 响应返回。

自定义注册

可以通过修改 App\Actions\Sentinel\CreateNewUser 动作来自定义用户验证和创建过程。

密码重置

请求密码重置链接

要开始实现密码重置功能,我们需要指导 Sentinel 如何返回我们的“忘记密码”视图。

可以使用通过 Sentinel\Sentinel\View 类提供的适当方法自定义所有认证视图的渲染逻辑。通常,您应从 SentinelServiceProviderboot 方法调用此方法

use Cratespace\Sentinel\Sentinel\View;

View::requestPasswordReset('auth.forgot-password);

Sentinel 将负责生成返回此视图的 /forgot-password 路由。您的 forgot-password 模板应包含一个表单,该表单向 /forgot-password 发送 POST 请求。/forgot-password 端点期望一个字符串 email 字段。此字段/数据库列的名称应与 sentinel 配置文件中的 email 值匹配。

如果密码重置链接请求成功,Sentinel 将将您重定向回 /forgot-password 路由,并向用户发送一封包含安全链接的电子邮件,该链接可用于重置密码。如果请求是 XHR 请求,将返回 200 HTTP 响应。

在成功请求后重定向回 /forgot-password 路由后,可以使用 status 会话变量显示密码重置链接请求尝试的状态。

@if (session('status'))
    <div class="mb-4 font-medium text-sm text-green-600">
        {{ session('status') }}
    </div>
@endif

如果请求不成功,用户将被重定向回请求密码重置链接屏幕,并通过共享的 $errors Blade 模板变量提供验证错误。或者,在 XHR 请求的情况下,验证错误将随 422 HTTP 响应返回。

重置密码

为了完成密码重置功能的实现,我们需要指导 Sentinel 如何返回我们的“重置密码”视图。

可以使用通过 Sentinel\Sentinel\View 类提供的适当方法自定义所有认证视图的渲染逻辑。通常,您应从 SentinelServiceProviderboot 方法调用此方法

use Cratespace\Sentinel\Sentinel\View;

View::resetPassword('auth.reset-password', ['request' => $request]);

Sentinel 将负责生成路由以显示此视图。您的 reset-password 模板应包含一个表单,该表单通过 /reset-password 发送 POST 请求。/reset-password 端点期望一个字符串 email 字段,一个 password 字段,一个 password_confirmation 字段,以及一个名为 token 的隐藏字段,其中包含 request()->route('token') 的值。该 "email" 字段/数据库列的名称应与 sentinel 配置文件的 email 值相匹配。

如果密码重置请求成功,Sentinel 将将用户重定向回 /login 路由,以便用户可以使用新密码登录。此外,还将设置一个 status 会话变量,以便您可以在登录屏幕上显示重置成功的状态。

@if (session('status'))
    <div class="mb-4 font-medium text-sm text-green-600">
        {{ session('status') }}
    </div>
@endif

如果是 XHR 请求,则将返回 200 HTTP 响应。

如果请求未成功,用户将被重定向回密码重置屏幕,并通过共享的 $errors Blade 模板变量提供验证错误。或者,在 XHR 请求的情况下,验证错误将与 422 HTTP 响应一起返回。

自定义密码重置

可以通过修改 App\Actions\Sentinel\ResetUserPassword 动作来自定义密码重置过程。

电子邮件验证

注册后,您可能希望用户在继续访问您的应用程序之前验证他们的电子邮件地址。要开始,请确保在 sentinel 配置文件的 features 数组中启用了 emailVerification 功能。接下来,您应确保您的 App\Models\User 类实现了 MustVerifyEmail 接口。此接口已经自动导入到该模型中。

完成这两个设置步骤后,新注册的用户将收到一封电子邮件,提示他们验证他们的电子邮件地址所有权。但是,我们需要通知 Sentinel 如何显示通知用户他们需要在电子邮件中点击验证链接的电子邮件验证屏幕。

use Cratespace\Sentinel\Sentinel\View;

View::verifyEmail('auth.verify-email');

当用户通过内置的 verified 中间件被重定向到 /email/verify 端点时,Sentinel 将负责生成路由以显示此视图。

您的 verify-email 模板应包含一条信息性消息,指示用户点击发送到他们电子邮件地址的电子邮件验证链接。您可以选择在模板中添加一个按钮,该按钮触发到 /email/verification-notification 的 POST 请求。当此端点收到请求时,将向用户发送新的验证电子邮件链接,允许用户在之前的链接意外删除或丢失时获取新的验证链接。

如果重发验证链接电子邮件的请求成功,Sentinel 将带有一个 status 会话变量重定向回 /email/verify 端点,允许您向用户显示一条信息性消息,告知他们操作成功。如果请求是 XHR 请求,则将返回 202 HTTP 响应。

重新发送电子邮件验证链接

如果您愿意,可以在应用程序的 verify-email 模板中添加一个按钮,该按钮触发到 /email/verification-notification 端点的 POST 请求。当此端点收到请求时,将向用户发送新的验证电子邮件链接,允许用户在之前的链接意外删除或丢失时获取新的验证链接。

如果重新发送验证链接邮箱的请求成功,Sentinel 将会将用户重定向回 /email/verify 端点,并带有 status 会话变量,允许您向用户显示一条信息消息,告知操作成功。如果是 XHR 请求,则返回 202 HTTP 响应。

@if (session('status') == 'verification-link-sent')
    <div class="mb-4 font-medium text-sm text-green-600">
        A new email verification link has been emailed to you!
    </div>
@endif

保护路由

为了指定某个路由或一组路由要求用户之前已验证其电子邮件地址,您应将 Cratespace 内置的 verified 中间件附加到该路由。

Route::get('/home', function () {
    // ...
})->middleware(['verified']);

用户资料

用于满足资料更新请求的逻辑可以在您应用程序中的动作类中找到。具体来说,当用户更新其资料时,将调用 App\Actions\Sentinel\UpdateUserProfile 类。此动作负责验证输入并更新用户的资料信息。

因此,您希望对应用程序中此信息的处理进行任何自定义都应该在这个类中完成。当调用时,动作接收当前认证的 $user 和一个包含所有传入请求输入的 $data 数组,包括适用的更新资料照片。

可以使用 Sentinel\Sentinel\View 类提供的适当方法来自定义所有用户资料视图渲染逻辑。通常,您应该从您的 SentinelServiceProviderboot 方法中调用此方法。

use Cratespace\Sentinel\Sentinel\View;

View::userProfile('users.show');

Sentinel 将负责生成返回此视图的 /user/profile 路由。您的 用户资料 模板应包含一个表单,该表单向 /user/profile 发出 PUT 请求。该端点期望一个字符串 email 字段。此字段/数据库列的名称应与 sentinel 配置文件中的 email 值相匹配。

如果更新请求成功,Sentinel 将将用户重定向回 /user/profile 路由。如果是 XHR 请求,则返回 204 HTTP 响应。

如果请求未成功,用户将被重定向回用户资料屏幕,并且可以通过共享的 $errors Blade 模板变量访问验证错误。或者,在 XHR 请求的情况下,验证错误将与 422 HTTP 响应一起返回。

自定义用户资料更新动作

可以通过修改 App\Actions\Sentinel\UpdateUserProfile 动作来自定义用户资料更新过程。

资料照片

Sentinel 的资料照片功能由在安装 Sentinel 时自动附加到您的 App\Models\User 类的 Sentinel\Models\Traits\HasProfilePhoto 特性支持。

此特性包含 updateProfilePhotogetProfilePhotoUrlAttributedefaultProfilePhotoUrlprofilePhotoDisk 等方法,如果需要自定义其行为,您可以通过自己的 App\Models\User 类重写这些方法。我们鼓励您阅读此特性的源代码,以便全面了解它为您的应用程序提供的功能。

updateProfilePhoto 方法是存储资料照片的主要方法,并由您的应用程序的 App\Actions\Sentinel\UpdateUserProfile 动作类调用。

账户删除

资料管理屏幕还可以包含一个操作面板,允许用户删除其应用程序账户。当用户选择删除其账户时,将调用 App\Actions\Sentinel\DeleteUser 动作类。您可以在该类中自由自定义应用程序的账户删除逻辑。

密码更新

像Sentinel的大多数功能一样,实现该功能的底层逻辑可以通过修改相应的动作类来自定义。

当用户更新密码时,将调用App\Actions\Sentinel\UpdateUserPassword类。此动作负责验证输入并更新用户的密码。

Sentinel使用自定义的Sentinel\Rules\PasswordRule验证规则对象。此对象允许您轻松自定义应用程序的密码要求。默认情况下,规则要求密码长度至少为八位。但是,您可以使用以下方法自定义密码要求

use Cratespace\Sentinel\Rules\PasswordRule;

// Require at least 10 characters...
(new PasswordRule())->length(10);

// Require at least one uppercase character...
(new PasswordRule())->requireUppercase();

// Require at least one numeric character...
(new PasswordRule())->requireNumeric();

// Require at least one special character...
(new PasswordRule())->requireSpecialCharacter();

当然,这些方法可以链接起来定义应用程序的密码验证规则

(new PasswordRule())->length(10)->requireSpecialCharacter();

密码确认

在构建应用程序时,您可能会遇到需要用户在执行操作之前确认其密码的情况。通常,这些路由由内置的password.confirm中间件保护。

要开始实现密码确认功能,我们需要指导Sentinel如何返回应用程序的“密码确认”视图。

use Cratespace\Sentinel\Sentinel\View;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    View::confirmPassword('auth.confirm-password');
}

Sentinel将负责定义返回此视图的/user/confirm-password端点。您的确认密码模板应包含一个表单,该表单通过向/user/confirm-password端点发送POST请求。/user/confirm-password端点期望一个包含用户当前密码的密码字段。

如果密码与用户的当前密码匹配,Sentinel将重定向用户到他们尝试访问的路由。如果请求是XHR请求,则返回201 HTTP响应。

如果请求未成功,用户将被重定向回确认密码屏幕,并通过共享的$errorsBlade模板变量提供验证错误。或者,在XHR请求的情况下,验证错误将随422 HTTP响应返回。

双因素认证

大多数Sentinel功能可以通过动作类进行自定义。但是,出于安全考虑,Sentinel的双因素认证服务封装在Sentinel中,不应需要自定义。

双因素认证动作缺少专用视图,应包含在用户配置文件视图中。

要启用双因素认证,用户需要向/two-factor-authentication发送POST请求。在发送请求以启用双因素认证之前,应确认密码。密码确认应通过单独的端点进行,而不是将password参数包含在启用双因素认证请求中。

要禁用双因素认证,必须向/two-factor-authentication发送DELETE请求。这也需要确认密码,但如果之前已确认密码,则可以绕过。

API令牌认证

API令牌主要用于认证从不同域请求您的API的第三方应用程序。您自己的第一方SPA应使用Sentinel内置的SPA认证功能

颁发API令牌

Sentinel允许您颁发API令牌/个人访问令牌,这些令牌可用于认证对应用程序的API请求。在请求时使用API令牌,令牌应包含在Authorization头中,作为Bearer令牌。

要开始为用户颁发令牌,您的用户模型应使用Cratespace\Sentinel\Models\Traits\HasApiTokens特质。

use c\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

要发放令牌,您可以使用createToken方法。该方法返回一个Cratespace\Sentinel\Actions\CreateAccessToken实例。在存储到数据库之前,API令牌会使用SHA-256散列算法进行散列,但您可以通过CreateAccessToken实例的plainTextToken属性访问令牌的纯文本值。您应该在创建令牌后立即将此值显示给用户。

use Illuminate\Http\Request;

Route::post('/tokens/create', function (Request $request) {
    $token = $request->user()->createToken($request->token_name);

    return ['token' => $token->plainTextToken];
});

您可以使用由HasApiTokens特性提供的Eloquent关系访问所有用户的tokens

foreach ($user->tokens as $token) {
    //
}

令牌能力

Sentinel允许您将“能力”分配给令牌。能力与OAuth的“作用域”具有类似的作用。您可以将字符串能力数组作为createToken方法的第二个参数传递。

return $user->createToken('token-name', ['server:update'])->plainTextToken;

当处理由Sentinel认证的传入请求时,您可以使用tokenCan方法确定令牌是否具有给定能力。

if ($user->tokenCan('server:update')) {
    //
}
由第一方UI发起的请求

为了方便起见,如果传入的已认证请求来自您的第一方SPA并且您正在使用sentinel的内置SPA认证,则tokenCan方法将始终返回true。

但这并不一定意味着您的应用程序必须允许用户执行该操作。通常,您的应用程序的授权策略将决定令牌是否已被授予执行能力的权限,并检查用户实例本身是否应被允许执行该操作。

例如,如果我们想象一个管理服务器的应用程序,这可能意味着检查令牌是否有权更新服务器,以及服务器是否属于该用户。

return $request->user()->id === $server->user_id &&
    $request->user()->tokenCan('server:update')

最初,允许在第一方UI发起的请求中调用tokenCan方法并始终返回true可能看起来很奇怪;然而,能够始终假设API令牌可用并且可以通过tokenCan方法进行检查是很方便的。通过采取这种方法,您可以在应用程序的授权策略中始终调用tokenCan方法,而无需担心请求是从应用程序的UI触发的还是由您的API的第三方消费者发起的。

保护路由

要保护路由,以确保所有传入请求都必须进行认证,您应在您的routes/web.phproutes/api.php路由文件中的受保护路由上附加sentinel认证守卫。此守卫将确保传入请求被认证为状态化、cookie认证请求或包含来自第三方请求的有效API令牌头。

您可能想知道为什么我们建议您使用sentinel守卫在应用程序的routes/web.php文件中对路由进行认证。记住,sentinel将首先尝试使用Laravel的典型会话认证cookie来认证传入请求。如果该cookie不存在,则sentinel将尝试使用请求的Authorization头中的令牌来认证请求。此外,使用sentinel认证所有请求确保我们可以始终在当前认证用户实例上调用tokenCan方法。

use Illuminate\Http\Request;

Route::middleware('auth:sentinel')->get('/user', function (Request $request) {
    return $request->user();
});

撤销令牌

您可以通过使用由Cratespace\Sentinel\Models\Traits\HasApiTokens特性提供的tokens关系从数据库中删除令牌来“撤销”令牌。

// Revoke all tokens...
$user->tokens()->delete();

// Revoke the token that was used to authenticate the current request...
$request->user()->currentAccessToken()->delete();

// Revoke a specific token...
$user->tokens()->where('id', $tokenId)->delete();

SPA认证

哨兵(Sentinel)还用于提供一种简单的方法来验证需要与Laravel API通信的单页应用程序(SPA)。这些SPA可能存在于与您的Laravel应用程序相同的存储库中,也可能是一个完全独立的存储库。

为此功能,哨兵不使用任何类型的令牌。相反,哨兵使用Laravel内置的基于cookie的会话验证服务。这种认证方法提供了CSRF保护、会话验证以及通过XSS防止认证凭证泄露的好处。

为了进行认证,您的SPA和API必须共享相同的顶级域名。然而,它们可以放置在不同的子域名上。

配置

配置您的第一方域名

首先,您应该配置您的SPA将从哪些域名发出请求。您可以使用sentinel配置文件中的stateful配置选项来配置这些域名。此配置设置确定哪些域名将在向API发出请求时使用Laravel会话cookie进行“有状态的”认证。

如果您正在通过包含端口号的URL(如127.0.0.1:8000)访问应用程序,您应该确保在域名中包含端口号。

哨兵中间件

接下来,您应该在app/Http/Kernel.php文件中的api中间件组中添加哨兵中间件。此中间件负责确保来自您的SPA的传入请求可以使用Laravel会话cookie进行认证,同时仍然允许来自第三方或移动应用的请求使用API令牌进行认证。

'api' => [
    \Cratespace\Sentinel\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
    'throttle:api',
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
],
CORS & Cookies

如果您无法从在单独子域名上执行的应用程序中认证您的应用程序,您可能错误地配置了您的CORS(跨源资源共享)或会话cookie设置。

您应该确保应用程序的CORS配置返回具有值为TrueAccess-Control-Allow-Credentials标题。这可以通过将应用程序的config/cors.php配置文件中的supports_credentials选项设置为true来实现(cors.php仅在Laravel应用程序中找到)。

此外,您应该在应用程序的全局axios实例上启用withCredentials选项。通常,这应该在您的resources/js/bootstrap.js文件中执行。如果您不在前端使用Axios进行HTTP请求,您应该在您的HTTP客户端上执行等效的配置。

axios.defaults.withCredentials = true;

最后,您应该确保应用程序的会话cookie域名配置支持根域的任何子域。您可以通过在应用程序的config/session.php配置文件中将域名前缀为一个点.来实现这一点。

'domain' => '.domain.com',

认证

CSRF保护

为了验证您的SPA,您的SPA的“登录”页面应首先向/csrf-cookie端点发出请求,以初始化应用程序的CSRF保护。

axios.get('/csrf-cookie').then(response => {
    // Login...
});

在此请求期间,哨兵将设置一个包含当前CSRF令牌的XSRF-TOKENcookie。然后,此令牌应随后续请求中的X-XSRF-TOKEN标题传递,一些HTTP客户端库(如Axios和Angular HttpClient)会自动为您完成此操作。如果您的JavaScript HTTP库没有为您设置值,您需要手动设置X-XSRF-TOKEN标题以匹配由此路由设置的XSRF-TOKENcookie的值。

登录

一旦CSRF保护初始化完成,你应该向你的Laravel应用程序的/login路由发送一个POST请求。

如果登录请求成功,你将认证通过,之后对应用程序路由的请求将自动通过Laravel应用程序向客户端发布的会话cookie进行认证。此外,由于你的应用程序已经向/csrf-cookie路由发送了一个请求,后续请求应该自动获得CSRF保护,只要你的JavaScript HTTP客户端在X-XSRF-TOKEN头部发送了XSRF-TOKENcookie的值。

当然,如果你的用户会话由于缺乏活动而过期,后续对Laravel应用程序的请求可能会收到401419的HTTP错误响应。在这种情况下,你应该将用户重定向到SPA的登录页面。

保护路由

为了保护路由,确保所有传入请求都必须进行认证,你应在routes/api.php文件中给你的API路由附加sentinel认证保护。这个保护将确保传入的请求被认证,无论是来自SPA的具有状态的认证请求,还是包含有效API令牌头部的第三方请求。

use Illuminate\Http\Request;

Route::middleware('auth:sentinel')->get('/user', function (Request $request) {
    return $request->user();
});

移动应用程序认证

你还可以使用Sentinel令牌来认证你的移动应用程序对API的请求。认证移动应用程序请求的过程与认证第三方API请求类似;然而,在颁发API令牌的方式上存在一些小的差异。

颁发API令牌

要开始,创建一个接受用户电子邮件/用户名、密码和设备名的路由,然后交换这些凭据以获得新的Sentinel令牌。分配给此端点的“设备名”仅供参考,可以是任何你希望的价值。通常,设备名值应该是用户会认识到的名称,例如“John的诺基亚”。

通常,你将从移动应用程序的“登录”屏幕发送请求到令牌端点。该端点将返回纯文本API令牌,然后可以将其存储在移动设备上,并用于执行其他API请求。

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;

Route::post('/create/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;
});

当移动应用程序使用令牌对应用程序进行API请求时,它应该在Authorization头部中作为Bearer令牌传递令牌。

在颁发给移动应用程序的令牌时,您还可以指定令牌能力。

保护路由

如前所述,您可以通过将sentinel认证保护附加到路由来保护路由,以确保所有传入请求都必须进行认证。

Route::middleware('auth:sentinel')->get('/user', function (Request $request) {
    return $request->user();
});

撤销令牌

为了允许用户撤销颁发给移动设备的API令牌,你可以在Web应用程序UI的“账户设置”部分按名称列出它们,并列出“撤销”按钮。当用户点击“撤销”按钮时,你可以从数据库中删除令牌。记住,您可以通过Cratespace\Sentinel\Models\Traits\HasApiTokens特质提供的tokens关系访问用户的API令牌。

// Revoke all tokens...
$user->tokens()->delete();

// Revoke a specific token...
$user->tokens()->where('id', $tokenId)->delete();

贡献

感谢您考虑为Sentinel做出贡献!您可以在这里阅读贡献指南。

行为准则

为了确保Cratespace社区对所有成员都友好,请阅读并遵守行为准则

安全漏洞

请查阅我们的安全策略以了解如何报告安全漏洞。

许可

Cratespace Sentinel是开源软件,采用MIT许可协议