GoogleTokenResponse does not contain ID token, intermittently - android

Intermittently, GoogleTokenResponse.parseIdToken() has an NullPointerExpection because the token response does not contain an ID token. Without changing any code, sometimes there is an ID token, and sometimes there isn't. Note that GoogleTokenResponse.getAccessToken() always works.
With no change to any code whatsoever, the ID token will be missing from one minute to the next, even if the access token is always available.
How can I debug this? Where to look?
I get the server auth code using this in an Android client using Google Play Games API:
PendingResult<Games.GetServerAuthCodeResult> pendingResult =
Games.getGamesServerAuthCode(mGoogleApiClient, Constants.web_client_ID);
pendingResult.setResultCallback(new ResultCallback<Games.GetServerAuthCodeResult>() {
#Override
public void onResult(Games.GetServerAuthCodeResult getTokenResult) {
sendToServer(getTokenResult.getCode());
}
});
On the server side (Google Cloud Endpoints), I exchange the code for a token using this code:
try {
tokenResponse = new GoogleAuthorizationCodeTokenRequest(
transport,mJFactory,
"https://www.googleapis.com/oauth2/v4/token",
web_client_ID,web_client_secret,
authCode,
"")
.execute();
} ...
String accessToken = tokenResponse.getAccessToken();
GoogleIdToken idToken = null;
try {
idToken = tokenResponse.parseIdToken(); //-- FAILES HERE INTERMITTENTLY!!!!
} ...

Since it seems that Play Games Services does not guarantee an ID token using Games.getGamesServerAuthCode one should follow the directions in this post and get the ID token as it recommends:
Once you have the access token, you can now call
www.googleapis.com/games/v1/applications//verify/ using that
access token. Pass the auth token in a header as follows:
“Authorization: OAuth ” The response value will contain
the player ID for the user.
See this for a full example.

Related

Agora.io token issue Android

We are generating agora token from token server provided by agora but the token expires immmediately after the generation it throws DYNAMIC-KEY-EXPIRY with error code 109 and i have tried using onRequestToken() call back method provided in SDK below is the code snippet
override fun onRequestToken() {
//this will be called when the token expires
//so generate new token and renew the existing token
runOnUiThread {
regenerateToken()
}
}
and getting another token from server and renewing the token using mRtcEngine.renewToken(token)
we have contacted agora team regarding this but unable to find the issue the token generated works sometimes,we have two account one is test account and other is main account when we use the one credentials it works for a day or so and after we need to change the credentials for every 2 days
and the new generated token is expiring just after the generation
So if any one faced the same issue and any help regarding this is appreciated
When you generate the token, you can set the token expiration time in the request parameter. Here is a guide for token generation. https://docs.agora.io/en/Video/token_server?platform=Android
If you have set the expiration time but still having the issue, please let me know.
We are generating the token using agora token server and the time expiration is 3600
const { RtcTokenBuilder, RtmTokenBuilder, RtcRole, RtmRole } = require('agora-access-token');
const role = RtcRole.PUBLISHER;
const expirationTimeInSeconds = 3600
const currentTimestamp = Math.floor(Date.now() / 1000)
const privilegeExpiredTs = currentTimestamp + expirationTimeInSeconds
const generateAuthTokenToInititateCall = async (channel, cb) => {
const token = RtcTokenBuilder.buildTokenWithUid(appID, appCertificate, channel, 0, role, privilegeExpiredTs);
cb({ "token": token, "channel": channel })
}
this is the node js code snippet
Is anyone able to resolve this? This is occurring despite setting the expiration to 3600 seconds (1 hour from current time).
let me make this token generation for you very simple and also the later process.
*firstly how to generate token?
you need a backend server which provides you with token when you provide the server a channel name. no where you will be told that this backend server is not necessary. rather you can copy - paste the token generation code directly into the app in a form of directory.
*where is this token generation code?
https://github.com/AgoraIO/Tools/tree/master/DynamicKey/AgoraDynamicKey/java/src
this code is available for flutter also research a little bit.
*how to generate token using it?
once you have the all java files in your project you can use them to generate the token. here you can see a variable expirationTimeInSeconds, that will be the total time you want token to be active. rest all work leave it to the code it will do it for you.
package io.agora.sample;
import io.agora.media.RtcTokenBuilder;
import io.agora.media.RtcTokenBuilder.Role;
public class RtcTokenBuilderSample {
static String appId = "970CA35de60c44645bbae8a215061b33";
static String appCertificate = "5CFd2fd1755d40ecb72977518be15d3b";
static String channelName = "7d72365eb983485397e3e3f9d460bdda";
static String userAccount = "2082341273";
static int uid = 2082341273;
static int expirationTimeInSeconds = 3600;
public static void main(String[] args) throws Exception {
RtcTokenBuilder token = new RtcTokenBuilder();
int timestamp = (int)(System.currentTimeMillis() / 1000 + expirationTimeInSeconds);
String result = token.buildTokenWithUserAccount(appId, appCertificate,
channelName, userAccount, Role.Role_Publisher, timestamp);
System.out.println(result);
result = token.buildTokenWithUid(appId, appCertificate,
channelName, uid, Role.Role_Publisher, timestamp);
System.out.println(result);
}
}
result here is the generated token which is valid for 3600 seconds, that is 1hour.

Firebase Auth Custom claims not propagating to client

I have a user with UID 1 where the custom claims are set as,
frompos=true
I am setting new custom claims to this user from the ADMIN SDK for java the following way:
Map<String,Object> claims = new HashMap<>();
claims.put("frompos",false);
FirebaseAuth.getInstance().setCustomUserClaimsAsync("1", claims).get(10000,
TimeUnit.MILLISECONDS);
I print the claims on the server side to check if the claims are set:
UserRecord user = FirebaseAuth.getInstance().getUserAsync("1").get(10000,
TimeUnit.MILLISECONDS);
LOG.debug("user new claims " + user.getCustomClaims());
The result as expected is that the claims get set:
user new claims {frompos=false}
Now on the android sdk side, I have the user already logged in so I am refreshing the ID token manually to propagate the claims as the docs say
(https://firebase.google.com/docs/auth/admin/custom-claims)
FirebaseAuth.getInstance().getCurrentUser().getIdToken(true).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
#Override
public void onComplete(#NonNull Task<GetTokenResult> task) {
if(task.isSuccessful()){
Log.d("FragmentCreate","Success refreshing token "+(FirebaseAuth.getInstance().getCurrentUser()==null));
Log.d("FragmentCreate","New token "+task.getResult().getToken());
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("FragmentCreate","Failure refreshing token "+(FirebaseAuth.getInstance().getCurrentUser()==null)+" "+e.toString());
}
});
Now I use the printed Id Token printed here and verify it on server side and print the claims from it
FirebaseToken tokenTest = FirebaseAuth.getInstance(ahmedabadRepoApp).verifyIdTokenAsync(token).get(10000,TimeUnit.MILLISECONDS);
LOG.debug("Token claims are "+tokenTest.getClaims());
But the claims printed here are:
{"aud":"ahmedabadrepo","auth_time":1514724115,"email_verified":false,"exp":1514730425,"iat":1514726825,"iss":"https://securetoken.google.com/ahmedabadrepo","sub":"1","frompos":true,"user_id":"1","firebase":{"identities":{},"sign_in_provider":"custom"}}
Thus the frompos value did not propagate to the client sdk even though I did refresh the Id token manually.
I was having the same issue in angular - I set the claim using the Admin SDK on the server, but then they would not be in the user on the client.
Using the following I can see the claims in the payload:
this.firebaseAuth.auth.currentUser.getIdToken().then(idToken => {
const payload = JSON.parse(this.b64DecodeUnicode(idToken.split('.')[1]))
console.log(payload);
}
)
b64DecodeUnicode(str) {
return decodeURIComponent(atob(str).replace(/(.)/g, function (m, p) {
var code = p.charCodeAt(0).toString(16).toUpperCase();
if (code.length < 2) {
code = '0' + code;
}
return '%' + code;
}));
}
Here is a good write up of this where I copied the above:
At the moment the client-side code must parse and decode the user’s ID
token to extract the claims embedded within. In the future, the
Firebase client SDKs are likely to provide a simpler API for this use
case.
Relevant info from Firebase Docs:
Custom claims can only be retrieved through the user's ID token.
Access to these claims may be necessary to modify the client UI based
on the user's role or access level. However, backend access should
always be enforced through the ID token after validating it and
parsing its claims. Custom claims should not be sent directly to the
backend, as they can't be trusted outside of the token.
Once the latest claims have propagated to a user's ID token, you can
get these claims by retrieving the ID token first and then parsing its
payload (base64 decoded):
// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
firebase.auth().currentUser.getIdToken()
.then((idToken) => {
// Parse the ID token.
const payload = JSON.parse(b64DecodeUnicode(idToken.split('.')[1]));
// Confirm the user is an Admin.
if (!!payload['admin']) {
showAdminUI();
}
})
.catch((error) => {
console.log(error);
This might help: https://stackoverflow.com/a/38284384/9797228
firebase.auth().currentUser.getIdToken(true)
The client sdk is caching the old token (old claims).
You should add a mechanism to refresh it after changing the claims (eg. push notification) or just wait for the old token to expires or user to lougout and login again.
It's explained here https://youtu.be/3hj_r_N0qMs?t=719
Edit
You can force the sdk to refresh the token using firebase.auth().currentUser.getIdToken(true)

Dropbox API v2, InvalidAccessTokenException at attempt to load file after auth

I've got InvalidAccessTokenException at any attempt to upload file to Dropbox.
For authorization I use next algorithm:
Auth.startOAuth2Authentication(context, APP_KEY);
//granting access
//previous activity is reopened
String accessToken = Auth.getOAuth2Token();
DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder(CLIENT_ID)
.withHttpRequestor(OkHttp3Requestor.INSTANCE)
.build();
DbxClientV2 client = new DbxClientV2(requestConfig, accessToken);
client.files().uploadBuilder(ADDRESS_IN_DROPBOX)
.uploadAndFinish(inputStream);
which returns InvalidAccessTokenException.
Maybe I'm doing something wrong.
Also maybe Auth.getOAuth2Token() return auth code instead of access code (which is a bit obvious from method name), but how to get access code when auth code is known?
PS I've used Android project as example - https://github.com/dropbox/dropbox-sdk-java/tree/master/examples/android and performed auth exactly as in example.
EDIT-----------------------------------------------------------------------------------------------
Problem occurs ONLY after revoking access to the app from account settings at Dropbox webpage.
You noted:
Problem occurs ONLY after revoking access to the app from account settings at Dropbox webpage.
In that case, this InvalidAccessTokenException is expected. If a user revokes the token, e.g., by unlinking the app via https://www.dropbox.com/account/security , any further API calls attempted with that token will fail with this exception. You should have your code catch this exception and prompt the user to re-link the app if they want to continue using it.
I found problem in my code.
I've used DropboxClientFactory like in example from Dropbox where client init looks like this
public static void init(String accessToken) {
if (sDbxClient == null)) {
DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder("ID")
.withHttpRequestor(OkHttp3Requestor.INSTANCE)
.build();
sDbxClient = new DbxClientV2(requestConfig, accessToken);
}
}
And when access token was revoked - init process was not called because sDbxClient is already initialized.
So I added variable accessCode to DropboxClientFactory to hold used access token and now init looks like this
public static void init(String accessToken) {
if (sDbxClient == null || !accessToken.equals(accessCode)) {
DbxRequestConfig requestConfig = DbxRequestConfig.newBuilder("ID")
.withHttpRequestor(OkHttp3Requestor.INSTANCE)
.build();
sDbxClient = new DbxClientV2(requestConfig, accessToken);
}
}

Google Plus Single Sign On Server Flow - Google_AuthException Error fetching OAuth2 access token, message: 'invalid_grant'

UPDATE 27th January 2013
I have now resolved this, Please check the accepted answer.
I am having trouble to get my refresh token and my access token when using the server side flow between my Android Application and my PHP server.
So I have managed to get my One Time Code by using the below:
AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
Bundle appActivities = new Bundle();
appActivities.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES,
"http://schemas.google.com/AddActivity");
String scopes = "oauth2:server:client_id:" + SERVER_CLIENT_ID +
":api_scope:" + SCOPE_STRING;
try {
code = GoogleAuthUtil.getToken(
OneTimeCodeActivity.this, // Context context
mPlusClient.getAccountName(), // String accountName
scopes, // String scope
appActivities // Bundle bundle
);
} catch (IOException transientEx) {
// network or server error, the call is expected to succeed if you try again later.
// Don't attempt to call again immediately - the request is likely to
// fail, you'll hit quotas or back-off.
System.out.println(transientEx.printStactTrace());
return "Error";
} catch (UserRecoverableAuthException e) {
// Recover
code = null;
System.out.println(e.printStackTrace());
OneTimeCodeActivity.this.startActivityForResult(e.getIntent(), REQUEST_AUTHORIZATION);
} catch (GoogleAuthException authEx) {
// Failure. The call is not expected to ever succeed so it should not be
// retried.
System.out.println(authEx.printStackTrace());
return "Error";
} catch (Exception e) {
System.out.println(authEx.printStackTrace());
}
}
Which will then store the token in the variable "code" and I call up the async task as
task.execute();
The code above will always bring up a popup message and throw UserRecoverableAuthException Need Permission that requires the user to grant offline access, which means the above will need to be called twice to retrieve the code and store it in "code"
I am now trying to send this across to my server which is implemented in PHP.
I have used the quick start https://developers.google.com/+/quickstart/php and managed to get that working.
In here, there is a sample signin.php
In here and according to the documentation this already implements a One Time Authorisation Server Side Flow.
So now my problem is sending this One Time Code to the server.
I used the photohunt Android Auth example for this located here.
https://github.com/googleplus/gplus-photohunt-client-android/blob/master/src/com/google/plus/samples/photohunt/auth/AuthUtil.java
I used the "authorization" method of the code and called up signin.php/connect through a post method shown below
$app->post('/connect', function (Request $request) use ($app, $client) {
$token = $app['session']->get('token');
if (empty($token)) {
// Ensure that this is no request forgery going on, and that the user
// sending us this connect request is the user that was supposed to.
if ($request->get('state') != ($app['session']->get('state'))) {
return new Response('Invalid state parameter', 401);
}
// Normally the state would be a one-time use token, however in our
// simple case, we want a user to be able to connect and disconnect
// without reloading the page. Thus, for demonstration, we don't
// implement this best practice.
//$app['session']->set('state', '');
$code = $request->getContent();
// Exchange the OAuth 2.0 authorization code for user credentials.
$client->authenticate($code);
$token = json_decode($client->getAccessToken());
// You can read the Google user ID in the ID token.
// "sub" represents the ID token subscriber which in our case
// is the user ID. This sample does not use the user ID.
$attributes = $client->verifyIdToken($token->id_token, CLIENT_ID)
->getAttributes();
$gplus_id = $attributes["payload"]["sub"];
// Store the token in the session for later use.
$app['session']->set('token', json_encode($token));
$response = 'Successfully connected with token: ' . print_r($token, true);
}
return new Response($response, 200);
});
Now when I send the code using the above implementation, I get an 500 messages that says the below
Google_AuthException Error fetching OAuth2 access token, message: 'invalid_grant'
in ../vendor/google/google-api-php-client/src/auth/Google_OAuth2.php line 115
at Google_OAuth2->authenticate(array('scope' => 'https://www.googleapis.com/auth/plus.login'), '{ "token":"xxxxxxxx"}') in ../vendor/google/google-api-php-client/src/Google_Client.php line 131
at Google_Client->authenticate('{ "token":"xxxxxxx"}') in ../signin.php line 99
at {closure}(object(Request))
at call_user_func_array(object(Closure), array(object(Request))) in ../vendor/symfony/http-kernel/Symfony/Component/HttpKernel/HttpKernel.php line 117
at HttpKernel->handleRaw(object(Request), '1') in ../vendor/symfony/http-kernel/Symfony/Component/HttpKernel/HttpKernel.php line 61
at HttpKernel->handle(object(Request), '1', true) in ../vendor/silex/silex/src/Silex/Application.php line 504
at Application->handle(object(Request)) in ../vendor/silex/silex/src/Silex/Application.php line 481
at Application->run() in ../signin.php line 139
Funny enough I have had to worked once where I did receive a 200, but I cannot recreate it.
So I know I have definitely got the implementation wrong, but I have no clue on how to send it and get my refresh token. I can't find anywhere on the web that explains this. Is someone able to help me please.
UPDATE 16 Jan 2014
Using https://www.googleapis.com/oauth2/v1/tokeninfo?access_token= I can see that the token being produced from getToken is valid and is indeed valid for 1 hour.
I can confirm the json formation is correct by changing the way I am inputting into the Post request and if I don't do it properly I get a total failure.
Now I am going deeper into the php and look at this section Google_OAuth2.php line 115 where it is breaking it is throwing a Google_AuthException. The code is below and this is provided in the quick starter pack
/**
* #param $service
* #param string|null $code
* #throws Google_AuthException
* #return string
*/
public function authenticate($service, $code = null) {
if (!$code && isset($_GET['code'])) {
$code = $_GET['code'];
}
if ($code) {
// We got here from the redirect from a successful authorization grant, fetch the access token
$request = Google_Client::$io->makeRequest(new Google_HttpRequest(self::OAUTH2_TOKEN_URI, 'POST', array(), array(
'code' => $code,
'grant_type' => 'authorization_code',
'redirect_uri' => $this->redirectUri,
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret
)));
if ($request->getResponseHttpCode() == 200) {
$this->setAccessToken($request->getResponseBody());
$this->token['created'] = time();
return $this->getAccessToken();
} else {
$response = $request->getResponseBody();
$decodedResponse = json_decode($response, true);
if ($decodedResponse != null && $decodedResponse['error']) {
$response = $decodedResponse['error'];
}
throw new Google_AuthException("Error fetching OAuth2 access token, message: '$response'", $request->getResponseHttpCode());
}
}
$authUrl = $this->createAuthUrl($service['scope']);
header('Location: ' . $authUrl);
return true;
}
I edit the code above to make sure the code, the client id and secret were correct and they were. So that is where I am now, I don't think it is scope issues as well as I hard coded it in the client setup and still does not work. Not too sure.
UPDATE 23rd January
OK, I think it is a time issue. I used https://developers.google.com/+/photohunt/android and base my design on the BaseActivity in the Photohunt using the AuthUtil, and I get invalid grant on my server. How do I move the time back on my server in code. I read somewhere I can do time() - 10 somewhere but not sure where...
It sounds like you may be sending the same authorization code multiple times. On Android GoogleAuthUtil.getToken() caches any tokens that it retrieves including authorization codes.
If you ask for a second code without invalidating the previous code, GoogleAuthUtil will return the same code. When you try to exchange a code on your server which has already been exchanged you get the invalid_grant error. My advice would be to invalidate the token immediately after you retrieve it (even if you fail to exchange the code, you are better off getting a new one than retrying with the old one).
code = GoogleAuthUtil.getToken(
OneTimeCodeActivity.this, // Context context
mPlusClient.getAccountName(), // String accountName
scopes, // String scope
appActivities // Bundle bundle
);
GoogleAuthUtil.invalidateToken(
OneTimeCodeActivity.this,
code
);
invalid_grant can be returned for other reasons, but my guess is that caching is causing your problem since you said it worked the first time.
This issue is now resolved. This was due to the implementation on the One Time Code exchange with the server
As specified in the my issue above, I used the photohunt example to do the exchange with my server. The Android code can be found on the below link
https://github.com/googleplus/gplus-photohunt-client-android/blob/master/src/com/google/plus/samples/photohunt/auth/AuthUtil.java
One line 44 it reads this
byte[] postBody = String.format(ACCESS_TOKEN_JSON, sAccessToken).getBytes();
This will only work if on the server side you handle the JSON. I did not.
When calling up $client->authenticate($code); in php, $code had a JSON string and therefore when calling https://accounts.google.com/o/oauth2/token the authorization code was wrong.
So it was easy as I was not sending the code in the right format.
I found this out when digging and testing https://accounts.google.com/o/oauth2/token and created a manual cURL to test the token.
As provided in the Google+ API it was stated that all examples included a One Time Code exchange, but I think the code across all platform are not consistent and one has to double check themselve to make sure everything flows correctly, which was my mistake.

Google Play Android Developer API from C#/.NET service - (400) Bad Request

I'm trying to access a Purchase Status API from my ASP.NET web server using Google APIs .NET Client Library which is a recommended way for using Purchase API v1.1. However, the Authorization page of this API suggests direct web requests to Google's OAuth2 pages instead of using the corresponding client libraries.
OK, I tried both methods with all variations I could imagine and both of them lead to "The remote server returned an error: (400) Bad Request.".
Now what I've done to get to my point. First I've made all steps 1-8 under the Creating an APIs Console project of the Authorization page. Next I generated a refresh token as described there. During refresh token generation I chose the same Google account as I used to publish my Android application (which is in published beta state now).
Next I've created a console C# application for test purposes in Visual Studio (may be console app is the problem?)
and tried to call the Purchase API using this code (found in some Google API examples):
private static void Main(string[] args)
{
var provider =
new WebServerClient(GoogleAuthenticationServer.Description)
{
ClientIdentifier = "91....751.apps.googleusercontent.com",
ClientSecret = "wRT0Kf_b....ow"
};
var auth = new OAuth2Authenticator<WebServerClient>(
provider, GetAuthorization);
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = APP_NAME
});
var request = service.Inapppurchases.Get(
PACKAGE_NAME, PRODUCT_ID, PURCHASE_TOKEN);
var purchaseState = request.Execute();
Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
}
private static IAuthorizationState GetAuthorization(WebServerClient client)
{
IAuthorizationState state =
new AuthorizationState(
new[] {"https://www.googleapis.com/auth/androidpublisher"})
{
RefreshToken = "4/lWX1B3nU0_Ya....gAI"
};
// below is my redirect URI which I used to get a refresh token
// I tried with and without this statement
state.Callback = new Uri("https://XXXXX.com/oauth2callback/");
client.RefreshToken(state); // <-- Here we have (400) Bad request
return state;
}
Then I tried this code to get the access token (I found it here: Google Calendar API - Bad Request (400) Trying To Swap Code For Access Token):
public static string GetAccessToken()
{
var request = WebRequest.Create(
"https://accounts.google.com/o/oauth2/token");
request.Method = "POST";
var postData =
string.Format(
#"code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code",
// refresh token I got from browser
// also tried with Url encoded value
// 4%2FlWX1B3nU0_Yax....gAI
"4/lWX1B3nU0_Yax....gAI",
// ClientID from Google APIs Console
"919....1.apps.googleusercontent.com",
// Client secret from Google APIs Console
"wRT0Kf_bE....w",
// redirect URI from Google APIs Console
// also tried Url encoded value
// https%3A%2F%2FXXXXX.com%2Foauth2callback%2F
"https://XXXXX.com/oauth2callback/");
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
using (var dataStream = request.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
}
try
{
// request.GetResponse() --> (400) Bad request again!
using (var response = request.GetResponse())
{
using (var dataStream = response.GetResponseStream())
{
using (var reader = new StreamReader(dataStream))
{
var responseFromServer = reader.ReadToEnd();
var jsonResponse = JsonConvert.DeserializeObject<OAuth2Response>(responseFromServer);
return jsonResponse.access_token;
}
}
}
}
catch (Exception ex) { var x = ex; }
return null;
}
So, to sum up all my long story:
Is it possible at all to pass OAuth2 authorization using either of methods above from a C# Console Application (without user interaction)?
I've double checked the redirect URI (since I saw a lot of discussed troubles because of it here on stackoverflow) and other parameters like ClientID and ClientSecret. What else I could do wrong in the code above?
Do I need to URL encode a slash in the refresh token (I saw that the first method using client library does it)?
What is the recommended way of achieving my final goal (Purchase API access from ASP.NET web server)?
I'll try to answer your last question. If you access your own data account, you dont need to use client id in oAuth2. Let's use service account to access Google Play API.
Create a service account in Google Developer Console > Your project > APIs and auth > Credentials > Create a new key. You will download a p12 key.
Create a C# project. You can choose console application.
Install google play api library from Google.Apis.androidpublisher. Nuget. You can find other library for dotnet in Google APIs Client Library for .NET
Link google api project with your google play account in API access
Authenticate and try to query information. I'll try with listing all inapp item. You can just change to get purchase's status
String serviceAccountEmail = "your-mail-in-developer-console#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"physical-path-to-your-key\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://www.googleapis.com/auth/androidpublisher" }
}.FromCertificate(certificate));
var service = new AndroidPublisherService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "GooglePlay API Sample",
});
// try catch this function because if you input wrong params ( wrong token) google will return error.
var request = service.Inappproducts.List("your-package-name");
var purchaseState = request.Execute();
// var request = service.Purchases.Products.Get(
//"your-package-name", "your-inapp-item-id", "purchase-token"); get purchase'status
Console.WriteLine(JsonConvert.SerializeObject(purchaseState));
You should do the following in your
private static IAuthorizationState GetAuthorization(WebServerClient client) method:
private IAuthorizationState GetAuthorization(WebServerClient client)
{
IAuthorizationState state = AuthState;
if (state != null)
{
return state;
}
state = new AuthorizationState()
{
RefreshToken = "4/lWX1B3nU0_Ya....gAI",
Callback = new Uri(#"https://XXXXX.com/oauth2callback/")
};
client.RefreshToken(state);
// Store and return the credentials.
HttpContext.Current.Session["AUTH_STATE"] = _state = state;
return state;
}
Let me know if it works for you.
Be aware that we know that the whole OAuth2 flow is awkward today, and we are working to improve it.

Categories

Resources