Cannot refresh token in Google API - php

The goal
I need to fetch data from Google Analytics after my users have logged out. Basically they will go to my site, authorize my app to access their GA data and at midnight I fetch the data and process it in my service.
My current flow
After reading many any posts and questions here is what I have come up:
The front end part looks like somewhat this:
<script src="https://apis.google.com/js/client:platform.js?onload=start" async defer></script>
<script>
function start() {
gapi.load('auth2', function() {
auth2 = gapi.auth2.init({
client_id: '{{CLIENT_ID}}.apps.googleusercontent.com',
scope: 'https://www.googleapis.com/auth/analytics',
access_type: 'offline'
});
});
}
$('#signinButton').click(function() {
auth2.grantOfflineAccess({
'redirect_uri': 'postmessage'
}).then(signInCallback);
});
function signInCallback(authResult) {
window.console.log('signInCallback: This code must be exchanged by the server, it grants access to request a refresh token: ',authResult, arguments);
if (authResult['code']) {
// Hide the sign-in button now that the user is authorized, for example:
$('#signinButton').attr('style', 'display: none').after('<p>The auth code is: ' + authResult['code'] + '</p>');
$.get('http://mysite.dev/oauth_callback.php?code=' + authResult['code'])
.fail(function(err){console.error(err)})
.done(function(data) {
var message;
if (typeof data.error == 'undefined') {
message = '<div style="border: green solid 1px;">Finished the request and got ' + data + '</div>';
} else {
message = '<div style="border: red solid 1px;">' + data.message + '</div>';
}
$('#signinButton').after(message);
});
} else {
// There was an error.
}
}
</script>
my oauth_callback.php checks the response from google and stores it in the database (please ignore the $.get vs $post bit).
From my tests I see that the auth_code along with the refresh_token and other parts are properly stored in my database.
Now, I manually trigger my midnight script which looks a little like this:
public function get_client() {
// Create and configure a new client object.
$client = new \Google_Client();
$path_to_config = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'my-client-secret.apps.googleusercontent.com.json';
$scopes = array(
'https://www.googleapis.com/auth/analytics'
);
$client->setAuthConfigFile($path_to_config);
$client->setApplicationName("My app");
$client->setAccessType('offline');
return $client;
}
public function fetch_visits() {
foreach ($this->_accounts as $account_id => $auth_json_string) {
$client = $this->get_client();
$ga_code_array = json_decode($auth_json_string, true);
$ga_code_string = $auth_json_string;
$client->setAccessToken($auth_json_string);
var_dump($client);
if ($client->isAccessTokenExpired()) {
$client->setAccessType('offline');
var_dump('Not authed because the access token expired. Requesting another one');
$client->refreshToken($auth_json_string);;
$is_authed_now = $client->isAccessTokenExpired();
var_dump($is_authed_now);
}
die('sadasdsdsa');
}
}
The error
With this code Im getting the infamous:
Google_Auth_Exception: Error refreshing the OAuth2 token, message: '{
"error" : "invalid_grant""
error, which I traced to the OAuth library and basically Google returning an http code 400 (instead of 200) which tells me that the $auth_json_string is not correct, but this is the same string that Google gave me after the user authorized my app, so I dont really know where is the problem.
Please keep in mind that this is a server-to-server app, the user will not be logged in when my service runs, thats why I store the auth json object.
Can you see what Im doing wrong?

For PHP I set the following
$client->setAccessType("refresh_token");
And this resolved my issue. I am using gmail for this but should work for the other Google Services. Remember to save the refresh_token when creating the first access_token request or you will have more issues.
So I now have a function called getClient()
function getClient(){
$googleUser = $this->getUserDetails();
$client = new Google_Client();
$client->setAuthConfigFile('client_secret.json');
$client->setApplicationName("YOUR_APP_NAME");
$data = $googleUser->attributes;
$client->setAccessToken(json_encode($data));
if($client->isAccessTokenExpired()){
$client->addScope(Google_Service_Gmail::MAIL_GOOGLE_COM);
$client->setAccessType("refresh_token");
$client->refreshToken($data['refresh_token']);
$access_token = $client->getAccessToken();
$data1 = json_decode($access_token);
$googleUser->access_token = $data1->access_token;
$googleUser->token_type = $data1->token_type;
$googleUser->expires_in = $data1->expires_in;
$googleUser->created_date = date('Y-m-d h:i:s',$data1->created);
$googleUser->created = $data1->created;
$googleUser->save();
$client->setAccessToken($access_token);
}
return $client;
}

Related

Firebase Auth JS/PHP

I've been tasked to build a web interface for an Android app based on firebase.
I've got a handful of endpoints, that interact with the database (Cloud functions). To access those endpoints I need to authenticate an user with email and password[1], retrieve an accessToken[2] und authorize every request to the endpoints with an Authorization: Bearer {accessToken} header.
I use php and struggle to wrap my mind around how to manage authenticated user in my app.
TL;DR please see my final solution in php only. https://stackoverflow.com/a/52119600/814031
I transfer the accessToken via ajax in a php session, to sign the cURL requests to the endpoints.
Apparently there is no other way around than use the firebase JS auth (not as far as I understand[4]).
My question is: Is it enough to save the accessToken in a php session and compare it with every page load via an ajax POST request (see code below)?
What would be a more robust strategy to handle that in php?
Edit: A user pointed out that using classic php sessions with JWT tokens don't make much sense and I read up about that topic.
So regarding Firebase - is this something to consider?
https://firebase.google.com/docs/auth/admin/manage-cookies
Firebase Auth provides server-side session cookie management for traditional websites that rely on session cookies. This solution has several advantages over client-side short-lived ID tokens, which may require a redirect mechanism each time to update the session cookie on expiration:
Here is what I got:
1. Login Page
As described in the Firebase examples[3]
function initApp() {
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// User is signed in.
// obtain token, getIdToken(false) = no forced refresh
firebase.auth().currentUser.getIdToken(false).then(function (idToken) {
// Send token to your backend via HTTPS
$.ajax({
type: 'POST',
url: '/auth/check',
data: {'token': idToken},
complete: function(data){
// data = {'target' => '/redirect/to/route'}
if(getProperty(data, 'responseJSON.target', false)){
window.location.replace(getProperty(data, 'responseJSON.target'));
}
}
});
// ...
}).catch(function (error) {
console.log(error);
});
} else {
// User Signed out
$.ajax({
type: 'POST',
url: '/auth/logout',
complete: function(data){
// data = {'target' => '/redirect/to/route'}
if(getProperty(data, 'responseJSON.target', false)){
// don't redirect to itself
// logout => /
if(window.location.pathname != getProperty(data, 'responseJSON.target', false)){
window.location.replace(getProperty(data, 'responseJSON.target'));
}
}
}
});
// User is signed out.
}
});
}
window.onload = function () {
initApp();
};
2. a php controller to handle the auth requests
public function auth($action)
{
switch($action) {
// auth/logout
case 'logout':
unset($_SESSION);
// some http status header and mime type header
echo json_encode(['target' => '/']); // / => index page
break;
case 'check':
// login.
if(! empty($_POST['token']) && empty($_SESSION['token'])){
// What if I send some bogus data here? The call to the Endpoint later would fail anyway
// But should it get so far?
$_SESSION['token'] = $_POST['token'];
// send a redirect target back to the JS
echo json_encode(['target' => '/dashboard']);
break;
}
if($_POST['token'] == $_SESSION['token']){
// do nothing;
break;
}
break;
}
}
3. the Main controller
// pseudo code
class App
{
public function __construct()
{
if($_SESSION['token']){
$client = new \GuzzleHttp\Client();
// $user now holds all custom access rights within the app.
$this->user = $client->request(
'GET',
'https://us-centralx-xyz.cloudfunctions.net/user_endpoint',
['headers' =>
[
'Authorization' => "Bearer {$_SESSION['token']}"
]
]
)->getBody()->getContents();
}else{
$this->user = null;
}
}
public function dashboard(){
if($this->user){
var_dump($this->user);
}else{
unset($_SESSION);
// redirect to '/'
}
}
}
Note: I'm aware of this sdk https://github.com/kreait/firebase-php and I read a lot in the issues there and in posts here on SO, but I got confused, since there is talk about full admin rights etc. and I really only interact with the endpoints that build upon firebase (plus firebase auth and firestore). And I'm still on php 5.6 :-/
Thanks for your time!
[1]: https://firebase.google.com/docs/auth/web/password-auth
[2]: https://firebase.google.com/docs/reference/js/firebase.User#getIdToken
[3]: https://github.com/firebase/quickstart-js/blob/master/auth/email-password.html
[4]: https://github.com/kreait/firebase-php/issues/159#issuecomment-360225655
I have to admit, the complexity of the firebase docs and examples and different services, got me so confused, that I thought, authentication for the web is only possible via JavaScript. That was wrong. At least for my case, where I just login with email and password to retrieve a Json Web Token (JWT), to sign all calls to the Firebase cloud functions. Instead of juggling with weird Ajax requests or set the token cookie via JavaScript, I just needed to call the Firebase Auth REST API
Here is a minimal case using the Fatfreeframework:
Login form
<form action="/auth" method="post">
<input name="email">
<input name="password">
<input type="submit">
</form>
Route
$f3->route('POST /auth', 'App->auth');
Controller
class App
{
function auth()
{
$email = $this->f3->get('POST.email');
$password = $this->f3->get('POST.password');
$apiKey = 'API_KEY'; // see https://firebase.google.com/docs/web/setup
$auth = new Auth($apiKey);
$result = $auth->login($email,$password);
if($result['success']){
$this->f3->set('COOKIE.token',$result['idToken']);
$this->f3->reroute('/dashboard');
}else{
$this->f3->clear('COOKIE.token');
$this->f3->reroute('/');
}
}
}
Class
<?php
use GuzzleHttp\Client;
class Auth
{
protected $apiKey;
public function __construct($apiKey){
$this->apiKey = $apiKey;
}
public function login($email,$password)
{
$client = new Client();
// Create a POST request using google api
$key = $this->apiKey;
$responsee = $client->request(
'POST',
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=' . $key,
[
'headers' => [
'content-type' => 'application/json',
'Accept' => 'application/json'
],
'body' => json_encode([
'email' => $email,
'password' => $password,
'returnSecureToken' => true
]),
'exceptions' => false
]
);
$body = $responsee->getBody();
$js = json_decode($body);
if (isset($js->error)) {
return [
'success' => false,
'message' => $js->error->message
];
} else {
return [
'success' => true,
'localId' => $js->localId,
'idToken' => $js->idToken,
'email' => $js->email,
'refreshToken' => $js->refreshToken,
'expiresIn' => $js->expiresIn,
];
}
}
}
Credits
Sounds like #Chad K is getting you on the right track (cookies and ajax - breakfast of champions... :), though I thought to share my code from my working system (with some 'privacy' things, of course!)
Look for /**** type comments for things you need to set up yourself (you may want to do some other firebase things differently as well - see the docs...)
LOGIN.php page (I found it simpler overall to keep this separate - see notes to learn why....)
<script>
/**** I picked this up somewhere off SO - kudos to them - I use it a lot!.... :) */
function setCookie(name, value, days = 7, path = '/') {
var expires = new Date(Date.now() + days * 864e5).toUTCString();
document.cookie = name + '=' + encodeURIComponent(value) + '; expires=' + expires + '; path=' + path;
}
function getCookie(c_name) {
if (document.cookie.length > 0) {
c_start = document.cookie.indexOf(c_name + "=");
if (c_start !== -1) {
c_start = c_start + c_name.length + 1;
c_end = document.cookie.indexOf(";", c_start);
if (c_end === -1) {
c_end = document.cookie.length;
}
return unescape(document.cookie.substring(c_start, c_end));
}
}
return "";
}
</script>
<script>
var config = {
apiKey: "your_key",
authDomain: "myapp.firebaseapp.com",
databaseURL: "https://myapp.firebaseio.com",
projectId: "myapp",
storageBucket: "myapp.appspot.com",
messagingSenderId: "the_number"
};
firebase.initializeApp(config);
</script>
<script src="https://cdn.firebase.com/libs/firebaseui/2.7.0/firebaseui.js"></script>
<link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/2.7.0/firebaseui.css"/>
<script type="text/javascript">
/**** set this url to the 'logged in' page (mine goes to a dashboard) */
var url = 'https://my.app/index.php#dashboard';
/**** by doing this signOut first, then it is simple to send any 'logout' request in the app to 'login.php' - one page does it.... :) */
firebase.auth().signOut().then(function () {
}).catch(function (error) {
console.log(error);
});
var signInFlow = 'popup';
if (('standalone' in window.navigator)
&& window.navigator.standalone) {
signInFlow = 'redirect';
}
var uiConfig = {
callbacks: {
signInSuccessWithAuthResult: function (authResult, redirectUrl) {
/**** here you can see the logged in user */
var firebaseUser = authResult.user;
var credential = authResult.credential;
var isNewUser = authResult.additionalUserInfo.isNewUser;
var providerId = authResult.additionalUserInfo.providerId;
var operationType = authResult.operationType;
/**** I like to force emailVerified...... */
if (firebaseUser.emailVerified !== true) {
firebase.auth().currentUser.sendEmailVerification().then(function () {
/**** if using this, you can set up your own usermgmt.php page for the user verifications (see firebase docs) */
window.location.replace("https://my.app/usermgmt.php?mode=checkEmail");
}).catch(function (error) {
console.log("an error has occurred in sending verification email " + error)
});
}
else {
var accessToken = firebaseUser.qa;
/**** set the Cookie (yes, I found this best, too) */
setCookie('firebaseRegistrationID', accessToken, 1);
/**** set up the AJAX call to PHP (where you will store this data for later lookup/processing....) - I use "function=....." and "return=....." to have options for all functions and what to select for the return so that ajax.php can be called for 'anything' (you can just call a special page if you like instead of this - if you use this idea, be sure to secure the ajax.php 'function' call to protect from non-authorized use!) */
var elements = {
function: "set_user_data",
user: JSON.stringify(firebaseUser),
return: 'page',
accessToken: accessToken
};
$.ajaxSetup({cache: false});
$.post("data/ajax.php", elements, function (data) {
/**** this calls ajax and gets the 'page' to set (this is from a feature where I store the current page the user is on, then when they log in again here, we go back to the same page - no need for cookies, etc. - only the login cookie is needed (and available for 'prying eyes' to see!) */
url = 'index.php#' + data;
var form = $('<form method="post" action="' + url + '"></form>');
$('body').append(form);
form.submit();
});
}
return false;
},
signInFailure: function (error) {
console.log("error - signInFailure", error);
return handleUIError(error);
},
uiShown: function () {
var loader = document.getElementById('loader');
if (loader) {
loader.style.display = 'none';
}
}
},
credentialHelper: firebaseui.auth.CredentialHelper.ACCOUNT_CHOOSER_COM,
queryParameterForWidgetMode: 'mode',
queryParameterForSignInSuccessUrl: 'signInSuccessUrl',
signInFlow: signInFlow,
signInSuccessUrl: url,
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
// firebase.auth.FacebookAuthProvider.PROVIDER_ID,
// firebase.auth.TwitterAuthProvider.PROVIDER_ID,
{
provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
requireDisplayName: true,
customParameters: {
prompt: 'select_account'
}
}
/* {
provider: firebase.auth.PhoneAuthProvider.PROVIDER_ID,
// Invisible reCAPTCHA with image challenge and bottom left badge.
recaptchaParameters: {
type: 'image',
size: 'invisible',
badge: 'bottomleft'
}
}
*/
],
tosUrl: 'https://my.app/login.php'
};
var ui = new firebaseui.auth.AuthUI(firebase.auth());
(function () {
ui.start('#firebaseui-auth-container', uiConfig);
})();
</script>
Now, on every page you want the user to see (in my case, it all goes through index.php#something - which makes it easier.... :)
<script src="https://www.gstatic.com/firebasejs/4.12.0/firebase.js"></script>
<script>
// Initialize Firebase - from https://github.com/firebase/firebaseui-web
var firebaseUser;
var config = {
apiKey: "your_key",
authDomain: "yourapp.firebaseapp.com",
databaseURL: "https://yourapp.firebaseio.com",
projectId: "yourapp",
storageBucket: "yourapp.appspot.com",
messagingSenderId: "the_number"
};
firebase.initializeApp(config);
initFBApp = function () {
firebase.auth().onAuthStateChanged(function (firebaseuser) {
if (firebaseuser) {
/**** here, I have another ajax call that sets up some select boxes, etc. (I chose to call it here, you can call it anywhere...) */
haveFBuser();
firebaseUser = firebaseuser;
// User is signed in.
var displayName = firebaseuser.displayName;
var email = firebaseuser.email;
var emailVerified = firebaseuser.emailVerified;
var photoURL = firebaseuser.photoURL;
if (firebaseuser.photoURL.length) {
/**** set the profile picture (presuming you are showing it....) */
$(".profilepic").prop('src', firebaseuser.photoURL);
}
var phoneNumber = firebaseuser.phoneNumber;
var uid = firebaseuser.uid;
var providerData = firebaseuser.providerData;
var string = "";
firebaseuser.getIdToken().then(function (accessToken) {
// document.getElementById('sign-in-status').textContent = 'Signed in';
// document.getElementById('sign-in').textContent = 'Sign out';
/**** set up another ajax call.... - to store things (yes, again.... - though this time it may be due to firebase changing the token, so we need it twice...) */
string = JSON.stringify({
displayName: displayName,
email: email,
emailVerified: emailVerified,
phoneNumber: phoneNumber,
photoURL: photoURL,
uid: uid,
accessToken: accessToken,
providerData: providerData
});
if (accessToken !== '<?php echo $_COOKIE['firebaseRegistrationID']?>') {
console.log("RESETTING COOKIE with new accessToken ");
setCookie('firebaseRegistrationID', accessToken, 1);
var elements = 'function=set_user_data&user=' + string;
$.ajaxSetup({cache: false});
$.post("data/ajax.php", elements, function (data) {
<?php
/**** leave this out for now and see if anything weird happens - should be OK but you might want to use it (refreshes the page when firebase changes things..... I found it not very user friendly as they reset at 'odd' times....)
/*
// var url = 'index.php#<?php echo(!empty($user->userNextPage) ? $user->userNextPage : 'dashboard'); ?>';
// var form = $('<form action="' + url + '" method="post">' + '</form>');
// $('body').append(form);
// console.log('TODO - leave this form.submit(); out for now and see if anything weird happens - should be OK');
// form.submit();
*/
?>
});
}
});
} else {
console.log("firebase user CHANGED");
document.location.href = "../login.php";
}
}, function (error) {
console.log(error);
}
);
};
window.addEventListener('load', function () {
initFBApp();
});
</script>
Hope this helps. It is from my working system, which includes some extra features I've put in there along the way, but mostly it is directly from firebase so you should be able to follow along well enough.
Seems a much simpler route to take than your original one.
You really aren't supposed to use sessions in PHP when using tokens. Tokens should be sent in the header on every request (or a cookie works too).
Tokens work like this:
1. You sign in, the server mints a token with some information encoded
2. You send that token back on every request
Based on the information encoded in the token, the server can get information about the user. Typically a User ID of some sort is encoded in it. The server knows it's a valid token because of the way it's encoded.
Send the token on every request you need to make, then in PHP you can just pass that token to the other API

Receive and send an authorization token in Swagger

I am creating a documentation page with Swagger, and I would like to automatically add the access token received from a correct /login call. I tried using
responseInterceptor: (responseObj) => {
if(responseObj.obj.access_token !== 'undefined'){
token = responseObj.obj.access_token;
}
return responseObj
}
to receive and store the token, and
requestInterceptor: (requestObj) => {
if(token !== 'undefined') {
requestObj.headers.Authorization = `Bearer ${token}`;
}
return requestObj
}
to pass it onto subsequent requests through the header, however this only works for one call, after which the token reverts back to null.
How could I keep the token value from being reverted?
Found a solution by declaring token as a global variable.
//Checks any reponse obtained for an access token
responseInterceptor: (responseObj) => {
if("access_token" in responseObj.obj){
window.token = responseObj.obj.access_token;
}
return responseObj
},
//Adds the authorization to the request header if token has been set
requestInterceptor: (requestObj) => {
if(window.token) {
requestObj.headers.Authorization = `Bearer ${window.token}`;
}
return requestObj
}
I'm new to JS and Swagger, so the answer wasn't obvious. I hope this helps someone eventually.

How to set up Google+ Sign-In for server-side apps for a website?

I am trying to add a sign in with google+ button on my website just to retrieve basic information.
but the documentation doesnt seem to make any sense to me.
(https://developers.google.com/+/web/signin/server-side-flow)
it appears out of date and not complete and there seems to be various api library's that can be used.
Can anyone explain all this more clearly or tell me how i should go about making this work and which api library to use etc?
a full sample with code would be very helpful.
thanx
Ok so i will add more detail. google development page gives this as an example for a login button :
<html>
<head>
<!-- BEGIN Pre-requisites -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
</script>
<script src="https://apis.google.com/js/client:platform.js?onload=start" async defer>
</script>
<!-- END Pre-requisites -->
</head>
<body>
<div id="signinButton">
<span class="g-signin"
data-scope="https://www.googleapis.com/auth/plus.login"
data-clientid="your-client-id"
data-redirecturi="postmessage"
data-accesstype="offline"
data-cookiepolicy="single_host_origin"
data-callback="signInCallback">
</span>
</div>
<div id="result"></div>
<script>
function signInCallback(authResult) {
if (authResult['code']) {
// Hide the sign-in button now that the user is authorized, for example:
$('#signinButton').attr('style', 'display: none');
// Send the code to the server
$.ajax({
type: 'POST',
url: 'plus.php?storeToken',
contentType: 'application/octet-stream; charset=utf-8',
success: function(result) {
// Handle or verify the server response if necessary.
// Prints the list of people that the user has allowed the app to know
// to the console.
console.log(result);
if (result['profile'] && result['people']){
$('#results').html('Hello ' + result['profile']['displayName'] + '. You successfully made a server side call to people.get and people.list');
} else {
$('#results').html('Failed to make a server-side call. Check your configuration and console.');
}
},
processData: false,
data: authResult['code']
});
} else if (authResult['error']) {
// There was an error.
// Possible error codes:
// "access_denied" - User denied access to your app
// "immediate_failed" - Could not automatially log in the user
// console.log('There was an error: ' + authResult['error']);
}
}
</script>
</body>
</html>
but it also provides:
<?php
// Create a state token to prevent request forgery.
// Store it in the session for later validation.
$state = md5(rand());
$app['session']->set('state', $state);
// Set the client ID, token state, and application name in the HTML while
// serving it.
return $app['twig']->render('index.html', array(
'CLIENT_ID' => CLIENT_ID,
'STATE' => $state,
'APPLICATION_NAME' => APPLICATION_NAME
));
// 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);
}
$code = $request->getContent();
$gPlusId = $request->get['gplus_id'];
// Exchange the OAuth 2.0 authorization code for user credentials.
$client->authenticate($code);
$token = json_decode($client->getAccessToken());
// Verify the token
$reqUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=' .
$token->access_token;
$req = new Google_HttpRequest($reqUrl);
$tokenInfo = json_decode(
$client::getIo()->authenticatedRequest($req)->getResponseBody());
// If there was an error in the token info, abort.
if ($tokenInfo->error) {
return new Response($tokenInfo->error, 500);
}
// Make sure the token we got is for the intended user.
if ($tokenInfo->userid != $gPlusId) {
return new Response(
"Token's user ID doesn't match given user ID", 401);
}
// Make sure the token we got is for our app.
if ($tokenInfo->audience != CLIENT_ID) {
return new Response(
"Token's client ID does not match app's.", 401);
}
// Store the token in the session for later use.
$app['session']->set('token', json_encode($token));
$response = 'Succesfully connected with token: ' . print_r($token, true);
?>
But it doesnt say where to put that last bit of code or how to refer to an api library or where to put the secret or anything. so i could do with some pointing in the righ direction please?
ok so if anyone else is having trouble.
i followed the tutorial on this link
I downloaded the api library from there, changed the configs file and used the example that is provided and it worked fine.
to make it work on a localhost you have to set your Authorized JavaScript origins to a localhost:# for example http://localhost:12345
then to make your browser accept the folder or the signin page in command prompt type in
cd c:/the/path/of/the/downloaded/api/example
then type in:
php -S localhost:12345
hope that helps anyone

How to solve the error: MethodNotAllowedHttpException

I am using Symfony to connect with Google Plus. Here is my code about connecting google after login and get access token:
$app->match('/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.
$gPlusId = $request->get['gplus_id'];
$client->authenticate($code);
$token = json_decode($client->getAccessToken());
// Verify the token
$reqUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=' .
$token->access_token;
$req = new Google_HttpRequest($reqUrl);
$tokenInfo = json_decode(
$client::getIo()->authenticatedRequest($req)->getResponseBody());
// If there was an error in the token info, abort.
if ($tokenInfo->error) {
return new Response($tokenInfo->error, 500);
}
// Make sure the token we got is for the intended user.
if ($tokenInfo->userid != $gPlusId) {
return new Response(
"Token's user ID doesn't match given user ID", 401);
}
// Make sure the token we got is for our app.
if ($tokenInfo->audience != CLIENT_ID) {
return new Response(
"Token's client ID does not match app's.", 401);
}
//$_SESSION['token']=$token;
// 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);
})->method('POST');
In the html file I use ajax to call the previous PHP code:
connectServer: function() {
$.ajax({
type: 'POST',
url: window.location.href + '/connect?state={{ STATE }}',
cache:false,
contentType: 'application/octet-stream; charset=utf-8',
success: function(result) {
console.log(result);
// helper.people();
},
error:function(e){
console.log(e);
},
processData: false,
data: this.authResult.code
});
But I always get an error: MethodNotAllowedHttpException: No route found for "GET /connect": Method Not Allowed (Allow: POST).
How can I solve the error?
It's looks like this one: No route found for "GET /user/register": Method Not Allowed (Allow: POST)
You have to define requirements _method for you route, like:
routeName:
pattern: /yourPattern
defaults: { _controller: AcmeBundle:Sth:action }
requirements:
_method: POST|GET

FB.login with PHP API

I've set up a Canvas Page which doe's a FB.login on click of a form submit button. During the following request it tries to access the users data via $facebook->api('/me') (last API version from Github). It works in Firefox and Chrome, but not in Safari and IE, where the API fails with "auth token required". Has anybody already had this problem or got an idea what could cause it?
BR Philipp
edit:
I call FB.login inside the click event of a form submit button:
$('.form-submit', this).click(function() {
FB.getLoginStatus(function(response) {
if (response.session) {
form.submit();
} else {
FB.login(function(response) {
if(response.session && (permissions == '' || response.perms)) {
form.submit();
}
else {
}
},{perms:permissions});
}
});
return false;
});
On server side in simply construct the php-api object and try to get user data:
$facebook = new Facebook(array(
'appId' => $appid,
'secret' => $appsecret,
'cookie' => TRUE,
));
if ($facebook) {
try {
$me = $api->api('/me');
}
catch (Exception $exc) {
// Failure in Safari and IE due to invalid auth token
}
}
The signed_request is passed inside a hidden form element.
I had the same problem and I've included a solution below.
I believe the reason this happens is because on a Javascript login attempt your server never receives any access tokens. The Javascript is only passing data between your browser and Facebook.com so your server has no idea what the authentication status is. Your server will only receive the new access tokens when the page is refreshed; this is where facebook hands over the access tokens.
Heres my solution.
Upon a successful login via FB.login you will receive the response object and inside it is an access_token. All you need to do is pass this access token to your script in some way. Here is an example:
// Hold the access token
var js_access_token = "";
// Connect to facebook
FB.login(function(response) {
if (response.session) {
if (response.perms) {
// user is logged in and granted some permissions.
// Save the access token
js_access_token = response.session.access_token;
// Do stuff on login
}
}
});
You then include the access token along with any requests. I've chosen an ajax example.
// Communication back to server.
$.ajax({
url: 'myurl.php',
data: {
js_access_token: js_access_token // Including the js_access_token
},
success: function(data) {
console.log(data);
}
});
Within your PHP you then need to have something which looks like this:
$facebook = new Facebook(array(
'appId' => $appid,
'secret' => $appsecret,
'cookie' => TRUE,
));
if ($facebook) {
// If we get the access token from javascript use it instead
if (isset($_REQUEST['js_access_token']) && $_REQUEST['js_access_token']) {
$facebook->setAccessToken($_REQUEST['js_access_token']);
}
try {
$me = $api->api('/me');
}
catch (Exception $exc) {
// Failure in Safari and IE due to invalid auth token
}
}
Hope this helps
I had a lot of troubles with the JS FB login stuff. I recommend using the simpler redirect login using oauth and the getLoginUrl function from php fb api.
So basically you do it from PHP, you check if you have your session, if not you use getLoginUrl and redirect to that page, your use will be then redirected to your app/site with a valid session (if he accepts).
Does this help ? I really lost HOURS trying to make the FB JS login work on any browser and I couldn't, I've switched since then to the simple redirect login method in all of my apps with complete success.
I liked Conor's answer as I had to pass my access token from the client side to server side as it was not working in Safari (cookie issues I presume). But this is an old question so a few things had to change. The vars are different now and as oliland pointed out we shouldn't be sending access tokens as GET params.
Anyway, here's what I ended up with in case it helps anyone
<a id="start-button" href="#">Start</a>
<form id="entry-form" action="nextpageurl" method="post">
<input type="hidden" name="access_token" />
</form>
<script>
$(document).ready(function() {
$('#start-button').click(function(e) {
e.preventDefault();
FB.login(function (response) {
if (response.authResponse) {
$('#entry-form input').val(response.authResponse.accessToken);
$('#entry-form').submit();
} else {
alert('Permissions required');
}
}, {});
});
});
</script>
and then in the PHP, pretty much same as Conor's answer, but getting the token from the $_POST var.

Categories