【问题标题】:How to manage OAuth flow in mobile application with server如何使用服务器管理移动应用程序中的 OAuth 流
【发布时间】:2022-01-27 20:23:30
【问题描述】:

我正在开发一款带有 Flutter(移动客户端)的 Sports 移动应用程序,用于跟踪其用户活动数据。在跟踪一项活动(游泳、跑步、步行……)后,它会调用我(使用 springboot)开发的 REST API,并通过 POST 传递该活动数据。然后,我的用户将能够通过 GET 调用 REST API 查看他跟踪的活动的日志。

由于我知道我自己的追踪开发不如Strava、Garmin、华为等,我想让我的应用用户连接他们的Strava、Garmin等账户以获取他们的活动数据,因此我需要用户授权我的应用使用 OAuth 获取该数据。

在第一种方法中,我已经设法使用授权代码授权开发了所有的 OAuth 流程。授权服务器登录由 Flutter 在用户代理(Chrome 选项卡)中启动,一旦资源所有者完成登录并授权我的 Flutter 应用程序,我的 Flutter 应用程序将获取授权代码和对授权服务器的调用以获取令牌.所以我可以说,我的客户端就是我的 Flutter App。 oauth 流程完成后,我将令牌发送到我的 Rest API,以便将它们存储在数据库中。

我的第一个想法是将这些令牌发送到我的后端应用程序,以便将它们存储在数据库中并开发一个流程来获取这些令牌,咨询资源服务器,将每个资源服务器的 json 响应活动解析为我的其余 API 活动模型的活动并存储在我的数据库中。然后,如果资源所有者调用我的 Rest API 来查询其活动,他将收到所有活动的响应(移动应用跟踪的活动 + Strava、Garmin、资源服务器等存储在我的数据库中)。

当用户按下同步按钮并将这些响应直接映射到我的客户端时,我已经放弃了直接从我的客户端和我的 rest api 调用资源服务器的选项,因为我需要这些资源服务器响应的数据在后端以实现奖牌功能。此外,Strava、Garmin 等都有使用限制,我不想让我的资源所有者能够按他们想要的时间按下按钮。

这是我的第一个想法的流程:

步骤:

  1. 客户端调用授权服务器启动用户代理以进行 oauth 登录。为了使资源所有者登录并授权。 url 和 params 是硬编码的,在我的客户端中是硬编码的。

  2. 资源所有者登录并授权客户端。

  3. 回调与代码一起发送。

  4. 客户端捕获回调代码并向授权服务器发帖以获取令牌。由于一些授权服务器接受 PKCE,因此我尽可能使用 PKCE,以避免攻击和在我的客户端中硬编码我的客户端密码。其他像 Strava 的不允许 PKCE,所以我必须在我的客户端中硬编码客户端密码才能获取令牌。

  5. 将令牌返回给我的客户后,我会将它们发送到我的 rest api 并存储在标识令牌资源所有者的数据库中。

调用资源服务器:

  1. 一个定期进程获取每个资源所有者的令牌,并使用每个资源服务器返回的活动更新我的数据库。

  2. 资源所有者调用rest api并获取所有活动。

第一个想法的问题在于,一些授权服务器允许实施 PKCE (Fitbit),而其他授权服务器则使用客户端密码来创建令牌 (Strava)。由于我需要客户端密码来获取其中一些授权服务器的令牌,因此我已在客户端中对密码进行硬编码,这并不安全。

我知道将客户端机密插入客户端是危险的,因为黑客可以反编译我的客户端并获取客户端机密。如果授权服务器中不允许 PKCE,我不知道如何在不对客户端密码进行硬编码的情况下获取 Strava 的资源所有者令牌。

由于我不想在我的客户端中硬编码我的客户端机密,因为它不安全并且我想将令牌存储在我的数据库中,所以我不认为我的第一种方法是一个好的选择。此外,我正在向我的 REST API 创建一个 POST 请求,以便将访问令牌和刷新令牌存储在我的数据库中,如果我没记错的话,该过程可以直接从后端完成。

我的情况是,我开发了一个公共客户端(移动应用程序),该客户端对客户端机密进行了硬编码,因为当授权服务器不允许 PKCE 获取令牌时,我不知道如何避免这样做.

所以在考虑了所有这些问题之后,我的第二个想法是利用我的 REST API 并从那里调用授权服务器。所以我的客户是保密的,我会使用服务器端应用程序执行 OAuth 流程。

我的想法是基于这张图片。

为了避免我的移动客户端中的客户端秘密硬编码,以下基于图像的代码流是否可以工作并且可以安全地连接到 Strava、Garmin、Polar....?

Strava 连接示例:

移动客户端

  • 移动公共客户端调用我的 Rest API 以获取 Strava 授权服务器登录的 URI,其中包含所需的参数,例如:回调、redirect_uri、client_it 等。

  • 移动客户端捕获 Rest API GET 响应 URI。

  • 移动客户端启动用户代理(Chrome 自定义选项卡)并监听回调。

用户代理

  • 向资源所有者显示 strava 的登录提示。

  • 资源所有者插入凭据并推送授权。

  • 回调启动

移动客户端

  • 当我的客户端检测到回调时,返回客户端并从回调 uri 中提取代码。

  • 通过帖子将该代码发送到我的 REST API。 (https://myrestapi 正文中的代码)

REST API 客户端

  • 现在,客户端是我的 REST API,因为它将使用移动客户端获取的代码调用授权服务器。客户端将获取该代码,并使用硬编码的客户端密码调用授权服务器。使用这种方法,客户端机密不再存在于移动客户端中,因此它是机密的。

  • 授权服务器返回令牌,我将它们存储在数据库中。

过程

  • 从我的数据库中获取这些令牌并调用 strava 的资源服务器以获取活动。然后将这些活动解析为我的模型并将它们存储到数据库中。

第二种方法是处理客户机密以避免将其公开的好方法吗?或者我做错了什么?我可以遵循什么样的流程以正确的方式做到这一点?我真的被这个案例困住了,而且由于我是 OAuth 世界的新手,我对我所阅读的所有信息感到不知所措。

【问题讨论】:

  • 我认为你可以使用 IDENTITY SERVER 进行身份授权看看identityserver.com/articles/…
  • oauth2中有4个方法。您能否看看标准的“授权码”或“隐式”模式,看看它是否符合您的需求?
  • @ch271828n 我正在使用授权码授权,但我的客户端的一部分在移动应用程序中,而在其余的 api 中。正如我所见,现在不推荐使用隐式代码流。我怀疑我的方法是否安全,因为 Stravas API 只允许使用客户端密码来获取令牌,而我无法在我的移动应用程序中对其进行硬编码。
  • @Tasnuvaoshin 你能解释一下你的答案吗? Identity Server 为我的方法提供了什么?

标签: flutter security oauth-2.0 oauth spring-security-oauth2


【解决方案1】:

您提出的第二种方法是实现 OAuth 流程的有效方法。您的后端可以充当您需要的机密客户端,并且您可以安全地从授权服务器获取令牌。这种模式有时被称为 Backand-For-Frontend(尽管这个名字现在有点过分了)。在 Curity,我们描述了类似的东西,我们称之为令牌处理程序模式。前提类似,但我们的主要目标是可以利用 cookie 管理会话的单页应用程序。

当然,您需要在后端和移动应用程序之间创建一个安全会话。在浏览器应用程序中,您只需使用带有 cookie 的普通旧会话。您必须确保在调用您的后端时没有人可以冒充您的应用的用户。

您可以探索的另一件事是 Stravia 是否支持动态客户注册标准。使用 DCR,您可以将移动应用程序的每个实例注册为单独的客户端。然后这些客户端中的每一个都会收到他们自己的客户端 ID 和密码。这样您就不必担心秘密被盗了。

您也可以联系 Stravia,询问他们为什么不支持公共客户的 PKCE 以及他们是否打算这样做。 PKCE 是一项重要的安全 OAuth 标准,公司应寻求支持。

【讨论】:

  • 我将尝试使用我的 REST API 作为 Strava 和 Polar 的机密客户端,因为它们只使用客户端密码和授权码 Grant。一旦我得到授权码,我会打电话给我使用该代码的其余 API 并获取将它们存储在我的数据库中的令牌。另一方面,Fitbit 和 Google 支持 PKCE 并且不需要客户端密钥,因此我可以在前端获取访问和刷新令牌,然后通过对我的 api 的 post 调用将它们发送到后端,以便将它们存储在我的数据库。我怀疑对于 Google 和 Fitbit,我是否也应该在后端获取令牌
  • 为了维护所有oauth(Fitbit、Google、Polar、Strava...)的统一流程而不是在前端将Googles和Fitbits授权码和验证码按顺序发送到后端从后端获取令牌就可以了。我认为在前端(Google,Fitbit)和后端(Strava,Polar)中获取一些令牌很奇怪。你怎么看?事实是我需要在后端拥有我的所有用户令牌,以便对 Strava、Polar、Google、Fitbit 的资源服务器进行后端调用......并用响应填充我的数据库
  • 如果您在后端需要这些令牌,我会以与 Strava 和 Polar 相同的方式实现所有提供程序。这样,这些令牌将永远不会到达您的移动应用程序,因此攻击者将无法窃取它们。
【解决方案2】:

据我了解,这里的主要问题是,您希望避免对客户端密码进行硬编码。
我以 keycloak 作为授权服务器的示例,但在其他授权服务器中也是如此以及因为实施必须遵循标准
在授权服务器中,有两种类型的客户端,一种是
1.机密客户端 - 这些客户端需要在您的 Rest api 调用中同时传递客户端 ID 和客户端秘密

CURL 是这样的,需要客户端密码

curl --location --request POST 'http://localhost:8080/auth/realms/testrealm/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=confidentialclient' \
--data-urlencode 'client_secret=<CLIENT_SECRET_VALUE>' \
--data-urlencode 'code=<AUTHORIZATION_CODE_VALUE>' \
--data-urlencode 'redirect_uri=http://localhost:8080/callback' \
--data-urlencode 'code_verifier=<CODE_VERIFIER_PKCE>'

2.Public Client - 如果您使用此选项创建客户端,则无需传递客户端密码。在进行 API 调用时,只有 client-id 就足够了。

对于公共客户端,您无需传递客户端密码,无需担心一个问题

curl --location --request POST 'http://localhost:8080/auth/realms/testrealm/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=publiclcient' \
--data-urlencode 'code=<AUTHORIZATION_CODE_VALUE>' \
--data-urlencode 'redirect_uri=http://localhost:8080/callback' \
--data-urlencode 'code_verifier=<CODE_VERIFIER_PKCE>'

这些选项将在并且应该在大多数标准授权服务器中可用。

【讨论】:

  • 我认为Strava的没有公共客户端选项,所以我必须使用客户端密码才能获得令牌。由于我无法在我的移动应用程序代码中对客户端密码进行硬编码,因此将授权代码作为 REST API 调用传递给我的后端并在后端获取令牌是一个错误的决定吗?如果我正在开发移动应用程序,你能告诉我如何获得 Strava 的代币吗?
  • 您可以在后端获取令牌,有一种称为 BFF(前端后端)的模式可用于处理令牌。您的移动应用程序不必处理令牌。虽然这种模式更倾向于 SPA 而不是移动应用程序,但您仍然可以尝试一下
  • 那么,在前端获取授权码并将其发送到后端以在后端获得令牌是否可以解决问题?这样,客户端密码将被放置在一个安全的地方,我可以将令牌存储在数据库中,从后端调用 API 并映射响应,以便将数据存储在我的数据库表中。我说的对吗?
  • 是的,尽管您可能必须使用内存数据库来存储令牌以提高效率,并且必须考虑如何刷新令牌、管理令牌到期。正如他们所说的“魔鬼在细节中:)”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-08
  • 2016-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-31
  • 1970-01-01
相关资源
最近更新 更多