Find invalid push tokens for ios and android - android

I am developing a mobile application having push notification feature [Android and iOS].I am using node-gcm and node-apn for sending push.
Is there any way to find tokens are invalid or not (iOS/Android registration token) ,so that I can remove them from my database?

This is how I solved it in my project:
[Android]
If you pass array of tokens to node-gcm in response you'll get an array with length equals tokens count. That array contains response for each token - success or error. Based on error you can decide whether to delete token or not:
// This is response from Google
response.results.map((item,index) => {
if (item.error) {
// If Google doesn't recognize token I don't need to keep it anymore
if (item.error === 'NotRegistered') {
failedTokens.push(androidTokens[index]);
} else {
logger.error(`Push notification was not sent because: ${item.error}`);
}
}
});
failedTokens.map(token => {
this.deleteDeviceToken('android', appName, token);
});
[iOS]
I have something similar for iOS. But worth noting that we use HTTP2 APN. So below solution will work for you only if you use HTTP2 for APN too:
// Response from Apple
response.failed.map(failure => {
if (failure.error) {
logger.error(`Error during sending notification: ${JSON.stringify(failure.error)}`);
} else {
// If APN returned HTTP 400 with status BadDeviceToken or HTTP 410 with status Unregistered
// then delete invalid tokens.
if (failure.response.reason === 'BadDeviceToken' || failure.response.reason === 'Unregistered') {
this.deleteDeviceToken('ios', appName, failure.device);
} else {
logger.error(`Push notification was not sent because: ${failure.response.reason}`);
}
}
});

Related

How do I implement JWT with pub sub push

I followed the documentation on pub/sub notifications with the push method here
And I want to have authentication on my call with JWT. I looked at their GitHub example here
app.post('/pubsub/authenticated-push', jsonBodyParser, async (req, res) => {
// Verify that the request originates from the application.
if (req.query.token !== PUBSUB_VERIFICATION_TOKEN) {
res.status(400).send('Invalid request');
return;
}
// Verify that the push request originates from Cloud Pub/Sub.
try {
// Get the Cloud Pub/Sub-generated JWT in the "Authorization" header.
const bearer = req.header('Authorization');
const [, token] = bearer.match(/Bearer (.*)/);
tokens.push(token);
// Verify and decode the JWT.
// Note: For high volume push requests, it would save some network
// overhead if you verify the tokens offline by decoding them using
// Google's Public Cert; caching already seen tokens works best when
// a large volume of messages have prompted a single push server to
// handle them, in which case they would all share the same token for
// a limited time window.
const ticket = await authClient.verifyIdToken({
idToken: token,
audience: 'example.com',
});
const claim = ticket.getPayload();
claims.push(claim);
} catch (e) {
res.status(400).send('Invalid token');
return;
}
// The message is a unicode string encoded in base64.
const message = Buffer.from(req.body.message.data, 'base64').toString(
'utf-8'
);
messages.push(message);
res.status(200).send();
});
But I have some questions.
What is the PUBSUB_VERIFICATION_TOKEN and how do I get it and store it in my environment?
const [, token] = bearer?.match(/Bearer (.*)/); throws the following error
Type 'RegExpMatchArray | null | undefined' must have a 'Symbol.iterator' method that returns an iterator.ts(2488)
Why do they push the claims and tokens in an array if they never check that array in this function for already existing tokens / claims?
I am trying to implement this with a Firebase Cloud Function and this is what I have. Is it even possible to cache the tokens / claims?
//Service account auth client
const authClient = new google.auth.JWT({
email: android_key.client_email,
key: android_key.private_key,
scopes: ["https://www.googleapis.com/auth/androidpublisher"]
});
export const handlePubSub = functions.region('europe-west1').https.onRequest(async (req, res) => {
// What is PUBSUB_VERIFICATION_TOKEN???
if (req.query.token !== PUBSUB_VERIFICATION_TOKEN) {
res.status(400).send('Invalid request');
return;
}
try {
const bearer = req.header('Authorization');
const [, token] = bearer?.match(/Bearer (.*)/); //Error Type 'RegExpMatchArray | null | undefined' must have a 'Symbol.iterator' method that returns an iterator.ts(2488)
tokens.push(token); // Why do this? Can I do this in firebase cloud functions
const ticket = await authClient.verifyIdToken({
idToken: token,
});
const claim = ticket.getPayload();
claims.push(claim); // Why do this? Can I do this in firebase cloud functions
} catch (e) {
res.status(400).send('Invalid token');
return;
}
const message = Buffer.from(req.body.message.data, 'base64').toString(
'utf-8'
);
console.log(message);
return res.status(200).json({
statusCode: 200,
method: req.method,
message: 'Recieved successfully'
});
});
What is the PUBSUB_VERIFICATION_TOKEN and how do I get it and store it
in my environment?
PUBSUB_VERIFICATION_TOKEN can be any value you want. Easiest way to set an environment variable is on the command line when running node:
PUBSUB_VERIFICATION_TOKEN=whatevertoken node app.js
The req.query.token that is compared too comes from the URL query string.
GET /whatever?token=whatevertoken
Type 'RegExpMatchArray | null | undefined' must have a
'Symbol.iterator' method that returns an iterator.ts(2488)
That's a bug in their code. bearer.match can return undefined/null which can't be spread into the array [, token]. The example will only work when there is a successful regex match. This will parse in plain javascript but typescript highlights this issue at compile time.
const bearer = req.header('Authorization');
const m = /Bearer (.*)/.exec(bearer)
if (m) tokens.push(m[1])
Why do they push the claims and tokens in an array if they never check
that array in this function for already existing tokens / claims?
The example comments // List of all messages received by this instance.
So more a debug store than something functional.

How to handle X-CSRF-TOKEN correctly in an angular-based cordova app?

I have an Angular (v10) WebApp, which handles the X-CSRF-TOKEN cookie correctly as explained in the Angular Guide by using the HttpClientXsrfModule in my imports, i.e.:
// app.module.ts
HttpClientModule,
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-XSRF-TOKEN',
}),
and by setting an relative Path in my services' requests, like:
// some service.ts
public deleteX(x_id: number): Observable<any> {
return this.httpClient.delete(`api/X/${x_id}`);
}
and now, the browser itself handles fetching the token from the server and sending it by each subsequent POST/DELETE/PUT/PATCH request successfully.
However, if I compile the application now to an Android app using cordova, the app sends a request (with x_id=1068) to file:///android_asset/www/api/X/1068.
I can modify my http services to use platform-specific absolute/relative paths easily, such as:
// some service.ts
public deleteX(x_id: number): Observable<any> {
if (this.cordovaService.platform === CordovaService.PLATFORM_ANDROID) {
return this.httpClient.delete(`${environment.baseUrl}/api/X/${x_id}`);
} else {
return this.httpClient.delete(`api/X/${x_id}`);
}
}
But then, my request's response from the Android application is
error: "access_denied"
error_description: "Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-XSRF-TOKEN'."
What can I do, to add correct handling of the X-XSRF-TOKEN for my cordova compiled Android app?
I ended up using the cordova-plugin-advanced-http, that is offering a response-cookie-fetching opportunity described here.
I've created a generic-http-service containg generic methods for each of the HTTP methods (GET,HEAD,PATCH,PUT,POST,DELETE), that is first checking for the running platform and then forwarding an adjusted request.
example for generic GET (pseudo-code):
// generic-http-service.ts
public get<T>(url: string, queryParams?: any): Observable<T> {
if (this.cordovaService.platform === CordovaService.PLATFORM_ANDROID) {
// android-specific solution
// 1. adjust params
// 2. set general headers + the XSRF-TOKEN from the previous sendt request
// 3. return Observable(obs) {
// 4. send:
cordova.plugin.http.get(`${environment.baseUrl}/${url}`, adjustedParams, adjustedHeaders,
successResponse => {
// 5. fetch & save XSRF-TOKEN
obs.next(JSON.parse(successResponse.data) as T);
}, errorResponse => {
obs.error(errorResponse);
})
}
} else {
// web-specific solution based on the angular guide
return this.httpClient.get(`${url}`);
}
}
Afterwards I just needed to adjust my services a little bit.

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)

NodeJS: Callback is not a function - Email verification

I'm trying to implement a user registration system, on android with node as my backend server.
I'm using Node 4.4.5, on localhost, and using the package "email-verification" - https://www.npmjs.com/package/email-verification
So on request from android, a confirmation email with a verification link is sent, which is working fine.
When the link is clicked, a GET request is made, which confirms the user, adds it to the MongoDB database, and a JSON response is sent.
An email is sent to the user that the account is confirmed.
After sending the confirmation email, the server crashes.
Here's my code--
router.get('/email-verification/:URL', function(req, res, next){
var url = req.params.URL;
console.log('email-verify-start');
nev.confirmTempUser(url, function(err, user) {
console.log('error is :' + err);
if (user) {
nev.sendConfirmationEmail(user.email, function(err, info) {
if (err) {
console.log('sending_conf_email_failed');
return res.json({'email': 'sending_conf_email_failed'});
}
console.log('user_confirmed');
res.json({
'email': 'user_confirmed'
});
console.log('Done, and confirmed');
});
} else {
console.log('conf_temp_ser_failed');
return res.json({'email': 'conf_temp_ser_failed'});
}
});
});
And here's my log--
error is :null
user_confirmed
Done, and confirmed
GET /register/email-verification/SfC9VlnUv91RkFBHDURIbHodnYme0RdfbTYBj0I4oXyywrpW 200 5177.724 ms - 26
h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\src\smtp-transport.js:136
return callback(null, info);
^
TypeError: callback is not a function
at h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\src\smtp-transport.js:136:20
at h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\node_modules\smtp-connection\src\smtp-connection.js:279:20
at SMTPConnection._actionStream (h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\node_modules\smtp-connection\src\smtp-connection.js:966:16)
at SMTPConnection.<anonymous> (h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\node_modules\smtp-connection\src\smtp-connection.js:594:14)
at SMTPConnection._processResponse (h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\node_modules\smtp-connection\src\smtp-connection.js:516:16)
at SMTPConnection._onData (h:\myapp\coep_updates\node_modules\email-verification\node_modules\nodemailer\node_modules\nodemailer-smtp-transport\node_modules\smtp-connection\src\smtp-connection.js:353:10)
at emitOne (events.js:77:13)
at TLSSocket.emit (events.js:169:7)
at readableAddChunk (_stream_readable.js:153:18)
at TLSSocket.Readable.push (_stream_readable.js:111:10)
at TLSWrap.onread (net.js:531:20)
Process finished with exit code 1
Till the server crashes, everything's working fine. I receive all emails and responses are sent properly, I even see the JSON response {"email":"user_confirmed"} on my browser. The only problem is that the server crashes afterwards.
EDIT 1
I tried adding return statements-- Still the same problem. I added them here--
return res.json({
'email': 'user_confirmed'
});
I also tried adding a return--
res.json({
'email': 'user_confirmed'
});
return;
No luck till now...
EDIT 2
Ok. so this is actually an open issue on GitHUB, this is reported as a bug.
https://github.com/whitef0x0/node-email-verification/issues/44
So, I tried the GitHUB the solution this way and it is now working flawlessly, even though an official fix is not released...
In the source folder of the module, in the file 'index.js' -->
Go to line 340 --
You'll see this line
callback = options.shouldSendConfirmation;
Change it to -->
callback = function(){};
Hope this helps...
You could change your nev.sendConfirmationEmail method to include the callback as the third argument:
nev.sendConfirmationEmail(user.email, function(err, info) {
if (err) {
console.log('sending_conf_email_failed');
return res.json({'email': 'sending_conf_email_failed'});
}
console.log('user_confirmed');
res.json({
'email': 'user_confirmed'
});
console.log('Done, and confirmed');
}, function(){});

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.

Categories

Resources