Android Google Drive Rest API: skip consent screen (hardcoded account) - android

I was trying to use Google Drive to sync some media files (video/images) and show them in my android app.
I've managed to make it all work fine but according to the docs (or at least from a first read) it looks like there must always be a consent screen where the user, after choosing his google account, agrees with the app getting data from his drive.
The point is that I'd like to 'hardcode' an account that programatically agrees to that since our app is always going to take the media from the same account.
Is that possible??
PD: Don't suggest any other storage service, Drive is a client's requirement.

To avoid the user consent, you can use a Service Account.
It's stated in the link that, Google APIs such as the Prediction API and Google Cloud Storage can act on behalf of your application without accessing user information. In these situations your application needs to prove its own identity to the API, but no user consent is necessary. Similarly, in enterprise scenarios, your application can request delegated access to some resources.
For these types of server-to-server interactions you need a service account, which is an account that belongs to your application instead of to an individual end-user. Your application calls Google APIs on behalf of the service account, and user consent is not required. (In non-service-account scenarios, your application calls Google APIs on behalf of end-users, and user consent is sometimes required.)
You can also check this related SO questions for more infor mation.
How to use Google Drive SDK in Android project with hardcoded credentials
Why does Google OAuth2 re-ask user for permission when i send them to auth url again

Finally I've managed to make it work altogether:
Create a service account credential from your google developers console.
Download your credential in json format.
Copy the file into assets/res/raw.
Rename your json into something without weirds chars (Ex: driveserviceprivatekey.json).
List item
InputStream privateJsonStream = dashboardActivity.getResources().openRawResource(R.raw.driveserviceprivatekey);
GoogleCredential serviceCredential = GoogleCredential.fromStream(privateJsonStream).createScoped(Arrays.asList(SCOPES));
Now you can use your drive service as follows:
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
this.mService = new com.google.api.services.drive.Drive.Builder(
transport, jsonFactory, null)
.setHttpRequestInitializer(credential)
.build();

Related

Android : inApp purchase receipt validation google play

I am using google wallet for my payment gateway, after purchasing the product google giving me a below response that
{
"orderId":"12999763169054705758.1371079406387615",
"packageName":"com.example.app",
"productId":"exampleSku",
"purchaseTime":1345678900000,
"purchaseState":0,
"developerPayload":"bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ",
"purchaseToken":"rojeslcdyyiapnqcynkjyyjh"
}
I am trying to make use of Receipt Validation that google play newly introduced.In Google Developer console I made certificate key by Service Account in the Permission. But I am confused how to make use of Receipt Validation after purchasing a Product from the Google Play-store.
So can anyone please help me how to do the Receipt validation of InApp Purchase.
Google provides receipt validation through the Google Play Developer API, within the API are two endpoints you will be most interested in: Purchases.products: get and Purchases.subscriptions: get.
Purchases.products: get can be used to verify a non-auto-renewing product purchase, where Purchases.subscriptions: get is for verifying and re-verifying auto-renewing product subscriptions.
To use either endpoint you must know the packageName, productId, purchaseToken all of these can be found in the payload you received on purchase. You also need an access_token which you can get by creating a Google API service account.
To get started with a service account first go to the Google play Developer console API access settings page and click the Create new project button:
You should now see a new Linked Project and a few new sections, in the the Service Account section, click the Create service account button.
You will be presented with an info box with instructions to create your service account. Click the link to Google Developers Console and a new tab will spawn.
Now click Create new Client ID, select Service account from the options and click Create Client ID.
A JSON file will download, this is your JSON Web Token you will use to exchange for an access_token so keep it safe.
Next, switch tabs back to the Google play Developer console and click Done in the info box. You should see your new service account in the list. Click on Grant access next to the service account email.
Next under the Choose a role for this user, select Finance and click Add user.
You have now set up your service account and it has all the necessary access to perform receipt validations. Next up is exchanging your JWT for an access_token.
The access_token expires after one hour of exchange you so need some server code to handle this and Google have provided several libraries in many languages to handle this (list not exhaustive):
Ruby: https://github.com/google/google-api-ruby-client
Node.js: https://github.com/google/google-api-nodejs-client
Java: https://github.com/google/google-api-java-client
Python: https://github.com/google/google-api-python-client
C#: https://github.com/googleapis/google-api-dotnet-client
I won't go into detail because there is plenty of documentation on how to use these libraries, but I will mention you want to use the https://www.googleapis.com/auth/androidpublisher as the OAuth2 scope, the client_email from the JWT as the issuer and the public key you can get from the private_key and the passphrase notasecret will be used for the signing_key.
Once you have the access_token you're good to go (at least for the next hour at which point you will want to request a new one following the same process in the above paragraph).
To check the status of a consumable (non-auto-renewing) purchase make a http get request to: https://www.googleapis.com/androidpublisher/v2/applications/com.example.app/purchases/products/exampleSku/tokens/rojeslcdyyiapnqcynkjyyjh?access_token=your_access_token
If you get a 200 http response code, everything went as planed and your purchase was valid. A 404 will mean your token is invalid so the purchase was most likely a fraud attempt. A 401 will mean your access token is invalid and a 403 will mean your service account has insufficient access, check that you have enabled Finance for the access account in the Google Play Developer console.
The response from a 200 will look similar to this:
{
"kind": "androidpublisher#productPurchase",
"purchaseTimeMillis": long,
"purchaseState": integer,
"consumptionState": integer,
"developerPayload": string
}
For an explanation of each property see https://developers.google.com/android-publisher/api-ref/purchases/products.
Subscriptions are similar however the endpoint looks like this:
https://www.googleapis.com/androidpublisher/v2/applications/packageName/purchases/subscriptions/subscriptionId/tokens/token?access_token=you_access_token
And the response should contain these properties:
{
"kind": "androidpublisher#subscriptionPurchase",
"startTimeMillis": long,
"expiryTimeMillis": long,
"autoRenewing": boolean
}
See https://developers.google.com/android-publisher/api-ref/purchases/subscriptions for the property descriptions and note that startTimeMillis and expiryTimeMillis will be subject to change depending on the duration of the subscription.
Happy validating!
Marc's answer is excellent. I will only add that the Google Play Developer API Client Library for Java makes it much simpler when connecting from your server to the Google Play servers.
The library automatically handles refreshing the auth token and also provides a typesafe API so you don't have to muck around with URLs.
Here's how you setup the Publisher singleton:
httpTransport = GoogleNetHttpTransport.newTrustedTransport();
jsonFactory = JacksonFactory.getDefaultInstance();
credential = GoogleCredential.fromStream(getClass().getResourceAsStream("/path/to/your/key.json")).createScoped(Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER));
publisher = new AndroidPublisher.Builder(httpTransport, jsonFactory, credential).setApplicationName(APP_NAME).build();
The following code queries a product purchase:
ProductPurchase product = publisher.purchases().products().get(PACKAGE_NAME, sku, token).execute();
Integer purchaseState = product.getPurchaseState();
product.getPurchaseTimeMillis();
product.getConsumptionState();
product.getDeveloperPayload();
You can similarly query for subscriptions:
SubscriptionPurchase sub = publisher.purchases().subscriptions().get(PACKAGE_NAME, sku, token).execute();
sub.getAutoRenewing();
sub.getCancelReason();
...
I faced a similar issue, the problem is in the settings we do in google developer project.
Refer to create-play-service-credentials for settings. Use the same primary account with which you created your in-app products.
Make sure you remove the previous one.
Link to a Google Developer Project
Your Play Developer account needs to be linked to a Google Developer Project.
1a. Open the Settings > Developer account menus and select API access
1b. Select Link to connect your Play account to a Google Developer Project
1c. Agree to the terms and conditions
2. Create Service Account
Next we need to create a service account. This is done from the Google API Console.
2a. Select Create Service Account
2b. Create Service account key credentials
2c. Enter details for service account - There is an "optional" step for granting role access. Make sure you grant access to the current user role.
2d. Download your JSON credential:
3. Grant Access
3a. In Play Console, select Grant Access on the newly created service account
3b. Grant the following permissions:
After this wait for 48 hours to allow Google to propagate all access rights for APIs.
#marc-greenstock provided a great answer, however, there is a very important thing about receipt validation using Google Play Android Developer API.
If you have any problems with using this API and you added your in-app product BEFORE granting permission or linking to your service account, you need to open "In-app products" and perform some update. You can for example edit description of your product and save. You should instantly get permission.
I spent a few hours thinking what did I do wrong...
This answer is excellent. One more issue we ran into while following the direction was that the service account didn't show up in the Google Play Console. We ended up finding this solution to help: Service account doesn't show up in Google Console after creation
Basically, go to IAM on Google API Console and add the new service account, then it will show up on Google Play Console.
screenshot

Is there a way to write to Google Drive from Android using application's account

I have a task to write files generated by an Android app to an application user account (or a service account).
I have been looking around for any help and found this sample
This sample app expects that the user account is setup on the phone.
It also asks for user's permission to write files to the drive account.
But given that my app uses "service account" or an application account, it should:
Write to the application's Google Drive account, without prompting the user for picking an account. Because it is guaranteed that the user's phone will and should not have application account.
Does not ask for any permissions, because user has no clue about scopes or drive account info.
Is it possible at all?
You can authorize the API with a service account without asking the user to pick an account and asking about permissions.
GoogleCredential credential = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("[[INSERT SERVICE ACCOUNT EMAIL HERE]]")
.setServiceAccountScopes(DriveScopes.DRIVE)
.setServiceAccountPrivateKeyFromP12File(new File("key.p12"))
.build();
Drive service = new Drive.Builder(
new NetHttpTransport(),
new GsonFactory(),
credential).build();
// make requests
service.files().list().execute();

Using Google Play services with Google Tasks API

I am trying to integrate my app with a Google API, say Google Tasks,
Before Google Play Services I would use the Google APIs Client Library for Java and the AccountManager to retrieve and access the OAUTH token for the API.
The pros of this methos is that I can:
Using the Java client APIs smoothly to access all the API methods my app may need.
the cons is that:
The Authentication Dialog is very bad looking and confusing to the user (As mentioned in Google IO Play services session)
I will need to add all kind of permissions to the app.
I guess 'Google play services' came to change all that:
No app permission is needed as its directly taken from the play services and then eventually from the user
It also offered the new beautiful AccountPicker that make authentication clearer to the user.
But the ease of access to the Java Client library API methods is lost!!! I have to create all the requests by myself.
The question is What is the best way to authenticate and request the OAUTH token using play services and after that continue to work with the Client library APIS to access the actual API methods?
You should be able to pass the result of GoogleAuthUtil.getToken (from Google Play Services) directly to GoogleCredential.setAccessToken (from the Google APIs Client Library).
An example of the latter can be found in TasksSample.java. In fact, since the AccountManager.getAuthToken and GoogleAuthUtil.getToken APIs are so similar, save for the asynchronous vs. synchronous difference, you should be able to modify that Tasks sample to use Google Play Services without too much difficulty.
Some very rough pseudocode would be:
...
// this should all be in a separate thread (e.g. AsyncTask)
final String token = GoogleAuthUtil.getToken(context, email, scope);
GoogleCredential credential = new GoogleCredential();
credential.setAccessToken(token);
Tasks service = new Tasks.Builder(transport, jsonFactory, credential)
.setApplicationName("Google-TasksAndroidSample/1.0")
.setJsonHttpRequestInitializer(new GoogleKeyInitializer(ClientCredentials.KEY))
.build();
List<String> result = new ArrayList<String>();
Tasks.TasksOperations.List listRequest = service.tasks().list("#default");
listRequest.setFields("items/title");
List<Task> tasks = listRequest.execute().getItems();
...

What do I need from OAuth 2.0 in this simple Android app case?

The Story: I am making an Android app that allows a user to purchase a subscription, and does not require the user to have an account or login. I want to check whether or not a user has purchased a subscription, and the Google Play Android Developer API seems to provide this service.
The Problem (TL;DR): Should I use OAuth as a "web application", "installed application", "service application", or none of the above?
The Problem: To get started with this, I am told:
Access to the Google Play Android Developer API is authenticated using
the OAuth 2.0 Web Server flow. Before you can use the API, you will
need to set up an APIs Console project, create a client ID and
generate a refresh token. -source
Fair enough. There are then setup instructions that go on to say:
On the second page, select web application and set the redirect URI
and Javascript origins.
My application does access the Internet, but it is an installed Android app, not a web application, so I don't have a "redirect URI" or "Javascript origins" to link it to. Additionally, this would require a user to log in, which I do not want and is not necessary in my case (I just want to check whether or not the user has purchased a subscription).
So if instead of a "web application" I try to create an "installed application (Android)", this still requires a user login, to be able to manage the user's resources.
I do not want this. There is a third alternative called a "service account" that does not require a user login:
A Service Account is used when you have a service that wants to handle
its "own" resources (e.g., an App Engine app that manages Compute
Engine resources), as opposed to the resources of an external user
(e.g., the standard OAuth flow). Using a Service Account the app will
be the owner of the resources... If you use a Service Account, you will only get data about the service's purchases. -source
I'm not sure if that is what I want in my case...
Finally, there is also this option:
The simplest flow is one where no end-user authorization is needed.
You still need to identify your client application using the API key. -source
This seems perfect! However, I was told initially that to use the Google Play Android Developer API I need to authenticate with OAuth 2.0, and this does not use a client ID which I was initially told that I specifically need.
There are at least 2 problems with what you are trying to achieve here:
As you would be handling the server response in your Android application, you would have something like this in your code:
if (isSubscriptionValid())
Somebody could tamper with your application's APK on his device (which is very easy) and simply replace that check with:
if (true)
The attacker would then have access to your content without ever being subscribed.
As calls to the API have to be authorized by your developer account and being personally logged in on each users device is obviously no option, you would have to go for Service Accounts, as you've already figured out correctly.
These however are only meant for server-to-server interactions, as otherwise it would require you to store your private key on everybodys device and as it is not possible to store data securely on an Android device, you wouldn't meet this requirement:
The private key must be stored and managed securely.
Google recommends you to have a backend server to do this kind of checks. So you can decide if a subscription is valid before handing over content to the client and other things:
The API is designed to be used from your backend servers as a way of securely managing subscriptions, as well as extending and integrating subscriptions with other services.
If you do not have a backend server available, you have to rely on In-app Billing Notifications.

Android AccountManager - Add New Account

I'm using the Android AccountManager to authenticate a users google account for access to Google Drive. However, I want to allow the user to access Drive accounts that are not on their phone. For example, I have a work google account that is not on my phone, but I would like to use in my app. Is there a way to allow users to authenticate accounts that are not necessarily stored in the AccountManager?
Thanks
Perhaps you could add another Google account to phone and allow user to select which account to use. If this option doesn't satisfy you and you really want to authenticate to Google Drive accounts not on phone you must then use OAuth.
If you also really, but really, want to use AccountManager facility in your app you have to make an account type that behaves like the following:
When you create a new account you open a browser widget and perform an OAuth cycle with Google Drive, then store the token in secure store
When you request a token via getAuthToken() it either releases you the stored token or triggers a token refresh cycle using OAuth
This has its security drawbacks: better perform OAuth cycle without AccountManager and store tokens in app memory

Categories

Resources