With FlutterFire cloud_functions, are all requests POST? - firebase

I am writing an API using Firebase Functions for a Flutter Web application. I cannot see any way to use the FlutterFire cloud_functions.dart package to call my functions using any http method other than 'POST.' Am I missing something?
With this Firebase function:
export const hello = functions.https.onRequest(async (req, res) => {
cors(req, res);
functions.logger.info(req.method);
switch (req.method.toLowerCase()) {
case 'get':
res.status(200).send({'data': 'get', 'error': false });
break;
case 'post':
res.status(201).send();
break;
case 'options':
res.status(200).send();
break;
default:
res.status(405).send({error: true, data: {}, message: 'method not allowed'});
}
});
Is there any way to have code like
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('hello');
HttpsCallableResult result = await callable();
print(result.data);
issue a GET request?

Requests made to callable functions with the Firebase Functions SDK are always POST and can't be changed. You can see exactly what it's doing if you read over the protocol specification for callable functions.
If you want to use the Firebase Functions SDK to invoke a function, you must also define your function using onCall instead of onRequest, as described in the documentation for callable functions.
If you define your function with onRequest, then you should use a standard HTTP client library to invoke it. The Functions client SDK will not work.
You can't really mix and match onRequest and onCall functions - they have different use cases and implementation details.

Related

Accessing Google Cloud Function from Vercel Serverless Function

I am seeking the best manner in which this should be done.
I have a https based GCF Function such as:
// google function
exports.someFunction = async (req, res) => {
try {
...
// some logic and access
res.status(200).send(data)
}
catch(error) {
res.status(400).send(error.message)
}
}
The API serverless function in Next.js is using axios. Is that the recommended method?
// next.js pages/api/call-google-func.js
async function handler(req, res) {
try {
const url = '....' //https://gcp-zone-project-xx834.cloudfunctions.net/someFunc
const res = await axios.get(url)
const resdata = res.data
res.status(200).send(resdata)
}
catch(error) {
res.status(400).send(error)
}
}
The problem with this method is that the GCF must have public access. How can we set up to access the GCF from Next.js by passing credentials as environment variables. Thanks
I think for this situation where a Vercel Serverless Function must communicate with the outside world, a Google Cloud Function, you'd want to create a JWT token on Vercel's side to pass to Google's side which you would then need to verify. I think Exchanging a self-signed JWT for a Google-signed ID token would be what you need.
Since either side doesn't know about the other, Google's IAM normal cloud privileges for allowing GCG<>GCF communication wouldn't apply here.

Trying to implement shopify webhooks but getting 'InternalServerError: stream is not readable'

I'm building an app for shopify and need to add the GDPR webhooks. My back end is handled using next.js and I'm writing a webhook handler to verify them. The docs havent been very helpful because they dont show how to do it with node. This is my verification function.
export function verifiedShopifyWebhookHandler(
next: (req, res, body) => Promise
): NextApiHandler {
return async (req, res) => {
const hmacHeader = req.headers['x-shopify-hmac-sha256'];
const rawBody = await getRawBody(req);
const digest = crypto.createHmac('sha256', process.env.SHOPIFY_API_SECRET).update(rawBody).digest('base64');
if (digest === hmacHeader) {
return next(req, res, rawBody);
}
const webhookId = req.headers['x-shopify-webhook-id'];
return res.status(401).end();
};
}
But I get this Error: error - InternalServerError: stream is not readable
I think it has to do with now Next.js parses the incoming requests before they are sent to my api. Any ideas?
I discovered the answer. Next.js was pre parsing the body in the context which made it so that I couldn't use the raw body parser to parse it. By setting this:
export const config = {
api: {
bodyParser: false
}
};
above the api function in the api file it prevented next from parsing it and causing the issue. I found the answer because people had the same issue integrating swipe and using the bodyParser.

Call Cloud Run from Cloud Function: IAM Authentication

I've deployed a small HTTP endpoint via Google Cloud Run. It is working fine when I turn off the authentication.
I now want to turn it on so that it is only callable by my Firebase Cloud Function. If I understand it right, I just have to add the correct service account mail address in the IAM settings of the Cloud Run as "Cloud Run invoker".
But which address is the correct one?
I've tried all addresses that I have found in Firebase Console -> Project Settings -> Service Accounts.
I think you can check the specific firebase function. In the UI, the service account used should be listed.
By default, GCF functions all use <project_id>#appspot.gserviceaccount.com
Thanks to #AhmetB - Google and #whlee's answer I got it working. Basically it is enough adding an Authorization Bearer token to the request, which you can get from a special endpoint: https://cloud.google.com/run/docs/authenticating/service-to-service#nodejs
Then you just have to add the service account of the function to the IAM list of the Cloud Run container: <project_id>#appspot.gserviceaccount.com
The nodejs example is using the deprecated request library, so here is my version using axios:
const getOAuthToken = async (receivingServiceURL: string): Promise<string> => {
// Set up metadata server request
const metadataServerTokenURL = 'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
const uri = metadataServerTokenURL + receivingServiceURL;
const options = {
headers: {
'Metadata-Flavor': 'Google'
}
};
return axios.get(uri, options)
.then((res) => res.data)
.catch((error) => Promise.reject(error));
}
Then you can just use the token in the actual request:
const url = `...`;
const token = await getOAuthToken(url);
axios.post(url, formData, {
headers: {
Authorization: `Bearer ${token}`,
}
}).then(...).catch(...);
#luhu 's answer was really helpful. I'd like to add just one note for those whose are willing to test with the emulators locally first. The metadata server (which is actually http://metadata.google.internal now) as they state
does not work outside of Google Cloud, including from your local machine.
As a workarund, you can use the google-auth-library and then get the token directly if you prefer sticking with axios. Remember to set the GOOGLE_APPLICATION_CREDENTIALS env variable pointing to a service account secret first as it's the only way to make it work (I've tested setting the credential field during admin.initializeApp() but didn't seem to like it).
const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();
const url_origin = '....'
const client = await auth.getIdTokenClient(url_origin);
const token = (await client.getRequestHeaders()).Authorization;
const url = '....'
const response = await axios.get(
url,
{
headers: {
Authorization: token,
},
}
);

Firebase cloud function onCall not working after changing region

I built few cloud functions like this one:
const addRoom = functions.https.onCall((data, context) => {
It works perfectly but I wanted to change region to europe-west. I followed this stackoverflow: firebase deploy to custom region (eu-central1)
const addRoom = functions.region('europe-west1').https.onCall((data, context) => {
It looks working fine for all functions (triggers) except onCall functions. I got this error when calling addRoom function on client side :
firebase.functions().httpsCallable("addRoom")(data)
Access to fetch at
'https://us-central1-myproject.cloudfunctions.net/addRoom' from origin
'http://localhost:4200/new-room' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
Redirect is not allowed for a preflight request.
At this moment I use default region for onCall functions but is there a way to correct that or is that an error from firebase ?
On the client side, you should specify the desired region at initialization and call the function as follows:
var functions = firebase.app().functions('europe-west1');
....
functions.httpsCallable("addRoom")(data)
See https://firebase.google.com/docs/functions/locations#http_and_client_callable_functions
for angular application you should provide the region like this in app module
providers: [
{ provide: REGION, useValue: 'europe-west3' }
]
and call the function like this:
constructor(private aff: AngularFireFunctions) {}
sendMail(data: { ... }): Promise<any> {
return this.aff.httpsCallable('function_name')(data).pipe(first()).toPromise()
}

Firebase Cloud Functions execution time is so fluctuate

I got this from google cloud console. I don't understand why after the function is executed, it should turn hot for some duration. However, after deploying the function and call it, I found cold start and then drop to actual and then increase to cold start again. Please help!
// index.js
import * as functions from 'firebase-functions'
import express from 'express'
import cors from 'cors'
import auth from 'controllers/auth'
const authApp = express()
authApp.use(cors({ origin: true }))
authApp.use(auth)
authApp.use('*', unknownPathHandler, errorMiddleware)
const authApi = functions.https.onRequest(authApp)
exports.auth = authApi
// controllers/auth.js
app.post('/user', verifySecretKey, (req, res, next) => {
const { email, password } = req.body
return appFirebase.auth().signInWithEmailAndPassword(email, password)
.then(() => {
return appFirebase.auth().currentUser.getIdToken().then((token) => {
return res.end(token)
})
})
.catch((err) => next(err))
})
firebase version
"firebase-admin": "5.12.0",
"firebase-functions": "1.0.2",
There is potential for a lot of variance in you function. It's doing all of the following tasks, none of which have guarantees how long they will take:
Using Firebase Auth to effectively sign in a user
Fetching an ID token for that user
Sending a response to the client, wherever in the world they are, with whatever connection speed.
If you want to understand the performance characteristics of your function, you should profile each one of these steps. The sending of the response may not be possible to benchmark, and could be highly variable based on their physical location and connection speed.
If you have solid benchmarks that suggest that Cloud Functions is underperforming compared to expectations, please send those to Firebase support. https://firebase.google.com/support/

Resources