Firebase admin on backend for verifyIdToken and use Firestore - node.js

I'm trying to use Firebase Admin on my backend for "faking" client authentication by verifying Id Token in order to use firestore from the backend.
The idea is to use my server as a middleware between my client and firestore.
I can initialize FirebaseAdmin on the backend and verifyIdToken() from client properly, but I don't have an idea for using firestore after that. Can you guys show me a way for doing it?
import * as firebaseAdmin from 'firebase-admin';
import firebaseServiceAccountKey from './firebaseServiceAccountKey.json';
if (!firebaseAdmin.apps.length) {
firebaseAdmin.initializeApp({
credential: firebaseAdmin.credential.cert(
firebaseServiceAccountKey
),
databaseURL: ##########################,
});
}
// This is working
function getUser(token) {
return firebaseAdmin
.auth()
.verifyIdToken(token)
.then((decodedToken) => {
return decodedToken;
})
.catch((error) => {
return error
});
}
/*
Now I want to use Firestore authenticated with this token, should I
import firebase from "firebase"
and then try auth() with token?
*/

Access to Firestore through the Admin SDK always happens with full administrative privileges. There is no way to access Firestore as the user whose token you verified.
If you want to use this middleware approach, you will have to ensure it only accesses data the user is authorized for in the code itself.
Also see:
Pass user auth to Firestore from Cloud functions
How to make Firebase Functions act as a user instead of being an admin?
If the goal is to have tighter control over who can sign in to your app, consider using custom authentication instead - where the server mints a custom token for each user, that the client-side SDK then uses to sign in.

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}})
}

How to use firebase.auth().signInWithEmailAndPassword on firebase-functions as API with nodejs?

I'm building API with Nodejs using firebase-funtions for login with email and password function. But when I use firebase.auth() then recieve error: firebase.auth() is not a function. I searched a long time but don't have result as expect. I have any questions:
1/ Can we use firebase.auth() (it's client-SDK) on firebase-functions ?
2/ If can't use it. What we should do authenticate email/password from firebase as RESTful API.
Here's my code:
const firebase = require('firebase');
const functions = require('firebase-functions');
const admin = require('firebase-admin');
var serviceAccount = require("./serviceAccountKey.json");
var firebaseConfig = {
// my config
};
firebase.initializeApp(firebaseConfig);
admin.initializeApp({
// my config
});
As I implied in my first comment, you can't sign in a user on the server side of your app. It's not clear to me why you need to do that. Each supported mobile and web platform has their own way of signing in the user locally. They should not be sharing some server code to do that particular task. There are other things you could delegate to a server, but signing in is not one of them.
The client needs to sign in on its own because it needs to generate a ID token that identifies the user, so that the client can make requests to other Firebase products that will be authorized for that particular user. This token must come from the client in order to be secure.

Firebase Admin SDK to Log user in from Server

I have been using Firebase Admin SDK in my Node JS code for authenticating users from the server side.
The Admin SDK provides a method admin.auth().createUser() to create new users.
admin.auth().createUser({
email,
password,
displayName: name
})
.then(function(user) {
console.log("Successfully created new user:", user.uid)
return res.send(user)
})
.catch(function(err) {
console.log("Error creating new user:", err)
return res.send(err)
})
But now how to make a user login like there is a method auth.signInWithEmailAndPassword(email, pass) in the Firebase Client SDK.
There is a method on the firebase admin SDK to get the user info by Email admin.auth().getUserByEmail(email). This method returns all the user information including password but that password is hashed. So now is there any workaround to have a proper authenticate users from Server.
My comment is a bit late but one option would be to use the Firebase REST API directly but integrated into your own server-side API for authentication. And then use a combination of that and the Admin SDK to wrap it all up. REST API docs can be found here https://firebase.google.com/docs/reference/rest/auth/#section-sign-in-email-password. You could keep your client light weight and wrap up all Firebase auth stuff, custom claims, login, logout etc. all through your own API. You would just need to use both methods to do so. This would abstract you away from any dependencies in your app and API as you could put it all in a single service provider. Just food for thought.
There is no way to log a user in with the Admin SDK. The Admin SDK runs with administrative privileges and has no need to log in.
You'll want to use one of the Firebase client-side SDKs (e.g. for Android, iOS or web users) to sign your users in to Firebase directly from the client-side code.
If you want your server-side code to know what user is signed in, you send the token from the client to your server and then decode and validate it there. See the document on verifying an ID token and the sample of server-side authentication with Cloud Functions.
Hi you can use for example in typescript the client sdk in your server side code
import firebase from "firebase/compat/app";
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: ""
};
firebase.initializeApp(firebaseConfig);
export const authSdk = firebase.auth()
// now you can do
authSdk.signInWithEmailAndPassword(userCredential.email, userCredential.password)
.then(loggedUser => {console.log(loggedUser.user?.uid)})

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.

Resources