Architecture Design - REST API to support Facebook

2020-02-20 03:05发布

问题:

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

There are two levels of authorization take place:

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) 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) Is it ok to cache access_token for the client in plain text format in redis and use that cache first?

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) 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) 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?

=================

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) 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) 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) 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) Should client store utoken in its cookie/cache? Isn't that scary?

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?

A design proposal somewhat smiliar to mine REST API for website which uses Facebook for authentication

回答1:

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: 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: Check out that facebook documentation:

Graph API calls can be made from clients or from your server on behalf of clients. Calls from a server can be better secured by adding a parameter called 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: 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: 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: You should save that token A ReST architecture uses a client cache constraint.

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 ass you say, you are using it just for login.

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 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: Remember short lived tokens. Tokens must expire!.