rinvex / laravel-oauth
Rinvex OAuth 为 Laravel 提供OAuth2服务器支持。
Requires
- php: ^8.1.0
- ext-json: *
- firebase/php-jwt: ^6.4.0
- illuminate/auth: ^10.0.0 || ^11.0.0
- illuminate/console: ^10.0.0 || ^11.0.0
- illuminate/contracts: ^10.0.0 || ^11.0.0
- illuminate/cookie: ^10.0.0 || ^11.0.0
- illuminate/database: ^10.0.0 || ^11.0.0
- illuminate/http: ^10.0.0 || ^11.0.0
- illuminate/support: ^10.0.0 || ^11.0.0
- lcobucci/jwt: ^5.0.0
- league/oauth2-server: ^8.5.0
- nyholm/psr7: ^1.7.0
- phpseclib/phpseclib: ^3.0.0
- psr/http-message: ^2.0.0
- rinvex/laravel-support: ^7.0.0
- rinvex/tmp-josephsilber-bouncer: ^1.0.0
- symfony/console: ^6.2.0
- symfony/http-foundation: ^6.2.0
- symfony/psr-http-message-bridge: ^2.2.0
Requires (Dev)
- mockery/mockery: ^1.6.0
- orchestra/testbench: ^8.0.0
- phpunit/phpunit: ^10.1.0
README
Rinvex OAuth 是一个简单易用的OAuth2服务器和API身份验证包。
用法
简介
Rinvex OAuth 可以在几分钟内为您的 Laravel 应用程序提供一个完整的 OAuth2 服务器实现。 Rinvex OAuth 受启发于并基于 Laravel Passport v10.3.1 的轻量级修改版本,该版本建立在 League OAuth2 server 之上,由 Andy Millington 和 Simon Hamp 维护。
注意: 本文档假设您已经熟悉 OAuth2。如果您对 OAuth2 一无所知,请在继续之前熟悉 OAuth2 的术语和功能。
Rinvex OAuth 或 Laravel Passport?
在开始之前,您可能需要确定您的应用程序更适合使用 Rinvex OAuth 还是 Laravel Passport。简短的回答是:使用 "Laravel Passport"! Rinvex OAuth 是 Laravel Passport 的轻量级修改版本,简化以适应我们的 Rinvex Cortex 项目,所以您询问两者之间的区别以及选择哪一个就足够您选择 Laravel Passport 而无疑问。
但是,如果您正在尝试使用或构建基于 Rinvex Cortex 的应用程序,则应使用 Rinvex OAuth。 Rinvex OAuth 是构建 Rinvex Cortex API 时必需和默认安装的。
安装 Rinvex OAuth 不需要 Laravel Passport。实际上,它是一个完整的轻量级替代品。
安装
-
要开始使用,请通过 Composer 包管理器安装 Rinvex OAuth
composer require rinvex/laravel-oauth
-
Rinvex OAuth 注册了其自己的数据库迁移目录,因此您应该在安装包后迁移数据库。《Rinvex OAuth》迁移将创建您的应用程序存储 OAuth2 客户端和访问令牌所需的表。
php artisan rinvex:migrate:oauth
-
将
Rinvex\Oauth\Traits\HasApiTokens
特性添加到您的App\Models\User
模型中。这个特性将为您的模型提供一些辅助方法,使您能够检查认证用户的令牌和作用域。namespace App\Models; use Rinvex\Oauth\Traits\HasApiTokens; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use HasApiTokens; }
-
最后,在您的应用程序的
config/auth.php
配置文件中,您应该将api
认证守护程序的driver
选项设置为oauth
。这将指示您的应用程序在验证传入的 API 请求时使用 Rinvex OAuth 的TokenGuard
。'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'oauth', 'provider' => 'users', ], ],
部署 Rinvex OAuth
当第一次将 Rinvex OAuth 部署到应用程序的服务器时,您可能需要运行 rinvex:oauth:keys
命令。此命令生成 Rinvex OAuth 需要的加密密钥,以生成访问令牌。生成的密钥通常不会保存在源代码控制中。
php artisan rinvex:oauth:keys
通常,Rinvex OAuth 的密钥是从 storage_path
加载的。
从环境变量加载密钥
或者,您可以使用 rinvex:publish:oauth
Artisan 命令发布 Rinvex OAuth 的配置文件。
php artisan rinvex:publish:oauth --resource=config
迁移定制
如果您不打算使用 Rinvex OAuth 的默认迁移,您应该使用此命令导出配置文件 php artisan rinvex:publish:oauth --resource=migrations
,并将配置中的迁移自动加载从 rinvex.oauth.autoload_migrations
设置为 false
。
配置
客户端密钥哈希
您的所有客户端密钥都是经过散列的,并且仅在创建后立即对用户可见。由于明文客户端密钥值从未存储在数据库中,因此如果丢失,无法恢复密钥值。
令牌有效期
默认情况下,Rinvex OAuth 会发放长期有效的访问令牌,有效期为一年的。如果您想配置更长或更短的令牌有效期,您可以通过配置选项更改默认设置。例如,rinvex.oauth.grants.Password.expire_in
,您可以用相同的方式配置其他授权的过期时间。
注意:Rinvex OAuth 的数据库表中的
expires_at
列是只读的,仅用于显示目的。在发放令牌时,Rinvex OAuth 将过期信息存储在已签名和加密的令牌中。如果您需要使令牌无效,应撤销它。
覆盖默认模型
您可以根据自己的需求扩展 Rinvex OAuth 内部使用的模型,通过定义自己的模型并扩展相应的 Rinvex OAuth 模型来实现。
use Rinvex\Oauth\Models\Client as BaseClient; class Client extends BaseClient { // ... }
定义您的模型后,您可以通过配置选项 rinvex.oauth.models
指示 Rinvex OAuth 使用您的自定义模型。
'models' => [ 'client' => \Rinvex\Oauth\Models\Client::class, 'auth_code' => \Rinvex\Oauth\Models\AuthCode::class, 'access_token' => \Rinvex\Oauth\Models\AccessToken::class, 'refresh_token' => \Rinvex\Oauth\Models\RefreshToken::class, ],
颁发访问令牌
通过授权码使用 OAuth2 是大多数开发者熟悉的方式。当使用授权码时,客户端应用程序将用户重定向到您的服务器,在那里他们将批准或拒绝为客户端发放访问令牌的请求。
管理客户端
首先,开发需要与您的应用程序的 API 交互的应用程序时,需要通过创建“客户端”来将其应用程序注册到您的应用程序中。通常,这包括提供应用程序的名称和您的应用程序可以重定向到以供用户批准其授权请求的 URL。
rinvex:oauth:client
命令
创建客户端的最简单方法是使用 rinvex:oauth:client
Artisan 命令。此命令可用于创建用于测试 OAuth2 功能的自己的客户端。运行 client
命令时,Rinvex OAuth 将提示您输入有关客户端的更多信息,并提供客户端 ID 和密钥。
php artisan rinvex:oauth:client
重定向 URL
如果您想为您的客户端允许多个重定向 URL,您可以在提示输入 URL 时使用逗号分隔列表指定它们。包含逗号的任何 URL 都应进行 URL 编码。
http://third-party-client-app.com/oauth/callback,http://fourth-party-client-app.com/oauth/callback
仪表板界面
由于您的应用程序的用户无法使用 client
命令,因此 Rinvex OAuth 有一个配套的包装模块 Cortex OAuth,它提供完整的后端和前端仪表板,您可以使用这些仪表板来创建客户端。这为您节省了手动编码控制器以创建、更新和删除客户端的麻烦。
请查看 Cortex OAuth 文档,以获取关于管理客户端的仪表板端点的详细信息。仪表板界面受 web
和 auth
中间件的保护;因此,只能由您自己的应用程序的认证用户调用。
请求令牌
授权重定向
创建客户端后,开发人员可以使用他们的客户端 ID 和密钥从您的应用程序请求授权代码和访问令牌。首先,消费应用程序应向您的应用程序的 /oauth/authorize
路由发送重定向请求,如下所示
use Illuminate\Http\Request; use Illuminate\Support\Str; Route::middleware(['web'])->get('oauth/redirect', function (Request $request) { $request->session()->put('state', $state = Str::random(40)); $query = http_build_query([ 'client_id' => 'client-id', 'redirect_uri' => 'http://third-party-client-app.com/callback', 'response_type' => 'code', 'scope' => 'scope-id-1 scope-id-2', 'state' => $state, ]); return redirect('http://oauth-server-app.com/oauth/authorize?'.$query); });
注意
- 作用域必须是已创建的有效权限,并且已分配给处理此授权请求的用户。
- 记住,如果您使用 Cortex OAuth,您不需要手动定义此路由
/oauth/authorize
,因为它已经由模块定义。
批准请求
如果您使用 Cortex OAuth,当接收授权请求时,Cortex OAuth 将自动显示一个模板给用户,允许他们批准或拒绝授权请求。如果他们批准请求,他们将被重定向回消费应用程序指定的 redirect_uri
。该 redirect_uri
必须与创建客户端时指定的 redirect
URL 匹配。
如果您想自定义授权批准屏幕,可以使用 cortex:publish:oauth
Artisan 命令发布 Rinvex OAuth 的视图。发布的视图将放置在 resources/views/vendor/cortex/oauth
目录中
php artisan cortex:publish:oauth --resource=views
有时您可能希望跳过授权提示,例如在授权第一方客户端时。您可以通过 扩展 Client
模型 并定义一个 skipsAuthorization
方法来实现。如果 skipsAuthorization
返回 true
,则客户端将被批准,并且用户将立即重定向回 redirect_uri
。
use Rinvex\Oauth\Models\Client as BaseClient; class Client extends BaseClient { /** * Determine if the client should skip the authorization prompt. * * @return bool */ public function skipsAuthorization() { return $this->firstParty(); } }
将授权代码转换为访问令牌
如果用户批准授权请求,他们将被重定向回消费应用程序。消费者应首先将存储在重定向之前的状态参数与值进行比较。如果状态参数匹配,则消费者应向您的应用程序发出 POST
请求以请求访问令牌。请求应包括应用程序在用户批准授权请求时发出的授权代码。
use Illuminate\Http\Request; use InvalidArgumentException; use Illuminate\Support\Facades\Http; Route::middleware(['web'])->get('oauth/callback', function (Request $request) { $state = $request->session()->pull('state'); throw_unless( strlen($state) > 0 && $state === $request->state, InvalidArgumentException::class ); $response = Http::asForm()->post('http://oauth-server-app.com/oauth/token', [ 'grant_type' => 'authorization_code', 'client_id' => 'client-id', 'client_secret' => 'client-secret', 'redirect_uri' => 'http://third-party-client-app.com/oauth/callback', 'code' => $request->code, ]); return $response->json(); });
此 /oauth/token
路由将返回包含 access_token
、refresh_token
和 expires_in
属性的 JSON 响应。expires_in
属性包含访问令牌到期前的秒数。
注意:如果您使用 Cortex OAuth,您不需要手动定义
/oauth/token
路由,因为它已经由模块为您定义,就像/oauth/authorize
一样。无需手动定义此路由。
刷新令牌
如果您的应用程序颁发短期访问令牌,则用户需要通过在颁发访问令牌时提供的刷新令牌来刷新其访问令牌。
use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('http://oauth-server-app.com/oauth/token', [ 'grant_type' => 'refresh_token', 'refresh_token' => 'the-refresh-token', 'client_id' => 'client-id', 'client_secret' => 'client-secret', 'scope' => 'scope-id-1 scope-id-2', ]); return $response->json();
此 /oauth/token
路由将返回包含 access_token
、refresh_token
和 expires_in
属性的 JSON 响应。expires_in
属性包含访问令牌到期前的秒数。
撤销令牌
您可以使用 Rinvex\Oauth\Models\AccessToken
的 revoke
方法撤销访问令牌。
app('rinvex.oauth.access_token')->where('identifier', $tokenId)->get()->revoke();
或者,您可以直接使用 Rinvex\Oauth\Repositories\AccessTokenRepository
上的 revokeAccessToken
方法来达到相同的结果。
use Rinvex\Oauth\Repositories\AccessTokenRepository; use Rinvex\Oauth\Repositories\RefreshTokenRepository; // Revoke an access token... $accessTokenRepository = app(AccessTokenRepository::class); $accessTokenRepository->revokeAccessToken($tokenId);
注意:撤销访问令牌,也将撤销所有相关的刷新令牌。
您可以通过在 Rinvex\Oauth\Models\RefreshToken
上使用 revoke
方法来撤销特定的刷新令牌。
app('rinvex.oauth.refresh_token')->where('identifier', $tokenId)->get()->revoke();
清除令牌
当令牌被撤销或过期时,您可能希望从数据库中删除它们。Rinvex OAuth 包含的 rinvex:oauth:purge
Artisan 命令可以为您完成此操作
# Purge revoked and expired tokens and auth codes... php artisan rinvex:oauth:purge # Only purge revoked tokens and auth codes... php artisan rinvex:oauth:purge --revoked # Only purge expired tokens and auth codes... php artisan rinvex:oauth:purge --expired
您还可以在应用程序的 App\Console\Kernel
类中配置一个 计划任务,以定期自动修剪您的令牌
/** * Define the application's command schedule. * * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void */ protected function schedule(Schedule $schedule) { $schedule->command('rinvex:oauth:purge')->hourly(); }
带有PKCE的授权码授予
带有“代码交换证明密钥”(PKCE)的授权代码是一种安全的方式,用于认证单页应用程序或原生应用程序以访问您的API。当您不能保证客户端密钥将保密存储或为了减轻攻击者截获授权代码的威胁时,应使用此授权。当交换授权代码以获取访问令牌时,使用“代码验证器”和“代码挑战”的组合来替换客户端密钥。
创建客户端
在您的应用程序可以通过带有PKCE的授权代码颁发令牌之前,您需要创建一个启用PKCE的客户端。您可以使用带有 --public
选项的 rinvex:oauth:client
Artisan 命令来完成此操作
php artisan rinvex:oauth:client --public
请求令牌
代码验证器 & 代码挑战
由于此授权协议不提供客户端密钥,因此开发人员需要生成一个代码验证器和代码挑战的组合,以请求令牌。
代码验证器应该是一个长度在43到128个字符之间的随机字符串,包含字母、数字以及 "-"
、"."
、"_"
、"~"
字符,如 RFC 7636 规范 所定义。
代码挑战应该是一个Base64编码的字符串,具有URL和文件名安全的字符。尾部的 '='
字符应该被删除,并且不应该包含换行符、空白或其他额外字符。
$encoded = base64_encode(hash('sha256', $code_verifier, true)); $codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_');
授权重定向
一旦创建了一个客户端,您就可以使用客户端ID和生成的代码验证器和代码挑战来从您的应用程序请求授权代码和访问令牌。首先,消费应用程序应向您的应用程序的 /oauth/authorize
路由发送重定向请求
use Illuminate\Support\Str; use Illuminate\Http\Request; Route::get('oauth/redirect', function (Request $request) { $request->session()->put('state', $state = Str::random(40)); $request->session()->put( 'code_verifier', $code_verifier = Str::random(128) ); $codeChallenge = strtr(rtrim( base64_encode(hash('sha256', $code_verifier, true)) , '='), '+/', '-_'); $query = http_build_query([ 'client_id' => 'client-id', 'redirect_uri' => 'http://third-party-app.com/callback', 'response_type' => 'code', 'scope' => 'scope-id-1 scope-id-2', 'state' => $state, 'code_challenge' => $codeChallenge, 'code_challenge_method' => 'S256', ]); return redirect('http://oauth-server-app.com/oauth/authorize?'.$query); });
将授权代码转换为访问令牌
如果用户批准了授权请求,他们将被重定向回消费应用程序。消费者应将 state
参数与重定向之前存储的值进行验证,就像在标准授权代码授权中一样。
如果状态参数匹配,消费者应向您的应用程序发出一个 POST
请求以请求访问令牌。请求应包括当用户批准授权请求时由您的应用程序发出的授权代码以及最初生成的代码验证器
use Illuminate\Http\Request; use Illuminate\Support\Facades\Http; Route::get('callback', function (Request $request) { $state = $request->session()->pull('state'); $codeVerifier = $request->session()->pull('code_verifier'); throw_unless( strlen($state) > 0 && $state === $request->state, InvalidArgumentException::class ); $response = Http::asForm()->post('http://oauth-server-app.com/oauth/token', [ 'grant_type' => 'authorization_code', 'client_id' => 'client-id', 'redirect_uri' => 'http://third-party-app.com/callback', 'code_verifier' => $codeVerifier, 'code' => $request->code, ]); return $response->json(); });
密码授予令牌
OAuth2密码授权允许您的其他第一方客户端(如移动应用程序)使用电子邮件地址/用户名和密码获取访问令牌。这允许您安全地向您的第一方客户端颁发访问令牌,而无需要求您的用户完成整个OAuth2授权代码重定向流程。
创建密码授予客户端
在您的应用程序可以通过密码授权发行令牌之前,您需要创建一个密码授权客户端。您可以使用带有 --password
选项的 rinvex:oauth:client
Artisan 命令来完成此操作。如果使用的是 Cortex OAuth 并已运行 cortex:install:oauth
命令,则不需要运行此命令:
php artisan rinvex:oauth:client --password
请求令牌
创建密码授权客户端后,您可以通过向 /oauth/token
路径发送包含用户电子邮件地址和密码的 POST
请求来请求访问令牌。记住,如果您使用的是 Cortex OAuth,此路由已经为您注册,因此无需手动定义。如果请求成功,您将在服务器的 JSON 响应中接收到 access_token
和 refresh_token
。
use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('http://oauth-server-app.com/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'client-id', 'client_secret' => 'client-secret', 'username' => 'my@email.com', 'password' => 'my-password', 'scope' => 'scope-id-1 scope-id-2', ]); return $response->json();
**注意:** 记住,访问令牌默认为长期有效。但是,如果需要,您可以 配置最大访问令牌有效期。
请求所有作用域
当使用密码授权或客户端凭证授权时,您可能希望授权令牌以使用您应用程序支持的所有范围。您可以通过请求 *
范围来实现这一点。如果您请求 *
范围,令牌实例上的 can
方法将始终返回 true
。此范围只能分配给使用 password
或 client_credentials
授权发行的令牌。
use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('http://oauth-server-app.com/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'client-id', 'client_secret' => 'client-secret', 'username' => 'my@email.com', 'password' => 'my-password', 'scope' => '*', ]);
自定义用户类型
如果您的应用程序使用多个 认证守卫,您可以在通过 artisan rinvex:oauth:client --password
命令创建客户端时指定密码授权客户端使用的守卫。提供的守卫名称应与您的应用程序 config/auth.php
配置文件中定义的有效守卫相匹配。然后您可以通过 使用中间件保护路由 来确保只有来自该守卫的用户被授权。
自定义用户名字段
在密码授权认证时,Rinvex OAuth 将您的可认证模型上的 email
属性用作“用户名”。但是,您可以通过在您的模型上定义 findForOAuth
方法来自定义此行为。
namespace App\Models; use Rinvex\Oauth\Traits\HasApiTokens; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use HasApiTokens, Notifiable; /** * Find the user instance for the given username. * * @param string $username * @return \App\Models\User */ public function findForOAuth($username) { return $this->where('username', $username)->first(); } }
自定义密码验证
在密码授权认证时,Rinvex OAuth 将您的模型上的 password
属性用于验证提供的密码。如果您的模型没有 password
属性或您希望自定义密码验证逻辑,您可以在您的模型上定义 validateForOAuthPasswordGrant
方法。
namespace App\Models; use Illuminate\Support\Facades\Hash; use Rinvex\Oauth\Traits\HasApiTokens; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use HasApiTokens, Notifiable; /** * Validate the password of the user for the OAuth password grant. * * @param string $password * @return bool */ public function validateForOAuthPasswordGrant($password) { return Hash::check($password, $this->password); } }
隐式授予令牌
隐式授权类似于授权码授权;但是,令牌在交换授权码之前返回给客户端。此授权通常用于无法安全存储客户端凭证的 JavaScript 或移动应用程序。您可以通过使用此命令 php artisan rinvex:publish:oauth --resource=config
发布配置文件并启用该授权选项来启用此授权。默认情况下,它是禁用的,不建议使用,因为其他授权更安全。
'grants' => [ 'Password' => ['enabled' => true, 'expire_in' => new DateInterval('P1Y')], 'Implicit' => ['enabled' => false, 'expire_in' => new DateInterval('P1Y')], 'AuthCode' => ['enabled' => true, 'expire_in' => new DateInterval('P1Y')], 'RefreshToken' => ['enabled' => true, 'expire_in' => new DateInterval('P1Y')], 'PersonalAccess' => ['enabled' => true, 'expire_in' => new DateInterval('P1Y')], 'ClientCredentials' => ['enabled' => true, 'expire_in' => new DateInterval('P1Y')], ],
一旦启用授权,开发者可以使用他们的客户端 ID 从您的应用程序请求访问令牌。消费应用程序应将重定向请求发送到您的应用程序的 /oauth/authorize
路径,如下所示:
use Illuminate\Http\Request; Route::get('redirect', function (Request $request) { $request->session()->put('state', $state = Str::random(40)); $query = http_build_query([ 'client_id' => 'client-id', 'redirect_uri' => 'http://third-party-app.com/callback', 'response_type' => 'token', 'scope' => 'scope-id-1 scope-id-2', 'state' => $state, ]); return redirect('http://oauth-server-app.com/oauth/authorize?'.$query); });
注意:记住,如果您使用的是 Cortex OAuth,则无需手动定义此路由
/oauth/authorize
,因为它已由模块定义。
客户端凭据授予令牌
客户端凭证授权适用于机器到机器的认证。例如,您可能使用此授权在执行维护任务的计划任务中使用 API。
在您的应用程序可以通过客户端凭证授权发行令牌之前,您需要创建一个客户端凭证授权客户端。您可以使用 rinvex:oauth:client
Artisan 命令的 --client_credentials
选项来完成此操作。
php artisan rinvex:oauth:client --client_credentials
接下来,为了使用这种授权类型,您需要在 app/Http/Kernel.php
文件的 $routeMiddleware
属性中添加 CheckClientCredentials
中间件。
use Rinvex\Oauth\Http\Middleware\CheckClientCredentials; protected $routeMiddleware = [ 'client' => CheckClientCredentials::class, ];
注意:请记住,如果您正在使用 Cortex OAuth,您不需要手动注册这个中间件
CheckClientCredentials
,因为它已经由模块为您注册了。
然后,将中间件附加到路由上。
Route::get('orders', function (Request $request) { ... })->middleware('client');
要限制对路由的访问以特定范围,您可以在将 client
中间件附加到路由时提供所需范围的逗号分隔列表。
Route::get('/orders', function (Request $request) { // ... })->middleware('client:scope-id-1,scope-id-2');
获取令牌
要使用此授权类型获取令牌,请向 /oauth/token
端点发送请求。
use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('http://oauth-server-app.com/oauth/token', [ 'grant_type' => 'client_credentials', 'client_id' => 'client-id', 'client_secret' => 'client-secret', 'scope' => 'your-scope', ]); return $response->json()['access_token'];
注意:请记住,如果您正在使用 Cortex OAuth,您不需要手动定义此路由
/oauth/token
,因为它已经由模块为您注册。
个人访问令牌
有时,您的用户可能希望在不经过典型的授权代码重定向流程的情况下向自己发放访问令牌。允许用户通过您的应用程序的用户界面发放令牌,可以方便用户尝试您的API,或者可能作为发放访问令牌的更简单方法。
创建个人访问客户端
在您的应用程序可以发放个人访问令牌之前,您需要创建一个个人访问客户端。您可以通过使用带有 --personal_access
选项的 rinvex:oauth:client
Artisan 命令来完成此操作。 如果您正在使用 Cortex OAuth 并已运行 cortex:install:oauth
命令,则不需要运行此命令:
php artisan rinvex:oauth:client --personal_access
创建您的个人访问客户端后,将客户端的 ID 和纯文本密钥值放入应用程序的 .env
文件中。
OAUTH_PERSONAL_ACCESS_CLIENT_ID="client-id-value" OAUTH_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value"
您也可以通过配置文件来配置此设置,首先使用此命令 php artisan rinvex:publish:oauth --resource=config
发布它,然后更新相关选项。
'personal_access_client' => [ 'id' => env('OAUTH_PERSONAL_ACCESS_CLIENT_ID'), 'secret' => env('OAUTH_PERSONAL_ACCESS_CLIENT_SECRET'), ],
管理个人访问令牌
一旦创建了一个个人访问客户端,您就可以使用 App\Models\User
模型实例上的 createToken
方法为给定用户发放令牌。 createToken
方法接受令牌名称作为其第一个参数,以及作为其第二个参数的可选的 范围 数组。
use App\Models\User; $user = User::find(1); // Creating a token without scopes... $token = $user->createToken('Token Name')->accessToken; // Creating a token with scopes... $token = $user->createToken('My Token', ['scope-id-1', 'scope-id-2'])->accessToken;
保护路由
通过中间件
Rinvex OAuth 包含一个 认证守卫,它将在传入请求上验证访问令牌。一旦您已将 api
守卫配置为使用 oauth
驱动,您只需在需要有效访问令牌的任何路由上指定 auth:api
中间件即可。
Route::get('user', function () { // ... })->middleware('auth:api');
多个认证守卫
如果您的应用程序认证不同类型的用户,这些用户可能完全使用不同的 Eloquent 模型,那么您可能需要为应用程序中的每个用户提供者类型定义一个守卫配置。这允许您保护针对特定用户提供者的请求。例如,给定以下 config/auth.php
配置文件中的守卫配置:
'api:member' => [ 'driver' => 'oauth', 'provider' => 'members', ], 'api:admin' => [ 'driver' => 'oauth', 'provider' => 'admins', ],
以下路由将使用 api:member
守卫,它使用 members
用户提供者来验证传入请求。
Route::get('customer', function () { // })->middleware('auth:api:member');
注意:有关使用 Rinvex OAuth 的多个用户提供者的更多信息,请参阅 密码授权文档。
传递访问令牌
当调用受 Rinvex OAuth 保护的路线时,您的应用程序的 API 消费者应在他们的请求的 Authorization
标头中指定他们的访问令牌作为 Bearer
令牌。例如,当使用 Guzzle HTTP 库时:
use Illuminate\Support\Facades\Http; $response = Http::withHeaders([ 'Accept' => 'application/json', 'Authorization' => 'Bearer '.$accessToken, ])->get('https://oauth-server-app.com/api/user'); return $response->json();
令牌作用域
作用域允许您的API客户端在请求访问账户的授权时请求一组特定的权限。例如,如果您正在构建一个电子商务应用程序,并非所有API消费者都需要下订单的能力。相反,您可能允许消费者只请求访问订单配送状态的授权。换句话说,作用域允许您的应用程序用户限制第三方应用程序代表他们执行的操作。
定义作用域
作用域使用由silber/bouncer包定义的底层能力,这意味着每个作用域ID必须匹配您系统中的一个有效的权限ID,这正是与创建权限文档相比,Rinvex OAuth与Laravel Passport的主要区别。在利用Rinvex OAuth的作用域功能之前,请首先查看创建权限文档。
这样,您就有了一个集中管理所有资源访问权限的地方。
注意:请记住,颁发新访问令牌的用户必须具有相同的API请求作用域,否则将禁止访问。
默认作用域
如果客户端未请求任何特定作用域,您可以在配置文件中配置您的Rinvex OAuth服务器将默认作用域附加到令牌。首先您需要发布配置文件
php artisan rinvex:publish:oauth --resource=config
然后您可以更新配置选项
'default_scope' => null,
将作用域分配给令牌
请求授权代码时
当使用授权代码授予请求访问令牌时,消费者应将他们希望的作用域作为scope
查询字符串参数指定。该scope
参数应是一系列作用域的空间分隔列表
Route::get('redirect', function () { $query = http_build_query([ 'client_id' => 'client-id', 'redirect_uri' => 'http://third-party-client-app.com/oauth/callback', 'response_type' => 'code', 'scope' => 'scope-id-1 scope-id-2', ]); return redirect('http://oauth-server-app.com/oauth/authorize?'.$query); });
颁发个人访问令牌时
如果您正在使用App\Models\User
模型的createToken
方法颁发个人访问令牌,您可以传递希望的作用域数组作为方法的第二个参数
$token = $user->createToken('My Token', ['scope-id-1', 'scope-id-2'])->accessToken;
检查作用域
Rinvex OAuth包含两个中间件,可用于验证传入的请求是否使用具有给定作用域的令牌进行身份验证。要开始使用,请将以下中间件添加到您的app/Http/Kernel.php
文件中的$routeMiddleware
属性
'scopes' => \Rinvex\Oauth\Http\Middleware\CheckScopes::class, 'scope' => \Rinvex\Oauth\Http\Middleware\CheckForAnyScope::class,
注意:请记住,如果您正在使用Cortex OAuth,您不需要手动注册这些中间件
CheckScopes
和CheckForAnyScope
,因为它们已经由该模块为您注册。
检查所有作用域
scopes
中间件可以被分配给一个路由,以验证传入请求的访问令牌是否具有所有列出的作用域
Route::get('orders', function () { // Access token has both "scope-id-2" and "scope-id-1" scopes... })->middleware(['auth:api', 'scopes:scope-id-2,scope-id-1']);
检查任何作用域
scope
中间件可以被分配给一个路由,以验证传入请求的访问令牌是否具有列出的至少一个作用域
Route::get('orders', function () { // Access token has either "scope-id-2" or "scope-id-1" scope... })->middleware(['auth:api', 'scope:scope-id-2,scope-id-1']);
在令牌实例上检查作用域
一旦访问令牌认证请求进入您的应用程序,您仍然可以按照以下方式检查令牌是否具有给定作用域
use Illuminate\Http\Request; Route::get('orders', function (Request $request) { $scope = 'scope-id-1'; if ($request->user()->token()->abilities->map->getRouteKey()->contains($scope)) { // } });
使用JavaScript使用您的API
在构建API时,能够从您的JavaScript应用程序中消费自己的API非常有用。这种API开发方法允许您的应用程序消费您与世界共享的同一API。该API可以由您的Web应用程序、移动应用程序、第三方应用程序以及您可能发布在各个包管理器上的任何SDK消费。
通常,如果您想从JavaScript应用程序中消费您的API,您需要手动将访问令牌发送到应用程序,并在每次请求中将它传递给您的应用程序。然而,Rinvex OAuth包含一个可以为您处理此操作的中间件。您需要做的只是将CreateFreshApiToken
中间件添加到您的app/Http/Kernel.php
文件中的web
中间件组
'web' => [ // Other middleware... \Rinvex\Oauth\Http\Middleware\CreateFreshApiToken::class, ],
注意:您应确保将
CreateFreshApiToken
中间件列在中间件堆栈中的最后。
此中间件将为您的出站响应附加一个 laravel_token
Cookie。此 Cookie 包含一个加密的 JWT,Rinvex OAuth 将使用它来验证来自您 JavaScript 应用的 API 请求。JWT 的生命周期等于您的 session.lifetime
配置值。现在,由于浏览器将自动将 Cookie 与所有后续请求一起发送,您可以在不显式传递访问令牌的情况下向您的应用 API 发送请求
axios.get('api/user') .then(response => { console.log(response.data); });
自定义 Cookie 名称
如果需要,您可以使用相应的配置选项自定义 laravel_token
Cookie 的名称。首先,您需要使用此命令 php artisan rinvex:publish:oauth --resource=config
发布配置文件,然后您可以按以下方式修改
'default_scope' => null,
CSRF 保护
当使用此方法进行身份验证时,您需要确保请求中包含有效的 CSRF 令牌头。默认的 Laravel JavaScript 模板包括一个 Axios 实例,它将自动使用加密的 XSRF-TOKEN
Cookie 值,在同源请求上发送 X-XSRF-TOKEN
头。
注意:如果您选择发送
X-CSRF-TOKEN
头而不是X-XSRF-TOKEN
,则需要使用csrf_token()
提供的未加密令牌。
更新日志
有关项目的完整历史记录,请参阅 更新日志。
支持
以下支持渠道尽在您的指尖
贡献 & 协议
感谢您考虑为这个项目做出贡献!贡献指南可在 CONTRIBUTING.md 中找到。
欢迎提出错误报告、功能请求和拉取请求。
安全漏洞
如果您在此项目中发现安全漏洞,请发送电子邮件至 help@rinvex.com。所有安全漏洞都将得到及时联系。
关于 Rinvex
Rinvex 是一家成立于埃及亚历山大的软件解决方案初创公司,专注于为中小企业提供一体化企业解决方案,自 2016 年 6 月成立以来。我们相信,我们的动力在于价值、触达和影响,这是我们与其他公司区分开来的因素,并通过软件的力量释放我们哲学的无尽可能性。我们喜欢称它为“生命速度的创新”。这就是我们如何为推进人类文明做出自己的贡献。
许可
本软件在 MIT 许可证 (MIT) 下发布。
(c) 2016-2022 Rinvex LLC,部分权利保留。