构建 API 密钥和访问令牌

Posted

技术标签:

【中文标题】构建 API 密钥和访问令牌【英文标题】:Architecturing API keys and access tokens 【发布时间】:2015-06-23 13:17:10 【问题描述】:

我有一个关于如何使用访问令牌和 API 密钥构建 REST API 的问题。

我有一个需要身份验证的 API。我想启用两个用例:

    用户使用 OAuth2(密码授予)登录界面,并被授予临时访问令牌。此令牌用于对用户进行身份验证。因此,本身使用 API 的 UI 可以获取数据并显示它。

    我还希望用户拥有一个 API 密钥来执行相同的调用,但在其应用程序中。显然,与访问令牌相反,我希望 API 密钥能够长期存在。此外,与绑定到给定用户的访问令牌相反(如果我们引入团队机制,每个用户将拥有不同的访问令牌,尽管他们访问相同的资源),API 密钥对于项目应该是唯一的。

虽然类似,但我不确定我应该如何构建它。我认为,在内部,API 密钥和访问令牌都应该存储在同一个表中,但 API 密钥没有过期时间。我说的对吗?

我也不确定客户的概念。似乎在规范中,客户端更像是一个外部应用程序。但是我真的可以在这里使用这个概念吗?

例如,每个“项目”实际上是一个不同的客户端(虽然这里的客户端是同一个应用程序,而不是第三方开发者创建的应用程序)。

因此,如果用户 A 在系统上创建帐户,则会自动创建一个客户端 A,其访问令牌与客户端 A 绑定,并具有长期访问令牌(也称为 API 密钥)。例如,这可以用来直接在他的代码上执行 API 调用。

然后,如果用户 A 登录仪表板,将创建一个临时访问令牌,但这次没有应用程序,而是与用户绑定,寿命很短。

这听起来合理吗?有没有人已经实现了这样的东西?

谢谢!

【问题讨论】:

API 密钥和访问令牌是不同的概念,分别建模和存储。 是的,但是您如何处理我想以同质方式访问 API 的事实?我不想有不同的方式来使用 API。我的网络应用程序是使用临时令牌使用它还是客户端使用 API 密钥直接从他的网络服务器使用 API。 【参考方案1】:

我写了一篇关于 RESTful 应用程序使用访问令牌的方法的帖子:https://templth.wordpress.com/2015/01/05/implementing-authentication-with-tokens-for-restful-applications/。也许这可以提供一些提示。

要回答您的问题,我认为我们需要有一些同质的东西。我的意思是您的所有身份验证机制都应该基于访问令牌。您的 API 密钥将允许您获取实际用于身份验证的访问令牌。

据我了解,您的应用程序有两种用户:

使用 Web UI 的最终用户(通过 OAuth2 使用密码登录) 应用程序(使用 API 密钥登录)

所以我会实现这两种用户并让他们能够获得访问令牌。在这两种情况下都将使用访问令牌来访问 RESTful 服务。

另外,我觉得这个回答可以给你一些其他的提示:Securing my REST API with OAuth while still allowing authentication via third party OAuth providers (using DotNetOpenAuth)。

希望它能回答你的问题。 蒂埃里

【讨论】:

【参考方案2】:

感谢您的回答。

我实际上对 OAuth2 本身很有经验,我的问题更针对 API 密钥。我喜欢 API 密钥交换访问令牌的想法,但我认为这不起作用。 API 密钥是固定不变的,而访问令牌可以过期。

问题是:应用如何知道这是访问令牌还是 API 密钥。我的意思是,好吧,假设在我的数据库中,每个用户的数据库中都有一个“api_key”列。

与访问令牌相反,api_key 不会过期(尽管用户最终可以轮换它)。正如我所说,我想要的是对身份验证的同质处理。

案例 1:我自己的网络应用做 API 调用

工作流程如下,使用OAuth2:

    用户输入他的邮件/密码。 授权服务器返回一个临时访问令牌(例如:“abc”)。 在 Web 应用程序中,所有 API 调用都使用此令牌完成。例如:“/payments/1”,授权标头:“Bearer abc”。

漂亮又简单。

案例 2:用户有一个 API 密钥,该密钥不会过期,可以在自己的应用中私下使用

显然,授权机制必须保持不变。所以:

    用户进入他的帐户,发现他的 API 密钥是“def”。 在他们的服务器代码中,他们可以使用相同的身份验证机制进行相同的调用。所以他可以使用 Authorization: "Bearer def" 调用 "/payments/1"。

而且它必须有效。如您所见,这两个示例都没有改变。他们访问相同的资源,相同的授权机制......但在一种情况下,我们有一个访问令牌,在另一种情况下,我们有一个 API 密钥。而且我不知道应该如何从数据库的角度和授权代码中实现它。

我的一个潜在想法是使用不同的身份验证机制。对于 OAuth,它将是“Authorization:Bearer accessToken”,而对于 API,它将是 Basic 身份验证:“Authorization:Basic apiKey”。

这听起来不错?

【讨论】:

“授权服务器如何返回临时访问令牌(例如:“abc”)。”通过验证用户凭据?因为该授权服务器必须是资源服务器的一部分,对吗?【参考方案3】:

我认为您不应将“API 密钥”视为访问令牌的替代品。

无论如何,您都必须使用访问令牌来承担请求之间的身份验证,因此您实际使用“API 密钥”建模的不是通常的不记名令牌的替代品,而是提供其他不记名令牌的不同客户端用于请求令牌的授权类型。

我个人实现的流程如下:

    用户使用密码授权类型进行身份验证,每个用户都有一个通用客户端(即您的“网络应用”客户端,它是公共的,即它没有 client_secret)。 然后用户可以创建自己的客户端。根据 OAuth2 规范,这些不是公开的,因此它们将由 client_idclient_secret 组成。这些就是您所说的“API 密钥”。 然后用户将能够通过他们的客户端请求访问令牌,使用您想要支持的任何给定授权类型(例如,直接客户端凭据、授权代码、隐式、第三方等)。您将不得不强调有关如何处理客户端凭据的应有安全实践。

显然,您必须以客户端可以属于特定用户并具有不同可接受的授权类型的方式实现 OAuth2 服务器(即,您可能不希望允许用户客户端使用密码授权,而您可能想要禁止您的 Web 应用程序客户端的密码类型以外的任何授权类型)。

然后,您将能够在每个客户端或每个授权类型的基础上定义令牌 TTL 或缺少令牌(例如,通过密码授权请求的访问令牌,仅可由 Web 应用客户端使用,将具有较短的 TTL,而授权代码授予将提供长期存在的令牌)。 不过,我建议不要完全缺少 TTL,而是使用refresh_token 授权类型来更新过期的访问令牌。

此外,您可能必须定义某种授权系统(ACL、RBAC 等),以定义哪个客户端可以执行什么操作。这意味着每个访问令牌都应包含对用于创建它的客户端的引用。

所以,总结一下,以下是关系:

用户 有一个 客户客户 有一个 用户。客户 有很多 令牌令牌 有一个 客户令牌 有一个 用户

双向 YMMV。

您应该能够使用任何给定平台的最常见的 OAuth2 服务器实现来实现我所描述的一切。

TL;DR:“API 密钥”实际上是 OAuth2 客户端

【讨论】:

感谢 Stefano ;)。听起来不错而且很合乎逻辑,尽管我不太喜欢用户需要同时传递 client_secret 和 client_id 的事实(这使事情变得更加复杂)。我试图通过分析请求来了解 Stripe API 的工作原理,似乎他们使用带有生成的 API 密钥(他们应该生成)的 Basic auth,以及带有访问令牌(他们的 web 应用程序)的 Bearer。 好吧,您将只使用一次客户端凭据来获取访问令牌,而使用基本身份验证,您将在每个请求中发送实际凭据。它更复杂,这是真的,但如果做得对,它也会更安全(因为它是整个 OAuth2 与 Basic 比较的任何东西)。

以上是关于构建 API 密钥和访问令牌的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Twitter API 获取用户访问令牌和访问密钥

OAuth(访问令牌)与 API 密钥

错误:未设置访问、刷新令牌或 API 密钥

在 Rest API 中使用 Facebook 在 Express 和 NodeJS 中维护 JWT 的密钥和访问令牌

添加声明以访问令牌密钥斗篷

获取访问令牌时服务帐户 API 错误