问题描述
我正在尝试设计 REST API 以支持各种移动客户端(iOS 和 Android 应用).这些应用程序将允许用户使用 Facebook 登录以及我们自己的电子邮件身份验证进行登录.你可以参考下图了解我的设计
I am trying to design REST APIs to support various mobile clients (iOS and Android apps). These apps will let user login using facebook login along with our own email authentication. You can refer to the diagram below to understand my design
有两个级别的授权:
第一个是使用 OAuth2 的客户端(或应用)授权".因此,当用户在移动设备上安装我们的应用程序并启动应用程序时,首先,应用程序会进行客户端(应用程序)授权",如上图(第一张图)所示.并且服务器向客户端发送回一个长期存在的 access_token
以用于所有后续调用.我的问题是:
First one is "Client (or App) Authorization" that uses OAuth2. So when user install our app on mobile device, and starts app, then very first thing, app makes "Client (App) Authorization" as shown in above diagram (1st image). And server sends back an long-lived access_token
to client to use for all subsequent calls. Here my question are:
Q1) 您可以看到客户端正在发送 client_key
和 client_secret
,我将它们存储在 client_info
表中.这个秘密应该是纯文本还是应该是可解密的格式?如果我加密它,我仍然需要在我的系统中的某个地方保留加密密钥.那么它将如何使其安全?同样在每次调用中,解密都将是一种开销.
Q1) You can see client is sending client_key
and client_secret
and I am storing them in client_info
table. Should this secret be in plain text or it should be in decryt-able format? If I encrypt it, I still need to keep encryption key somewhere in my system. So how it will make it secure? Also in every call, decryption will be an overhead.
Q2) 是否可以在 redis 中以纯文本格式为客户端缓存 access_token
并先使用该缓存?
Q2) Is it ok to cache access_token
for the client in plain text format in redis and use that cache first?
Q3) 为了更加安全,我要求客户发送 appsecret_proof
以确保他们发送的 access_token 仅属于该客户.它使用与 Facebook 相同的概念 https://developers.facebook.com/docs/graph-api/securing-requests#appsecret_proof.它是 hash_hmac('sha256', access_token, client_secret)
Q3) In order to be extra safe, I am asking clients to send appsecret_proof
to make sure the access_token, they are sending belongs to this client only. It uses the same concept as Facebook https://developers.facebook.com/docs/graph-api/securing-requests#appsecret_proof. And it is hash_hmac('sha256', access_token, client_secret)
Q4) 我们将只有自己的 2 个移动应用程序(iOS 和 Android 各一个),并且不提供第三方使用我们的 API 来开发其他应用程序.这意味着,对于每种类型的应用,我们的 client_info
表将只有两行.那么,在应用程序代码中,我们可以对 client_key
和 client_secret
进行硬编码吗?如果是,那么将来当我们必须使新机密失效并使用新机密时,我们将如何实现替换这些信息?
Q4) We will only have our own 2 mobile app (each for iOS and Android) and not providing third party to use our API to develop other apps. That means, our client_info
table will only have two rows one for each type of apps. So is it okay, that in app code, we keep client_key
and client_secret
hardcoded? If yes, then in future when we have to invalidate and use new secret then how will we achieve replacing those info?
Q5) 由于它是我们自己的应用程序已有几年,因此将针对相同的 client_key
和 client_secret
创建多个 access_token.为了保存所有这些,将 client_key
存储为 key 并将所有 access_tokens 的 array
存储为 redis 中的值是个好主意.以后我们的API什么时候开放给第三方,那么这个redis存储设计还能扩展吗?
Q5) Since it is our own apps for couple of years, so there would be multiple access_token will get created against same client_key
and client_secret
. In order to save all of them, is it a good idea to store client_key
as key and an array of all access_tokens
as value in redis. In future, when we will open our API to third party, then this redis storage design can still scale?
==================
=================
稍后,用户决定对我的应用执行一些操作,为此我们需要用户登录到他的帐户.对于该用户,单击facebook 登录".我的应用程序从 facebook 获取 facebook access_token
和 fb 用户的 id 并将这些信息传递给 API 服务器(如图 2 所示).API 服务器获取该令牌并调用 facebook API 以验证其 access_token
.一旦令牌被验证,服务器使用一些与该用户相关的元数据以及 FB 访问令牌来生成我们自己的 user_access_token
,比如说 utoken
.并将该 utoken
传回客户端,以在每个后续用户特定的 API 调用中传回.我的问题是:
Later on, user decides to perform some actions on my app, for that we need user to login to his account. For that user click on "facebook login". My app gets facebook access_token
and fb user's id from facebook and pass those info to API server (as shown in 2nd diagram). API server takes that token and call facebook API to validate its access_token
. Once token is validated, server uses some metadata related to that user along with FB access token to generate our own user_access_token
, lets say utoken
. And pass that utoken
back to client to pass back in every subsequent user specific API calls. Here my questions are:
Q1) 是否可以将该 utoken
保存在数据库、user_token
表中.这个 utoken
应该是纯文本还是应该是可解密的格式?如果我加密它,我仍然需要在我的系统中的某个地方保留加密密钥.那么它将如何使其安全?同样在每次调用中,解密都将是一种开销.
Q1) Is it ok to save that utoken
in database, user_token
table. Should this utoken
be in plain text or it should be in decryt-able format? If I encrypt it, I still need to keep encryption key somewhere in my system. So how it will make it secure? Also in every call, decryption will be an overhead.
Q2) 在每个用户特定的 API 调用中,我是否应该每次都调用 facebook 来检查 facebook access_token
是否仍然有效?我相信我不应该,因为这不会给我带来任何好处.请注意,Facebook 仅用于Facebook 登录".
Q2) In every user specific API calls, should I call facebook every time to check facebook access_token
is still valid? I believe I should not, as that is not going to get anything to me. Please note, Facebook is ONLY used for "facebook login".
Q3) 我应该加密哪些信息来生成utoken
?我想拥有一个散列或关联数组的用户 email
、user id
、role
和 facebook token
和然后序列化该数据结构并最终对其进行加密.你认为这样就足够了.我理解它符合我的要求,但作为标准或通用应用程序,它们是否足够好?或者有什么最佳实践吗?
Q3) What are the information I should encrypt to generate utoken
? I am thinking to have a hash or associative array of user's email
, user id
, role
and facebook token
and then serialize that data structure and finally encrypt it. Do you think that would be good enough. I understand its per my requirement, but as a standard or common app, are they good enough? Or is there any best practice?
Q4) 客户端是否应该将 utoken
存储在其 cookie/缓存中?是不是很吓人?
Q4) Should client store utoken
in its cookie/cache? Isn't that scary?
Q5) 请注意,用户可能有多个设备,使用相同的用户凭据登录.这意味着,在 user_token 表中,我们必须为那些登录的会话存储多个 utoken
,而它们都属于同一个用户.听起来对吗?
Q5) Please note user may have multiple devices, logged in with same user credential. That means, in user_token table, we would have to store multiple utokens
for those logged-in session, while all of them will belong to the same user. Does that sound right?
一个有点类似于我的设计方案 用于网站的 REST API使用 Facebook 进行身份验证
A design proposal somewhat smiliar to mine REST API for website which uses Facebook for authentication
推荐答案
Q1.1:不!客户端凭据不打算以这种方式使用.如果您的客户端是单页应用程序或移动应用程序,您将被迫将您的客户端凭据存储在不安全的环境中,即用户的机器.您应该使用 OAuth 的隐式流程
Q1.1: No!. Client credentials is not intended to be used that way. If your client is a Single Page App or a Mobile App, you will be forced to store your client credentials in an insecure environment, the user's machine. You should be using OAuth's Implicit flow
Q1.2:假设令牌是短暂的,缓存它没有问题.OAuth 的关键是,除了确保您可以依赖其他应用程序来验证您的用户之外,您还可以使用短期令牌有效地替换长期存在的用户或应用程序凭据.因此,如果有人获得了令牌的访问权限,至少,他们对系统的访问将在时间上受到限制.
Q1.2: Assuming the token is short lived, no problem caching it. The key of OAuth, apart from ensuring that you can rely on other application to authenticate your users, is that you effectively substitute user or application credentials, which are long lived, with a short lived token. So if someone gains access to the token,at least, their access to the system will be limited in time.
Q1.3:查看 Facebook 文档:
Q1.3: Check out that facebook documentation:
Graph API 调用可以从客户端或代表客户端从您的服务器进行.来自服务器的调用可以通过添加名为 appsecret_proof 的参数来更好地保护.
它声明 appsecret_proof
将用于代表用户从服务器调用.这里的重点与 Q1.1 有关.如果您将 client_secret 存储在用户的设备中,他们可以生成 appsecret_proof
.
It states that appsecret_proof
is to be used for calls from the server on behalf of the user. The point here has to do with Q1.1. If you were storing your client_secret in your user's device, they could generate the appsecret_proof
.
Q1.4:再次,不!您应该很好地阅读 OAuth 规范并了解不同的流类型以及何时使用它们.另请记住,如果您为应用程序提供 API,则该 API 是公开的,任何人都可以滥用.唯一的区别是它没有记录.Web 应用程序也会发生同样的情况.一旦它在互联网上,我就可以编写一个抓取工具并滥用 Web 应用程序.这是完全正常的,请记住,互联网上的任何内容都不是私密的,只是没有记录.
Q1.4: Again, No! You should have a good read of OAuth Specification and understand the different flow types and when to use each. Also bear in mind, that if you provide an API for your App the API is public for any one to abuse. The only difference is that it is not documented. The same will happen with a Web App. Once it is in the internet, I could write a scraper and abuse the Web App. This is perfectly normal, just bear in mind that anything on the internet is not private, it is just undocumented.
Q1.5:同样,代币应该是短暂的.如果它们的生命周期与凭据相同,在用户更改它们之前一直有效,那么令牌就失去了用途.
Q1.5: Again, tokens should be short lived. If their lifespan is the same of the credentials, which live until the user change them, then tokens lose their purpose.
Q2.1:您应该保存该令牌 ReST 架构使用客户端缓存约束.
Q2.1: You should save that token A ReST architecture uses a client cache constraint.
Q2.2:我不这么认为.Facebook 只是告诉您,获得该令牌的用户具有某些身份(例如电子邮件),您可以将其关联到您系统中的某个用户.一旦您知道该关联,您就不必太在意 Facebook 令牌,而应该调用 Facebook API.但正如你所说,你只是为了登录使用它.
Q2.2: I don't think so. Facebook is just telling you that the user that obtained that token has some identity (an email, for example) that you can associate to a user in your system. Once you know that association you should't care much about the Facebook token but to make calls to Facebook API. But as you say, you are using it just for login.
Q2.3:看起来不错,但再次检查 Oauth 规范,因为您似乎正在构建隐式流并使用 JWT 令牌.根据您想在令牌中存储的内容,似乎没问题.
Q2.3: Seems not bad but check again the Oauth Specification as you seem to be building an Implicit flow and using JWT tokens. As per what you want to store in your token, seems fine.
Q2.4:客户端必须缓存 JWT 令牌.没什么可怕的,因为它们对客户端是不透明的,因为它们是加密的.客户端随每个请求发送 JWT 令牌,API 服务器使用私钥(从未在服务器外公开)解密令牌,并可以检查用户的身份.
Q2.4: JWT tokens must be cached by the client. Nothing scary, because they are opaque to the client as they are encrypted. The client sends the JWT token with each request and the API server decrypts the token with a private key (that has never been exposed outside the server) and can check the identity of the user.
Q2.5:记住短暂的代币.令牌必须过期!
Q2.5: Remember short lived tokens. Tokens must expire!.
这篇关于架构设计 - REST API 支持由移动应用程序完成的 Facebook 登录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!