公共第一方客户端的正确 OAuth2 流程

Posted

技术标签:

【中文标题】公共第一方客户端的正确 OAuth2 流程【英文标题】:Proper OAuth2 flow for public first-party clients 【发布时间】:2016-09-09 02:21:22 【问题描述】:

我是堆栈溢出的常客,但这是我的第一个问题。

我正在使用 OAuth2 规范开发授权服务器。而且我只是被困在如何在使用密码流时确保第一方客户端的真实性。我阅读了很多论坛,这就是我得到的:

    javascript 单页客户端

    Alex Bilbie 的这篇博文指出,为了避免 client_secret 问题,我们应该:

    这很简单;通过瘦服务器端组件代理所有 API 调用。这个组件(让我们从这里开始称它为代理) 将验证来自用户会话的 ajax 请求。访问 和刷新令牌可以以加密形式存储在 cookie 中 只有代理才能解密。应用程序客户端凭据 也将被硬编码到代理中,因此它们不会公开 也可以访问。

    但是现在这个代理可以被冒充我的人访问 角度应用程序。然后我看到了安迪的这篇博文 守场员:How Secure is the OAuth2 Resourc Owner Password Flow for Single Page Apps。他基本上说要依靠CORS来 避免冒充 JS 客户端。

    使用这两种方法来保护我的 JS 应用程序是个好主意吗?

    原生应用(桌面和移动)

    在移动应用的情况下,我只找到了 Authorization 的情况 代码和隐式流。这不是我想要的,因为重定向 会影响用户体验。所以我的想法是:

    我将使用 ROP 流程,然后使用 client_id 为此特定安装生成并附加它 到用户帐户,收到access_tokenclient_secret 作为回应。此提出的任何其他令牌请求 客户端必须携带此凭据(因为 client_id 是特定的 对于安装,我将能够检查此客户端是否 已经认证)。这样,如果有人使用任何凭证 冒充客户,甚至注册虚假客户,我可以接受 撤消用户和客户端访问权限的措施。

我知道这可能是多虑了,而且我也知道其中一些事情并不能避免任何事情。我只是觉得尽我所能保护我的 API 是我的工作。

非常感谢您对此事的看法!我真的想多了?我应该只使用“公共客户”的概念并继续吗?

谢谢大家,编码愉快!

【问题讨论】:

你有没有找到这个问题的答案,我也有同样的想法 【参考方案1】:

首先,这个问题不是一个普遍的优先级,因为大多数应用程序都是先用网站开发的,然后再用 API 开发。这可能是原因,因为没有人知道如何使用 oauth2 处理第一批客户,因为每个人都开发了其他方法来做到这一点,而 oauth2 仅用于授予用户对第三方应用程序的访问权限。

即使您只为您的第一个客户端应用程序开发了 oauth2 授权服务器(考虑单一身份验证机制而不是开发多个),您也应该尝试开发授权代码或隐式授权类型。您将意识到您需要一种方法来检查实际登录的用户

常用的两种方法是:

用户会话(基于 Cookie) 来自 localStorage 的用户访问(基于 javascript)

无论哪种方式,您都需要检查应用程序的安全性,用户会话容易受到 CSRF 的攻击,localStorage 容易受到 XSS 的攻击。有很多关于如何保护您的网站免受其中任何一种攻击的文章,所以我不会在这里提出任何建议,您只需要知道它们存在即可。

现在您选择了身份验证方法,我们可以开始考虑:

Javascript 单页应用程序

    代理 在我看来,拥有一个过滤所有请求的代理就像有一扇总是插入钥匙的门。连门都没有用。 但是,对于基于会话的身份验证,这是唯一的方法。在您的 Rest API 上允许会话身份验证将引发 CSRF 安全问题,因此您需要有一个代理层来获取用户会话、从会话中检索访问令牌并向 Rest API 添加Authorization 标头。

    CORS 使用此方法需要将用户访问令牌存储在 localStorage 中,因为令牌是直接从 Js 客户端获取的。 使用 CORS,您可以确定其他网站无法从浏览器向您的 Rest API 发出请求。但是您的第一个客户需要公开(即:它没有client_secret)。

原生应用(桌面和移动)

在我的第一个应用程序中,我尝试使用您建议的相同机制来保护身份验证流程。但是,这种机制要求您以独特的方式识别每个用户客户端。对于privacy reasons,这在 ios 中是不可能的,并且很有可能在未来的 android 版本中会被拒绝。因此,您应该依赖公共客户端并仅在您的本机应用程序代码中添加 client_id

这意味着你的原生应用客户端/你的js客户端可以非个性化了? 是的,没有办法通过 oAuth2 资源所有者密码凭据授予类型来防止这种情况

这主要是因为 oAuth2 不用于身份验证,仅用于第三方授权,并且添加了该授权类型仅适用于受信任足以直接使用用户密码的特定第三方应用程序。你可以阅读更多关于这个论点here 和here。

最后

您仍然需要一种方法来授权您的用户,我认为使用 oAuth2 可以达到的最佳效果就是 Auth0 所做的。 本质上,这个 Saas 使用 oAuth2 服务器 + OpenID 连接来管理您的用户,因此您始终像管理第三方应用程序一样管理您的用户,并且一切正常。

确实,您可以在this page 上看到,对于移动应用程序,他们建议使用基于浏览器的登录表单,因为任何反编译您的应用程序的人都可以对原生表单进行非个性化处理,但是如果您将其包装到授权代码流中它工作正常。

【讨论】:

"你应该尝试开发授权码或隐式授权类型" - 你不应该使用隐式授权类型:tools.ietf.org/html/…和tools.ietf.org/html/…

以上是关于公共第一方客户端的正确 OAuth2 流程的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security OAuth2 Demo —— 密码模式(Password)

何时在oauth2流中为非交互式客户端请求新的access_token?

Cognito - 没有为 OAuth2.0 流启用客户端

如何使用 Oauth2 和 javascript 客户端(Spring Oauth2 + Angularjs)实现长期登录会话

基于OAuth2.0的统一认证登录方案

OAuth2.0流程