5

I'm working on a webapp that will allow users to authenticate using simpleauth. For now I will be supporting Google and Facebook. Other than logging in and out (using webapp2), the webapp will consist of Cloud Endpoint APIs. The clients will be web, Android, and iOS.

My questions is, using Endpoints Proto Datastore, can I have user_required=True and call endpoints.get_current_user() to get my user from an @Model.method if the user's auth provider is Facebook (or any of the other supported OAuth2 providers? If it is not possible, does that mean I should not have user_required=True, and should instead get a permanent user id from the provider given the OAuth2 token and keep it in the datastore, generate my own auth token for that user, and then pass that token to each request?

EDIT: Instead of passing the auth token around, would it make sense to have an authenticated user request an "API token" that they can pass to the API methods? Would this token have to be included in the POST or GET body, or can it be put in a header/cookie (I saw some questions elsewhere on SO regarding headers and cookies with Cloud Endpoints, but it's been some time since then). This is all assuming that non-Google auth won't work.

Eliezer
  • 7,209
  • 12
  • 56
  • 103

1 Answers1

2

This answer is not going to directly answer your question but should give you a good idea how you can implement authentication in a safe way. I've implemented something similar recently and spent quite some time figuring out which is the best way to do authentication with Google AppEngine environment.

Google supports OpenId Connect protocol. Facebook's implementation should be very similar according to Getting Started with OAuth 2.0 book. I will focus more on Google's implementation, as I am more familiar with it but the concepts should be very similar when using other OAuth providers.

OpenId Connect will give you an id_token, after successfully authenticating the user. It will also give you an access token. Access token is something that should be kept a secret. Never send it over the wire. Id token on the other hand is very handy. It contains some information about your user, and it's encrypted so it doesn't expose any information about the user "just like that". You'd have to crack the id_token to find out anything about user. In the unlikely event that it gets cracked it doesn't expose anything critical about the user. What you can do you can store it as a cookie and then use it in all subsequent requests to verify it the user by checking if there's an access token that matches the id_token. The only drawback is that id_token is quite long - it takes around 650bytes. That would mean that every http request carries that payload. If sending that much information is too much for your use case you can send only first few characters, maybe 12 or so and then match just the first part. The id_token has can also be useful when analysing your data. It will show up when analysing http requests but will not reveal any information about the user and you can still differentiate requests that came from different users.

Also on a side note, don't try using AppEngine's users service as it doesn't work very well with any kind of custom authentication.

Hope this gives you an idea and puts you on the right track.

markovuksanovic
  • 15,676
  • 13
  • 46
  • 57
  • Thanks! What I plan on doing is using a unique user id from the third party service (eg the Facebook account's id, or the Google account's id) with a provider prefix (eg google:12345) and storing that in webapp2's user model. I will then generate my own access tokens, and only go back to the third party service when I need to re-authenticate. My biggest issue is how I can use this with cloud endpoints, without having to send the access (or API) token in each request (either in the body, or as a header) manually, and then have to manually resolve the user in the endpoint. – Eliezer Jan 06 '14 at 01:04
  • @Eliezer if you store it as a cookie it will be sent automatically with every http request. You will still need to check, on the server, if the client with that id (retrieved from cookies) is authorized to make the request (but this step you will have to do even if you go for google's users service as mentioned in cloud endpoints documentation - https://developers.google.com/appengine/docs/python/endpoints/auth) – markovuksanovic Jan 06 '14 at 01:44
  • In java you can set up servlet filters and do the authorization check in there. I'm sure python has a similar feature. – markovuksanovic Jan 06 '14 at 01:45
  • I'm not 100% sure if you can access the cookie header using Endpoints Proto Datastore (although if you can that would make life much easier for me). I'd much rather call, `endpoints.get_current_user()` then have to write the code to manually check if token is valid for this user. My fallback is to use annotations to handle the auth check server side, but I'd love to hear from the appengine team (or someone who knows for sure) if a more elegant solution exists. – Eliezer Jan 06 '14 at 01:51
  • 1
    @eliezer The cookie is sent with every http request. I'm sure there is a way to access it. The get_current_user will return User object which plays very well as long as you stay in AppEngine playground (you use AppEngines's authentication services). As soon as you try to do more fancy stuff, i.e. integrate facebook, AppEngine's users service stops being very useful. You will also have a lot more problems then benefits. Although I'd be interested to hear from Google and appengine team as well on this issue. – markovuksanovic Jan 06 '14 at 02:06