How to I pass a Stripe Firebase Cloud Function Error To The Client - node.js

When I create a cloud function to process a charge on a user’s card (by writing a stripe token to firebase and using a cloud function to charge), how do I pass errors (like a declined card due to insufficient funds) to the client. If it’s important, I’m using the firebase web sdk to send the tokens.

write the errors to a firebase database so that you can read the errors from the database and show them where you need to.

I decided to use a Firebase HTTP cloud function and just send the token to the link firebase sets for the function. Like so,
exports.addSourceToCustomer = functions.https.onRequest((req, res) => {
const token = req.body.token // use the stripe token however you like here
// when an error occurs use res.status(errorCode).send(errorMessage);
// which sends the error back to the client that made the request
});

Related

Node.js: Google Calendar On Behalf of User

I am trying to access multiple users' Google Calendars in a search functionality with Flutter and Firebase Functions in Node.js.
I originally ask for user permission here in the app (Flutter):
clientViaUserConsent(_credentialsID, _scopes, prompt)
.then((AuthClient client) async {
The credentials for the user are then stored locally and in Firestore via a restricted Firebase Function for security reasons.
Calling Firebase Function (Node.js):
FirebaseFunctions.instance
.httpsCallable('addCalendar')
.call(<String, dynamic>{
'calendarAuthToken': client.credentials.accessToken.data,
'calendarRefreshToken': client.credentials.refreshToken!,
'calendarExpiration':
client.credentials.accessToken.expiry.toString(),
'idToken': client.credentials.idToken.toString()
});
Firebase Function:
return await admin.firestore().collection('[example text]').doc('[example text]').set({
CalendarAccessToken: calendarAccessToken,
CalendarRefreshToken: calendarRefreshToken,
CalendarExpiration: calendarExpiration,
// CalendarIDToken: calendarIDToken
}).then(() => {
I have a search functionality which needs access to all users' calendars. Obviously this should never be done locally due to the sensitivity of the data, so I am trying to do this in a Firebase Function as well.
exports.isAvailableNow = functions.https.onCall(async (data, context) => {
const id = data.id;
const cal= await admin.firestore().collection('[example text]').doc(id)
Should I be reconstructing user's credentials in the Firebase Function, or should I instead be using a service account? The reconstruction could get blocked because it is running on Firebase, not locally like the user permitted. If I need to use a service account, how does that change the original authorization in flutter?
EDIT: Changed the title
UPDATE: I managed to reconstruct the authentication for the user on the server-side, but my fear has become a reality. Google is saying I am not authorized for the user. How do I authorize a user on the client then access their data on the backend?

How to use firestore as the back-end for a REST API [duplicate]

This question already has answers here:
Firebase admin on backend for verifyIdToken and use Firestore
(1 answer)
Restrict firestore access to admin-sdk
(2 answers)
Closed 1 year ago.
Firestore is amazing for creating serverless applications but I'm currently trying to use firestore as the primary system for my REST API. I'd like to be able to create sessions using a rest endpoint and passing token's to and from the client. I'd like to be able to use these tokens to make Authenticated calls and have firebase auth automatically handle database rules through the rules I've set as if I were using the firestore client sdk.
I am familiar with Firebase Admin but for obvious reasons I can't use that for Authenticated requests, at least not directly. I was wondering if there was a "Firebase-Admin" where I can pass a token.
Yes, there is Firebase Admin SDK which supposed to be used in secure environments like Cloud functions or your own server. It uses a service account and has full access to your Firebase project's resources and does not obey any security rules.
That being said you need to manually authenticate your users and check if they are allowed to access the resource that they are requesting. You must pass the Firebase ID Token in your REST API requests in headers or body (I usually pass it in authorization header as 'Bearer <firebase_id_token>'). You must not pass the UID of user itself under any circumstances.
Follow these steps to get Admin SDK up and running:
1. Installing the Admin SDK:
npm install firebase-admin
# yarn add firebase-admin
2. Create a middleware to verify the ID Tokens:
const express = require("express")
const app = express()
const admin = require("firebase-admin")
admin.initializeApp(...) // <-- use the service account to initialize
app.use(async function(req, res, next) {
const {authorization} = req.headers
if (!authorization) return res.sendStatus(401)
const decodedToken = await admin.auth().verifyIdToken(authorization)
console.log(decodedToken)
next()
})
// other endpoints
app.listen(3000)
The decodedToken contains user's UID, custom claims and other metadata. You can read more about that in the documentation.
You can download your Service Account Key from here: https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk
Firebase generates the ID Token (Access Token) and a Refresh Token when a user logs in. You just need to pass that access token in your request. You can use the getIdToken method to get it.
async function callAPI() {
const idToken = await firebase.auth().currentUser.getIdToken()
const response = await fetch("url", {headers: {authorization: idToken}})
}

GCP Consume a REST API after OAuth in Node.js

I am working to implement a Node.js webapp to be deployed on GCP App Engine.
Following the Node.js Bookshelf App sample, I did manage to implement a basic user authentication flow using the passport-google-oauth20 and retrieve basic profile information. I basically just got rid of what was not needed for my purposes
My custom code is available at: gist.github.com/vdenotaris/3a6dcd713e4c3ee3a973aa00cf0a45b0.
However, I would now like to consume a GCP Cloud Storage API to retrieve all the storage objects within a given buckets with the logged identity.
This should be possible by:
adding a proper scope for the request.
authenticating the REST requests using the user session token obtained via OAuth.
About the post-auth handler, the documentation says:
After you obtain credentials, you can store information about the
user. Passport.js automatically serializes the user to the session.
After the user’s information is in the session, you can make a couple
of middleware functions to make it easier to work with authentication.
// Middleware that requires the user to be logged in. If the user is not logged
// in, it will redirect the user to authorize the application and then return
// them to the original URL they requested.
function authRequired (req, res, next) {
if (!req.user) {
req.session.oauth2return = req.originalUrl;
return res.redirect('/auth/login');
}
next();
}
// Middleware that exposes the user's profile as well as login/logout URLs to
// any templates. These are available as `profile`, `login`, and `logout`.
function addTemplateVariables (req, res, next) {
res.locals.profile = req.user;
res.locals.login = `/auth/login?return=${encodeURIComponent(req.originalUrl)}`;
res.locals.logout = `/auth/logout?return=${encodeURIComponent(req.originalUrl)}`;
next();
}
But I do not see where the token is stored, how can I retrieve it and how to use it to consume a web-service (in my case, GCP storage).
I am not at all a node.js expert, so it would be nice having a bit more clarity on that: could someone explain me how to proceed in consuming a REST API using the logged user credentials (thus IAM/ACL privileges)?
If you want to access Cloud Storage through the use of a token obtained with OAuth, when the application requires user data, it will prompt a consent screen, asking for the user to authorize the app to get some of their data. If the user approves, an access token is generated, which can be attached to the user's request. This is better explained here.
If you plan to run your application in Google App Engine, there will be a service account prepared with the necessary authentication information, so no further setup is required. You may need to generate the service account credentials (generally in JSON format), that have to be added to the GOOGLE_APPLICATION_CREDENTIALS environment variable in gcloud.
Here is an example of how to authenticate and consume a REST API with the token that was obtained in the previous step. This, for example, would be a request to list objects stored in a bucket:
GET /storage/v1/b/example-bucket/o HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer [YOUR_TOKEN]

Firebase Admin Token is being generated with invalid signature

I have created an App using the Parse server, and have now decided to implement some aspects of firebase into my app. Im trying to accomplish this by doing the signInWithCustomToken method in swift on ios. Im calling a rest service to generate a firebase token, and then signing in with that token returned upon valid sign in on my current auth system. However, the token being generated appears to have an invalid signature when pasting it into jwt.io. The environment that im using is node based (inside the parse server). It seems very simple and i have followed the firebase instrucutions/poured over questions on this over the last few days and im really not sure what im doing wrong! Heres my rest service on how i generate the token, i initialize the server with this method:
Parse.Cloud.define("initServer", function(request, response){
var admin = require('firebase-admin');
var serviceAccount = require('/home/bitnami/apps/parse/htdocs/firebase/serviceAccountKey.json');
console.log(serviceAccount);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'https://urabo-acb7a.firebaseio.com'
});
response.success("Server Init OK");
});
this is the post method i call to generate the token from firebase-admin sdk:
Parse.Cloud.define("generateFirebaseToken", function(request, response) {
var admin = require('firebase-admin');
admin.auth().createCustomToken(request.params.uid)
.then(function(customToken) {
// Send token back to client
response.success(customToken);
})
.catch(function(error) {
console.log("Error creating custom token:", error);
});
});
And of course I went into the firebase console and generated the private key, and then scp'd it over to my server. Im not really sure why this is not working, it generates a token it just doesnt appear to be valid or linked to my account. Am i missing some weird encoding issue or something with the token? Does anyone have insight on this?? Thanks so much!
—The main question is are your users signing in to the REST service through your app and then you are also trying to re-authenticate them again with the token generated in your system?
—If they will be accessing further REST functions once authenticated, then why not authenticate them successfully when 'a' token is returned?
—Usually token usage or handling is restricted by the API providers. Another option is instead of involving user auth directly with the API service, have a separate auth system — the usual SignIn process and then make API calls based on the requested API feature. That way your app is interacting with the APIs and users remain at the front end.

Push notifications using Firebase Cloud Function iOS

Trying to send remote push notifications through firebase cloud functions. Resources I've been following achieves this through sendToDevice method, which takes a String as an argument. A resource from GitHub says its a "device notification token" that is retrieved when user agrees to receive notifications in app. Firebase says its a "registration token that comes from the client FCM SDKs". What should be the input here, and how to I retrieve it?
// Send notification to device via firebase cloud messaging.
// https://firebase.google.com/docs/cloud-messaging/admin/send-messages
// https://github.com/firebase/functions-samples/blob/master/fcm-notifications/functions/index.js
//
admin.messaging().sendToDevice(request.query.tokenId, payload).then(response => {
response.results.forEach((result, index) => {
const error = result.error
if (error) {
console.log("Failure sending notification.")
}
});
});
You need to integrate FCM into your iOS app. Pay attention to the part about receiving the current registration token.
Registration tokens are delivered via the FIRMessagingDelegate method
messaging:didReceiveRegistrationToken:. This method is called
generally once per app start with an FCM token. When this method is
called, it is the ideal time to:
If the registration token is new, send it to your application server (it's recommended to implement server logic to determine whether the
token is new).
Subscribe the registration token to topics. This is required only for new subscriptions or for situations where the user has
re-installed the app.
So, you'll have to get a hold of this token in your app, store it somewhere that the Cloud Function can get a hold of (traditionally, Realtime Database), and query for it at the time the function runs.

Resources