LinkedIn API :: 如何获取不记名访问令牌

LinkedIn API :: how to obtain the bearer access token

官方LinkedIn不好用API,找不到有效的文档

official documentation 之后,我创建了一个新应用程序以获取客户端 ID 和客户端密码

当我现在通过 Postman 向 https://www.linkedin.com/oauth/v2/accessToken 发出 POST 调用时,这就是我获得的结果:

{
    "error": "invalid_grant_type",
    "error_description": "The passed in grant_type is invalid"
}

我哪里错了?

EDIT AFTER HELP FROM @Amit Singh

感谢@AmitSingh,我能够创建 2 个不同的应用程序,使用 Client Credentials flow 的测试结果导致我在检索令牌时出错:

{
    "error": "access_denied",
    "error_description": "This application is not allowed to create application tokens"
}

当我尝试使用 LinkedIn 三足工作流程时,我收到 Unauthorized

编辑 3:通过 POSTMAN

到达那里

我现在知道我可以让 Postman 完成这项工作,但是当我按下 Get New Access Token 时,它会打开一个错误页面。我认为错误可能出在这 4 个元素中:

...此外,在 guide you linked 中它表示应用程序需要具有:

我的什么都没有:

最近创建的仍在审核中。它说最多可能需要 90 天。是真的吗?

4th EDIT: I WANT TO BELIEVE!

我们到了,至少现在我收到了错误:Bummer, something went wrong. The redirect_uri does not match the registered value。这太棒了:终于出现了一个错误,说明问题出在哪里!

在应用程序的“产品”选项卡上,我选择 Sign In with LinkedIn。作为 我为您的应用设置的授权重定向 URL https://www.getpostman.com/oauth2/callback

在 Postman 中,我设置了 Auth URLAccess Token URL,如您所说:

假设您已经创建了您的应用程序,添加了正确的重定向 URL 并为您的应用程序启用了“Sign In with LinkedIn”产品,您遇到的问题可能是第一次调用 returns 一个登录页面,您的用户应该在其中进行身份验证。

  1. 将请求提交给 https://www.linkedin.com/oauth/v2/authorization(您似乎已经这样做了)
  2. 解析步骤 1 的响应并提取所有表单值,添加用户名和密码以模拟用户登录
  3. 发出 POST 请求并使用上一步的值作为 x-www-form-urlencoded 数据
  4. 手动遵循第 3 步中的重定向 header
  5. 记下第二个重定向 header 但不要遵循它 - 而是提取代码
  6. POST 从上一步到 https://www.linkedin.com/oauth/v2/accessToken 的代码并得到 access_token 作为响应

从这里开始,我能够按照以下步骤成功转换到授权码。 我不确定你是否使用在线 Postman,但这是我完整的 collection 导出文件以供参考:

{
    "info": {
        "_postman_id": "397761c9-4287-43f2-860a-3c34cb710d50",
        "name": "Linkedin oAuth",
        "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
    },
    "item": [
        {
            "name": "01 request Login form",
            "event": [
                {
                    "listen": "test",
                    "script": {
                        "exec": [
                            "const $ = cheerio.load(pm.response.text());\r",
                            "var inputs = $('form').serializeArray();\r",
                            "var payload = '';\r",
                            "inputs.forEach(i => {\r",
                            "    payload += encodeURIComponent(i.name)+ '=' + encodeURIComponent(i.value) + '&';\r",
                            "})\r",
                            "payload += 'session_key='+ encodeURIComponent(pm.collectionVariables.get('username')) + '&'\r",
                            "payload += 'session_password='+ encodeURIComponent(pm.collectionVariables.get('password'))\r",
                            "\r",
                            "pm.collectionVariables.set(\"form_data\", payload);"
                        ],
                        "type": "text/javascript"
                    }
                }
            ],
            "request": {
                "method": "GET",
                "header": [],
                "url": {
                    "raw": "https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id={{client_id}}&redirect_uri={{redirect_uri}}&scope=r_liteprofile&state={{$guid}}",
                    "protocol": "https",
                    "host": [
                        "www",
                        "linkedin",
                        "com"
                    ],
                    "path": [
                        "oauth",
                        "v2",
                        "authorization"
                    ],
                    "query": [
                        {
                            "key": "response_type",
                            "value": "code"
                        },
                        {
                            "key": "client_id",
                            "value": "{{client_id}}"
                        },
                        {
                            "key": "redirect_uri",
                            "value": "{{redirect_uri}}"
                        },
                        {
                            "key": "scope",
                            "value": "r_liteprofile"
                        },
                        {
                            "key": "state",
                            "value": "{{$guid}}"
                        }
                    ]
                }
            },
            "response": []
        },
        {
            "name": "02 Submit login form",
            "event": [
                {
                    "listen": "prerequest",
                    "script": {
                        "exec": [
                            ""
                        ],
                        "type": "text/javascript"
                    }
                },
                {
                    "listen": "test",
                    "script": {
                        "exec": [
                            "var url = 'https://www.linkedin.com'+ pm.response.headers.get(\"Location\");\r",
                            "pm.collectionVariables.set('first_redirect', url);\r",
                            "//console.log(pm.collectionVariables.get('first_redirect'));"
                        ],
                        "type": "text/javascript"
                    }
                }
            ],
            "protocolProfileBehavior": {
                "followRedirects": false
            },
            "request": {
                "method": "POST",
                "header": [
                    {
                        "key": "Content-Type",
                        "value": "application/x-www-form-urlencoded",
                        "type": "text"
                    }
                ],
                "body": {
                    "mode": "raw",
                    "raw": "{{form_data}}",
                    "options": {
                        "raw": {
                            "language": "text"
                        }
                    }
                },
                "url": {
                    "raw": "https://www.linkedin.com/checkpoint/lg/login-submit",
                    "protocol": "https",
                    "host": [
                        "www",
                        "linkedin",
                        "com"
                    ],
                    "path": [
                        "checkpoint",
                        "lg",
                        "login-submit"
                    ]
                }
            },
            "response": []
        },
        {
            "name": "03 handle login-success redirect",
            "event": [
                {
                    "listen": "test",
                    "script": {
                        "exec": [
                            "var sdk = require('postman-collection');\r",
                            "var redirect = new sdk.Url(pm.response.headers.get(\"Location\"));\r",
                            "pm.collectionVariables.set('code', redirect.query.filter(q => q.key === 'code').map(k => k.value)[0]);\r",
                            "//console.log(pm.collectionVariables.get('code'));"
                        ],
                        "type": "text/javascript"
                    }
                },
                {
                    "listen": "prerequest",
                    "script": {
                        "exec": [
                            "console.log(pm.variables.get('first_redirect'));\r",
                            "pm.request.url.update(pm.variables.get('first_redirect'));"
                        ],
                        "type": "text/javascript"
                    }
                }
            ],
            "protocolProfileBehavior": {
                "followRedirects": false
            },
            "request": {
                "method": "GET",
                "header": [],
                "url": {
                    "raw": "{{first_redirect}}",
                    "host": [
                        "{{first_redirect}}"
                    ]
                }
            },
            "response": []
        },
        {
            "name": "04 Get Auth Code",
            "request": {
                "method": "POST",
                "header": [],
                "url": {
                    "raw": "https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code={{code}}&redirect_uri={{redirect_uri}}&client_id={{client_id}}&client_secret={{client_secret}}",
                    "protocol": "https",
                    "host": [
                        "www",
                        "linkedin",
                        "com"
                    ],
                    "path": [
                        "oauth",
                        "v2",
                        "accessToken"
                    ],
                    "query": [
                        {
                            "key": "grant_type",
                            "value": "authorization_code"
                        },
                        {
                            "key": "code",
                            "value": "{{code}}"
                        },
                        {
                            "key": "redirect_uri",
                            "value": "{{redirect_uri}}"
                        },
                        {
                            "key": "client_id",
                            "value": "{{client_id}}"
                        },
                        {
                            "key": "client_secret",
                            "value": "{{client_secret}}"
                        }
                    ]
                }
            },
            "response": []
        }
    ],
    "event": [
        {
            "listen": "prerequest",
            "script": {
                "type": "text/javascript",
                "exec": [
                    ""
                ]
            }
        },
        {
            "listen": "test",
            "script": {
                "type": "text/javascript",
                "exec": [
                    ""
                ]
            }
        }
    ],
    "variable": [
        {
            "key": "client_id",
            "value": "your app id"
        },
        {
            "key": "client_secret",
            "value": "your app secret"
        },
        {
            "key": "redirect_uri",
            "value": "your urlencoded redirect uri such as https%3A%2F%2Flocalhost%3A8080"
        },
        {
            "key": "username",
            "value": "user login"
        },
        {
            "key": "password",
            "value": "user password"
        }
    ]
}

LinkedIn 凭证工作流程

LinkedIn 提供 2 种不同的凭证工作流程。

  1. LinkedIn 3-legged workflow - 当您想要使用 API 来访问 LinkedIn 会员的数据时。 需要授权码授予类型
  2. LinkedIn Client Credentials flow - 当您想要使用将访问 non-member 资源的 API 时。 需要授予客户端凭据

什么是赠款类型?

“授权类型”是指您在 OAuth 工作流程中获取访问令牌的方式。

支持多种授权类型。其中一些是:

  1. Client Credentials - 当您想要访问您自己的资源而不是任何其他用户时使用

  2. Authorization Code - 当应用程序想要访问客户端数据时使用

  3. Refresh token - 将过期的访问令牌交换为有效的访问令牌,用于避免用户重复参与

  4. Password - 当应用程序和用户之间存在高度信任时使用,例如LinkedIn 移动应用程序,您提供您的用户名和密码

客户端凭证流

你需要知道什么

  • 此处使用的授权类型是客户端凭据 - client_credentials.
  • 记得将 OAuth 中所有 POST 请求的 Content-Type 设置为 application/x-www-form-urlencoded

步骤

  1. 创建一个应用程序并获取您的客户端 ID 和客户端密码。步骤显示在上面链接的相应文档中。假设它们有值 - <client_id><client_secret>.

  2. 发送 POST 要求 https://www.linkedin.com/oauth/v2/accessToken 并提供以下信息。

    参数

    grant_type : client_credentials
    client_id  : <client_id>
    client_secret : <client_secret>
    

    注意: client_credentials 是要为 grant_type 输入的文字文本。

    响应将 return JSON Object 包含您的访问令牌及其过期时间(以秒为单位)。

    响应

    {
       "access_token" : <access_token>,
       "expires_in" : "1800"
    }
    
  3. 使用步骤 2 中获得的 <access_token> 进行 API 请求。

    例子

    Request URL: https://www.linkedin.com/v2/jobs
    Request type: GET
    
    Parameters
    Authorization: Bearer <access_token>
    

LinkedIn 三足工作流程

你需要知道什么

  • 授权类型将是授权代码 - code,因为您想要访问用户的数据。

  • 对于 OAuth 中的所有 POST 请求,您的 Content-Type 应该是 application/x-www-form-urlencoded

  • Redirect URLs 是 URL,您的 OAuth 服务器将在成功授权后重定向用户。

    • 这些已根据您提供的重定向 URL 进行验证,以确保它不是欺诈性的。
    • 这些应该是绝对的URLs.
    • URL 参数被忽略并且不能包含 #.

步骤

  1. 创建应用并提供重定向 URL(如果尚未提供)。查看 docs 了解有关如何执行此操作的信息。

  2. 获取您的客户端 ID 和客户端密码。假设值是 <client_id><client_secret>.

  3. 生成一个随机的、难以猜测的字符串。假设它是 <random-string>.

  4. 选择第 1 步中提供的重定向 URL 之一,您希望用户在授权后重定向。假设它是 <redirect_uri>.

  5. 假设您想要:

    • r_emailaddress - 获取他的电子邮件地址
    • w_member_social - Post,代表用户发表评论和点赞。

    这些被称为“权限范围”,因为用户对您进行身份验证的权限是什么。在您的请求中发送这些范围时,它们应该是 URL-encoded 和 space-delimited。在此特定实例中,我们的范围将是 scope: r_emailaddress%20w_member_social。我们有 URL-encoded 上面提到的范围。

    从 Microsoft 文档中添加有关作用域的更多信息:

    The scopes available to your app depend on which Products or Partner Programs your app has access to. Your app's Auth tab will show current scopes available. You can apply for new Products under the Products tab. If approved, your app will have access to new scopes.

  6. https://www.linkedin.com/oauth/v2/authorization 发送 POST 请求并提供以下信息。

    参数

    response_type : code
    client_id  : <client_id>
    redirect_uri : <redirect_uri>
    state : <random_string>
    scope : r_emailaddress%20w_member_social
    
  7. 请求后,用户将看到 LinkedIn 的授权屏幕并要求其批准请求。

  8. 用户批准请求并验证 <redirect_uri> 后,用户将被重定向到提供的 <redirect_uri> 以及访问代码 <access_code> 和一个值在 state 参数中。假设状态参数是 <state_value>.

  9. 为了安全起见,在使用 <access_code> 获取访问令牌之前,请验证 <state_value> 是否等于 <random_string>。此外,出于安全原因,请在发出后 30 分钟内使用 <access_code>

  10. 接下来,使用以下信息向 https://www.linkedin.com/oauth/v2/accessToken 发送 POST 请求以获取访问令牌。

    参数

    grant_type : authorization_code
    client_id  : <client_id>
    client_secret : <client_secret>
    redirect_uri : <redirect_uri>
    code : <access_code>
    

    注意 : authorization_code 是要在 grant_type.

    中传递的文字文本

    您应该会得到与客户端类似的响应包含您的访问令牌和有效期的凭证工作流。

    响应

    {
       "access_token" : <access_token>,
       "expires_in" : "1800"
    }
    
  11. 使用第9步得到的<access_token>进行API请求。

    例子

    Request URL: `https://www.linkedin.com/v2/me`
    Request type: GET
    
    Parameters:
    Authorization: Bearer <access_token>
    

如何在 Postman 中执行此操作?

  1. 创建一个新的 Collection。
  2. 右键单击,select 编辑 collection 并移至授权选项卡。
  3. 在“类型”中,select“OAuth2.0”,单击“获取新访问令牌”。
  4. 您将看到一个屏幕,上面提到的所有熟悉的术语都在那里。填写这些,选中“通过浏览器授权”复选框进行授权。
  5. 现在您有了访问令牌,可以继续进行 API 调用。

Postman 旨在使此类操作更容易,但您必须知道如何操作。更多详情,您可以阅读他们的 official docs.

感谢@timur 和@AmitSingh,我终于到了 LinkedIn 进行身份验证 API。

图片中的简单分步解决方案:

  • 您的应用的授权重定向 URL = https://oauth.pstmn.io/v1/callback

  • OAuth 2.0 范围 = 必须有 r_emailaddressr_liteprofile

  • 在“产品”选项卡中设置 Sign In with LinkedIn

现在打开 Postman > Collections > New Collection > Authorization 并如图设置参数:

  • 类型 = OAUTH 2.0
  • 代币名称=随心所欲
  • 回调URL = https://oauth.pstmn.io/v1/callback(勾选Authorize using browser后应该会变灰)
  • 勾选Authorize using browser
  • 授权 URL = https://www.linkedin.com/oauth/v2/authorization
  • 访问令牌URL = https://www.linkedin.com/oauth/v2/accessToken
  • 客户 ID = 您在 LinkedIn 应用程序中找到的客户 ID
  • 客户机密 = 您在 LinkedIn 应用程序中找到的客户机密
  • 范围=r_liteprofiler_emailaddress
  • 状态=随便放什么

现在点击 Get New Access Token,将在您的浏览器中打开一个页面,您将能够使用您的 LinkedIn 帐户登录。完成后,您将通过身份验证。

现在使用@timur 提供的代码,在 Postman 上转到“导入”>“上传文件”并导入该 .JSON 文件。您现在将拥有 4 个查询,您可以将它们拖放到您的集合中。