Google Push notification giving ParseError - google-api

I am trying to use google push notification for calendar to be notified if a user's event's start or/and end dateTime changes.
I have followed all the steps from push doc regarding registering and verifying my domain.
My code is as follows:
#blueprint.route("/notifications", methods={'GET','POST'})
def timeBlocker():
email = '....#gmail.com'
user = User.query.filter_by(email=email).first()
thecredentials = {
'client_id': os.getenv('client_id'),
'client_secret':os.getenv('client_secret'),
'refresh_token':user.refresh,
'grant_type': 'refresh_token',
}
req = requests.post(url='https://oauth2.googleapis.com/token', data = thecredentials)
req_ = req.json()
accessToken = req_["access_token"]
print('access token is ' + accessToken)
body = {
'id': '01234567-89ab-cdef-0123456789ab',
'type': 'web_hook',
'address': 'https://dayliii.com/notifications'
}
headers = {'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json'
}
calendar = '....#group.calendar.google.com'
req = requests.post(url='https://www.googleapis.com/calendar/v3/calendars/....#group.calendar.google.com/events/watch', data = body, headers=headers)
return str(req.json())
Error:
{'error': {'errors': [{'domain': 'global', 'reason': 'parseError', 'message': 'Parse Error'}], 'code': 400, 'message': 'Parse Error'}}
I tried converting from double quotes to single quote as this similar post suggested yet it didn't work.
Lastly, I was curious to know if I should register & verify domain ownership of 'http://localhost:5000/' when working with push notifications in dev mode? As that's the error I am expecting to get that not sure about the way around it.

The data parameter in request() function accepts json format.
You can try converting your body variable into a json string using json.dumps().
Your request code should look like this:
req = requests.post(url='https://www.googleapis.com/calendar/v3/calendars/....#group.calendar.google.com/events/watch', data = json.dumps(body), headers=headers)

Related

Google Calendar Push Notification: 'reason' : 'notFound'

I want to setup a push notification for the user's calendarID per the doc.
#blueprint.route("/notifications", methods={'GET','POST'})
def timeBlocker():
email = request.json.get('email')
user = User.query.filter_by(email=email).first()
acessToken = accessToken(email, user.refresh)
body = {
'id': '01234567-89ab-cdef-0123456789ab',
'type': 'web_hook',
'address': 'https://<registeredDomain>/notifications'
}
headers = {'Authorization': 'Bearer ' + acessToken,
'Content-Type': 'application/json'
}
calendarID = '...#group.calendar.google.com'
req = requests.post(url=f'https://www.googleapis.com/calendar/v3/calendars/{calendarID}#group.calendar.google.com/events/watch', data = json.dumps(body), headers=headers)
return str(req.json())
However, I keep on getting:
{'error': {'errors': [{'domain': 'global', 'reason': 'notFound', 'message': 'Not Found'}], 'code': 404, 'message': 'Not Found'}}
which isn't meaningful. At the same time, the doc gives such code as an example:
POST https://www.googleapis.com/calendar/v3/calendars/my_calendar#gmail.com/events/watch
Authorization: Bearer auth_token_for_current_user
Content-Type: application/json
with my_calendar#gmail.com taken a calendarID when in reality the calendarIDs I've been receiving all end with #group.calendar.google.com. Thus, I am not sure if this is causing the case but keen to know how I shall fix the error.

Bing spell check api doesn't work - error code 404

I've followed through the guide written here
https://learn.microsoft.com/en-us/azure/cognitive-services/bing-spell-check/quickstarts/python, but I'm getting a 404 error code. This is the code from the guide:
import requests
import json
api_key = myke
example_text = "Hollo, wrld" # the text to be spell-checked
endpoint = "https://api.cognitive.microsoft.com/bing/v7.0/SpellCheck"
data = {'text': example_text}
params = {
'mkt':'en-us',
'mode':'spell'
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Ocp-Apim-Subscription-Key': api_key,
}
response = requests.post(endpoint, headers=headers, params=params, data=data)
json_response = response.json()
print(json.dumps(json_response, indent=4))
But when I create a resource, the endpoint I get is either https://api.bing.microsoft.com/ or https://spellcheck3.cognitiveservices.azure.com/ depending on the guide.
How do I correctly run this code?
The code as written in the guide doesn't seem to work; here's a solution I found.
search_url = "https://api.bing.microsoft.com/v7.0/spellcheck"
search_term = "wrld helath"
params = {
'mkt':'en-us',
'mode':'spell',
'text' : search_term
}
headers = {"Ocp-Apim-Subscription-Key": subscription_key}
response = requests.get(search_url, headers=headers, params=params)
response.raise_for_status()
search_results = response.json()

Handling base64 string as application/pdf for a single endpoint on API Gateway

We have an API that has multiple different endpoints, as you'd expect. We have the requirement to add a new endpoint which will return an application/pdf along with the file data.
To do this, we return the following:
return {
statusCode: 200,
headers: {
'Content-Type': 'application/pdf',
'Content-disposition': `attachment; filename=${filename}.pdf`,
'Accept': 'application/pdf',
},
body: fileData,
isBase64Encoded: true,
};
The isBase64Encoded only works when a binary media type is set in the API Gateway. As detailed here:
https://medium.com/#ngchiwang/aws-api-gateway-lambda-return-binary-image-ba8faa660839
The issue we have is that by setting the binary media type to * / * (no spaces) on the API Gateway, this, in turn, affects all other endpoints on the API.
Example This breaks one endpoint on the OPTIONS cors check, returning an InternalServerErrorException without reason. This endpoint is just a GET with no data in the request.
Does this mean we need a separate API just for this one endpoint, or is there a way we can include this in the same APIG?
For further clarification, this is a POST that includes a small amount of JSON in the request: {"someValue":1234} and returns the above application/pdf content type.
I'm just tackling this issue and resolved it like this:
Send base 64 string just as normal json response and handle the pdf part on the client
const sendRes = (status:number, body:any) => {
var response = { statusCode: status, headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) };
return response;
};
return sendRes(201, {pdf:your-base64-string} );
Then on the client (Nuxt in my case):
let res = await this.$store.dispatch('pdf/makePdf')
const linkSource = `data:application/pdf;base64,${res.data.pdf}`;
const downloadLink = document.createElement("a");
const fileName = "your-pdf-filename.pdf";
downloadLink.href = linkSource;
downloadLink.download = fileName;
downloadLink.click();
This open a download window and lets you save the file locally

Getting "unauthorized" error when trying to refresh access token

I am trying to refresh the access token for a user following this tutorial.
However, I am getting
{
"error":"unauthorized",
"error_description":"Full authentication is required to access this resource"
}
and I do not see what's missing.
The following is how I am constructing the oauth/refresh request in my Angular application:
refreshToken() {
this.logger.info('Attempting to refresh access token');
const headers = new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')
// CLIENT_ID:CLIENT_SECRET
.set('Authorization', 'Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=');
const payload = {
refresh_token: AuthenticationService.getRefreshToken(),
grant_type: 'refresh_token'
};
return this.http.post(environment.apiUrl + '/oauth/refresh',
payload, {headers: headers})
.pipe(map(r => r));
}
What am I missing here?
Okay, I was almost right.
First, I did use the wrong endpoint /oauth/refresh - I don't know why I thought this existed. It has to be /oauth/token.
Also payload gets send via URL parameters:
const payload = `refresh_token=${AuthenticationService.getRefreshToken()}&grant_type=refresh_token`;
So in the end I got this working with:
refreshToken() {
this.logger.info('Attempting to refresh access token');
const headers = new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8')
.set('Authorization', 'Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=');
const payload = `refresh_token=${AuthenticationService.getRefreshToken()}&grant_type=refresh_token`;
return this.http.post(environment.apiUrl + '/oauth/token',
payload, {headers: headers})
.pipe(map(r => r));
}

Figuring out who has authenticated with basicAuth on Node while processing a POST request

I am using basicAuth to authenticate POSTs on a specific address.
On the client side I am using a command of the form:
$.ajax({
type: "POST",
accepts: "text/plain",
url: "http://localhost:3000/somewhere",
data: JSON.stringify(something),
contentType: "application/json; charset=UTF-8",
dataType: "json",
success: function(data) {
window.alert("Received back: '" + data + "'");
},
username: theUsername,
password: "a password"
});
This is working fine, in the sense that the username stored in theUsername passes the authentication mechanism that I have on node. While the user is authenticated I can print a console.log statement and see who has actually authenticated (I am not validating the password at the moment). But then the actual processing starts for the POST request. However, at that point how can I figure out the username and the password used in the original request? I tried to look on the headers of the request but I don't see anything there.
When you receive a Basic authentication request you should be able to read the "authorization" header in req.headers.authorization You have to pull out the the base64 encoded credentials and then decode them. Presumably, in Express you use req.header("authorization") or req.get("authorization")
For a standalone example, take a look at https://gist.github.com/charlesdaniel/1686663 which I have copied underneath for future reference
var http = require('http');
var server = http.createServer(function(req, res) {
// console.log(req); // debug dump the request
// If they pass in a basic auth credential it'll be in a header called "Authorization" (note NodeJS lowercases the names of headers in its request object)
var auth = req.headers['authorization']; // auth is in base64(username:password) so we need to decode the base64
console.log("Authorization Header is: ", auth);
if(!auth) { // No Authorization header was passed in so it's the first time the browser hit us
// Sending a 401 will require authentication, we need to send the 'WWW-Authenticate' to tell them the sort of authentication to use
// Basic auth is quite literally the easiest and least secure, it simply gives back base64( username + ":" + password ) from the browser
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
res.end('<html><body>Need some creds son</body></html>');
}
else if(auth) { // The Authorization was passed in so now we validate it
var tmp = auth.split(' '); // Split on a space, the original auth looks like "Basic Y2hhcmxlczoxMjM0NQ==" and we need the 2nd part
var buf = new Buffer(tmp[1], 'base64'); // create a buffer and tell it the data coming in is base64
var plain_auth = buf.toString(); // read it back out as a string
console.log("Decoded Authorization ", plain_auth);
// At this point plain_auth = "username:password"
var creds = plain_auth.split(':'); // split on a ':'
var username = creds[0];
var password = creds[1];
if((username == 'hack') && (password == 'thegibson')) { // Is the username/password correct?
res.statusCode = 200; // OK
res.end('<html><body>Congratulations you just hax0rd teh Gibson!</body></html>');
}
else {
res.statusCode = 401; // Force them to retry authentication
res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
// res.statusCode = 403; // or alternatively just reject them altogether with a 403 Forbidden
res.end('<html><body>You shall not pass</body></html>');
}
}
});
server.listen(5000, function() { console.log("Server Listening on http://localhost:5000/"); });

Resources