架构API密钥和访问令牌

30
我有一个问题,关于如何使用访问令牌和API密钥架构REST API。
我有一个需要身份验证的API。 我想要实现两种用例:
1.用户使用OAuth2(密码授权)登录接口,并被授予临时访问令牌。 此令牌用于对用户进行身份验证。因此,使用API的UI可以获取数据并显示它。 2.我还希望用户拥有API密钥来执行相同的调用,但是在其应用程序中。 显然,与访问令牌相反,我希望API密钥具有长期生存力。 此外,与访问令牌绑定到给定用户不同(如果我们引入团队机制,则每个用户将具有不同的访问令牌,尽管他们访问相同的资源),API密钥应该唯一地属于项目。
虽然类似,但我不确定如何进行架构设计。 我认为,在内部,API密钥和访问令牌都应存储在同一表中,但是API密钥没有过期时间。 我对吗?
另一件事我也不确定的是客户端的概念。 这似乎在规范中更像是外部应用程序。 但是我可以在这里使用此概念吗?
例如,每个“项目”实际上都是一个不同的客户端(尽管此处的客户端是同一应用程序,而不是第三方开发人员创建的应用程序)。
因此,如果用户A在系统上创建帐户,则将自动创建客户端A,该客户端与客户端A绑定的访问令牌具有长期生存期(也称为API密钥)。 这可用于直接在他的代码中执行API调用,例如。
然后,如果用户A登录仪表板,则将创建临时访问令牌,但这次没有应用程序,而是与用户绑定,并且生命周期短。
听起来合理吗? 是否已经有人实现了这样的东西?
谢谢!

1
API密钥和访问令牌是不同的概念,应分别进行建模并分别存储。 - Pieter Herroelen
1
是的,但是你如何处理我想以同质化的方式访问API这一事实呢?我不想有不同的使用API的方式。无论是我的Web应用程序使用临时令牌还是客户端直接从他的Web服务器使用API密钥消耗API。 - Michael Gallego
3个回答

15

我认为你不应该把"API密钥"视为访问令牌的替代品。

无论如何,您都需要使用访问令牌来承载请求之间的身份验证,因此,您用"API密钥"建模的实际上并不是通常的承载令牌的替代品,而是提供其他授权类型以请求令牌的不同客户端。

我个人实现的流程如下:

  1. 用户使用密码授权类型与每个用户的公共客户端(即您的"Web应用程序"客户端,它是公共的,即没有client_secret)进行身份验证。
  2. 然后,用户可以创建自己的客户端。根据OAuth2规范,这些不是公共的,因此它们将由client_idclient_secret组成。这就是您所说的"API密钥"。
  3. 然后,用户将能够通过其客户端请求访问令牌,使用您想要支持的任何授权类型(例如,直接客户端凭据、授权代码、隐式、第三方等)。您必须非常强调如何处理客户端凭证的安全实践。

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

然后,您将能够根据每个客户端或授权类型的需求定义令牌TTL或缺乏TTL(例如,在使用密码授权请求的访问令牌仅可由Web应用程序客户端使用时,其TTL会很短,而授权代码授权将提供长时间的令牌)
但是我建议不要完全缺乏TTL,而是使用refresh_token授权类型来更新过期的访问令牌。

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

综上所述,以下是关系:

用户 拥有 客户端
客户端 拥有 用户
客户端 拥有多个 令牌
令牌 拥有 客户端
令牌 拥有 用户

双向性可能因情况而异。

你应该能够在任何平台的最常见的OAuth2服务器实现中实现我描述的所有内容。

简而言之:“API密钥”实际上是OAuth2客户端


谢谢 Stefano ;). 听起来不错,也很合乎逻辑,尽管我并不喜欢用户需要传递客户端密钥和客户端 ID 的事实(这会使事情变得更加复杂)。我试图通过分析请求来理解 Stripe API 的工作原理,看起来他们使用了基本身份验证和生成的 API 密钥(他们应该生成),以及访问令牌(他们的 Web 应用程序)的 Bearer。 - Michael Gallego
好的,你只需要使用客户端凭据一次来获取访问令牌,而使用基本认证则需要在每个请求中发送实际凭据。 这更加复杂,确实如此,但如果正确使用,它也更安全(就整个OAuth2与基本认证的比较而言)。 - Stefano Torresi

2
我写了一篇关于如何使用访问令牌进行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)
希望它回答了您的问题。 Thierry

2
谢谢您的回答。
我对OAuth2本身相当有经验,我的问题更针对API密钥。我喜欢API密钥交换访问令牌的想法,但我认为这行不通。API密钥是固定的,不会改变,而访问令牌可能会过期。
问题是:应用程序如何知道这是访问令牌还是API密钥。我的意思是,在我的数据库中,每个用户都有一个“api_key”列。
与访问令牌不同,API密钥不会过期(尽管用户最终可以更改它)。正如我所说,我想要的是统一的身份验证处理方式。
案例1:我的Web应用程序进行API调用
使用OAuth2,工作流程如下:
1. 用户输入他的邮件/密码。 2. 授权服务器返回临时访问令牌(例如:“abc”)。 3. 在Web应用程序中,所有API调用都使用此令牌。例如:“/payments/1”,带有授权标头:“Bearer abc”。
非常简单易懂。
案例2:用户拥有一个API密钥,该密钥不会过期并可在其自己的应用程序中私下使用
显然,授权机制必须保持不变。所以:
1. 用户进入他的帐户,并阅读他的API密钥为“def”。 2. 在他们的服务器代码中,他们可以使用相同的调用和相同的身份验证机制。因此,他可以使用Authorization: "Bearer def" 调用“/payments/1”。
它必须起作用。正如您所看到的,在这两个示例中都没有改变。他们访问相同的资源,相同的授权机制……但在一个案例中,我们有一个访问令牌,而在另一个案例中,我们有一个API密钥。我不知道我应该如何从数据库角度和授权代码方面实现它。
我想到的一个潜在想法是使用不同的身份验证机制。对于OAuth,它将是“Authorization: Bearer accessToken”,而对于API,它将是基本身份验证:“Authorization: Basic apiKey”。
这听起来不错吗?

授权服务器如何通过验证用户凭据来返回临时访问令牌(例如:“abc”)?为此,授权服务器必须是资源服务器的一部分,对吗? - Sundar Rajan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接