Fitbit URL callback giving a response of NULL - aws-lambda

I'm having trouble getting a response from a callback uri and I would really appreciate any help you could give me.
I am trying to use the Fitbit API which requires you to use a callback url to get an Auth Code.
Workflow:
1. Go to Fitbit url to get user to allow the app access to their personal data.
2. User agrees to the conditions
3. User gets redirected to my API
4. The API returns the code from (Code is located in URL and I can access it)
5. I console.log the code out to verify it
6. API returns the code
7. I work with code then exchanging it for an access token.
The problem is that I don't return the code (Or anything )when I return to the app even though I can console.log it on the API. The response I get is NULL
Here is the URL:
url = "https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https://REDIRECT_URL&scope=activity%20heartrate%20location%20nutrition%20profile%20settings%20sleep%20social%20weight&expires_in=604800";
I then open the URL in the InAPPBrowser successfully:
if (url !== "") {
const canOpen = await Linking.canOpenURL(url)
if (canOpen) {
try {
const isAvailable = await InAppBrowser.isAvailable()
if (isAvailable) {
const result =InAppBrowser.open(url, {
// iOS Properties
dismissButtonStyle: 'done',
preferredBarTintColor: 'gray',
preferredControlTintColor: 'white',
// Android Properties
showTitle: true,
toolbarColor: '#6200EE',
secondaryToolbarColor: 'black',
enableDefaultShare: true,
}).then((result) => {
console.log("Response:",JSON.stringify(result))
Linking.getInitialURL().then(url => {
console.log("Tests: ",url)
this._setTracker(url as string);
});
})
} else Linking.openURL(url)
} catch (error) {
console.log("Error: ",error)
}
}
}
From here the URL opens successfully.
Here is the API now which is done in Typescript on AWS serverless and Lambda
export const handler: APIGatewayProxyHandler = async (event, _context, callback) =>{
let provider = event.path
//prints code
let x = event.queryStringParameters
console.log("Code: ",x)
const response = {
statusCode: 200,
body: "Success"
};
return response;
}
Please let me know if further detail is required?
Thank you!

Right so it turns out what I was doing was correct apart from the response should have been 301 which is a redirect response.
const response= {
statusCode: 301,
headers: {
"location": `app://CALLBACK RESPONSE ADDRESS?type=${provider}`
},
body: "Boom"
}

Related

Window object is undefined after deploy to netlify

I want to build an email verification. After the user registers, the user gets an email and clicks on it for verification purposes. The email-link invokes a netlify lambda function (api end point). Inside the link is a jwt token, which I decode on the backend. I used
window.location.href
for it and sliced the part I needed and decoded it. On localhost, it works fine, however, if I deploy it to netlify, I get an
window is undefined
error. I read that you have to check for
typeof window !== 'undefined'
However, if I add that to my lambda function I don't get any console.log statements.
exports.handler = async (event, context, callback) => {
if (typeof window !== 'undefined') {
let url = window.location.href
let index = url.indexOf("=");
let token = url.slice(index+1)
console.log(token, 'token here')
const decoded = jwt.verify(token, process.env.SECRET);
console.log('confirm registration route triggered',decoded)
if (decoded) {
const { email } = decoded;
console.log(decoded, 'decoded here')
User.findOneAndUpdate({email: email}, {verified: true },(...e)=>{
console.log(e)
});
} else {
console.log('could not update user')
//redirect user to page with message about email confirmation link expiration
//and proposal to register again
}
console.log('confirm registration got invoked')
}
return {
statusCode: 400,
body: "Oops"
}
};
I read that the function first runs on the server when deployed and afterwards on the client. Seems like it does not run on my client, as I invoke the api-endpoint directly? I'm quite a beginner when it comes to API-Endpoints, thanks for reading!
In case you have the same issue when deploying to netlify, you have to run
event.queryStringParameters
which gives you access to the query parts of your url.

Resending a graphql mutation after re-authenticating using Apollo's useMutation

I have an issue where we're using apollo client and specifically the useMutation react hook to perform mutation calls to our GraphQL Server.
At certain times, the server may return a 401 unauthorized response - at which point, we can make a call to special endpoint which re-authenticates the client and refreshes the cookie/token whatever.
I want to be able to re-run the same mutation again once the client is re-authenticated. So basically I would like to know if it is possible to do the following:
useMutation --> Receive 401 Unauthorized --> call to refresh token --> rerun same initial mutation
This is how our useMutation looks like:
const [mutationFunction, { data, ...rest }] = useMutation(query, {
onError(_err: any) {
const networkError = error?.networkError as any;
if (networkError?.statusCode === 401 && !refreshFailed) {
// eslint-disable-next-line prefer-destructuring
loading = true;
error = undefined;
fetch('/authentication/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
})
.then(response => response.json())
.then(token => {
localStorage.setItem(jwtLocalStorageKey, token);
// re fetch here
})
.catch(() => {
refreshFailed = true;
});
} else {
showAlert(_err.message, 'error');
}
}
});
and this is how we call it currently:
const {
mutationFunction: updateTournamentUserMutation,
loading: updateTournamentUserLoading,
error: updateTournamentUserError,
data: updateTournamentUserData
} = useMutationHook(gqlUpdateTournamentUser);
updateTournamentUserMutation({ variables: { input } });
Because we're using hooks and the way we're using it above, I'm not entirely sure how we can save or reuse the same data that is initially sent in the first mutation (that is the mutation parameters)
Is it possible to do so using the current way we're doing it?

How to get each http body updates on angular Http request?

I'm using an express api (my back-end) and an angular app (my front-end).
One express js end point (let's call it '/foo') is processing a lot of files,
i send data using res.write() after each treatment so the http response body is update.
I would like to get this update on my angular app.
I was using ajax in a previous version and it worked fine with ajax call :
xhrFields: {
// Getting on progress streaming response
onprogress: function(e)
{
var progressResponse;
var response = e.currentTarget.response;
if(lastResponseLength === false)
{
progressResponse = response;
lastResponseLength = response.length;
}
else
{
progressResponse = response.substring(lastResponseLength);
lastResponseLength = response.length;
}
actualResponse += progressResponse
}
Unfortunatly i found nothing to get partial http body. I tried to use 'reportProgress' Parameter but it's not working.
For some more context my front-end angular code:
service.ts :
setHolidaysDirectory(holidaysKey: string, path: string): Observable<Object>{
const setHolidayDirectoryStreamHttpRequest =
new HttpRequest('POST', 'http://localhost:8089/holidays/pictures/edit', { 'key': holidaysKey,
'path': path
}, {headers: this._httpHeaders, reportProgress: true, responseType: 'text'});
// pipe stream answer
return this._http.request(setHolidayDirectoryStreamHttpRequest);
}
and my component just call the service and subscribe :
this._holidaysService
.setHolidaysDirectory(key, finalHolidaysForm.path)
.subscribe((stream) => {
console.log('new answer');
console.log(stream);
}, error => console.log(error));
But unfortunatly i got empty answer and all the http body is recovered after res.end() (server side)
Can anyone help pls !
Thank a lot !

Ember Simple Auth on Firefox: authentication throws Error

I am extending Ember Simple Auth's base authentication class to allow authentication with Google. So far, it works on Safari 8 and Chrome 41 (both on Yosemite) with no errors. However, on Firefox 35, it throws an Error that does not occur on the other browsers. Here is my Google authenticator class:
App.GoogleAuthenticator = SimpleAuth.Authenticators.Base.extend({
// constants for Google API
GAPI_CLIENT_ID: 'the client id',
GAPI_SCOPE: ['email'],
GAPI_TOKEN_VERIFICATION_ENDPOINT: 'https://www.googleapis.com/oauth2/v2/tokeninfo',
// method for scheduleing a single token refresh
// time in milliseconds
scheduleSingleTokenRefresh: function(time) {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
Ember.run.later(self, function() {
gapi.auth.authorize({
client_id: self.GAPI_CLIENT_ID,
scope: self.GAPI_SCOPE,
immediate: true
}, function(data) {
if (data && !data.error) {
resolve(data);
} else {
reject((data || {}).error);
}
});
}, time);
});
},
// WIP: recursive method that reschedules another token refresh after the previous scheduled one was fulfilled
// usage: scheduleTokenRefreshes(time until token should refresh for the first time, time between subsequent refreshes)
// usage: scheduleTokenRefreshes(time between refreshes)
scheduleTokenRefreshes: function(time1, time2) {
var self = this;
// if there is a time2, schedule a single refresh, wait for it to be fulfilled, then call myself to schedule again
if (!Ember.isEmpty(time2)) {
self.scheduleSingleTokenRefresh(time1)
.then(function() {
self.scheduleTokenRefreshes(time2);
});
// if there isn't a time2, simply schedule a single refresh, then call myself to schedule again
} else {
self.scheduleSingleTokenRefresh(time1)
.then(function() {
self.scheduleTokenRefreshes(time1);
});
}
},
// method that restores the session on reload
restore: function(data) {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
console.log(data);
if (Ember.isEmpty(data.access_token)) {
reject();
return;
}
// schedule a refresh 15 minutes before it expires or immediately if it expires in < 15
var timeNow = Math.floor(Date.now() / 1000);
var expiresAt = +data.expires_at;
var timeDifference = expiresAt - timeNow;
var schedulingDelay = Math.floor(timeDifference - 15 * 60);
schedulingDelay = schedulingDelay < 0 ? 0 : schedulingDelay;
self.scheduleTokenRefreshes(schedulingDelay * 1000, 45 * 60);
resolve(data);
});
},
// method that authenticates
authenticate: function() {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
gapi.auth.authorize({
client_id: self.GAPI_CLIENT_ID,
scope: self.GAPI_SCOPE
}, function(data) {
if (data && !data.error) {
// schedule a refresh in 45 minutes
var schedulingDelay = 45 * 60;
self.scheduleTokenRefreshes(schedulingDelay * 1000);
resolve(data);
} else {
reject((data || {}).error);
}
});
});
},
// method that logs the user out and revokes the token
invalidate: function(data) {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
// send a GET request to revoke the token
Ember.$.ajax({
type: 'GET',
url: 'https://accounts.google.com/o/oauth2/revoke?token=' + self.get('session.access_token'),
contentType: 'application/json',
dataType: 'jsonp'
})
.done(function(successData) {
resolve(successData);
})
.fail(function(error) {
reject(error);
});
});
}
});
When the popup window closes after a successful login on Google's end, this error appears on Firefox's console:
Error: Assertion Failed: Error: Permission denied to access property 'toJSON' ember.js:13749
"__exports__.default<.persist#http://127.0.0.1/~jonchan/test/bower_components/ember-simple-auth/simple-auth.js:1524:1
__exports__.default<.updateStore#http://127.0.0.1/~jonchan/test/bower_components/ember-simple-auth/simple-auth.js:1195:11
__exports__.default<.setup#http://127.0.0.1/~jonchan/test/bower_components/ember-simple-auth/simple-auth.js:1149:9
__exports__.default<.authenticate/</<#http://127.0.0.1/~jonchan/test/bower_components/ember-simple-auth/simple-auth.js:1066:13
tryCatch#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:47982:16
invokeCallback#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:47994:17
publish#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:47965:11
#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:29462:9
Queue.prototype.invoke#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:848:11
Queue.prototype.flush#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:913:13
DeferredActionQueues.prototype.flush#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:718:13
Backburner.prototype.end#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:143:11
createAutorun/backburner._autorun<#http://127.0.0.1/~jonchan/test/bower_components/ember/ember.js:546:9
" ember.js:29488
Here is the version information:
DEBUG: Ember : 1.9.1
DEBUG: Ember Data : 1.0.0-beta.14.1
DEBUG: Handlebars : 2.0.0
DEBUG: jQuery : 2.1.3
DEBUG: Ember Simple Auth : 0.7.2
The most confounding thing is that this only appears on Firefox. Is it a bug in Ember Simple Auth or Ember? How do I fix it?
I do not know about only Firefox throwing an error (I've had a similar error with Chrome 40), but there is a bug in ember-simple-auth 0.7.2 with Ember 1.9 that prohibits sending an actual error response in the authenticate method in the authenticator.
If you return reject() in the rejection function of authenticate it will not throw an additional error. This will however not propagate the errorstatus or message, so I consider this a bug.
A work-around was proposed on github about this issue by setting Ember.onerror=Ember.K temporarily so additional errors will not be propagated, although it will propagate the original authenticate rejection with the error-status.
The issue in the github repo only mentions problems with testing this, but I've had this problem in normal code.
see: https://github.com/simplabs/ember-simple-auth/issues/407
Turns out the error was on the resolve part of the authenticate method. Here is what fixed it:
App.GoogleAuthenticator = SimpleAuth.Authenticators.Base.extend({
authenticate: function() {
return new Ember.RSVP.Promise(function(resolve, reject) {
gapi.auth.authorize({
client_id: 'the client id',
scope: ['the scopes'],
}, function(data) {
if (data && !data.error) {
resolve({
access_token: data.access_token // !! passing the entire 'data' object caused the error somehow
});
} else {
reject((data || {}).error);
}
});
});
},
// ...
});
I'm still not quite sure why this caused the error. Perhaps the Google API's response (in its entirety) is somehow incompatible with Ember Simple Auth.

How can I send an AJAX request to a node.js server via HTTPS?

I have the following node.js server set up listening to port 9001
var https = require('https');
var fs = require('fs');
var qs = require('querystring');
var options = {
key: fs.readFileSync('privatekey.pem'),
cert: fs.readFileSync('certificate.pem')
};
https.createServer(options, function (req, res) {
res.writeHead(200);
console.log('Request Received!');
console.log(req.method);
if (true || req.method == 'POST') {
var body = '';
req.on('data', function (data) {
body += data;
});
req.on('end', function () {
console.log(body);
var POST = qs.parse(body);
console.log(POST);
});
}
res.end("hello, world\n");
}).listen(9001);
and I am trying to get this server to respond to an AJAX call
function form_save()
{
console.log("submitted!");
var data_obj = {
data1: "item1",
data2: "item2"
}
$.ajax({
url: 'https://adam.testserver.com:9001/',
type: "POST",
dataType: "json",
data: data_obj,
success: function() {
console.log("success!");
},
complete: function() {
console.log("complete!");
}
});
}
There are two problems occurring with my arrangement. The first is that if I start the server and then click the button that triggers my form_save() the node server does not respond and I get the following error:
submitted!
OPTIONS https://adam.testserver.com:9001/ Resource failed to load
jQuery.extend.ajaxjquery.js:3633
$.ajaxjquery.validate.js:1087
form_savew_worksheet.php:64
confirm_deletew_worksheet.php:95
jQuery.event.handlejquery.js:2693
jQuery.event.add.handlejquery.js:2468
w_worksheet.php:73
complete!
At this point if I access that url directy (https://adam.testserver.com:9001/) I get the expected "hello, world" output as well as the console message "Request Received!
GET". From this point on if I click the button to trigger my AJAX call I get a new error.
submitted!
XMLHttpRequest cannot load https://adam.testserver.com:9001/. Origin
https://adam.testserver.com is not allowed by Access-Control-Allow-Origin.
w_worksheet.php:73
complete!
I don't understand why I get this message as both my form and node server reside on the same server. Thanks for taking the time to read, I appreciate any help I can get on this. I've been stuck for a while now!
You've run into the Cross-Origin Resource Sharing (CORS) specification.
Note the OPTIONS in your output. The OPTIONS HTTP Verb is used by the browser to query the web server about the URL, not to GET its contents or POST data to it.
Your server doesn't respond with the correct header data on a CORS request, so your browser assumes it has no rights to access the data, and refuses to GET or POST to the URL.
If you truly want to let any website in the world run that AJAX request, you can do something similar to the following:
function handleOptions(request, response) {
response.writeHead(200, {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Method": "POST, GET, OPTIONS",
"Access-Control-Allow-Headers": request.headers["access-control-request-headers"]
});
response.end();
}
function server(request, response) {
if(request.method == "POST") {
handlePost(request, response);
} else if(request.method == "OPTIONS") {
handleOptions(request, response);
} else {
handleOther(response);
}
}
https.createServer(sslObj, server).listen(9001);
You can fill in the details and whether you should handle GET separately, and so on (handleOther should return an appropriate error code for each request method you don't support).

Resources