我做这整个 API、客户端应用程序、Oauth/OpenId 连接的事情对吗?

Am I doing this whole API, client app, Oauth/OpenId Connect thing right?

我有一些编程经验,但仅限于 PHP 和 Java 企业系统。但现在我对我的新工作中的网络应用程序有了一些想法。由于我是新手,我想分享一下我是如何在服务器、浏览器应用程序和身份验证中使用 Google 的 OpenID Connect 完成整个 API(我阅读了很多关于 Oauth 和 OpenID 的内容连接,最有用的来源是:https://developers.google.com/identity/protocols/OpenIDConnect).

服务器:Laravel - hxxps://coolapp-api.mycompany.com

客户端:Angular - hxxps://coolapp.mycompany.com

TL;DR 版本:

1) 用户访问 hxxps://coolapp.mycompany.com,获取 Angular 应用程序登录页面。输入他们的电子邮件,单击“使用 Google 登录”;

2) 应用发送邮件至hxxps://coolapp-api.mycompany.com/api/sign-in。服务器使用所有需要的参数将用户重定向到 hxxps://accounts.google.com/o/oauth2/auth;

3) 用户登录到他们的 Google 帐户,如果这是他们第一次登录我的应用程序权限,然后 Google 将他们重定向到我的服务器 hxxps://coolapp-api.mycompany.com/sign-in/google/callback.服务器检查所有内容,如果一切正确,它会创建一个 JWT 令牌并将重定向发送到位于 hxxps://coolapp.mycompany.com/login/callback?token=JWT-TOKEN[=13= 的客户端应用程序]

4) 客户端应用程序获取令牌,将其存储在本地存储中,并在每次 API 调用时将其发送到服务器

更详细的版本:

1) 用户访问 hxxps://coolapp.mycompany.com,获取 Angular 应用程序登录页面。输入他们的电子邮件,单击“使用 Google 登录”;

2) 应用发送邮件至hxxps://coolapp-api.mycompany.com/api/sign-in。服务器创建一个状态令牌并将其存储在缓存中,与收到的电子邮件相关联。然后服务端创建Google的oauthURL并在响应体中发送给客户端。我尝试使用 HTTP 重定向来实现,但 Google 的服务器响应时出现 CORS 错误。 Angular 应用从响应中读取 Google 的 url 并转到那里。

3) 用户登录到他们的 Google 帐户,如果这是他们第一次登录我的应用程序权限,然后 Google 将他们重定向到我的服务器 hxxps://coolapp-api.mycompany.com/sign-in/google/callback?code=AUTHCODE&otherstuff。服务器将它收到的代码(以及所有其他需要的参数)发送到 hxxps://accounts.google.com/o/oauth2/token。它会收到包含该用户的电子邮件和基本信息的 id_token。这个应用程序不是 public,所以我不希望任何拥有 Google 帐户的人登录,只希望我将其电子邮件添加到服务器数据库的客户登录。所以现在服务器检查令牌中用户的电子邮件是否在数据库中。如果不是,它会向用户发送 HTTP 401 - 未经授权。然后服务器检查其缓存中与收到的电子邮件相关联的状态令牌。如果它等于通过 Google 的重定向收到的那个,那么服务器会创建另一个 JWT 令牌,但现在由我的服务器签名。最后,它使用新令牌向 hxxps://coolapp.mycompany.com/login/callback?token=JWT-TOKEN 发送 HTTP 重定向。

4) 客户端应用程序获取令牌,将其存储在本地存储中,并在每次 API 调用时将其发送到服务器

一些评论:

所以我的问题是:

我是不是做错了什么、不安全、奇怪?

非常感谢大家

1) 每次用户要登录时都输入电子邮件地址很繁琐。如果用户已经在 Google 登录,则不需要。用户只需单击 "Log in with Google" 按钮即可登录,无需输入任何内容。 state 参数可以是随机字符串 - 与用户的电子邮件无关。

2) 如果您希望您的后端处理来自 Google 的重定向(使用授权代码流 - 后端在 OAuth2 术语中具有客户端角色),后端还应该启动重定向到 Google - 不是通过发送包含重定向的数据 URL。要实现它,点击 "Log in with Google" 按钮后,执行整个页面导航(而不是 XHR 请求)到 /api/sign-in 如果后端 returns HTTP 302,浏览器将正确重定向到Google.

3) 在获取令牌并检查用户是否存在之前,您应该执行请求验证(state 参数)。 出现错误(访问被拒绝)时,您可以考虑将用户重定向到包含错误详细信息的错误页面,而不是返回 HTTP 401,因为 HTTP 代码会导致向用户显示一般错误屏幕。如果你想继续使用HTTP代码,我觉得HTTP 403 Forbidden会更合适。

4) 考虑使用 sessionStorage 而不是 localStorage。 sessionStorage 在关闭 browser/tab 后被清除,并且不在选项卡之间共享。它使它更安全,并允许用户在不同的浏览器选项卡中使用不同的身份。 你后台发的token,有效期有限制吗?用户是否需要在一段时间(短)后获得新令牌?否则,有效的令牌值可能会保留在 localStorage 和浏览器的页面历史记录中,这可能是一个安全问题。

您可以考虑使用您自己的 OAuth2 身份验证服务器(例如 RedHat Keycloak),它会接受 Google(以及后来的一些其他提供商)进行身份验证,并且它还会颁发您所接受的访问令牌后端。