I'm developing a website that is primarily accessed via an app, and I want to use OAuth2 for user registration and authentication. Since it is an Android app I will start using Google's OAuth2 stuff, since it provides a decent UI on Android.
Google states that "You can choose to use Google's authentication system as a way to outsource user authentication for your application. This can remove the need to create, maintain, and secure a username and password store." which is what I want to do. However when I go through all their examples and whatnot, I can only find stuff about having a website or an app authenticate a user against Google's services.
And indeed, when I go to register my app ("client") with Google's OAuth2 there are options for website clients and "installed" clients (i.e. a mobile app) but not both. I can create two separate clients but I read the OAuth2 draft and I think there will be a problem, which I will now explain.
Here's how I did envisage it working:
User asks MyApp to access his private data.
App uses Android's AccountManager class to request an access token for Google's APIs.
Android says to user "The app 'MyApp' wants access to your Basic Information on Google. Is this ok?"
User says yes.
AccountManager connects to Google's OAuth2 server using the credentials stored on the phone, and asks for an access token.
Access token (which follows the green lines) is returned.
AccountManager returns the access token to MyApp.
MyApp sends a request to MySite for the user's private data, including the access token.
MySite needs to verify the user, using the access token. It validates the token as described here, with Google - "Google, is this token valid?".
Now, what I want to happen is that Google says "Yes, whoever gave it to you is indeed that user.", but what I think will actually happen (based on the OAuth2 draft and Google's documentation) is that it will say "No way! That token is only valid for MyApp, and you're MySite. GTFO!".
So how should I do this? And PLEASE don't say "Use OpenID" or "Don't use OAuth2" or other similarly unhelpful answers. Oh and I would really like to keep using the nice AccountManager UI rather than crappy popup WebViews
Edit
Provisional answer (I will report back if it works!) from Nikolay is that it should actually work, and Google's servers won't care where the access token came from. Seems a bit insecure to me, but I will see if it works!
Update
I implemented this pattern with Facebook instead of Google and it totally works. The OAuth2 server doesn't care where the access token comes from. At least Facebook's doesn't, so I assume Google's doesn't either.
In light of that it is a very very bad idea to store access tokens! But we also don't want to have to hit Facebook/Google's servers to check authentication for every request since it will slow everything down. Probably the best thing is to add an additional authentication cookie for your site that you hand out when their access token is validated, but a simpler way is just to treat the access token like a password and store a hash of it. You don't need to salt it either since access tokens are really really long. So the steps above become something like:
9. MySite needs to verify the user, using the access token. First it checks its cache of hashed valid access tokens. If the hash of the token is found there it knows the user is authenticated. Otherwise it checks with Google as described here, with Google - "Google, is this token valid?".
10. If Google says the access token is invalid, we tell the user to GTFO. Otherwise Google says "Yes that is a valid user" and we then check our registered user database. If that Google username (or Facebook id if using Facebook) is not found we can create a new user. Then we cache the hashed value of the access token.
I just posted an answer to a similar StackOverflow question.
Google calls this Hybrid Apps and explains how an "Android app obtains offline access for Web back-end".
The gist of it is that you'll have to pass a massaged scope string into GoogleAuthUtil.getToken in order to get it to return an Authorization Code (not an OAuth2 Token). That Authorization Code can be passed from your mobile app to your server and be exchanged for an OAuth2 Token and Refresh Token, according to this schematic.
The scope parameter needs to look something like this:
oauth2:server:client_id:<your_server_client_it>:api_scope:<scope_url_1> <scope_url_2> ...
You can use the access token retrieved by the mobile application anywhere else. Drive SDK has a nice and simple intro that goes through the flow on https://developers.google.com/drive/quickstart-android
it describes exactly what you want:
https://developers.google.com/identity/protocols/CrossClientAuth
You probably need OpenID Connect, which uses OAuth tokens for authentication. As for AccountManager, the current OAuth support is a bit hacky, the new Google Play Services, set to be released 'soon' should hopefully make this better. See here for a demo.
At least with Google, the access token eventually expires. This is why the android AccountManager has the invalidateAuthToken method--the cached access token has expired, and you need to tell the AccountManager to stop giving you the old one and instead get a new one. This makes it somewhat safer to cache the token, as the token itself doesn't give you eternal access as that user. Instead, when valid, it merely says "at some point in the recent past, this token was acquired by a trusted source."
Here are a couple of things I've found helpful when working with tokens. The first is Google's tokeninfo endpoint. The token itself is just base64-encoded JSON. This means it isn't encrypted, so you need to be sure to be using HTTPS for communication. However, it also means that you can examine the token and have a better idea of what's going on.
https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=
If your token was "abcdef", you would navigate to:
https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=abcdef
and Google would unpack the token for you. It is a simple JSON object that includes an "expires_in" field telling you the number of seconds for which the token is still valid. At 6:03 in the video below you can see the unpacked token:
https://developers.google.com/events/io/sessions/383266187
That video includes a thorough overview of OAuth2 and is well worth watching in its entirety if you're going to be dealing with OAuth and tokens. The speaker also discusses other forms of Oauth2 tokens, that are not access tokens, that do not expire.
Another useful resource is the OAuth Playground. This lets you do basic things like request scopes, make up requests, and get back tokens. This link seems to work sporadically, and on Chrome I had to install the Oauth Playground app:
https://developers.google.com/oauthplayground/
And here is a tutorial by Tim Bray, the speaker in the video, explaining how to use access tokens to communicate to a server from an Android app. This was useful to me because I began to understand how the different things in the Google API Console work together:
http://android-developers.blogspot.in/2013/01/verifying-back-end-calls-from-android.html
With regards to the actual answer to your question, I would say you never need to cache the access token on the server. As explained in the Verifying Back End Calls from Android link above, verifying a token is almost always a fast static call, meaning there's no reason to cache the tokens:
The libraries can cache the Google certs and only refresh them when required, so the verification is (almost always) a fast static call.
Finally, you can indeed use the AccountManager to get access tokens. However, Google now instead encourages the use of the GoogleAuthUtil class in the Play Services library instead:
In a nutshell what's the difference from using OAuth2 request getAuthToken and getToken
Here note the comment by Tim Bray, the same guy yet again from the above links, saying that they are putting their efforts into the GoogleAuthUtil route. Note, however, that this means you would be limited to Google authentication. I believe that the AccountManager could be used to get, for example, a Facebook token instead--not the case with GoogleAuthUtil.
When we had a need to do something similar on a non-google OAuth Server, we kept the tokens in a DB on the website. The app would then use web services to request the token when needed to request data.
The user could do the OAuth registration on either the web or app. They shared the same application token, so they could share the same access token. After the registration we would store the access and refresh tokens in the DB for use from whichever app needed it.
Related
I am writing an Android app with a Google Cloud Endpoints backend, and I want to restrict my backend with a Google signin.
I have followed the Android instructions, and have successfully logged in. In other words, I have received a token, which I can then pass to the server and verify the user it stands for. Great.
However, I am missing the bigger picture here. Questions:
Am I supposed to be sending this token with each request back to the server, and repeat the process of verifying it in each request?
If yes, the token will expire at some point (1 hour I believe). I suppose the user does not have to login again, there should be away to avoid this, right?
Is a way to refresh the token (I think Google Signin is OAuth2)?
And most importantly, is all this the standard way someone uses Google signin to protect their backend? I was expecting this to be very straightforward process, since I am only using Google products. However, I am finding myself lost in pages and pages of documentation on Android and Cloud Enpoints with pieces of the puzzle.
Any help or insight is appreciated.
I have not used android authentication but google uses outh2 for all its authentication. Google SDKs may help you alleviate some of the pain of using oauth2. To answer your questions
Yes - You are in the world of token based authentication and you do send the token with every request.
Yes. token will expire after an hour. To get around this when you first do an oauth2 authentication, you also get a refresh token. When the token of the user expires you use the refresh token to get the new token. This refresh token can be stored on the client side. Client will find during one of its requests that that existing token is expired and would request for a new token using refresh token
Yes you use the google refresh token URL to use the refresh token and get the new token. I have given the important oauth URLs of google below.
This is indeed oauth2 process my friend.
since you are using cloud endpoint I believe that you would be making the authentication endpoints.
You generally make the following endpoints when doing oauth2 authentication using a service provider(google, facebook, github, etc):
https://webiste.com/auth/signin/{provider}
https://webiste.com/auth/callback/{provider}
https://webiste.com/auth/refresh/{provider}
Following are the google URLs for oauth2 that you would use:
oauth_url: 'https://accounts.google.com/o/oauth2/v2/auth', //start auth
token_url: 'https://accounts.google.com/o/oauth2/token', //get id_token, access_token, refresh_token token
refresh_token_url : 'https://www.googleapis.com/oauth2/v4/token',
token_info_url: 'https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=', //validate token
You would also want to go through google's oauth2 documentation at https://developers.google.com/identity/protocols/OAuth2.
I think you should Firebase UI Android Library for Authentication. It provides you with Google, Facebook, Email, Twitter and Github signin options and good part is that it can be done by writing only few lines of code.
For more information click here.
It's best practice to use 3ed party code to do this. It will take much less time, will have much less bugs and will be very easy to expand to other methods later.
I would recommend Firebase because it's very easy, free, works well and owned by Google.
My Android app needs to send an authorization code to my server so that the server can use that to acquire an access token for the user's Google Drive account. I have been trying to figure out how to acquire the authorization code and I found this in the Google API documentation (Using OAuth 2.0 for Installed Applications):
This sequence starts by redirecting a browser (system browser or
embedded in the application as a web view) to a Google URL with a set
of query parameters that indicate the type of Google API access the
application requires. Like other scenarios, Google handles the user
authentication and consent, but the result of the sequence is an
authorization code. The authorization code is returned in the title
bar of the browser or as a query string parameter (depends on the
parameters sent in the request).
After receiving the authorization code, the application can exchange
the code for an access token and a refresh token. The application
presents its client_id and client_secret (obtained during application
registration) and the authorization code during this exchange. Upon
receipt of the refresh token, the application should store it for
future use. The access token gives your application access to a Google
API.
Now I am not sure how to get this authorization code in my Android app since the Android examples I have seen seem to get the access tokens directly. I am looking at the Android AccountManager class and it has a method getAuthToken but this seems to refer to the access token and not the authorization code.
So how does one acquire the authorization code that can be shared with a server? If it is possible I would greatly appreciate some example code. If this is not possible what are the possible workarounds?
You may want to take a look at the Cross-client Identity document. It should keep you from needing to pass user tokens back and forth.
I believe you can actually take the access token returned by the Android AccountManager, send this to your server, then have your server make a call against the Google Drive API using that same access token - it is a bearer token and not bound to the channel that created it, so please take good care of it and only send over encrypted connections.
Documentation on how to get that access token can be found here:
https://developers.google.com/drive/quickstart-android
While that access token is good for immediate use, it will expire in less than 1 hour, so if you are looking for a solution that enables your backend server to have continued access to the Drive data, without the user being present at your app at the time of request, an alternate approach will be needed.
Is there a way to request for offline access using the Google play services sdk on android? I know that the raw HTTP api has an option to do this by requesting for a refresh token, but couldn't find a way to do it via the new Google Play services sdk.
The new sdk gives the app an access token using the GoogleAuthUtil.getToken() method, but the access token expires every hour. I could make the raw http request and have the user sign in from a web view or the browser, but would prefer a way to do it natively using the sdk, since that is a much better experience for the user.
Searching the Google docs near and far, it does look like that this is possible. Google calls this "Cross-client Identity" under its "Hybrid Apps" category.
You can apparently massage the string for the scope parameter you pass into GoogleAuthUtil.getToken(...) method to coax it into returning an "Authorization Code" rather than an OAuth2 Token. (For the difference between these, I've found this chart helpful.)
The details are here, specifically the last section titled "Android app obtains offline access for Web back-end".
It seems you'll need to pass in the following as "scope" string to getToken:
oauth2:server:client_id:<your_server_client_id>:api_scope:<scope_url_1> <scope_url_2> ...
The doc then claims this:
In this case, GoogleAuthUtil.getToken() will first require that the
user has authorized this Project for access to the two scopes.
Assuming this is OK, it will return, not an OAuth token, but a
short-lived authorization code, which can be exchanged for an access
token and a refresh token.
Disclaimer: I've not tried this myself yet; our Android developer will shortly. Please report if this is working for you.
After reading all sorts of Stackoverflow postings and various documentation including some on http://code.google.com/p/google-api-java-client/ site I feel utterly confused. So can someone explain to me the best way of achieving the following:
Let user to approve my app. I don't really care if this is done by accessing their registered Google account in accounts/settings and approving the app (preferred way) or by calling Google OAuth authentication page
Obtain the authentication token that can be used to communicate with Google Reader
Another point of confusion for me - I was able to approve my app using AccountManager and get auth token from it but it won't work with Google Reader. So how to balance Account information from the AccountManager and OAuth? Do I still need to do anything with OAuth after I get approved by user in AccountManager settings?
Code example would be nice but I look more for some clear explanation on how all these pieces are related to each other
The account manager lets you get authentication tokens for different services. There are different concrete implementation under the hood: ClientLogin, OAuth, etc. To get it to work with, say, Google Reader, you need to pass the appropriate token type. For ClientLogin, those are short string like "ah" (App Engine) and "reader" (Google Reader). For OAuth, they are scopes, as defined by each service. So, what are you passing as the authTokenType parameter?
Getting the token is asynchronous, since it may involve network access. If there is a cached token, it will be returned right away, if valid. The flow is not that complicated, see the link above for the pretty picture. Once you get the token, you put it in the appropriate header, and use the API as per the spec.
Here's an Android training class on AccountManager that might help:
http://developer.android.com/training/id-auth/authenticate.html
Also, using AccountManager with Google Reader is currently the only preferred way of doing this. Directing your users to a login page with a WebView is not very secure and using a browser isn't supported by Google APIs as far as I know (I'm also not sure if Reader uses OAuth2 or not).
I'm developing an android app that consumes a webservice that I will develop too (I'm thinking in using a RESTFul webservice)..
and I want to secure the connection between the app and the server but I need to authenticate users too..
My problem is in the last part, to secure the connection I think the best way to do it is to use SSL (https), am I wrong?
I don't know what's "the best way" to authenticate users, to make sure that a user cannot consume the webservice as another user..
I have some ideas, like using a authenticate(login,pass) method on the webservice that returns a token.. And for any operation that requires authentication the user would need to pass that token as a parameter.. The thing is, is this a good way to do this? whats the most common technique used to auth users in a situation like this?
If a token based auth is a good idea how should I generate the token?
Sorry for this long text..
Any help will be usefull
Thanks
Make sure you understand a trendy standard like OAuth before you go down that path. Most OAuth flows are centered around a user logging in to your server through a web browser. This can lead to pretty bad user experience for a mobile app. The standard does allow for alternatives. Here's a decent introduction.
You could also use an existing identity provider like Google, Facebook, Twitter, etc. instead of implementing your own authN/authZ. On Android, you can ask for a Google auth token using the AccountManager. This usually works because the user needs to be logged in to their Google account to access the Android Market. Anyway, this will prompt the user to grant authorization to your app. You could then use the Google auth token to login your user to your service with your own token. The login would essentially be your server verifying the Google token is valid (by contacting Google's servers) and then issuing its own token to be used for calls to your web services. If you don't like Google, you could use the Facebook SDK, etc.
As for what to use for tokens... The OAuth spec has stuff on that as well. You could do something as simple as a random string or something as complex as encrypted SAML assertions.
You should implement a token based OAuth, which will require the users to log in once, and then permanently have access.
You can use Google App Engine which already provides user authentication services for you (your Android users most likely already have google accounts) But this is only one of many options.
You can also look into Amazon's Identity Access Management (IAM) which will allow you to manage the users who have access to your web service, and authorize them accordingly.
I think the best way to do it is to use SSL (https), am I wrong?
This only prevents certain types of malicious use, but not everything. There is still nothing to prevent people from accessing your database on the phone, and retrieving credentials that way.