Accessing a public calendar using Google API without requiring a user to log in - node.js

I'd like to access a public calendar using Google's REST API.
Google's calendar API suggests I need an OAuth token to get access to calendars:
https://developers.google.com/google-apps/calendar/auth
However, I'm accessing a public calendar and am doing this from the server so can't/shouldn't have to ask the user to authenticate.
I'm using the node.js api:
googleapis
.discover('calendar', 'v3').execute(function(err, client) {
client.calendar.calendars.get({ calendarId: '***#group.calendar.google.com' })
.execute(function (err, response) {
console.log('response from google', response);
});
});
This returns "Your client has issued a malformed or illegal request. That’s all we know."
Calling .withApiKey('***') after .calendars.get() returns the same error.
Any suggestions?

Oh come on guys! You can fetch data without OAuth without requiring a user to login as long as the calendar is set to be public! I've tested it myself.
$.ajax({
url:"https://www.googleapis.com/calendar/v3/calendars/" + cal_id + "/events?key=" + api_key,
success: function(data) {
console.log(data);
}
});
Just try it.
The documentation is here https://developers.google.com/google-apps/calendar/v3/reference/events/list#examples

As the documentation states more than once
All requests to the Google Calendar API must be authorized by an authenticated user.
Hence, if you want to use the google calendar API you need an authenticated user. This can be easily achieved using modules like Passport.js and Passport-google-oauth.
However, if you can leave aside the API for a moment and
the calendar is public
you want read only access
you can easily grab the public address and consume the calendar through ical, xml or html. Have a look at the UK holidays public calendar: http://imgur.com/UGjPtqp
You can access the data publicly from:
xml https://www.google.com/calendar/feeds/en.uk%23holiday%40group.v.calendar.google.com/public/basic
ical https://www.google.com/calendar/ical/en.uk%23holiday%40group.v.calendar.google.com/public/basic.ics
html https://www.google.com/calendar/embed?src=en.uk%23holiday%40group.v.calendar.google.com&ctz=Europe/London
At this point, it's trivial to fetch such resources with node and get the data you want.

Related

Is there is a way to grant my nodejs application access to my google calendar?

so I have a nodejs application that sends invites to an event in google calendar to some people, for now I am using my gmail account and OAuth Playground to get a temporary Access token and it works, but the access token is just available for minutes and each time I need to refresh the Access token manually and give access to my google account calendar and this is the problem, now I want to make something dynamic without me interfering in the process.
This application is hosted in wix.
any suggestions ?
Thanks
IF you are only letting them access a calendar that you own and control then you can use a service account.
let google = require('googleapis');
let privatekey = require("./privatekey.json");
  Now let’s use the private key to configure a JWT auth client and authenticate our request.
// configure a JWT auth client
let jwtClient = new google.auth.JWT(
privatekey.client_email,
null,
privatekey.private_key,
['https://www.googleapis.com/auth/calendar']);
//authenticate request
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
console.log("Successfully connected!");
}
});
Create service account credentials in google developer console. then take the service account email address and share the calendar with it via the google calendar website. The rest of the code you have should be the same just swap out the auth section with this.
To access the calendar API please follow the Quickstart
The code provided creates a refresh token that will automatically generate a new access token for you, whenever the old one expires.
Be careful with unnecessary using service accounts, especially for adding invitees to a calendar event - there are currently issues with this feature.

401 Error when sending data to Stripe `Customers` API

I want to create a new customer in Stripe upon form submit and add that customer's credit card to their account. As of now, I'm using the following code upon submit in my React App. The create customer call is then made separately from my server:
async submit(ev) {
let {token} = await this.props.stripe.createToken({name: "Name"});
let response = await fetch("https://api.stripe.com/v1/customers", {
method: "POST",
headers: {"Content-Type": "text/plain"},
body: token.id
});
When sending that data, I get a 401 error on the let response = ... line. I know that a 401 is an auth error, but my test API keys are definitely correct and don't have limits on how they can access my stripe account. Can anyone advise?
The issue here is that you are trying to create a Customer object client-side in raw Javascript. This API request requires your Secret API key. This means you can never do this client-side, otherwise anyone could find your API key and use it to make refunds or transfer for example.
Instead, you need to send the token to your own server. There, you will be able to create a Customer or a Charge using one of Stripe's official libraries instead of making the raw HTTP request yourself.
In my case, it's throwing the error due to a missing of stripe public key
var stripe = Stripe('{{ env("STRIPE_KEY") }}');
then I pass the public key as above, and it worked like a charm.

Google Sheets v4 API calls using Node.js and the result is inconsistent

I'm trying to extract a short data from Google Sheet, the flow is as follow;
Authorizing the user (access type: offline)
Storing/caching the token per the oAuth2 flow.
Loading the cached token and use it to extract the data.
But once I uploaded on the server, the returned result become inconsistent.
I don't know what causing it because most of the time it returns the right data, and it works perfectly on my local.
Error Message;
Google sheet API read: Error: The request is missing a valid API key.
Server Information: Google App Engine, trial version;
runtime: nodejs
env: flex
The node.js code is below;
getUser(id) {
this.sheets.spreadsheets.values.get({
auth: this.oauth2Client,
spreadsheetId: id,
range: 'Sheet1!A1:E'
}, (err, response) => {
if(err) {
this.reject({
status: 0,
message: "Google sheet API read: " + err
});
return;
}
// The rest is omitted.
})
}
You can try the test request here (I haven't added any security measure for debugging purposes);
https://deep-contact-179901.appspot.com/v1/google/users
Based from this thread, all Google APIs require that you create a project on Google Developer Console and identify yourself and your application, even to access public data. Go to Google Developer Console and create a public api key. Remember to activate the Google Sheets API. Then just add key=[YourKey] as a parameter on your request.
Google sheet API read: Error: The request is missing a valid API key.
Means that you have not identified yourself to Google. In 2015 Google Start to require that we identify ourselves you cant just use a Google API without telling google who you are. You do that by creating a project on Google developer console. Create an API key and use that API key in all of your requests. This only works with Public data.
Note: With private user data you would need to use OAuth and use either access_token=your token or set the header
An access token is not the same as a API Key.
Hope this helps!

NodeJS Google Api client: how do I know the access token is expired?

I am working on Nodejs Google Api client Oauth process. I follow what the code example for oauth, https://github.com/google/google-api-nodejs-client/blob/master/examples/oauth2.js.
I have one question. How do I check if the access token is expired and how do I use the refresh token to get another access token again?
To be more specific, let's say get access to google+ user profile, so I use the access token to get user profile:
getAccessToken(oauth2Client, function() {
// retrieve user profile
getUserProfile(client, oauth2Client, 'me', function(err, profile) {
if (err) {
console.log('An error occured', err);
return;
}
console.log(profile.displayName, ':', profile.tagline);
});
});
In addition, in the client side of the application(backbonejs), if I am attempting to use google api JS client to access the google drive api (not google plus), I am not sure if I can use the access token I get from server side of the application (nodejs) or I have to do another OAuth using google api JS client.
Best practice to determine if an access token is expired is to try and use it. Although the bundle returned includes the *expires_in* parameter, indicating the number of seconds until the access token expires, this isn't reliable, since it may be revoked and replaced for other reasons at any time.
The procedure then typically is
Attempt to make the call using the access token
If you get an "unauthorized" response, use the referesh token to get a new access token. If this fails, your permission has been revoked
Attempt to make the call using the new access token again
If you're using the library to do other Google API calls - this will be handled for you automatically.

Google Plus auth for REST API

I'm trying to create a rest api for a service I'm working on.
The service has two parts to it - the website and the mobile client. Basically, the mobile device keeps its location up to date via the api, the website displays the data via the api.
Seeing as my application only targets Android, I'm hoping to use 'Sign in with Google' as the authentication mechanism for both the mobile and website clients.
The API is using Node.js and Express.js. I'm running into trouble when generating new user accounts though. Seeing as I don't want to trust data from the client, my expected sign up process was something like this:
Through the website:
User visits website, hits 'Sign up with Google'.
User accepts the app request to see their Google details.
Website gets a google auth token back, which it sends to the api.
API contacts google with that auth token to get the user details.
API creates a new user in the database along with my own form of access token.
API returns my own access token to the client for future request signing.
Through the Android app:
User downloads the app, runs and hits 'Sign up with Google'.
User accepts authorisation step presented by google.
App gets a token, which it sends to the API.
API contacts google with that auth token to get the user details.
API realises the user exists and registers this new device with that user.
API returns my own access token to the app for future request signing.
I'm running into a lot of trouble here as soon as the token gets to the server though. Every time I use the token generated, I just get an error 'Invalid Credentials'.
Initially I started to use Passport.js, but what I found was this. In the documentation it states setup happens like so:
passport.use(new GoogleStrategy({
returnURL: 'http://www.example.com/auth/google/return',
realm: 'http://www.example.com/'
},
function(identifier, profile, done) {
User.findOrCreate({ openId: identifier }, function(err, user) {
done(err, user);
});
}));
But when I log the contents of 'identifier' it is actually something like
https://www.google.com/accounts/o8/id?id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
I assume the ID is something unique to me but I can't seem to discover exactly what it is. Furthermore I don't know if it is time-limited or will last forever. As a final problem, I don't even know if I can get that same value when signing up via Android because I don't know where the value comes from. It's not the kind of API access token that I was expecting. When I output the contents of profile, it's just my name and email address - nothing that I can use for contacting the Google API with to verify the user.
The above solution I don't like anyway because it means the server hosting the client site has to make an api request in order to pass the id to the api. Or it sends the id details to the client so that it can pass them on to the api server. Or, the website server puts it into the api database, which is also a bad solution.
So next I figured I would use the javascript library from the Google sign in docs. I have something like this:
Website Client:
<script type="text/javascript">
function createUser(token)
{
$.ajax({
url:"http://api.example.com/user",
dataType: 'jsonp',
data: 'token='+token,
success:function(json){
alert("Success: "+json);
},
error:function(jqXHR, textStatus, errorThrown){
alert("Error "+textStatus+" "+errorThrown);
}
});
}
function signinCallback(authResult)
{
if(authResult['access_token'])
{
document.getElementById('signinButton').setAttribute('style', 'display: none');
alert('RES: '+JSON.stringify(authResult));
createUser(authResult['access_token']);
}
else if(authResult['error'])
{
alert('There was an error: ' + authResult['error']);
}
}
</script>
Node API user handling function:
function(req, res)
{
var callback = req.query.callback;
if(callback == null)
{
res.send("{valid:false}");
}
else
{
var token = req.query.token;
if(token == null)
{
res.send("{valid:false}");
}
else
{
var oauth2Client = new OAuth2Client('xxxxxx', 'xxxxxx', '');
oauth2Client.credentials = {
access_token: token
};
googleapis
.discover('plus', 'v1')
.execute(function(err, client){
if(client == null)
{
console.log("Client is null");
}
else
{
var request1 = client.plus.people.get({ userId: 'me' })
.withApiKey('xxxxxx');
request1.execute(function(err, result){
console.log("Result: " + (err ? err.message : result.displayName));
});
}
});
res.set('Content-Type', 'text/javascript');
res.send(callback+"({ret:'User Test'});");
}
}
}
This works fine on the client side - I see the alert box with my access token and some other details. The trouble is that when I call the google api functions on my api server for getting the user details, I get 'Invalid Credentials' returned. I assume this is because I generated the access token in javascript for a website and I'm using it from somewhere else.
So, that pretty much leaves me out of ideas. Is there an easy way to achieve this that I'm missing? I just want to be able to generate a token from a website and from an Android app that I can use on the server for validating the user's details. The generated token doesn't even need to work on the website or the Android app, just from the api server. The API server can't do the process of directing the user to Google for authorisation though because the user doesn't directly interact with it.

Resources