Laravel Passport密码授予 - 客户端身份验证失败

时间:2016-09-19 12:15:42

标签: php laravel oauth laravel-5.3

在听了很多关于laravel护照之后,我想把它实现到我的新项目中,我的要求是创建一个可以在移动应用程序中使用的API。

所以我的移动应用程序是client,它将进一步拥有其用户。

我按照mentioned by Taylor的步骤阅读了here。简而言之,我遵循了以下步骤:

  1. 已安装laravel/passport
  2. 创建了一个网站用户。
  3. 生成的护照密钥php artisan passport:install
  4. 使用client_id
  5. 生成client_secretphp artisan passport:client
  6. redirection
  7. 中添加了callbackweb.php路由
  8. 授权用户并获得最终访问令牌。
  9. 然后我尝试拨打api/user(标题Authorization包含值Bearer eyJ0eXAiOiJKV1...(token)

    我收到了数据。非常简单和整洁。

    但我的应用用户不会有这些细节。所以我想到配置Password Grant Tokens完全符合我的要求。

    现在开始真正的头痛。我一直试图在最近3天内设置这个并不断获得

    {"error":"invalid_client","message":"Client authentication failed"}
    

    我几乎尝试了我在网上关注的所有指南:Redirection IssuesAdd Atleast One Scope SolutionP100Y Issue等。

    但我仍然遇到invalid client错误。这是我通过POSTMAN传递给oauth/token

    {
        "grant_type": "password,"
        "client_id": "3,"
        "client_secret": "8BUPCSyYEdsgtZFnD6bFG6eg7MKuuKJHLsdW0k6g,"
        "username": "test@gmail.com,"
        "password": "123456,"
        "scope": ""
    }
    

    任何帮助都将不胜感激。

19 个答案:

答案 0 :(得分:15)

首先检查您的凭据是否正确,然后检查您的模型表使用\Laravel\Passport\HasApiTokens特征是否包含email列,因为默认情况下它用于在验证凭据时识别用户。如果您的表具有username列或用于验证凭据的任何其他列,则必须在该模型中定义函数findForPassport。像这样,

public function findForPassport($username) {
       return self::where('username', $username)->first(); // change column name whatever you use in credentials
    }

我使用用户名和密码列来验证用户, 在{project_directory}\vendor\laravel\passport\src\Bridge\UserRepository.php

此功能验证您的凭据,

public function getUserEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity)
    {
        if (is_null($model = config('auth.providers.users.model'))) {
            throw new RuntimeException('Unable to determine user model from configuration.');
        }

        if (method_exists($model, 'findForPassport')) { // if you define the method in that model it will grab it from there other wise use email as key 
            $user = (new $model)->findForPassport($username);
        } else {
            $user = (new $model)->where('email', $username)->first();
        }

        if (! $user || ! $this->hasher->check($password, $user->password)) {
            return;
        }

        return new User($user->getAuthIdentifier());
    }

注意第二个if语句,你就会知道那里发生了什么。

希望这有帮助:)

答案 1 :(得分:10)

这可能有助于指明您正确的方向,因为我们自己正在实施密码授权。

1 - 在第4步而不是运行:

php artisan passport:install

运行以下命令以创建密码授予客户端:

php artisan passport:client --password

上述命令将提供一个后续问题来设置密码授予客户端的名称。请务必在数据库中获取此新记录的ID和密码。

2 - 您的POSTMAN Post请求对象应该包含在类似于以下内容的对象中:

'form_params' => [
    'grant_type' => 'password',
    'client_id' => 'my_client-id',
    'client_secret' => 'my_client-secret',
    'username' => 'username or email', <-pass through variables or something
    'password' => 'my-user-password', <- pass through variables or something
    'scope' => '',
],

2016年10月10日更新:

关于这个问题的进一步调查,我遇到了你所处的同一个地方,并被迫潜入护照的兔子洞。

目前,我看到系统已将客户端标识符设置为“授权号码”。什么时候需要密码&#39;。我正在研究如何解决您遇到的这个问题,因为我也遇到了这个问题。我会发布代码,以帮助人们跟进我已经去过的地方以及我一直在做的事情,但现在是2:40,所以我需要睡一觉。

更新2 10/12/2016

现在已经调试了7个小时的问题。护照内有一种方法可以验证客户。此方法接受您在对象中添加的输入(如上所述),并尝试根据您传入的内容通过客户端存储库类获取客户端实体。如果找到客户端,它将是检查客户端实体接口中是否确实存在客户端实体的实例,其中LOOKS类似于从数据库中提取客户端的凭据。但是,有一段代码返回NULL从我在测试中获得的内容。到目前为止,所有字段都被正确引入,但是存储库和接口之间似乎存在混淆。这导致错误被抛出。

最后,我觉得我已经接近解决这个问题了。非常感谢您的耐心等待。 =)

更新3 10/12/2016

经过长时间的调试后,我发现了最初的问题。有些领域没有匹配。简而言之,这是一个设置问题。

SO

这种情况下的解决方案是:

双重检查所有字段在数据库中,传入所有凭据。直到句点,破折号,空格密码,客户端ID,客户端密码,VALID重定向URI / URL,grant_types和范围(如果您正在使用它们):

确保数据库中的客户端在&#34; password_client&#34;下的ENUM为1;列,如果不是你需要使用上面提到的php artisan命令创建一个,我将在这里引用:

php artisan passport:client --password

确保您传入的密码与数据库字符中为该客户列出的字符匹配

确保您传递匹配的ID并且是整数数据类型。

确保客户端有某种名称

使用密码授予时,请不要担心user_id列

确保路线正确

确保您输入的电子邮件(用户名)是数据库中用户表内的实际用户(或者您自定义laravel 5.3系统作为用户表接受的任何表格,我将添加有关如何使用的链接自定义laravel 5.3系统,如果需要,可以接受不同的用户表作为默认值。)

确保grant_type拼写正确

确保您已设置为接受响应承载令牌(表示access_token和refresh_token)。

除此之外,我在本答复的上述部分中概述的初始步骤应该让您回到正确的轨道上。这里还有其他人提到了一些有用的类似步骤,但也解决了与单独问题相关的相同错误(我确信这是与其特定系统设置相关的设置问题的结果)。

我希望这会有所帮助。

答案 2 :(得分:4)

请在所有护照命令后尝试运行php artisan config:cache

答案 3 :(得分:1)

我遇到了同样的问题,并通过设置basic.auth标头得到修复,其中client_id为用户名,密码为密码。

然后它运作良好。

答案 4 :(得分:1)

使用以下代码生成访问令牌,它之前适用于我:

$request->request->add([
      'client_id' => '2',
      'client_secret' => 'm3KtgNAKxq6Cm1WC6cDAXwR0nj3uPMxoRn3Ifr8L',
      'grant_type' => 'password',
      'username' => 'support@eliteride.co.uk',
      'password' => 'pass22'
    ]);
    $tokenRequest = $request->create('/oauth/token', 'POST', $request->all());

    $token =  \Route::dispatch($tokenRequest);

答案 5 :(得分:0)

我通过确保在 oauth_clients 表中我的客户端将 password_client 列设置为 1 来修复它:

来源:https://github.com/laravel/passport/issues/589#issuecomment-357515110

<块引用>

上述问题可能有很多场景,我来解释一下 由一个。你总是需要传递 application/json 头。

  1. 确保客户端 ID 和密码在 oauth_clients 中,并且您 正确传递这些参数。为了生成那些客户端ID 并秘密使用 php artisanpassport:install。
  2. 确保使用密码 授予客户。识别该 goto oauth_clients 表并检查是否 password_client 设置为 1,personal_access_client 设置为 0 和 撤销为 0 。我希望这将解决问题。谢谢

答案 6 :(得分:0)

尝试注释掉 Passport::hashClientSecrets(); 文件中的这一行 AppServiceProvider。您不能使用散列的客户端机密。

答案 7 :(得分:0)

确保您不尝试获取带有散列client_secret的令牌... 如果您要在Passport::hashClientSecrets();中用AppServiceProvider散列客户机密,则生成的令牌将仅在终端输出一次中显示。丢失此令牌,它将永远丢失。

我急忙设置护照,却忘了我正在哈希生成的客户机密...

我的请求正文应该包含一个加密的客户端密钥,但是我从数据库中复制并粘贴了一个加密的密钥。

要解决此问题,请立即保存您的客户端机密,或删除Passport::hashClientSecrets();并创建一个 new 客户端。

答案 8 :(得分:0)

希望这将为我节省调试时间的时间:

我下了兔子洞,找出了为什么身份验证不起作用的原因。原来,我是无意中两次在UsersTableSeeder中对密码进行哈希处理,这就是哈希检查失败的原因。

如果列出的其他答案对您不起作用,可能值得检查。

答案 9 :(得分:0)

就我而言,解决方案是将其包装在Form-data中, 如果您对邮递员使用常规参数,请不要错过它,它将无法正常工作, 确保使用设置为FORM-DATA的正文请求。

答案 10 :(得分:0)

我有同样的问题。当您执行php artisan passport:install时,会向您发出两种类型的机密,一种是CLIENT授予类型,另一种是PASSWORD授予类型。

我正在使用CLIENT机密来发行PASSWORD令牌。进行更改后,我便可以正常生成access_tokenrefresh_token

答案 11 :(得分:0)

如果您尝试了所有操作但仍然无法正常工作,则请尝试此解决方案!! 只需给出范围*。

$query = http_build_query([
    'client_id' => 1,
    'redirect_uri' => 'http://localhost:3000/callback',
    'response_type' => 'code',
    'scope' => '*'
]);
在我的情况下,这是解决方案。我检查了

答案 12 :(得分:0)

我也遇到了同样的问题,我从这个链接得到了解决方案。请检查一下。 https://github.com/laravel/passport/issues/71

它说: 你调用/ oauth / token就像你总是得到你的令牌一样。但是使用grant_type的custom_request这样做。您需要在用户模型(或您配置为使用Passport的任何模型)中添加 byPassportCustomRequest($ request)方法。如果您能够通过请求对用户进行身份验证,那么该方法将接受Illuminate \ Http \ Request并返回用户模型,否则它将返回null。

答案 13 :(得分:0)

彻底检查授权类型。

{
    "grant_type": "password",
    "client_id": 2,
    "client_secret": 
    "username": ,
    "password": 
    "scope": ""
}

在上面的例子中,它说密码。现在查看表格: oauth_clients 并确保列password_client = 1

的值

或查找password_client = 1

条目

答案 14 :(得分:0)

{"error":"invalid_client","message":"Client authentication failed"}

表示您当前发送的client_id和client_secret是以下之一:

    发送
  1. client_id和client_secret在您的oauth服务器中未注册
  2. 发送的
  3. client_id和client_secret是您在帖子正文中指定的不是授权类型
  4.   

    对我来说,该特定客户端的password_client列值   是0.我手动将其更改为1但它没有帮助。 - Kanav 10月4日   3:25

    当我读到您对某些答案的评论时,很明显您使用了错误类型的client_id和client_secret作为密码授予流程,因此您的案例是后者。您可以通过在oauth_clients表中查找所提供的client_id和client_secret

    中的条目来验证这一点。
    "grant_type": "password,"    
    "client_id": "3,"
    "client_secret": "8BUPCSyYEdsgtZFnD6bFG6eg7MKuuKJHLsdW0k6g,"
    
    SELECT * FROM oauth_clients WHERE id = 3 AND secret = '8BUPCSyYEdsgtZFnD6bFG6eg7MKuuKJHLsdW0k6g' AND password_client=1;
    
    在oauth_clients表中,检查oauth服务器中提供的client_id和client_secret 是否存在密码授予流程。而且,您不需要通过简单地切换该列的值来手动更改客户端类型,您必须为密码授予类型创建新客户端

    php artisan migrate
    

    此命令将迁移laravel / passport迁移文件,为laravel / passport目前支持的每种类型生成两个默认客户端。您还可以使用

    为密码授予流创建客户端
    php artisan passport:client --password
    

    如果您有新的密码授予类型的客户端,请再次尝试POST到/ oauth / token以使用新生成的client_id和client_secret生成令牌

答案 15 :(得分:0)

在护照服务器上设置客户端时,检查重定向/回调URL。检查从中获取客户端ID和密码的条目,即重定向URL是否正确。

我收到了同样的错误,发现重定向网址不正确。

它需要指向/ callback路由(在Taylor提供的示例中)

答案 16 :(得分:0)

尝试使用php artisan passport:client --password创建新的密码客户端 那么很可能用户不会被分配给该密码客户端。编辑数据库行并向其添加一个exsting用户ID。

{
    "grant_type": "password,"
    "client_id": "" <- edited and added user id
    "client_secret": "8BU......," <- new secret
    "username": "test@gmail.com," <- email of above added user id
    "password": "123456,"<- password 
    "scope": ""
}

希望你现在可以获得令牌

答案 17 :(得分:0)

我设法让Laravel Passport - Password_Grant_Token通过RESTED(类似POSTMAN的服务)工作,没有任何额外的控制器或中间件。这就是我成功的方法!

在laravel doc's here中安装默认的Auth-Scaffolding 安装护照doc's here
通过auth-scaffolding注册用户
转到RESTED或POSTMAN,或任何其他类似的服务
检查oauth_clients表中的数据以获取form_params
使用RESTED通过Request_body_form_data而不是标题写入你的forms_params并向* / oauth / token发送POST请求(需要完整网址)

(所需的form_params是grant_type,client_id,client_secret,用户名和密码)

你应该得到一个带有4个键的对象(token_type,expires_in,access_token&amp; refresh_token),状态为200 OK。

如果到目前为止一切都成功了......

打开另一个RESTED窗口:

向/ api / user发送GET请求(需要再次提供完整网址) 在标题中写入2个名称 - 值对(Accept =&gt; application / json,Authorization =&gt; Bearer ACCESS_TOKEN_GOTTEN_IN_PREVIOUS_REQUEST(to / oauth / token)

那应该是它。您应该从/ api / user获取用户对象作为响应。

答案 18 :(得分:-1)

你可以定义两个变量,比如

<?php
 defined('BASEPATH') OR exit('No direct script access allowed');
 class Product extends CI_Controller { 

    var $data = array();

并且条件在你的函数中就像这样

<?php echo form_open_multipart('Product/upload'); ?>
    <input type="file" name="userfile" />

    <div class="form-group">
        <label for="name">Name</label>
        <input type="text" name="name">
    </div>

    <input type="submit" name="submit" value="test" />

</form>