Firebase Cloud Function undefined request query parameters from Android App - android

I have this Firebase Cloud Function:
exports.verifyToken = functions.https.onRequest(async (req, res) => {
const token = req.query.token;
console.log("token: " + token);
return firebaseAdmin
.auth()
.createCustomToken(token, {provider: 'TEST'})
.then((firebaseToken) => {
console.log("Returning firebase token to user: " + firebaseToken);
return res.json({firebase_token: firebaseToken});
});
});
And this is my android code:
return Single.create<String> { emitter ->
val token = authToken.accessToken
val data = HashMap<String, String>()
data.put("token", token)
FirebaseFunctions.getInstance()
.getHttpsCallable("verifyToken")
.call(data)
.continueWith { task ->
return#continueWith task.result?.data as String
}
.addOnSuccessListener { firebaseToken ->
emitter.onSuccess(firebaseToken)
}
.addOnFailureListener {
emitter.onError(it)
}
}
When I try to run the cloud function via firebase emulator and Postman, it works fine. The function was able to get the value of token. But whenever I do it via Android, I get this from the Firebase Functions Logs:
4:29:40.837 AM
verifyToken
Function execution started
4:29:41.411 AM
verifyToken
token: undefined //<--- this here says that the token is undefined.
4:29:41.554 AM
verifyToken
Unhandled rejection
4:29:41.560 AM
verifyToken
Error: `uid` argument must be a non-empty string uid. at FirebaseAuthError.FirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:43:28) at FirebaseAuthError.PrefixedFirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:89:28) at new FirebaseAuthError (/workspace/node_modules/firebase-admin/lib/utils/error.js:148:16) at FirebaseTokenGenerator.createCustomToken (/workspace/node_modules/firebase-admin/lib/auth/token-generator.js:233:19) at Auth.BaseAuth.createCustomToken (/workspace/node_modules/firebase-admin/lib/auth/auth.js:96:36) at /workspace/index.js:25:6 at cloudFunction (/workspace/node_modules/firebase-functions/lib/providers/https.js:51:16) at /layers/google.nodejs.functions-framework/functions-framework/node_modules/#google-cloud/functions-framework/build/src/invoker.js:100:17 at processTicksAndRejections (internal/process/task_queues.js:79:11)
4:29:41.562 AM
verifyToken
Function execution took 725 ms, finished with status: 'crash'
If you have noticed, the code is almost similar to what Firebase has in their getting started pages. But then it doesn't fully work on my end.
I have already checked with my google-services.json and it's already updated.
I am currently set as the owner of the project, so no issues with firebase deploy
I also have the service-account.json updated and included with the firebase functions.
I might have missed something in my code or configuration. Any inputs are greatly appreciated!

It's not possible to use the Firebase Functions SDK to invoke onRequest type functions. The Firebase SDK implements the client side of a callable function that you declare with onCall. You're using onRequest here, which means you're writing a standard HTTP type function. For that type of function, you should use a standard HTTP client (not the Firebase SDK). If you actually did want to use the Firebase SDK to invoke your function, you will have to write a callable function instead. Note that callable functions have their own spec, and you won't be able to easily invoke them from postman.

Related

How to perform Firebase scheduled function only if value from realtime database is equal X?

I'm starting my adventure with Firebase cloud functions in my adnroid app in Android Studio and I have no experience with it. What is more I have never used javascript before so everything seems to be new for me. I would like to know if I can make a scheduled function that works like this :
At first function checks if value from realtime databse isn't zero.
If not, function checks if another value from realtime database is not bigger than 7.
If not, the value in database is increased by 1.
And then the notification is send.
I made test function to check if data from database are taken corectly but it execute with error "Firebase is not defined".
exports.scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
var user = user.uid;
var myRef = firebase.database().ref(user + "/CurrentChallenge/numOfActiveChallenge");
myRef.on('value', (snapshot) => {
const data = snapshot.val();
console.log(data);
});
In a Cloud Function, if you want to interact with the Firebase services, you need to use the Admin SDK.
Also, if you want to read a database node in a Cloud Function, it is more appropriate to read once the node (with get() or once()) instead of setting a listener with on(). As a matter of fact the CF has a short life time and setting a listener is therefore not the correct approach.
It is nor clear how you get the value of the user variable. There is no user in a Scheduled Cloud Function. You need to adapt this line, because, as such it will not work.
Finally, it is important to note that you need to terminate a Cloud Function when all the asynchronous work is completed, see the doc. In the case of a background triggered Cloud Function (e.g. a Pub/Sub schedules Cloud Function) you must return the entire chain of promises returned by the asynchronous method calls. Another possibility is to use async/await, as shown below, and return a value (e.g. null) when all the asynchronous work is completed.
So, the following code skeleton should do the trick:
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
exports.scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun(async (context) => {
var user = ... // Set the value of user
const db = admin.database(); // Admin SDK
const snapshot1 = await db.database().ref("...").get();
if (snapshot1.val() !== 0) {
const snapshot2 = await db.database().ref("...").get();
if (snapshot2.val() <= 7) {
await db.ref("...").update({
fieldName: firebase.database.ServerValue.increment(1)
});
//send the notification
// See https://github.com/firebase/functions-samples/blob/main/fcm-notifications/functions/index.js
} else {
return null;
}
} else {
return null;
}
});
Cloud functions are secure environment just like any server. Generally you use the Firebase Admin SDK when using Firebase in Cloud functions or your own servers. To add firebase admin, open terminal and go to the function directory and run the following command:
npm install firebase-admin
The important thing to note is admin sdk doesn't obey any database security rules as the name says. It has privileged access.
You can try the following code.
const admin = require("firebase-admin")
admin.initializeApp()
exports.scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun(async (context) => {
const myRef1Value = (await admin.database().ref("path/to/resoures").once("value")).val()
if (myRef1Value > 0) {
//make another request
}
}
Similarly make multiple requests as needed (sorry for throwing bunch of JS concepts but feel free to ask any queries)
Another thing I noticed is you are trying to get user ID in it. Scheduled Cloud Functions are not invoked by any user so you can't get any UID in that Cloud function. Can you clarify what is your use case so we can figure out a work around for this?
But what you want to achieve is simple chain if else statements and doing stuff.
You'll need to import and initialize the Firebase Admin SDK as shown here:
// The Firebase Admin SDK to access the database
const admin = require('firebase-admin');
admin.initializeApp();
With that, you can then use it with:
var myRef = admin.database().ref(user + "/CurrentChallenge/numOfActiveChallenge");
myRef.once('value', (snapshot) => {
...

Android reCAPTCHA: Verifying the user's response: all the functions must be called client-side! Can I use my backend however?

I want to verify the reCAPTCHA of my Android user. So I'm reading this documentation: https://developers.google.com/recaptcha/docs/verify:
For Android library users, you can call the SafetyNetApi.RecaptchaTokenResult.getTokenResult() method to get response token if the status returns successful.
In the manual of this function, the following description is written about getTokenResult (https://developers.google.com/android/reference/com/google/android/gms/safetynet/SafetyNetApi.RecaptchaTokenResult.html#getTokenResult()):
Gets the reCAPTCHA user response token, which must be validated by calling the siteverify method described in Verify the user's response.
The manual of the siteverify function describes the following (https://developers.google.com/android/reference/com/google/android/gms/safetynet/SafetyNetClient.html#verifyWithRecaptcha(java.lang.String)):
Provides user attestation with reCAPTCHA.
If reCAPTCHA is confident that this is a real user on a real device it will return a token with no challenge. Otherwise it will provide a visual/audio challenge to attest the humanness of the user before returning a token.
My question
I want to use my backend server (Cloud Functions) to verify the reCAPTCHA. However, according to the Android documentation, all the above functions seem to be put client-side. Indeed, siteverify should be called with the token got with getTokenResult, and both seem to be part of the Android SecureNET ReCAPTCHA Android API...
However, I think that using Cloud Functions would be more secure! Can I use my backend however?
Edit: back-end call to siteverify in Cloud Functions
exports.verifyRecaptcha = functions.https.onRequest((request, response) => {
const user_response_token = request.query.user_response_token;
if(user_response_token == '') {
throw new functions.https.HttpsError('invalid-argument', 'The function must be called with an adequat user response token.');
}
const remote_url = 'https://www.google.com/recaptcha/api/siteverify';
const secret = null;
request.post({url: remote_url, form:{secret: secret, response: user_response_token}}, function(error, response, body) {
if(error) {
throw new functions.https.HttpsError('unknown', error);
}
if(!response.statusCode != 200) {
throw new functions.https.HttpsError('unknown', 'Something went wrong. Status code: ' + response.statusCode + '.');
}
if(!body.success) {
throw new functions.https.HttpsError('unknown', 'Unable to verify this captcha.');
}
return response;
});
});
You can take the token returned from getTokenResult(), send it to your backend, and have your backend call the web API version of siteverify:
https://www.google.com/recaptcha/api/siteverify

How to get verification callback on firebase auth signInWithPhoneNumber in react-native-firebase?

I am trying out firebase auth signInWithPhoneNumber in my react-native app. Following https://rnfirebase.io/docs/master/auth/phone-auth.
However on
firebase.auth().signInWithPhoneNumber(phoneNumber)
.then(confirmResult => {
console.log(confirmResult)
}).catch(error => {
console.log(error)
});
confirmResults doesn't have a confirm method.
ConfirmationResult {_auth: Auth {_app: App, _customUrlOrRegion: undefined, namespace: "auth", _user: null, _settings: null, …}
_verificationId: "AM5PThBgtChKnxaZPAnPE_9zR_4qd1p2YchqZLNexlWtTiBxkUhEoOd79z0oujNP9pYrs9rgUbTsluCnlsqFjJCdbv83d89vg9LhZXJWbsnTB6w8lmxn00OqVe8S_Qc3Pfnw2qHMgZzV"
verificationId: (...)
__proto__: Object
What am I doing wrong?
It looks like you're doing everything right. The console output shows that you are receiving an instance of a ConfirmationResult class.
When you perform console.log() on an instance of a class, the methods of that class aren't shown.
If you instead store that instance in a variable and then call confirmationResult.confirm() on that stored instance of the class, the confirm method will be called and the code supplied to confirm() will be verified.
See RNFirebase Phone Auth Docs for ConfirmationResult for more details about this class.

Firebase Database Update Every 1 hour Using AWS lambda function

In my scenario i need to update firebase database every 1 hour.so i decided to run this in AWS lamda function because of no schedule trigger in cloud function.
Below is my code unable to add firebase library to AWS Lamda function .
'use strict';
import * as admin from 'firebase-admin';
var Firebase = require('firebase');
exports.handler = (event, context, callback) => {
// TODO implement
context.callbackWaitsForEmptyEventLoop = false; //<---Important
var config = {
apiKey: "AIzaSy########################",
authDomain: "########.firebaseapp.com",
databaseURL: "https://a########.firebaseio.com",
projectId: "aws#####",
storageBucket: "",
messagingSenderId: "83526964121"
};
getting below error:
Cannot find module 'firebase'"
"errorMessage": "Unexpected token import",
"errorType": "SyntaxError",
"stackTrace": [
" ^^^^^^",
"SyntaxError: Unexpected token import",
"createScript (vm.js:56:10)",
Cannot find module 'firebase'"
How to add Firebase module to Aws Lamda function.Please give me a hint..
Thanks in advance
if your admin is imported in your lamda function successfully just initialize it with credentials (json file or required fields). See: https://firebase.google.com/docs/admin/setup .
You don't need firebase dependency to do it since lamda function is not visible to users it can use admin access to your database.
Now when your admin sdk is initialize you just need to get the database reference like this (javascript style) :
let db= admin.database();
Now you have reference to the database and you can easily write to the any location since admin has full access to your database.
db.ref().child('/someNode').set({"key":"value"});

Cordova/Phonegap facebook plugin An active access token must be used to query information

I am using https://github.com/Wizcorp/phonegap-facebook-plugin to connect my cordova Android app to Facebook.
I can login with:
facebookConnectPlugin.login(["email"],
fbLoginSuccess,
function (error) { alert("ERROR:" + JSON.stringify(error)); }
);
, logout even call facebookConnectPlugin.getLoginStatus and get:
userID
accessToken
expiresIn
sig
status
but when FB.api('/me', function(response){...}) is called, I receive
{error:
{
message: An active access token must be used to query information about the current user,
type: OAuthException,
code: 2500
}
}
Also this only happens when the app is built, not tested in browser.
Solved issue by manually giving FB.apicall a token with:
FB.api('/me?access_token='+userdata.authResponse.accessToken, function(response) {...
where userdata is the response of facebookConnectPlugin.getLoginStatus
I just don't understand why doesn't it provide token automatically in android app like it does in browser.
Send the access token and the data retrieving fields with the apiString
const fields = ['email', 'first_name', 'last_name', 'gender', 'birthday', 'picture.type(large)'];
const apiString = `me?access_token=${accessToken}&fields=${fields.join(',')}`;

Categories

Resources