Handle POST request with Firebase function - firebase

I am using fulcrum to collect data. fulcrum has a webhook feature
I have created a firebase function and linked the firebase function to fulcrums webhook feature with the functions URL. https://us-central1-example.cloudfunctions.net/fulcrumHook
Here is my existing function.
exports.fulcrumHook = functions.https.onRequest((request, response) => {
console.log(response.data.form_id)
response.send(200)
})
Through hours of debugging, in the logs I can see that the data I want is coming through but I am struggling to access it in the function itself.
When I log the request I get IncomingMessage { _readableState: ReadableState { objectMode: false,.....
When I log the response I get ServerResponse { domain: null, _events: [Object: null prototype] { finish: [ [.... as well as the body much further down with the actual data i need in it.
I have search for all the keywords i can think of about how to handle this data but I am completely stumped.
Do I need to handle the response like a promise with response.then(data => ...stuff)
Do I need to establish a connection like a socket with response.on('data', (data) => ...stuff)

Everything you need is in the documentation for HTTP triggers.
The request and response are essentially Express Request and Response objects.
Used as arguments for onRequest(), the Request object gives you access to the properties of the HTTP request sent by the client, and the Response object gives you a way to send a response back to the client.
You can click through to those linked APIs to understand in detail how they work.
Data passed to the function can be found by reading values from the request. If it's a POST request, form values are read like this:
request.body.form_id
The response is sent using response.send(). Just pass it an object that will get automatically serialized as JSON. Or use the linked API for the response object from above to learn more about your options.

Related

Browser not saving cookie sent by Golang backend

I know this question has been asked a bunch of times, but I tried most of the answers and still can't get it to work.
I have a Golang API with net/http package and a JS frontend. I have a function
func SetCookie(w *http.ResponseWriter, email string) string {
val := uuid.NewString()
http.SetCookie(*w, &http.Cookie{
Name: "goCookie",
Value: val,
Path: "/",
})
return val
}
This function is called when the user logs in, and I expect it to be sent to all the other endpoints. This works as expected with Postman. However, when it comes to the browser, I can't seem to get it to remember the cookie or even send it to other endpoints.
An example of JS using an endpoint
async function getDataWithQuery(query, schema){
let raw = `{"query":"${query}", "schema":"${schema}"}`;
let requestOptions = {
method: 'POST',
body: raw,
redirect: 'follow',
};
try{
let dataJson = await fetch("http://localhost:8080/query/", requestOptions)
data = await dataJson.json();
}catch(error){
console.log(error);
}
return data;
}
I tried answers like setting SameSite attribute in Golang, or using credential: "include" in JS with no luck.
Thanks to the discussion in the comments, I found some hints about the problem.
Saving cookies (both API and frontend on the same host)
I used document.cookie to save the cookie. I set the options by hand since calling res.cookie on the response of the API fetch only returned the value. An example is document.cookie = `goCookie=${res.cookie}; path=/; domain=localhost;.
Sending cookies
This has been answered before in previous questions and answered again in the comments. The problem was that I used credential:'include' instead of the correct credentials:'include' (plural).
CORS and cookies
In case the API and the frontend are not on the same host you will have to modify both the API and the frontend.
frontend
The cookie has to have the domain of the API since it's the API that requires it, not the frontend. So, for security reasons, you can't set a cookie for a domain (API) from another domain (frontend). A solution would be redirect the user to an API endpoint that returns Set-Cookie header in the response header. This solution signals the browser to register that cookie with the domain attached to it (the API's domain, since the API sent it).
Also, you still need to include credentials:'include' in the frontend.
API
You will need to set a few headers. The ones I set are
w.Header().Set("Access-Control-Allow-Origin", frontendOrigin)
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, withCredentials")
w.Header().Set("Access-Control-Allow-Methods", method) // use the endpoint's method: POST, GET, OPTIONS
You need to expose the endpoint where the frontend will redirect the user and set the cookie in the response. Instead of setting the domain of the API by hand, you can omit it, the browser will fill it with the domain automatically.
To handle the CORS and let JS send the cookie successfully, you will have to set the SameSite=None and Secure attributes in the cookie and serve the API over https (I used ngrok to make it simple).
Like so
func SetCookie(w *http.ResponseWriter, email string) string {
val := uuid.NewString()
http.SetCookie(*w, &http.Cookie{
Name: "goCookie",
Value: val,
SameSite: http.SameSiteNoneMode,
Secure: true,
Path: "/",
})
// rest of the code
}
I recommend you also read the difference between using localStorage and document.cookie, it was one of the problems I had.
Hope this helps.

Firebase push notifications always arrive as an empty message

I have been trying to get push notifications working using firebase. So far I have got as far as successfully sending an empty message "tickle". The problem is adding the message payload seems to have no affect on what the client receives. That is the service worker just sees it as another empty message.
I started by going through googles guide here - https://developers.google.com/web/ilt/pwa/introduction-to-push-notifications
After going through how to send an empty message it says the message payload must be encrypted and suggests using an existing library to do it. To quote - "As with anything related to encryption, it's usually easier to use an actively maintained library than to write your own code".
I tried to use web-push-php which is one of the libraries recommended by googles guide. After having trouble with that i discovered web-php-push doesn't actually support firebase.
Looking on here i find examples that look really simple and don't event encrypt the message payload. It is simply sent in plain json. Doing this has no affect and the receiving end still thinks it's an empty message. See my code below.
I am at a complete loss with this and i'm confused why googles guide says the message data must be encrypted but there are countless examples on SO where it is just send in plain json text.
This is what i am posting from my server to the end point.
POST https://fcm.googleapis.com/fcm/send Authorization: key=[my server
key] Content-Type: application/json {"priority":10,"to":"[subscriber
id]","notification":{"body":"test body","title":"test title"}}
Here is my event listener in my service-worker.js
self.addEventListener('push', function(e) {
var body;
if (e.data) {
body = e.data.text();
} else {
body = "No message "+JSON.stringify(e);
}
var options = {
body: body
};
e.waitUntil(
self.registration.showNotification('Launtel Residential', options)
);
});
When i run the post request above the push notification occurs and triggers the service worker 'push' event as expected but no message data is present. e.data returns null. The 'e' object always just contains a flag set to true. e.isTrusted==true

Encoded / Encrypted body before verifying a Pact

A server I need to integrate with returns its answers encoded as a JWT. Worse, the response body actually is a json, of the form:
{d: token} with token = JWT.encode({id: 123, field: "John", etc.})
I'd like to use a pact verification on the content of the decoded token. I know I can easily have a pact verifying that I get back a {d: string}, I can't do an exact match on the string (as the JWT contains some varying IDs). What I want is the following, which presumes the addition of a new Pact.JWT functionality.
my_provider.
upon_receiving('my request')
.with(method: :post,
path: '/order',
headers: {'Content-Type' => 'application/json'}
).will_respond_with(
status: 200,
headers: {'Content-Type' => 'application/json; charset=utf-8'},
body: {
d: Pact::JWT( {
id: Pact.like(123),
field: Pact.term(generate: "John", matcher: /J.*/
},signing_key,algo
)
})
Short of adding this Pact::JWT, is there a way to achive this kind of result?
I am already using the pact proxy to run my verification. I know you can modify the request before sending it for verification (How do I verify pacts against an API that requires an auth token?). Can you modify the request once you receive it from the proxy server?
If that's the case, I can plan for the following work around:
a switch in my actual code to sometimes expect the answers decoded instead of in the JWT
run my tests once with the swich off (normal code behaviour, mocks returns JWT data encoded.
run my tests a second time with the swich off (code expect data already decoded, mocks return decoded data.)
use the contract json from this second run
hook into the proxy:verify task to decode the JWT on the fly, and use the existing pact mechanisms for verification. (Step that I do not know how to do).
My code is in ruby. I do not have access to the provider.
Any suggestions appreciated! Thanks
You can modify the request or response by using (another) proxy app.
class ProxyApp
def initialize real_provider_app
#real_provider_app = real_provider_app
end
def call env
response = #real_provider_app.call(env)
# modify response here
response
end
end
Pact.service_provider "My Service Provider" do
app { ProxyApp.new(RealApp) }
end
Pact as a tool, I don't expect it to give this behavior out of the box.
In my opinion, the best is,
Do not change source code only for tests
Make sure your tests verifies encoded json only (generate encoded expected json in test & verify that with actual)

Handling authentification to Firebase Database with Fetch in a Service Worker

I'm trying to query a Firebase database from a Service Worker using the Fetch API. However it doesn't work as expected as I can't get authenticated correctly.
Basically what I'm trying to do is from origin https://myproject.firebaseapp.com inside a Service Worker I do a call like this :
var fetchOptions = {};
fetchOptions.credentials = 'include';
var url = options.messageUrl;
var request = new Request('https://myproject.firebaseio.com/user/foobar.json', fetchOptions);
messagePromise = fetch(request).then(function(response) {
return response.json();
});
I'm getting this error :
Fetch API cannot load https://myproject.firebaseio.com/user/foobar.json. Response to preflight request doesn't pass access control check: Credentials flag is 'true', but the 'Access-Control-Allow-Credentials' header is ''. It must be 'true' to allow credentials. Origin 'https://myproject.firebaseapp.com' is therefore not allowed access.
Any idea of a way to fix it? How one should do to query/update the Firebase database from a SW?
I've read https://jakearchibald.com/2014/using-serviceworker-today/ and one of the gotcha was exactly that problem, the fact that Fetch request do not send authentification.
Ideally it would be great to be able to use the Firebase JS API inside a SW but this doesn't seem to work as well.
Firebase doesn't store authentication info as a cookie or in anything that would be sent along in the credentials, so there's no need to send them in your fetch request. Instead, you'll need to pull the token from Firebase Auth:
firebase.auth().currentUser.getToken(true).then(function(token) {
// token is the value you'll need to remember for later
});
Once you've got the token, you should be able to add it as a query parameter to the REST request e.g. ?auth={THE_TOKEN}. This will allow you to make your authenticated request in the Service Worker.

External use of Meteor method? (to receive SMS from Nexmo)

In my Meteor application I want to receive text messages through Nexmo. How do I create the callback function? I'm thinking of something like
Meteor.methods
'receive_sms': (values) ->
console.log values
But http://hitchticker.meteor.com/receive_sms doesn't really work of course. I can see my method is working when I do Meteor.call('receive_sms', 'test') in my browser, but the network debugger is not really giving me a lot of useful information. The Meteor docs aren't very helpful either.
How do I access the method from elsewhere?
Iron Router and then server side routes. Something like:
Router.route('/download/:file', function () {
// NodeJS request object
var request = this.request;
// NodeJS response object
var response = this.response;
this.response.end('file download content\n');
}, {where: 'server'});
In order to receive sms from nexmo you should make the callback (incoming url) available over the internet. Nexmo won’t be able to call localhost to send the incoming sms messages.
Here are some resources to tunnel request over the internet to localhost.
https://ngrok.com/
http://localtunnel.me/
https://pagekite.net/

Resources