第一方应用程序的 Laravel Passport 密码授予流程

Posted

技术标签:

【中文标题】第一方应用程序的 Laravel Passport 密码授予流程【英文标题】:Laravel Passport's Password Grant Flow for First-party Apps 【发布时间】:2019-03-31 07:38:42 【问题描述】:

我正在使用 Laravel Passport 将我的 API 的某些部分的访问权限授予第三方应用。

但是,我也通过自己的第一方原生 android 应用程序使用自己的 API。 因此,我查看了整个互联网以寻找在这种情况下的最佳做法,但一直无法得出结论。

以下是我发现的可能性:

可能性 #01

我可以关注User Credential Password Grant Flow。 在这种情况下,我需要将 client_secretclient_id 传递给授权服务器。为了保证它们的安全,我不能在我的移动应用程序的源代码中编写它们(APK 是可反编译的......)。

所以,我有两个选择。

可能性 #01 - 选择 A

通过我自己的服务器代理并在调用 oauth 端点之前注入秘密:

$proxy = Request::create('/oauth/token', 'post', [
    'grant_type' => 'password',
    'client_id' => 1,
    'client_secret' => 'myownclientsecretishere',
    'username' => $username,
    'password' => $password
]);
$proxy->headers->set('Accept', 'application/json');
$response = app()->handle($proxy);

可能性 #01 - 选择 B

在使用中间件调用 oauth 端点时注入密钥:

class InjectPasswordGrantSecret

    public function handle($request, Closure $next)
    
        $request->request->add([
            'client_id' => 1,
            'client_secret' => 'myownclientsecretishere'
        ]);
        return $next($request);
    

这些都是有效的例子,但它们也很贪婪。 我尝试在本地计算机上使用 Apache 基准测试,我得到了大约 9 个请求/秒。

可能性 #02

我可以关注Personal Access Grant。 这看起来不像OAuth2 中的标准,它允许我们通过任何自定义路由创建令牌,就像这样:

if (! auth()->attempt(compact('username', 'password'))) 
    return error_response(__('auth.failed'));

$user = auth()->user();
$token = $user->createToken(null)->accessToken;

使用 Apache benchmark 我得到了更好的结果(大约 30 个请求/秒)。

但是,令牌生命周期默认情况下是不可配置的,并且设置为 1 年(请注意,有一些变通方法可以使用自定义提供程序来配置此生命周期)。

我真的想知道这个解决方案是否打算在生产环境中使用。

最初,我使用JWT tymon 库,因为我只有自己的应用程序。但是现在我需要让它与第一方和第三方应用程序一起使用,我认为 OAuth2(通过 Laravel Passport)将是一个很好的解决方案......

我希望有人能帮我解决这个问题,并解释什么是让它在生产服务器上安全且[而不是缓慢地]工作的好解决方案。

【问题讨论】:

你试过laravel.com/docs/5.7/passport#token-lifetimes吗?我认为它适用于个人和密码授权令牌。 看看这个:github.com/laravel/passport/issues/162#issuecomment-319157793 @AliFarhoudi 就像我说的(您的链接证实了这一点)。个人访问授权有效期可使用自定义提供程序进行配置(解决方法)。这就是为什么我不确定是否应该将其用于生产中的自己的 Android 应用程序的原因。 自定义提供程序无需担心。当框架没有考虑到这一点时,您可以以您的自定义方式实现它。 @Marc 如果可能的话,我会说升级到 6,所以你可以在 AuthServiceProvider 中使用 Passport::personalAccessTokensExpireIn 【参考方案1】:

我之前所做的是实现了我自己的控制器,它扩展了Laravel\Passport\Http\Controllers\AccessTokenController。您不仅可以设置此控制器来处理创建令牌,还可以添加用于刷新和撤销令牌的路由。

例如,我有一个用于显示登录表单的create 方法,一个用于创建访问令牌的store 方法,一个用于使用刷新令牌刷新过期访问令牌的refresh 方法,以及一个@987654325 @方法用于撤销提供的访问令牌和相关的刷新令牌。

我无法提供确切的代码,因为该项目不是开源的,但这是一个只有 store 方法的快速示例控制器:

use Illuminate\Http\Request;
use Psr\Http\Message\ServerRequestInterface;
use Laravel\Passport\Http\Controllers\AccessTokenController;

class MyAccessTokenController extends AccessTokenController

    public function store(Request $request, ServerRequestInterface $tokenRequest)
    
        // update the request to force your grant type, id, secret, and scopes
        $request->request->add([
            'grant_type' => 'password',
            'client_id' => 'your-client-id',
            'client_secret' => 'your-client-secret',
            'scope' => 'your-desired-scopes',
        ]);

        // generate the token
        $token = $this->issueToken($tokenRequest->withParsedBody($request->request->all()));

        // make sure it was successful
        if ($token->status() != 200) 
            // handle error
        

        // return the token
        return $token;
    

refresh 方法基本相同,但grant_type 将是refresh_token

revoke 方法将获取经过身份验证的用户的令牌 ($token = $request->user()->token(),将其撤销 ($token->revoke()),然后手动通过 oauth_refresh_tokens 表获取相关令牌 (access_token_id = $token->id)并将revoked 字段更新为true

创建一些路由以使用您的自定义控制器,一切顺利(注意:revoke 路由将需要 auth:api 中间件)。

【讨论】:

所以,如果我理解的话,在您的解决方案中,您将数据添加到您的请求(秘密、授权类型 ..)以发出令牌(使用来自 AccessTokenController 的 issueToken 方法),从而避免额外的 http要求 ? [与我的解决方案 1a 相比] 您能解释一下是什么让这比简单地使用个人访问授权或其他解决方案更好吗?【参考方案2】:

这是我经常参考的页面:https://oauth2.thephpleague.com/authorization-server/which-grant/

它说

我们强烈建议您使用授权码流程 密码授予有几个原因。 我们消除了密码授予选项。

然后,它在图表中明确说明您应该使用

使用 PKCE 授予授权码

也表示

如果客户端是一个完全在前端运行的 Web 应用程序 端(例如,单页 Web 应用程序)或本机应用程序,例如 作为移动应用程序,您应该使用授权码授予 PKCE 扩展。 您可以在文档中进一步阅读。

此外,这里有一个很好的教程,用一个例子解释了流程的每个细节: https://auth0.com/docs/architecture-scenarios/mobile-api

我希望这些帮助。

PS:当我需要在我的第一方应用程序中授权我的用户时,我通过引用这个图表来使用密码授予。但是,它似乎已更改,密码授予现在不再是最佳做法,不推荐使用。

【讨论】:

以上是关于第一方应用程序的 Laravel Passport 密码授予流程的主要内容,如果未能解决你的问题,请参考以下文章

Laravel Passport 来自同一端点的响应同时来自两个用户的两个不同的 access_tokens 在两种情况下都返回第一个用户

无法找到 [Laravel\Passport\Client] 的工厂

Laravel passport认证

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

将 Laravel Passport 与移动应用程序一起使用

laravel + passport 搭建 api 认证系统 基础应用