I am building a SPA using vue.js which has a PHP backend server (slim framework 3). These are two separate projects, leave on two different servers and the backend has no front end at all.
SPA (vue.js) makes requests to backend via ajax.
Now I want to implement Google Calendar API to create a calendar and events every time user creates a todo item. To do that I need server to server access to Google Calendar API (I might need to make changes to the event on GCAL even if user is not logged in).
What I am trying to understand, how can I get the access token (and refresh token) using Google JS library using vue.js and save this in the db so that my backend can use it to make offline requests to GCAL Api.
When I use the Oauth v.2 using the JS library, all I get is the access_token which cannot be using for server to server communications.
[UPDATE]
Ok, a little bit more information. I am following the guides from Google and my front end looks like this at the moment
jsbin
So I can successfully authorise user and access their calendar using the javascript sdk. But the token Javascript SDK returns is something like this
{
_aa: "1"
access_token: "xxxxxxx"
client_id: "yyyyyyyyyy"
cookie_policy: undefined
expires_at: "1456400189"
expires_in: "3600"
g_user_cookie_policy: undefined
issued_at: "1456396589"
response_type: "token"
scope: "https://www.googleapis.com/auth/calendar"
state: ""
status: Object
google_logged_in: false
method: "AUTO"
signed_in: true
token_type: "Bearer"
}
I send this token to my backend server and try to make a request to GCAL api as follows
$token = $request->getParam('token');
$client = new Google_Client();
$client->setApplicationName('Web');
$client->setScopes([Google_Service_Calendar::CALENDAR]);
$client->setAuthConfigFile(ROOT_DIR . '/client_secret.json');
$client->setAccessType('offline');
$client->setAccessToken(json_encode($token));
$service = new Google_Service_Calendar($client);
$calendarId = 'primary';
$optParams = array(
'maxResults' => 10,
'orderBy' => 'startTime',
'singleEvents' => TRUE,
'timeMin' => date('c'),
);
$results = $service->events->listEvents($calendarId, $optParams);
And it returns error saying the token is expired. I checked the Google Code and found out the reason it returns this error is because of these lines
public function isAccessTokenExpired()
{
if (!$this->token || !isset($this->token['created'])) {
return true;
}
// If the token is set to expire in the next 30 seconds.
$expired = ($this->token['created']
+ ($this->token['expires_in'] - 30)) < time();
return $expired;
}
As you can see the token that comes from the front end doesn't have created field as well as no refresh_token field.
Thanks for updating the question! I am thinking the issue is that using the client-side flow does not allow you to get a refresh token. From the docs:
OAuth 2.0 client-side flow (AKA Implicit flow) is used to obtain
access tokens (it does not support the issuance of refresh tokens) and
is optimized for public clients known to operate a particular
redirection URI. These clients are typically implemented in a browser
using a scripting language such as JavaScript.
The authorization server MUST NOT issue a refresh token.
see for more: How to get refresh token while using Google API JS Client
You'd need to use the server-auth flow to get a token you can refresh and use long-term. Here's a quickstart guide for PHP.
One other thing to consider is that you will only receive a refresh_token the first time someone authorizes your app. After that, auth attempts will only return an access token. So if you lose the refresh token, you will need to either disable the authorization from your google account, or use the "force re-auth" option in the API.
Related
I am working on Excel Web Add-In. I am using OfficeDev/office-js-helpers library for authenticating user. Following code is working fine. But I don't know how to get user's email, user name etc.
Is there any function available in OfficeDev/office-js-helpers through which I can get user info ?
if (OfficeHelpers.Authenticator.isAuthDialog()) {
return;
}
var authenticator = new OfficeHelpers.Authenticator();
// register Microsoft (Azure AD 2.0 Converged auth) endpoint using
authenticator.endpoints.registerMicrosoftAuth('clientID');
// for the default Microsoft endpoint
authenticator
.authenticate(OfficeHelpers.DefaultEndpoints.Microsoft)
.then(function (token) {
/* My code after authentication and here I need user's info */ })
.catch(OfficeHelpers.Utilities.log);
Code sample will be much helpful.
This code only provides you the token for the user. In order to obtain information about the user, you'll need to make calls into Microsoft Graph API. You can find a full set of documentation on that site.
If you're only authenticating in order to get profile information, I'd recommend looking at Enable single sign-on for Office Add-ins (preview). This is a much cleaner method of obtaining an access token for a user. It is still in preview at the moment so it's feasibility will depend on where you're planning to deploy your add-in.
Once you have the Microsoft token, you can send a request to https://graph.microsoft.com/v1.0/me/ to get user information. This request must have an authorization header containing the token you got previously.
Here is an example using axios :
const config = { 'Authorization': `Bearer ${token.access_token}` };
axios.get(`https://graph.microsoft.com/v1.0/me/`, {
headers: config
}).then((data)=> {
console.log(data); // data contains user information
};
I'm trying to get graph api data for public Facebook events rendered in an html document. The site I need to put it on is made with a website builder so I don't have control over the server that generates the html. I can only insert JavaScript code into the page.
The only solution I've found now is by using an app access_token in the client side request:
FB.api(
"/<event_id>?fields=start_time,description",
function (response) {
if (response && !response.error) {
console.log(response.start_time, response.description);
}
},
{access_token: "<app access token>"}
);
I've read through the docs on access tokens
and the JavaScript sdk docs, but the app token solution I've found is not secure (from the docs):
... app access token should never be hard-coded into client-side code.
I don't think the user access token is the flow I want because I don't want people to have to log in; it's only for public events. I don't think the page access token is right either.
Is there:
a way to use the Graph API for public events without requiring a user to log in or without introducing an insecurity?
or a way to make sharing my app access token with the world have no bad consequences?
I need to retrieve a facebook page's list of posts (feed) using their javascript SDK, just like they explain in their docs: https://developers.facebook.com/docs/graph-api/reference/v2.4/page/feed
/* make the API call */
FB.api(
"/{page-id}/posts",
function (response) {
if (response && !response.error) {
/* handle the result */
}
}
);
I need it to be my website's "news section", so users should see it even if they are not connected to facebook.
The problem
Cool, but there is a problem... It returns: An access token is required to request this resource.
Holy cow... I'd like to get some access token for you #facebook, but my app doesn't make use of your authentication tools/plugins.
ANYWAY, I tried with FB.getLoginStatus(); but doesn't work, because the only way it can return an access_token is if the user is actually connected to the application. My users may not even be logged to facebook!
So, ¿How can I get an access_token to be stored into a variable, and later be used to get /{my-page}/posts?
I've already payed a look to this SO question, but it doesn't solves my problem, simply because there are no such "generic tokens".
I've also read https://developers.facebook.com/docs/facebook-login/access-tokens/ and that also relies on tokens generated through facebook login methods... So, can't I display a list of fb page's posts in my website, without being connected into facebook, hence an application?
ADD: My app is build with angularJS, I'm not dealing with server-side code. I shall rely purely on javascript methods.
You could either use an page or an app access token, but as you'd be using them on the client-side, neither of them are an option.
See
https://developers.facebook.com/docs/facebook-login/access-tokens#apptokens
https://developers.facebook.com/docs/facebook-login/access-tokens#pagetokens
Note that because this request uses your app secret, it must never be made in client-side code or in an app binary that could be decompiled. It is important that your app secret is never shared with anyone. Therefore, this API call should only be made using server-side code.
I'd strongly recommend to build a simple server-side script (PHP for example) to proxy the request to the Graph API. You could then call this via AJAX for example and load the posts asynchronously (and alse get rid of the FB JS SDK!). There is NO way to handle this in a secure manner if you don't want to use FB Login for all of your users (which also doesn't make much sense IMHO).
I think it's straightforward :)
Since pages' posts are always public, it only needs a valid access token to retrieve page posts.
Quoting what you've written:
So, ¿How can I get an access_token to be stored into a variable, and later be used to get /{my-page}/posts?
You only require an access token.
My suggestion would be;
- Generate an access token for yourself (no extra permission needed)
- request page-id/posts
This way you don't require other users to be connected to facebook, you can simply requests page-id/posts to retrieve posts with access token you generated for yourself.
I hope it solves your problem :D
TIP: As long as posts are public, you only require a valid access token, it doesn't need to be user or page specific.
Can I access Google Analytics data using a service account in a client-side application? If not, are there other ways of achieving the same outcome?
Must be entirely client-side, and must not require users to authenticate (hence the desire to use a service account).
Yes you can in https://code.google.com/apis/console make sure you say that its a Service account it will give you a key file to download. With that you dont need a user to click ok to give you access.
For a service acccount to work you need to have a key file. Anyone that has access to that key file will then be able to access your Analytics data. Javascript is client sided which means you will need to send the key file. See the Problem? You are handing everyone access to your account. Even if you could get a service account to work using javascript for security reasons its probably not a very good idea.
You can use the official (and alpha) Google API for Node.js to generate the token. It's helpful if you have a service account.
On the server:
npm install -S googleapis
ES6:
import google from 'googleapis'
import googleServiceAccountKey from '/path/to/private/google-service-account-private-key.json' // see docs on how to generate a service account
const googleJWTClient = new google.auth.JWT(
googleServiceAccountKey.client_email,
null,
googleServiceAccountKey.private_key,
['https://www.googleapis.com/auth/analytics.readonly'], // You may need to specify scopes other than analytics
null,
)
googleJWTClient.authorize((error, access_token) => {
if (error) {
return console.error("Couldn't get access token", e)
}
// ... access_token ready to use to fetch data and return to client
// even serve access_token back to client for use in `gapi.analytics.auth.authorize`
})
If you went the "pass the access_token back to client" route:
gapi.analytics.auth.authorize({
'serverAuth': {
access_token // received from server, through Ajax request
}
})
FIXED NOW! But I can't answer my own question yet. See my comment below. And thanks for helping.
I've searched and searched and read the docs and still can't figure this out.
I have a web page about an event. There's also a public Facebook "event" for my event. I'm trying to use the FB Javascript SDK to get the number of attendees for the Facebook event and add it to the number of people who've registered through the website.
I've created an app and I have an appID and secret string. I can get an access token from:
https://graph.facebook.com/oauth/access_token?client_id=XXXX&client_secret=XXXXX&grant_type=client_credentials
and I can then use that access token to get the attendees for a public event:
https://graph.facebook.com/331218348435/attending?access_token=XXXXXXXXX
That's all fine.
I'm now trying to do this same thing using the Javascript SDK.
I've loaded the SDK and done an init:
FB.init({
appId : 'XXXXXXXX',
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
xfbml : true // parse XFBML
});
and I know the SDK is working because I can get an object with the data that doesn't need an access token:
FB.api( '/331218348435', function (response) { console.log ( response ) } );
but when I try to get the attendee data that needs the access token:
FB.api( '/331218348435/attending', function (response) { console.log ( response ) } );
I get an OAuthException: "An access token is required to request this resource."
All the tutorials and information I can find all refers to using the .login method, but I don't want a user to login, I want to login using my app ID without any user interaction.
I'd assumed that the API took the SDK's init request and granted me an access token when I called the .init method, the authentication being done against my website's address (the HTTP referrer - yes I have set my website URL in the Facebook app settings).
Any ideas what might be causing this to not work? How can I get the access token using the Javascript SDK without doing a .login? Am I missing a step? Is this even possible?
Thanks
Form what the rather circular documentations says, getting the attending feed requires a 'generic access_token`. In Facebook terms:
Any valid access_token
Any valid access token returned by our APIs. An access token may not be valid if, for example, it has expired. No special permissions are required. Occasionally, this is referred to as a generic access_token.
So this means that you can use any token you like to access the attending feed, as long as the event is public. The easiest access token to get seems to be an app token: http://developers.facebook.com/docs/authentication/#applogin. You can get this token using only your App ID and Secret, and no user interaction is required.
To summerise the link content: You can get an application access token by sending a GET request to
https://graph.facebook.com/oauth/access_token?client_id=YOUR_APP_ID&client_secret=YOUR_APP_SECRET&grant_type=client_credentials
You can then use that access_token to make the call for you attending list
FB.api('MyEvent/attending?access_token=ACCESS_TOKEN');
Oh. OK.
Well, for whatever reason, I went away and had my dinner and when I come back it's working fine.
When I updated the settings for my app Facebook said it might take a few minutes for the change to get around the servers. Turned out to take over an hour!
My code was fine.
Thanks for your help.