In the Google documentation for OAuth2, building a GoogleCredential with an auth token is described here:
Credential and Credential Store
In particular this code snippet is offered:
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Plus plus = Plus.builder(new NetHttpTransport(), new JacksonFactory())
.setApplicationName("Google-PlusSample/1.0")
.setHttpRequestInitializer(credential)
.build()
When I try to build a GoogleCredential in this way I am tersely informed:
Please use the Builder and call setJsonFactory, setTransport and setClientSecrets
in the message field of an exception. I downloaded the libraries for this last week so I am not sure what is happening. Is the documentation simply outdated, and if so, what method has replaced this one as best practice for building from an existing auth token and refresh token?
Incidentally, the reason using the Builder was not an option was that there WAS no client secret provided by the Google application console; it says that they are no longer provided for Android apps and the like. setClientSecrets(...), therefore, couldn't be called.
I met this problem recently and figured out the solution for my case.
Here is the running conditions: the program is running on Android 4.0 and does not use Google Drive SDK, because it could not allow our program to read the files on Google Drive which are not created by our program. I use the com.google.api.services.drive.* and com.google.api.client.googleapis.auth.oauth2.* Java libraries.
the code arose this problem is like below :
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
httpTransport, jsonFactory, CLIENT_ID, CLIENT_SECRET, Arrays.asList(DriveScopes.DRIVE))
.setAccessType("offline")
.setApprovalPrompt("auto").build();
GoogleTokenResponse response = null;
try {
response = flow.newTokenRequest(authorizationCode).setRedirectUri(REDIRECT_URI).execute();
} catch (IOException e) {
e.printStackTrace();
}
GoogleCredential credential = new GoogleCredential().setFromTokenResponse(response);
but above code worked well if the parameter to setAccessType("online");
and the resolution for this issue depends on what kind of accessType you want.
if you want "onLine" as the accessType, then use setFromTokenResponse()should be ok.
if you want "offline" as the accessType, then you need to use blow code :
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setClientSecrets(CLIENT_ID, CLIENT_SECRET)
.build()
.setFromTokenResponse(response);
One thing need to be mentioned is that you need to keep the accessType setting to be consistent for GoogleTokenResponse and GoogleAuthorizationCodeFlow creation.
You need to set up your Android app key in Google Dev Console.
Choose your project, select API & Auth, then click Credentials
Create new client id (though it has other client ids)
Select installed app -> android
Fill in your package name and SHA1 correctly
Create new Key (though it has other client keys)
Select Android key
Fill in the SHA1;packageName like this: 45:B5:E4:6F:36:AD:0A:98:94:B4:02:66:2B:12:17:F2:56:26:A0:E0;com.example
Your problem will be automatically solved. Be sure to create client id and key with both your debug keystore and release keystore.
Check the Android quickstart sample in the Google Drive SDK documentation for step-by-step instructions to correctly setup GoogleCredential:
https://developers.google.com/drive/quickstart-android
Related
I followed the official document for implementing sing-in with Google. It was not difficult, but the official document stopped at getting the basic information such as name or e-mail. I need to get birthday.
So, when creating the option, I added this:
.requestScopes(Scope("https://www.googleapis.com/auth/user.birthday.read"))
Since the official document stopped there I could not get how to get birthday. So I have searched the web and found a method using a plain HTTP GET request to https://people.googleapis.com/v1/people/me. (Or is this covered by the Google Play Services API?)
But the method needs the access token, which was not included in the GoogleSignInAccount which was returned after signing in. There was an example for getting the access ID by creating an OAuth server ID and then use getServerAuthCode() to get the access token, but that is complicated and it is not done on a server, it is done inside the app, right after singing in.
In this case, do I have to create an OAuth server ID, and use the access token to get the birthday?
After a lot of Google searching, I have found this example. Before finding this, I found an example of using Plus.API, but this answer said it was deprecated. The funny thing is, now even this answer is outdated and does not work. I had to search a lot more to fix it. If only Google's sign-in document page included an example of getting additional data other than the default fields, all these miseries could have been avoided.
Anyways, this is the code that worked for me.
First, sign in with Google according to the official documentation.
Then,
val HTTP_TRANSPORT = AndroidHttp.newCompatibleTransport();
val JSON_FACTORY = JacksonFactory.getDefaultInstance();
inner class DamnTask(var email:String) :AsyncTask<Unit,Unit,Unit>()
{
override fun doInBackground(vararg params: Unit?)
{
var credential = GoogleAccountCredential.usingOAuth2(context,
Collections.singletonList(Scopes.PROFILE));
credential.selectedAccount = Account(email, "com.google");
var service = People.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName("MyApp")
.build();
var me = service.people().get("people/me")
.setRequestMaskIncludeField("person.Birthdays")
.execute();
Log.d("damn", "Birthday " +me.birthdays.toString());
}
}
To make it work, I had to add the following libraries to the dependencies.
compile 'com.google.api-client:google-api-client:1.23.0'
compile 'com.google.api-client:google-api-client-android:1.22.0'
compile 'com.google.apis:google-api-services-people:v1-rev4-1.22.0'
Also, I had to enable the "People" API in the project's settings. This needs to be done in the Google's web console. I forgot the URL, but if you do not enable it, the exception message will show the exact URL to enable it.
You can find more field names here.
I implemented in-app billing in my mobile app. It uses auto renewing subscription. I want to check the subscription expiry date through the app. I found this link to check the subscription details : Purchases.subscriptions:get
The documentation shows that some authorization needed. I have tried but I am not able to get the result. I got client secret.json file but it does not contain client secret Id. So please help me to get the subscription expiry date.
My answer is late, but maybe it helps somebody else. See my answer here: Server side authorization with Google Play Developer API?
And these are the steps when you would do it manually:
1.) Create credentials in your OAuth2 configuration in Google API with the following link: https://developers.google.com/mobile/add?platform=android&cntapi=signin&cnturl=https:%2F%2Fdevelopers.google.com%2Fidentity%2Fsign-in%2Fandroid%2Fsign-in%3Fconfigured%3Dtrue&cntlbl=Continue%20Adding%20Sign-In
2.) Go to your Developer API console. If you have done the first step correct, the result should look like something like that:
3.) Go to Google Play Developer Console -> All Apps -> Settings -> API Access and link the project you defined in Developer API Console (step 2, black bar top left). (If you can't link your project in Google Play, because you didn't find some, you used different google accounts for Google Developer API and Google Play Developer Console.)
4.) Invoke the following link with your callback url:
https://accounts.google.com/o/oauth2/auth?scope=https://www.googleapis.com/auth/androidpublisher&response_type=code&access_type=offline&redirect_uri=http://www.yourdomain.com/oauth2callback&client_id=[WEBAPPLICATION_ID_FROM_ABOVE.apps.googleusercontent.com]
My /oauth2callback script is a simple php script. (This is not production safe. It's just for illustration):
oauth2callback.php
<?php
print_r($_REQUEST);
?>
5.) After you invoked the url from above, you will be asked for your google account and allowance to access the api. After you confirmed, you will be redirected to your callback url and get a result which looks like this:
4/vPv_eYX-az6YucwdpBzATpJEu8129gN9aYsUIMI3qgQ
6.)ยน Get an oauth2 access token and refresh token by invoking a POST request to https://accounts.google.com/o/oauth2/token with the following headers:
grant_type=authorization_code
code=4/vPv_eYX-az6YucwdpBzATpJEu8129gN9aYsUIMI3qgQ
client_id=[YOUR_WEBAPPLICATIONID.apps.googleusercontent.com]
client_secret=[YOUR CLIENT SECRET OF THIS WEBAPPLICATION ID]
redirect_uri=http://www.yourdomain.com/oauth2callback
(The client secret can be found when you click on the Web Client ID of the OAuth2 client IDs in the Developer Console from step 2)
The result will look like this:
{
"access_token": "ya29.GdsCBbnM584k3SUzoxDgIdaY26pEM_p_AdhkIkFq3tsai8U7x8DuFKq3WEF7KquxkdLO89KHpuUFdJOgkhpGbGyDfxkD32bK1ncnsu2IkA0e_5ZayOEr86u4A1IN",
"expires_in": 3600,
"refresh_token": "1/U5HF1m0nHQwZaF2-X35f_xyFaSOofdw3SEubnkkYUQ0",
"token_type": "Bearer"
}
7.) The access token is needed to invoke the Google Developer API requests, for example: https://www.googleapis.com/androidpublisher/v2/applications/[packageName]/purchases/subscriptions/subscriptionId/tokens/[purchaseToken]?access_token=ya29.GdsCBbnM584k3SUzoxDgIdaY26pEM_p_AdhkIkFq3tsai8U7x8DuFKq3WEF7KquxkdLO89KHpuUFdJOgkhpGbGyDfxkD32bK1ncnsu2IkA0e_5ZayOEr86u4A1IN
8.) The access token expires very quickly. With the refresh you can fetch new access tokens by using the following url and headers:
POST https://accounts.google.com/o/oauth2/token
grant_type=refresh_token
client_secret=[YOUR CLIENT SECRET]
refresh_token=1/U5HF1m0nHQwZaF2-X35f_xyFaSOofdw3SEubnkkYUQ0
client_id=[YOUR_WEBAPPLICATION_ID.apps.googleusercontent.com]
Result:
{
"access_token": "ya29.GlsCBZuN7hYJILi5VaVggIsCIb1_5feGvcjvQFmJRnPYXsnhsi_w3Md87tQwGd_WXmifo4s5739c3IU5INPmby8q64k0LdDFkO2JpNRG13K9sizvU1Sc-3cWzbf8",
"expires_in": 3600,
"token_type": "Bearer"
}
HINT (1): If you don't get a refresh token from the request of step 6, attach the following query params to your request: prompt=consent
I also struggled a bit with this, but at least I found what needs to be done.
Google provides an android library google-api-services-androidpublishe that can be used to access the Purchase.Subscription.Get API.
implementation 'com.google.apis:google-api-services-androidpublisher:v3-rev74-1.25.0'
Here is a Kotlin code example:
import com.google.api.client.http.apache.ApacheHttpTransport
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.androidpublisher.AndroidPublisher
...
fun retrieveSubscriptionInfo(sku: String) {
val packageName = ""
val sku = ""
val purchasedSubscriptionToken = ""
val apacheHttpTransport = ApacheHttpTransport()
val jacksonJsonFactory = JacksonFactory.getDefaultInstance()
val AndroidPublisher.Builder(apbacheHttpTransport, jacksonJsonFactory, null).build()
val request = publisher.Purchases().subscriptions().get(packageName, sku, purchasedSubscriptionToken);
return request.execute()
}
Note that before the above code is executed you must first Authorize the AndroidPublisher. You would have to request the users permission in order to be able to query the Purchase.Subscription.Get
Hope that this is helpful.
Google's documentation here is horrible, no clear examples of implementation. I finally found the answer after a LOT of Googling thanks to #Sabeeh's answer here and lots of other snippets spread out online.
1. First step is to add the dependencies:
implementation "com.google.apis:google-api-services-androidpublisher:v3-rev142-1.25.0" // Update based on latest release
implementation "com.google.auth:google-auth-library-oauth2-http:1.12.1" // Update based on latest release
2. Follow these steps to link the Google Play Console with Google Play Developer API (choose the "Use a service account", not "Use OAuth clients" and follow until "Additional information").
3. Download the services JSON file from your Google Cloud service account (click on the account that you set up in the previous step). You can find/create this file under the "Manage Keys" action or the "Keys" tab. Add the exported JSON file in your assets folder in Android
4. Then you can call the Google Play Developer API to query subscriptions like this (important to call from a Thread, didn't work from the UI thread, not sure why):
new Thread(() -> {
InputStream inputStream = context.getAssets().open("service_account_google_play.json"); // JSON file from step 3
GoogleCredentials credentials = GoogleCredentials.fromStream(inputStream)
.createScoped(AndroidPublisherScopes.ANDROIDPUBLISHER);
AndroidPublisher androidPublisher = new AndroidPublisher(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
new HttpCredentialsAdapter(credentials)
);
SubscriptionPurchase purchase = androidPublisher.purchases().subscriptions().get(
context.getPackageName(), subscriptionId, purchaseToken
).execute();
// Do with the purchase object what you want here
}).start();
At the risk of being overly descriptive, the subscriptionId is the ID of your subscription in the Play Console (e.g. subscription_monthly or whatever you called it), and the purchaseToken is the token you get from the Purchase token after querying the BillingClient (querying subscriptions is explained in detail here).
Let me know if anything is unclear or doesn't work yet. This took me 6 hours to figure out and I'm happy to save others that pain.
So, I want to use Plus.me or lets say userinfo.profile scope with Google Cloud Endpoints with python as a backend.
Server Configuration:
#endpoints.api( name='sidebar', version='v1',# auth=AUTH_CONFIG,
allowed_client_ids=[WEB_CLIENT_ID,
ANDROID_CLIENT_ID,
endpoints.API_EXPLORER_CLIENT_ID],
audiences=[ANDROID_AUDIENCE],
scopes=[endpoints.EMAIL_SCOPE, "https://www.googleapis.com/auth/plus.me"])
class Sidebar(remote.Service):
Does anybody have an idea how to send the appropriate scope from android client?
All I know about setting up the android client is:
Android Client Configuration:
String WEB_CLIENT_ID = "xxxxxxxxxx-bbbbbbbbbbbbbbbbbbbbbbbbb.apps.googleusercontent.com";
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience(
mContext, AUDIENCE);
credential.setSelectedAccountName(accountName);
Sidebar.Builder api = new Sidebar.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), credential);
api.setRootUrl(mRootUrl);
So, using this configuration doesnt work. get_current_user() returns None and there is an error in the server logs:
"Oauth framework user didn't match oauth token user."
If I remove the "https://www.googleapis.com/auth/plus.me" scope then the get_current_user() returns the user.
This thing is expected as I am not supplying a token for this scope or the scope itself from android client. So, How can I get this working? What changes do I need to do in the android client?
I've set up a project in Google API's for use with BigQuery. I've generated Client ID credentials and am using the BigQuery client for Android.
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(getApplicationContext(), Collections.singleton(BigqueryScopes.BIGQUERY));
...Display User Picker...
credential.setSelectedAccountName("account.selected.by.user#gmail.com");
Bigquery bigQuery = new Bigquery.Builder(AndroidHttp.newCompatibleTransport(), GsonFactory.getDefaultInstance(), credential)
.setApplicationName("My-App/1.0").build()
...use bigQuery to run a job or do whatever...
We can successfully connect to BigQuery, but only for Project Member Accounts as seen at https://console.developers.google.com/project/apps~my-project-name/permissions. Use of other accounts results in 403 Access Denied JSON errors from the BigQuery Client.
The app is to be deployed to any number of users where we do not know their accounts ahead of time. This workflow doesn't support that, unless I'm missing some trick.
It's starting to smell like we need to set up an App Engine app, use GoogleAccountCredential.usingAudience, and set up web services as a pass-through to BigQuery.
Any ideas or thoughts will be appreciated with up votes.
This can be accomplished by using a Service Account, which is typically geared for server-to-server communication.
Follow the instructions at https://developers.google.com/bigquery/docs/authorization#service-accounts-server.
GoogleCredential credential = new GoogleCredential.Builder().setTransport(TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId("XXXXXXX#developer.gserviceaccount.com")
.setServiceAccountScopes(SCOPE)
.setServiceAccountPrivateKeyFromP12File(new File("my_file.p12"))
.build();
bigquery = new Bigquery.Builder(TRANSPORT, JSON_FACTORY, credential)
.setApplicationName("BigQuery-Service-Accounts/0.1")
.setHttpRequestInitializer(credential).build();
Save the p12 file to your assets folder, and generate a physical File object out of the InputStream from the p12 file in assets.
In this way, you can have your Android application act as a non-user-centric client of BigQuery. Awesome!
I'm creating an Android app that uses a regular Google Drive account as an application-owned account as described, with incredible brevity, here:
Use regular Google accounts as application-owned accounts
Unfortunately all the descriptions of how to do anything on Drive refer to a client secret, and as described here:
Google APIs Console - missing client secret
that doesn't really exist any more for installed apps. I managed to get an OAuth2 token, at incredible pains, by building an actual web page. From this I obtained an access token and a refresh token, as described in the first link. I want to make a GoogleCredential object with these, and as far as I can see this should work like this:
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(jsonFactory)
.setClientSecrets(
new GoogleClientSecrets().setInstalled(
new Details()
.setAuthUri(AUTH_URI)
.setClientId(CLIENT_ID)
//.setClientSecret("but I haven't got one!")
.setRedirectUris(REDIRECT_URIS)
)
)
.build()
.setAccessToken(ACCESS_TOKEN) //defined above
.setRefreshToken(REFRESH_TOKEN); //defined above
Drive.Builder driveBuilder = new Drive.Builder(httpTransport, jsonFactory, null);
driveBuilder.setHttpRequestInitializer(credential);
I've put in the setClientSecret(...) call as commented out, because I don't have a client secret for the installed application.
Basically, I can't make this credential, and every suggestion about making credentials I have seen falls into one of these three categories:
designed for users to log into their own account. Not acceptable; I want one Drive account for all users BUT a regular account, not a service account (the client specified).
based on AccountManager or some other system of authentication. Not acceptable for the same reason.
throws up its hands in horror at the egregiousness of the Google API. Suggests making the request the old-fashioned way.
By the way, without the client secret set, I get a NullPointerException in Preconditions.class. It doesn't tell me a lot about this. I also tried to make a GoogleCredential directly by subclassing it, and it instructed me to use a GoogleCredential.Builder, which is why I find myself here.
What do I do to get round this client secret problem?
The answer seems to be to uncomment the line. The GoogleCredential.Builder succeeds in constructing a credential object as long as something is in the client secret field. But does it actually get the drive? Stick around.
EDIT: no, I'm getting an "invalid_client" error. This is intensely frustrating.
Here's a link to a question that provides its own answer in an edit, using the help of the accepted answer:
How setup Google Drive Credentials within Android App?
In short, the main thing is that you need to get a simple api access key from the developer api console. If you can't find it, then drive api access probably needs to be set.