Blocking auth triggers in cloud functions for firebase second generation - firebase

I am referring this https://firebase.google.com/docs/functions/beta/auth-blocking-events documentation to implement Blocking auth triggers using 2nd generation cloud function for firebase,
I have created and deployed functions but the problem is that, while registering the triggers under the Blocking functions tab in the Firebase Authentication console, It does not shows that deployed triggers (beforeUserSignedIn, beforeUserCreated) for registering.
Below is my code,
import functions from 'firebase-functions/v2'
import admin from 'firebase-admin'
import { beforeUserCreated, beforeUserSignedIn } from 'firebase-functions/v2/identity'
import serviceKey from './serviceKey.json' assert {type: 'json'}
admin.initializeApp(serviceKey)
export const beforecreated = beforeUserCreated((event) => {
console.log("EVENT DATA FOR AUTH beforecreated....", event.data)
return;
});
export const beforesignedin = beforeUserSignedIn((event) => {
console.log("EVENT DATA FOR AUTH beforesignedin....",event.data)
});
I have tried Blocking auth triggers for first-generation cloud functions for firebase and it's working fine, and also I can register that blocking auth triggers. But this is not working for the second generation cloud function for firebase.

Related

Inconsistency with Authenitcaiton User-Creation in Firebase React Native

I am trying to incorporate the Firebase JS SDK into a managed Expo React Native project. I installed Firebase through npm i firebase and I am using the latest version.
The problem is that whenever I start the expo app, through an Android emulator, I, when passing the input from formik into createUserWithEmailAndPassword first get the auth/network-request-failed error message, but after clicking the signup button several times, more than 10, it somehow works. The user is then added to the Firebase console. After this it works flawlessly, and I can create multiple accounts without fail. However, should I restart the app, then I would have to repeat the steps above.
I have divided the main firebase function to a separate file, and imported it.
The exported file:
import { initializeApp } from 'firebase/app';
import { getAuth, createUserWithEmailAndPassword } from 'firebase/auth';
import firebaseConfig from './app/config/Firebase/firebaseConfig';
let myApp = initializeApp(firebaseConfig);
const auth = getAuth(myApp);
export function signupEmail(email, password) {
console.log(email, password);
createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
const user = userCredential.user;
console.log('Signed in');
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
});
}
The main parts of the sign-up file:
function handleSignup(values) {
signupEmail(values.email, values.password);
}
<SubmitButton
style={styles.signupButton}
title='SignUp'
onPress={handleSubmit}
/>
onSubmit={(values) => [handleSignup(values)]}
After pressing Signup I get the email and password printed out, as in the signupEmail function. I get the email and password message whether or not it is successful in the end, which I assume implies the problem doesn't lie with formik, since the data is correctly passed to the signupEmail function. Though, from here it often stands idle, sometimes for more than 10s before the network error message comes up. When it is successful, I think, is always instant.
I also have a print-out auth.currentUser button, which returns null whilst the process is loading, and if successful logs out the expected object. Though, sometimes it logs out the object to an account whose email that I didn't add during the current session. I did also add a Sign Out function which turns the auth.currentUser object into null.
I don't know where to go from here, and would appreciate any help!
This issue seems to be related to the Android emulator, Expo Go on my phone worked without issue.

Firebase: How to trigger HTTPs onRequest function within a Realtime Database onCreate function?

Upon clicking the 'checkout' button in my VueJS ecommerce app, a new 'order' field containing order params will be created in my firebase 'orders' child node. I've created a Realtime Database onCreate 'newBuyerOrder' function to send the user an email notifying him of this new order once a new 'order' field is created. Now I'd also like to call a REST API '/checkout' that I've built with the HTTPs onRequest function to process the order via Paypal REST SDK. How can I do that?
A workaround that I've tried is to build HTTPs onCall functions that the client browser can call to process the order via Paypal with order params and send the email separately with the 'newBuyerOrder' onCreate function. But unfortunately, HTTPs onCall does not allow client redirect. And, lo and behold, client redirect is required while making Paypal REST calls so HTTPs onCall does not work for my purpose.
in functions/package.json
"dependencies": {
"#sendgrid/mail": "^6.3.1",
"firebase-admin": "~6.0.0",
"firebase-functions": "^2.1.0",
"paypal-rest-sdk": "^1.8.1"
}
in functions/src/index.ts
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as sendgrid from '#sendgrid/mail'
import * as paypal from 'paypal-rest-sdk'
// init firebase admin
admin.initializeApp()
// set sendgrid api in function config
const SENDGRID_API_KEY = ...
// set paypal api in function config
paypal.configure({
...
});
// setup paypal payment object and redirect user to paypal payment
page
export const checkout = functions.https.onRequest((req, res) => {
// 1.Set up a payment information object, Build PayPal payment
request
const payReq = JSON.stringify({
...
})
// 2.Initialize the payment and redirect the user.
paypal.payment.create(payReq, (error, payment) => {
if (error) {
// handle error
} else {
// redirect to paypal approval link
for (let i = 0; i < payment.links.length; i++) {
if (payment.links[i].rel === 'approval_url') {
res.redirect(302, payment.links[i].href)
}
}
}
})
})
// send email and trigger paypal checkout api given new buyer order
export const newBuyerOrder = functions.database
.ref('users/{userId}/orders/{orderId}')
.onCreate((snapshot, context) =>
// expected solution to call 'checkout' REST API from above
// send email via sendgrid
const msg = {...}
return sendgrid.send(msg)
})
I expect the '/checkout' API to be called once a new order field is created in the Realtime Database and that the client gets redirected to the Paypal approval page.
Calling one Cloud Function from another Cloud Functions is almost never a good idea. For one thing, you'll be paying for two invocations, where the work could be done in just one. There are better ways to re-use the code.
For example, you could include the code for the payment in the Realtime Database triggered Cloud Function?.
Or you could extract the business functionality of that Cloud Function, and put that in a regular JavaScript function, so that you an call if from the Realtime Database triggered function.
A database trigger comes from the Realtime Database to Cloud Functions. No client-side application code is involved, so it also can't be redirected.
If you want to send a response back to the client when the operation has completed, you typically write the response for the client back into the database in a place that the client watches. For example, you could write the response to: /users/{userId}/order_results/{orderId} and the client can then wait for a value at this location.

firebase serve: From a locally served app, call locally served functions

How can I properly simulate a cloud function locally so that it has all data as when being invoked on firebase servers? (e.g. the context.auth)
I am serving my project with firebase serve, it runs ok on http://localhost:5000/, however, my cloud functions are being called from https://us-central1-<my-app>.cloudfunctions.net/getUser. (The function is not even deployed.)
To avoid XY problem, I am trying to debug my function, but calling it from firebase shell results in context.auth being undefined, same when calling via postman from http://localhost:5000/<my-app>/us-central1/getUser.
This is my ./functions/src/index.ts file
import * as functions from 'firebase-functions'
import admin from 'firebase-admin'
import { inspect } from 'util'
admin.initializeApp()
export const getUser = functions.https.onCall((data, context) => {
console.debug('== getUser called =========================================')
console.log('getUser', inspect(data), inspect(context.auth))
return admin.database().ref('userRights/admin').child(context.auth.uid).once('value', snapshot => {
console.log(snapshot.val())
if (snapshot.val() === true) {
return 'OK'
// return {status: 'OK'}
} else {
return 'NOK'
// return {status: 'error', code: 401, message: 'Unauthorized'}
}
})
})
file ./firebase.functions.ts
import { functions } from '~/firebase'
export const getUser = functions.httpsCallable('getUser')
Consumer ./src/pages/AdminPanel/index.tsx
import { getUser } from '~/firebase.functions'
//...
getUser({myDataX: 'asd'}).then(response => console.debug('response', response))
UPDATE - April/2021
As of April/2021, method useFunctionsEmulator has been deprecated. It is suggested to use method useEmulator(host, port) instead.
Original post:
By default, firebase serve sends queries to CLOUD function instead of localhost, but it is possible to change it to to point to localhost.
#gregbkr found a workaround for that at this github thread.
You basically add this after firebase initialization script (firebase/init.js) in html head.
<script>
firebase.functions().useFunctionsEmulator("http://localhost:5001");
</script>
Make sure to REMOVE it when deploying to SERVER
There is currently no support for local testing of callable functions like this. The team is working on a way for you to specify the URL endpoint of a callable function so that you can redirect it to a different location for testing.
Just found a workaround.
using fiddlers AutoResponder to redirect the function call to the local served function.
step 1
copy the target url of the function from the client
step 2
copy the local served function url
step 3
active the auto respoder and use the following rules
(the second rule is also importent to allow all outher requests
That worked for me, thank you #GorvGoyl!
script src="/__/firebase/init.js?useEmulator=true"></script

Integrate Firebase with 3rd party API

I'd like to integrate my firebase project with some 3rd party API's, like the twitter API.
3rd party API
The following code will listen to new tweets containing the certain text 'little rocket man':
var Twitter = require('twitter');
// setup new twitter client
var client = new Twitter({
consumer_key: '',
consumer_secret: '',
access_token_key: '',
access_token_secret: ''
});
// open new twitter stream
let stream = this.client.stream('statuses/filter', { track: 'little rocket man' });
stream.on('data', (event: any) => {
let tweetText = event && event.text; // this should be written to the firebase db
});
Firebase Cloud Functions
The following firebase cloud functions listens to incoming HTTP GET requests on a specific route and saves data back to the firebase db:
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin'; // Firebase Admin SDK
admin.initializeApp(functions.config().firebase);
// this example uses a HTTP trigger, but how can I trigger it whenever new tweets containint 'little rocket man' are detected??
exports.addMessage = functions.https.onRequest((req, res) => {
const original = req.query.text;
admin.database().ref('/messages').push({original: original}).then(snapshot => {
res.redirect(303, snapshot.ref);
});
});
Question
How can I write the tweets I'm recieving from the twitter client back to the firebase db? If possible, I'd like to run all the code on firebase cloud functions.
Disclaimer:
I'm new to firebase and although googling around for a few hours I wasn't able to find the solution to my problem on the net. I'd like to apologize in advance, should I have overseen it.
You can't use streaming APIs like this in Cloud Functions. A function may only respond to some distinct event, such as an HTTP request, or some change in your database. Functions can't run indefinitely.
If you want to collect tweets that match some query into your database, you can use IFTTT to periodically send them to a function as they become available. I recently finished a small project that does exactly that.

Can you trigger a Google Cloud Functions via firebase event without a server?

I will be implementing an elastic search index alongside my firebase application so that it can better support ad-hoc full text searches and geo searches. Thus, I need to sync firebase data to the elastic search index and all the examples require a server process that listens for firebase events.
e.g. https://github.com/firebase/flashlight
However, it would be great if I can just have a google cloud function triggered by an insert in a firebase node. I see that google cloud functions has various triggers: pub sub, storage and direct... can any of these bridge to a firebase node event without an intermediate server?
firebaser here
We just released Cloud Functions for Firebase. This allows you to run JavaScript functions on Google's servers in response to Firebase events (such as database changes, users signing in and much more).
I believe Cloud Functions for Firebase are what you are looking for.
Here are a few links:
Official Documentation
Intro video
Google Cloud Functions and Firebase (Google Cloud Next '17)
yes, you can trigger a Google Cloud Functions via firebase event without a server.
As per documents,Firebase allows for example you can send notifications using a cloud function when a user write into firebase database.
For that, I had to write a javascript as below
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/articles/{articleId}')
.onWrite(event => {
// Grab the current value of what was written to the Realtime Database.
var eventSnapshot = event.data;
var str1 = "Author is ";
var str = str1.concat(eventSnapshot.child("author").val());
console.log(str);
var topic = "android";
var payload = {
data: {
title: eventSnapshot.child("title").val(),
author: eventSnapshot.child("author").val()
}
};
// Send a message to devices subscribed to the provided topic.
return admin.messaging().sendToTopic(topic, payload)
.then(function (response) {
// See the MessagingTopicResponse reference documentation for the
// contents of response.
console.log("Successfully sent message:", response);
})
.catch(function (error) {
console.log("Error sending message:", error);
});
});

Resources