app.auth(...).signInWithCredential is not a function firebase error - node.js

I'm trying to add google login to my node js firebase app. I'm getting the following error:
app.auth(...).signInWithCredential is not a function
Here's the code on the server
var credential = firebase.auth.GoogleAuthProvider.credential(id_token);
return app.auth().signInWithCredential(credential);
On the client
// We need to register an Observer on Firebase Auth to make sure auth is initialized.
var unsubscribe = app.auth().onAuthStateChanged((firebaseUser) => {
unsubscribe();
// Check if we are already signed-in Firebase with the correct user.
// Build Firebase credential with the Google ID token.
var credential = firebase.auth.GoogleAuthProvider.credential(googleUser.getAuthResponse().id_token);
// Sign in with credential from the Google user.
firebase.auth().signInWithCredential(credential).catch( (error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
console.dir(errorMessage);
}).then( (success) => {
console.dir(success);
var id_token = googleUser.getAuthResponse().id_token;
axios.post('/auth/google-login', {
id_token: id_token
}).then(response => {
var result = response.data;
if (result.status === "ok") {
this.setState({complete: true});
} else if (result.error) {
this.setState({error: result.error});
}
}).catch(error => {
console.dir(error);
this.setState({error: "error"});
});
});
});
What I'm I doing wrong?
At the top of the nodejs file, I have
var app = require('./base');
where base :
var serviceAccount = require("./service-account-file.json");
const app = fadmin.initializeApp({
credential: fadmin.credential.cert(serviceAccount),
databaseURL: "https://test.firebaseio.com"
});
module.exports = app;

Hi there are hints that the problem starts with node version 6.21. that
app.auth().signInWithCredential(credential)
Don't work
It works in v6.2.0.
Try signInWithEmailAndPassword
Use latest version of node

The function signInWithCredential is part of the Firebase JavaScript SDK
The Firebase JavaScript SDK implements the client-side libraries used by applications using Firebase services.
The Firebase Admin SDK is intended to run on a server or other privileged environment. The Admin SDK does not include sign-in functions.

Related

Error trying to authenticate with FCM servers when sending message using Firebase Admin SDK in node.js

When I try to send to send an FCM notification to a topic I get the following error...
Error: An error occurred when trying to authenticate to the FCM
servers. Make sure the credential used to authenticate this SDK has
the proper permissions.
I am using a brand new generated service account key and pointing to it correctly. I have confirmed that the path to the key is correct. I have also enabled Cloud Messaging for the project.
const { messaging } = require('firebase-admin');
var admin = require('firebase-admin');
console.log(process.cwd());
async function run() {
try {
var serviceAccount = require("/Users/myUser/src/my_project/node_admin/my_project-5617a-firebase-adminsdk-lwpk6-5dad9000e0.json");
const topic = 'all';
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
const payload = {
notification: {
title: "Test1234",
body: "body",
sound: 'test-sound.wav'
}
};
var options = {
priority: "high"
}
await admin.messaging().sendToTopic(topic , payload , options);
} catch (e) {
console.log(e);
}
}
run();
Hello i was copy code and run --> it working
so i think you can check path of file auth json
var serviceAccount = require("/Users/myUser/src/my_project/node_admin/my_project-5617a-firebase-adminsdk-lwpk6-5dad9000e0.json");
good luck!

How to get an idToken that is valid for development from firebase without having to spin up my frontend?

I am working on some firebase functions. This one will check if an user is logged in in firebase first. However this is a bit of a hassle in development. Now I need to login on the frontend first to get the id_token, pass it to my function url, then see the result.
The process I am following is described in the official docs: https://firebase.google.com/docs/auth/admin/verify-id-tokens
node.js
const admin = require('firebase-admin');
admin.initializeApp();
module.exports = function( request, response ) {
if( !request.query.id_token )
response.status(400).json({message: 'id token has not been provided'});
admin.auth()
.verifyIdToken( request.query.id_token )
.then( token => {
// TODO: redirect to payment portal
return response.status(200).json({message: 'Success'});
})
.catch( error => {
return response.status(401).json({message: 'You are currently not logged in as an authorised user'});
})
}
Is there a way to get an id_token that is valid from firebase without having to spin up my frontend? Good and simple alternatives solutions are welcome too.
NOTE: I am using the firebase emulators during development.
Since you're using the Firebase emulators you may create a fake user and retrieve an id token programmatically. The code below creates and logs in a user and returns an id_token that will be accepted by your function.
var firebase = require("firebase/app");
require("firebase/auth");
// Initialize Firebase and connect to the Authentication emulator
var firebaseConfig = {
// Insert Firebase config here
};
firebase.initializeApp(firebaseConfig);
firebase.auth().useEmulator('http://localhost:9099/');
// Create a fake user and get the token
firebase.auth().createUserWithEmailAndPassword("example#example.com", "password")
.then((userCredential) => {
console.log("User created")
});
firebase.auth().signInWithEmailAndPassword("example#example.com", "password")
.then((userCredential) => {
console.log("User logged in")
userCredential.user.getIdToken().then((idToken) => {
console.log(idToken)
});
});

.auth().verifyIdToken error: Firebase ID token has incorrect algorithm. Expected "none" but got "RS256"

Solved this problem by using: "firebase serve --only hosting,functions"
verifyIdToken with production Auth is working as expected
I am building a SPA with firebase and using Express.js for user validation & routing in my backend.
In my login script I use firebase-u-auth for Google Sign In, then if user logged in
var app = firebase.initializeApp(config);
firebase.auth(app).setPersistence(firebase.auth.Auth.Persistence.LOCAL)
.then(() => {
firebase.auth(app).onAuthStateChanged(function (user) {
if (!user)
return;
redirectSuccesful(user);
});
});
function redirectSuccesful(user) {
// for development purposes, enable insecure cookies for http on local server
let secure = window.location.protocol.toLowerCase() === 'https:' || (window.location.hostname !== 'localhost' && window.location.hostname !== '127.0.0.1' && window.location.hostname !== '0.0.0.0');
user.getIdToken().then((token) => {
let expDate = expiryDateFromJwt(token);
let cookies = `__session=${token}; samesite=strict; path=/${expDate ? '; expires=' + expDate.toUTCString() : ''}${secure ? '; secure' : ''}`;
document.cookie = cookies;
window.location.assign('/');
});
}
In my functions folder I use express with
const serviceAccount = require('...json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "<my-url>"
});
expressApp.get('/', wrap (async function(req, res){
const token = getSessionToken(req); //custom function that gets clean saved token from session
if(token){
const decodedToken = await admin.auth().verifyIdToken(token);
...
}
}))
Token seems correct if I pass it in https://jwt.io/, however the function verifyIdToken(token) shows "Firebase ID token has incorrect algorithm. Expected "none" but got "RS256. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token."
Where does the error comes from? Admin config?
Thanks!
You can configure the authentication to work with the emulator:
Connect your app to the Authentication Emulator
import auth = firebase.auth;
import FirebaseUser = firebase.User;
if (!environment.production) {
firebase.auth().useEmulator('http://localhost:9099/');
}

Cloud Function to Authenticate a User

I am attempting to authenticate a user to access various scopes in the user Gsuite. I can run the code locally but I cannot seem to get it accepted as a cloud function.
I have tried deploying with firebase and with gcloud. I have checked my eslint settings.
This code is coming from https://github.com/googleapis/google-api-nodejs-client/blob/master/README.md#oauth2-client
'use strict';
const fs = require('fs');
const path = require('path');
const http = require('http');
const url = require('url');
const opn = require('open');
const destroyer = require('server-destroy');
const {google} = require('googleapis');
/**
* To use OAuth2 authentication, we need access to a a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI. To get these credentials for your application, visit https://console.cloud.google.com/apis/credentials.
*/
const keyPath = path.join(__dirname, 'credentials.json');
let keys = {redirect_uris: ['']};
if (fs.existsSync(keyPath)) {
keys = require(keyPath).web;
}
/**
* Create a new OAuth2 client with the configured keys.
*/
const oauth2Client = new google.auth.OAuth2(
keys.client_id,
keys.client_secret,
`http://localhost:3000/oauth2callback`
);
/**
* This is one of the many ways you can configure googleapis to use authentication credentials. In this method, we're setting a global reference for all APIs. Any other API you use here, like google.drive('v3'), will now use this auth client. You can also override the auth client at the service and method call levels.
*/
google.options({auth: oauth2Client});
const scopes = ['https://www.googleapis.com/auth/documents'];
/**
* Open an http server to accept the oauth callback. In this simple example, the only request to our webserver is to /callback?code=<code>
*/
async function authenticate(){
// grab the url that will be used for authorization
const authorizeUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: scopes
});
const server = http.createServer(async (req, res) => {
try {
if (req.url.indexOf('/oauth2callback') > -1) {
const qs = new url.URL(req.url, 'http://localhost:3000').searchParams;
res.end('Authentication successful! Please return to the console.');
server.destroy();
const {tokens} = await oauth2Client.getToken(qs.get('code'));
oauth2Client.credentials = tokens; // eslint-disable-line require-atomic-updates
resolve(oauth2Client);
}
} catch (e) {
reject(e);
}
})
.listen(3000, () => {
// open the browser to the authorize url to start the workflow
opn(authorizeUrl, {wait: false}).then(cp => cp.unref())
.catch(
error => {
console.log(error);
});
});
destroyer(server)
.then(client => runSample(client)).catch(
error => {
console.log(error);
});
};
module.exports.authenticate=authenticate;
async function runSample(client) {
// retrieve user profile
console.log(client);
const docs = google.docs({
version: 'v1',
auth: client
});
const createResponse = await docs.documents.create({
requestBody: {
title: 'Your new document!',
},
});
}
I expect it to load as a cloud function to firebase or gcloud.
However:
Firebase returns "Deploy complete" but it never shows in the functions.
gcloud returns "SyntaxError: Unexpected token function" with the word function indicated in "async function authenticate(){"
I'm new to node.js and may be missing something really obvious to others.
You will never get User Credentials (Client ID/Client Secret) to work
in Cloud Functions (meaning authenticate and create credentials).
OAuth requires a web browser and a human. Neither one exists in Cloud
Functions. Use a Service Account instead. – John Hanley

NodeJS Example - Firebase Cloud Functions - Instantiate an Admin SDK Directory service object

Goal
Use googleapis with Firebase Cloud Functions to get a list of all users in my G Suite domain.
Question
How do I Instantiate an Admin SDK Directory service object. I do not see a NodeJS example, and I'm not clear how to setup and make the request with googleapis.
Context
This code runs from Firebase Cloud Functions, and it seems to authenticate okay. Now, how do I setup the service object at //TODO in the following code:
// Firebase Admin SDK
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
// Google APIs
const googleapis = require('googleapis')
const drive = googleapis.drive('v3')
const gsuiteAdmin = googleapis.admin('directory_v1')
// Service Account Key - JSON
let privatekey = require("./privatekey.json")
let jwtClient = new googleapis.auth.JWT(
privatekey.client_email,
null,
privatekey.private_key,
['https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/admin.directory.user'])
// Firebase Cloud Functions - REST
exports.authorize = functions.https.onRequest((request, response) => {
//authenticate request
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err)
return
} else {
console.log("Successfully connected!")
}
// TODO
// USE SERVICE OBJECT HERE??
// WHAT DOES IT LOOK LIKE?
response.send("Successfully connected!")
})
})
Order of Operations:
Create Service Account Credentials in Google Cloud Console
Add Domain-Wide Delegation to the Service Account
Authorize the API in G Suite - Security - Advanced
Go back to the Service Account and Download the .json key file
I downloaded the .json key file too soon, e.g., before authorizing the APIs in G Suite. The order, Setting up the Service Account with DwD and then authorization the API in G Suite API and then downloading the .json key file is important.
The Example
// Firebase Admin SDK
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
// Google APIs
const googleapis = require('googleapis')
const drive = googleapis.drive('v3')
const directory = googleapis.admin('directory_v1')
// Service Account Key - JSON
let privatekey = require("./privatekey.json")
let impersonator = 'example#example.com'
let jwtClient = new googleapis.auth.JWT(
privatekey.client_email,
null, // not using path option
privatekey.private_key,
['https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/admin.directory.user',
'https://www.googleapis.com/auth/admin.directory.user.readonly'],
impersonator
)
// Firebase Cloud Functions - REST
exports.getUsers = functions.https.onRequest((request, response) => {
//authenticate request
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err)
return
} else {
console.log("Successfully connected!")
}
//Google Drive API
directory.users.list ({
auth: jwtClient,
domain: 'example.com',
maxResults: 10,
orderBy: 'email',
viewType: 'domain_public'
}, function(err, res) {
if (err) {
console.log('The API returned an error: ' + err)
return;
}
var users = res.users;
if (users.length == 0) {
console.log('No users in the domain.');
} else {
console.log('Users:');
for (var i = 0; i < users.length; i++) {
var user = users[i];
console.log('%s (%s)', user.primaryEmail, user.name.fullName)
}
response.send(users)
}
})
})
})
UPDATE
The example above is not secure. A Cloud Function, especially with G Suite Domain-wide Delegation, should not respond to http requests unless they come from your application. See in this example that the Cloud Function uses admin.auth().verifyIdToken(idToken)... to validate that the request is authenticated by Firebase.
If you don't properly handle your G Suite DwD Cloud Function, you risk exposing your G Suite API to the public.

Resources