Google play services sdk: Requesting for offline access during auth - android

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.

Related

Login Internally to Dropbox Account from Android App

In my project, I have an activity that downloads files from Dropbox and the basic method that uses the Dropbox account works. In the developer console, they provided an access token to make calls to the API without going through the authentication. However, there is no sample or hint to do so. I saw an example in this link, but it is for Ruby. Also, there is no DropboxClient in Android API. How can I make calls to the Dropbox API using the access token only?
The generated OAuth 2 access token is generally only meant for testing/developing with your own account.
If you're using the Dropbox Android Core SDK, you should follow the instructions in the tutorial for implementing the app authorization flow. This results in an access token for the user, (e.g., you, during testing, or each actual end-user when your app is released) that your app can store and re-use for future API calls for that user.
If this app is only for you though and you just want to supply your own OAuth 2 access token, you can use AndroidAuthSession constructior.

Should I verify the OAuth2 token in the Android client or will App Engine itself authenticate user based on the credential passed to the backend API?

I am creating an Android app with an App Engine backend and am trying to use OAuth2 to authenticate users through their Google Account on the Android device, but am not able to figure out if I need to carry out all of the following steps or whether just step 1 would suffice.
Step 1: In this tutorial by Google, they have created a GoogleAccountCredential using the Google account found on the device and passed it to the backend API hosted on App Engine.
Step 2: In this other tutorial, they have passed this credential only if getting an OAuth2 access token in the Android app returns no error.
Step 3: In yet another tutorial, it has been advised that the backend should check the token sent by the Android client to verify that Google generated this token and that the device that asked for the token matches the audience value in the backend.
So my question is: do we really need steps 2 and 3 in an Android app whose backend is hosted on App Engine or does App Engine take care of 2 and 3 if we pass a credential created for the Google Account found on the phone to the backend API?
Another thing is how often and where in my code should I authenticate the app user:
1. Is it required before each endpoint call?
2. Or is it enough to just run the authentication code just when the app launches?
3. Or better yet, if it is enough to authenticate based on just step 1, would it be okay if I get the user's Google email address from the AccountManager, store it with SharedPreferences and create a GoogleAccountCredential based on the stored email whenever I make an endpoint call until the user explicitly asks to sign out or switch account?
Please help me decide which approach would make most sense. Like always, thanks so much for helping out! :)
Tim's article (step 3) above is correct. When a server receives a token it must verify that it was intended for them. This is something that Google Cloud Endpoints will do for you, by configuring the client IDs / audience fields such as per the example here: https://developers.google.com/appengine/docs/java/endpoints/auth
I ended up passing the GoogleAccountCredential created with the account name found on the phone to the endpoint builder. Then, in the endpoint API method, I added a User parameter which was automatically populated by App Engine after authenticating the user based on the credential passed to the endpoint builder. As a final check, I compared whether User.email was giving the same email address that I used to create the GoogleAccountCredential.
For sure authenticate on the back end. If you pass them in in the standard way GAE should automatically use that as your credentials. The UserService should give you the user details with no effort on your part on the server side.

Getting a OAuth2 authorization code that can be shared with a server

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.

Authenticating with OAuth2 for an app *and* a website

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.

Get Google AuthSub token from android app

I'm writing an android program which must interact with google documents, so I have watched this sample, but it uses an AuthSub token. How can I get an AuthSub Token from an android app?
For accessing Google Docs or any other Google service (or any OAuth based service for that matter) you will need to find a way to do an OAuth based authentication, after which you can u get a secure token which you can use to access a service based on the users credentials.
There are some really good examples to get you started:
This is an example of getting AccountManager to work with a Google
service like Google Tasks, this shows you how to generate tokens and
then how to use them:
https://developers.google.com/google-apps/tasks/oauth-and-tasks-on-android
This is an in-depth look into how you can authenticate based on the
users Android credentials, has an example of how things work and how
the UI should be:
http://www.finalconcept.com.au/article/view/android-account-manager-step-by-step-2
For something specific to Google Docs have a look at
http://code.google.com/p/google-api-java-client/wiki/Android
http://code.google.com/p/gdata-java-client/source/browse/trunk/java/sample/docs/DocumentResumableUploadDemo.java

Categories

Resources