I'm fairly new to Firebase and Node.js. I have created this function in my Cloud Functions to login users with a custom token:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
var serviceAccount = require("./service-account.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: functions.config().firebase.databaseURL
});
const cors = require('cors')({origin: true});
exports.login = functions.https.onRequest((req, res) => {
cors(req, res, () => {
//doing some validation..
//get password from db and match it with input password
var userRef = admin.firestore().collection('users')
userRef.where('username', '==', username).get()
.then(snapshot => {
if(snapshot.size > 1){
res.status(200).send("Invalid account!");
return;
}
snapshot.forEach(doc => {
var userPass = doc.data().password;
//if password matches, generate token, save it in db and send it
if(userPass && password == userPass){
var uid = doc.data().uid;
var admin = Boolean(doc.data().admin);
var server = Boolean(doc.data().server);
var additionalClaims = {
admin: admin,
server: server
};
admin.auth().createCustomToken(uid, additionalClaims)
.then(function(customToken) {
res.status(200).send("token:" + customToken);
})
.catch(function(error) {
res.status(200).send("Token creation failed!");
});
//save token in db..
}else{
res.status(200).send("Invalid credentials!");
}
});
})
.catch(err => {
res.status(200).send("User authentication failed!");
});
});
});
I used the token generation method in the documentation, but whenever I try to login a user it throws the error:
TypeError: admin.auth is not a function
at snapshot.forEach.doc (/user_code/index.js:128:27)
at QuerySnapshot.forEach (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/firestore/src/reference.js:1012:16)
at userRef.where.get.then.snapshot (/user_code/index.js:110:13)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
What could it be that I'm doing wrong?
This declaration of admin:
var admin = Boolean(doc.data().admin);
is hiding this one:
const admin = require('firebase-admin');
Use a different name, such as:
var docAdmin = Boolean(doc.data().admin);
Related
Im trying to call a Clound function using a Volley POST request but it is returning error Unexpected response code 500. What this code does is basically request a Token string from the user and then reformat it with new info and then return the new token to the user.
Here is the Java code
RequestQueue mRequestQueue = Volley.newRequestQueue(this);
StringRequest mCloudRequest = new StringRequest(Request.Method.POST,
"my_function_url_from_firebase", new Response.Listener<String>() {
#Override
public void onResponse(String response) {
signInToFirebaseWithCustomToken(response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}){
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("id_token", huaweiAccount.getIdToken());
params.put("uid", uid);
params.put("name", huaweiAccount.displayName);
if (huaweiAccount.email != null) {
params.put("email", huaweiAccount.email);
} else {
params.put("email", "");
}
params.put("photoURL", "");
return params;
}
};
And here is the index.js file.
const app = express();
const bodyParser = require('body-parser');
const PORT = process.env.PORT || 5000;
const functions = require('firebase-functions');
// Firebase Admin SDK
const admin = require('firebase-admin');
const serviceAccount = require('./serviceAccountKey.json');
// For make network calls
const request = require('request-promise');
// Initialize Firebase Admin
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'my_firebase_server_url',
});
// Initialize Express and create endpoint
app
.use(bodyParser.json()) // Parse json in request body
.use(bodyParser.urlencoded({
extended: true,
}))
.post('/createCustomToken', (req, res) => {
if (req.body.id_token === undefined) {
// idToken is not find
const ret = {
error_message: 'id_token not found',
};
return res.status(400).send(ret);
}
// Verify idToken
// Create new user on Firebase if user doesn't exist
// Generate custom auth token
// Return client
return verifyHuaweiToken(req.body)
.then((customAuthToken) => {
const ret = {
firebase_token: customAuthToken,
};
return res.status(200).send(ret);
}).catch((err) => {
return res.status(400).send(err);
});
})
.listen(PORT, () => console.log(`Listening on ${ PORT }`));
// Verify idToken on Huawei Server
function verifyHuaweiToken(body) {
return request({
method: 'GET',
uri: 'https://oauth-login.cloud.huawei.com/oauth2/v3/tokeninfo?id_token=' + body.id_token,
json: true,
}).then((response) => {
// Token invalid. Throw an error and stop process
if (response.error !== undefined) {
return Promise.reject(new Error('Something went wrong'));
}
// Get user
return getFirebaseUser(body);
}).then((userRecord) => {
// After user created on Firebase, create new custom token based on user uid
return admin.auth().createCustomToken(userRecord.uid);
}).then((token) => {
// Return token to client
return token;
});
}
function getFirebaseUser(body) {
const firebaseUid = 'huawei_' + body.uid;
// Find user by user uid
return admin.auth().getUser(firebaseUid).then(function(userRecord) {
return userRecord;
}).catch((error) => {
// If user is not exist on Firebase, create new one
if (error.code === 'auth/user-not-found') {
return admin.auth().createUser({
uid: firebaseUid,
displayName: body.name,
photoURL: body.picture,
email: body.email,
});
}
return Promise.reject(error);
});
}
exports.app = functions.https.onRequest(app);
Does anyone know what is wrong with my backend code? or is it the user side code the one causing the problem?
Please help me. Im not a Backend expert.
a simple example in firebase functions to create custom token would be like this one:
const functions = require('firebase-functions');
const firebaseAdmin = require('firebase-admin');
const express = require('express');
const app = express();
firebaseAdmin.initializeApp({
serviceAccountId: 'SERVICE-ACCOUNT_EMAIL',
databaseURL: 'https://PTOJECT-id.firebaseio.com'
});
app.post('/token', async (req, res) => {
try {
const token = await firebaseAdmin.auth().createCustomToken(req.body.userUID);
res.status(200).send(token)
} catch (error) {
res.status(500).send("something went wrong")
}
});
module.exports.app = functions.https.onRequest(app);
make sure that your service account has at least the iam.serviceAccounts.signBlob permission which is included in the role roles/iam.serviceAccountTokenCreator
I have a function that just fetches the data from my firebase and displays it. This works perfectly when deployed, but not locally.
I've attached my code just in case, but seeing as it works when deployed, I dont think that will be the problem, also its copy pasted from freecodecamp tutorial.
Directory is as follows:
firebase folder
|functions
||APIs
|||todos.js
||util
|||admin.js
||index.js
Also, the local version does have an output, its just the empty array initialised in todos.js line 9.
//todos.js
const { db } = require('complete file path');
exports.getAllTodos = (request, response) => {
db
.collection('todos')
.orderBy('createdAt', 'desc')
.get()
.then((data) => {
let todos = [];
data.forEach((doc) => {
todos.push({
todoId: doc.id,
title: doc.data().title,
body: doc.data().body,
createdAt: doc.data().createdAt,
});
});
return response.json(todos);
})
.catch((err) => {
console.error(err);
return response.status(500).json({ error: err.code});
});
};
//admin.js
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
module.exports = { admin, db };
//index.js
const functions = require('firebase-functions');
const app = require('express')();
const {
getAllTodos
} = require('./APIs/todos')
app.get('/todos', getAllTodos);
exports.api = functions.https.onRequest(app);
I also performed export GOOGLE_APPLICATION_CREDENTIALS="path/to/key.json" to no avail.
You initialized the the app without any credentials:
const refreshToken; // Get refresh token from OAuth2 flow
admin.initializeApp({
credential: admin.credential.refreshToken(refreshToken),
databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});
[Reference this site for more information:] https://firebase.google.com/docs/admin/setup/#initialize-without-parameters
I am using firebase to validate that the data sent by the client is authentic using a google or facebook token. But when uploading to functions it generates an error.
Function execution took 60003 ms, finished with status: 'timeout'
Function execution took 232 ms, finished with status code: 304
On my home pc it runs me normally. Is what I am doing correct or is there some other way to validate the authenticity of the data
firebase.js
var firebase = require("firebase/app");
require("firebase/auth");
require("firebase/firestore");
var firebaseConfig = {
apiKey: ....,
authDomain: ....,
projectId: ....,
storageBucket: ....,
messagingSenderId: ....,
appId: ....
measurementId: ....
};
let app = null
if (!firebase.apps.length) {
app = firebase.initializeApp(firebaseConfig)
}
const auth = firebase.auth();
const google = new firebase.auth.GoogleAuthProvider();
const facebook = new firebase.auth.FacebookAuthProvider();
module.exports = {auth, google, facebook, firebase}
datos.js
const admin = require("firebase-admin");
var serviceAccount = require("./whatsapp-f91a0-firebase-adminsdk-f4fes-a0490d7a8f.json");
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://whatsapp-f91a0.firebaseio.com"
});
}
const db = admin.firestore();
module.exports = db;
Code functions
const db = require('./datos');
const {auth, google, facebook, firebase} = require('./firebase')
module.exports = function (req, res, next) {
if (req.method === 'POST') {
const body = []
req.on('data', (chunk) => {
body.push(chunk)
})
req.on('end', () => {
try {
const event = JSON.parse(body)
let xIdPagina = event.xIdPagina;
let xTokenUser = event.xTokenUser;
let xProveedor = event.xProveedor;
let xMotivoReporte = event.xMotivoReporte;
// Build Firebase credential with the Google ID token.
var credential = google.credential(xTokenUser);
// Sign in with credential from the Google user.
auth.signInWithCredential(credential)
.then(result =>{
if(xMotivoReporte != "" && xTokenUser != "" && xTokenUser != ""){
if(xMotivoReporte != "eliminar"){
let grupos = db.collectionGroup('reportes');
let query = grupos
.where('xIdPagina','=', xIdPagina)
.where('xUserId','=', result.user.uid)
.limit(1)
.select('xUserId')
.get()
.then(querySnapshot => {
const documents = querySnapshot.docs.map(doc => doc.data())
if(documents.length < 1 ){
let grupo = {
xIdPagina: xIdPagina,
xUserId: result.user.uid,
xProveedor: xProveedor,
xEmail: result.user.email,
xMotivoReporte : xMotivoReporte
}
let addDoc = db.collection('facebook_spa').doc(xIdPagina).collection('reportes').add(
grupo).then(ref => {
res.statusCode = 200
});
}
else{
console.log('Errores de aqui');
res.statusCode = 400
}
})
.catch(err => {
console.log('Error getting documents', err);
res.statusCode = 404
});
}else{
let grupos = db.collection('facebook_spa');
let query = grupos.where('xEstado', '==', true)
.where('xId','=', xIdPagina)
.where('xUserId','=', result.user.uid)
.limit(1)
.select('xUserId')
.get()
.then(querySnapshot => {
const documents = querySnapshot.docs.map(doc => doc.data())
if(documents.length != 0){
const res = db.collection('facebook_spa').doc(xIdPagina).delete();
}
else{
res.statusCode = 404
}
})
.catch(err => {
console.log('Error getting documents', err);
res.statusCode = 404
});
}
}
else{
throw Error('reporte');
}
})
.catch(function(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;
// ...
});
} catch (error) {
console.log("El error esta aqui: " + error)
res.statusCode = 400
}
finally {
res.end()
}
})
}
}
I am currently working on a little project with oAuth2 with node js.
Node js with express and node-oauth2-server as a rest full service to login etc...
Everything is working just fine, they can register, verify their email address and login (forgott password etc. is not finished yet)
But I can not set the expire value of the token.
My favourite implementation would be a login with or without permanent login (in the UI this common little switch beneath the login form).
Also I would like to store the client information with the accessToken, something like Browser, Location etc.
So that a user can request where he is currently logged in (like you can do in facebook).
Most of my oAuth2 code comes from this tutorial:
https://blog.cloudboost.io/how-to-make-an-oauth-2-server-with-node-js-a6db02dc2ce7
My main problem is, that I don't know where to handle the data. In my register (etc.) endpoints everything runs through my own middleware. But with the node-oauth2-server I have no middleware.
Thanks!
Chris
Here is my server.js:
if(process.env.NODE_ENV === undefined)
process.env.NODE_ENV = "dev"
/* REQUIRE */
const oAuth2Server = require('node-oauth2-server');
const express = require('express');
const bodyParser = require('body-parser');
const util = require('util');
const dbCon = require('./subsystem/mySql')
const oAuthModel = require('./endpoints/auth/authModel')(dbCon);
/* CONST */
let port = 3000;
if(process.env.NODE_ENV !== 'production')
port = 3000;
else
port = 80;
const debug = true;
const app = express();
/* INIT */
app.oauth = oAuth2Server({
model: oAuthModel,
grants: ['password'],
debug: debug
})
/* ROUTER */
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(app.oauth.errorHandler());
const authRoutes = require('./router/auth')(express.Router(), app, dbCon)
app.use('/auth', authRoutes);
app.all('*', (req, res) => {
res.status(404).send({message: "This service was not found"});
});
/* Start Server */
app.listen(port, () => {
console.log(`listening on port ${port} in ${process.env.NODE_ENV} mode`)
})
Here is my authModel:
let dbCon;
module.exports = injectedDbCon => {
dbCon = injectedDbCon;
return {
getClient: getClient,
saveAccessToken: saveAccessToken,
getUser: getUser,
grantTypeAllowed: grantTypeAllowed,
getAccessToken: getAccessToken
}
}
const userDB = require('../user/userDB')(dbCon);
const authDB = require('./authDB');
function getClient(clientID, clientSecret, callback){
const client = {
clientID,
clientSecret,
grants: null,
redirectUris: null
}
callback(false, client);
}
function grantTypeAllowed(clientID, grantType, callback) {
console.log('grantTypeAllowed called and clientID is: ', clientID, ' and grantType is: ', grantType);
callback(false, true);
}
function getUser(email, password, callback){
console.log('getUser() called and email is: ', email, ' and password is: ', password, ' and callback is: ', callback, ' and is userDBHelper null is: ', userDB);
//try and get the user using the user's credentials
userDB.getUserFromCrentials(email, password)
.then(data => {callback(false,data[0][0])})
.catch(error => {callback(error,null)})
}
/* saves the accessToken along with the userID retrieved the specified user */
function saveAccessToken(accessToken, clientID, expires, user, callback){
console.log('saveAccessToken() called and accessToken is: ', accessToken,
' and clientID is: ',clientID, ' and user is: ', user, ' and accessTokensDBhelper is: ', authDB)
//save the accessToken along with the user.id
authDB.saveAccessToken(accessToken, user.id)
.then(data => {callback(null)})
.catch(error => {callback(error)})
}
function getAccessToken(bearerToken, callback) {
//try and get the userID from the db using the bearerToken
authDB.getUserIDFromBearerToken(bearerToken)
.then(data => {
const accessToken = {
user: {
id: data,
},
expires: null
}
callback(true,accessToken)
})
.catch(error => {callback(false,error)})
}
Here is my authDB:
const dbCon = require('../../subsystem/mySql')
const saveAccessToken = (accessToken, userID) => {
return new Promise((resolve,reject) => {
//execute the query to get the user
dbCon.query(`INSERT INTO access_tokens (access_token, user_id) VALUES (?, ?) ON DUPLICATE KEY UPDATE access_token = ?;`,[accessToken,userID,accessToken])
.then(data => {resolve(true)})
.catch(error => {reject(error)})
})
}
const getUserIDFromBearerToken = bearerToken => {
return new Promise((resolve,reject) => {
//create query to get the userID from the row which has the bearerToken
const getUserIDQuery = `SELECT * FROM access_tokens WHERE access_token = ?;`
//execute the query to get the userID
dbCon.query(getUserIDQuery,[bearerToken])
.then(data => {
if(data.results != null && data.results.length == 1)
resolve(data.results[0].user_id)
else
reject(false)
})
.catch(error => {reject(error)})
})
}
module.exports.saveAccessToken = saveAccessToken
module.exports.getUserIDFromBearerToken = getUserIDFromBearerToken
you can pass accessTokenLifetime (in seconds) as option to the oAuth2Server constructor.
/* INIT */
app.oauth = oAuth2Server({
model: oAuthModel,
grants: ['password'],
debug: debug,
accessTokenLifetime: 4 * 60 * 60
})
As descripted in the docs (https://oauth2-server.readthedocs.io/en/latest/api/oauth2-server.html#new-oauth2server-options) you can pass any option for the authenticate, authorize and token methods to the oAuth2Server constructors options.
The accessTokenLifetime option is an option of the token method (https://oauth2-server.readthedocs.io/en/latest/api/oauth2-server.html#token-request-response-options-callback).
I get the above error when I try and send messages to devices:
let functions = require("firebase-functions");
const admin = require("firebase-admin");
var serviceAccount = require("./configs.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://pushmessage-bd1eb.firebaseio.com"
});
const db = admin.firestore();
exports.getUsers = functions.https.onRequest(async (req, res) => {
db.collection("users")
.get()
.then(snapshot => {
const messaging = admin.messaging();
let registrationTokens = [];
snapshot.forEach(doc => {
let id = doc.id;
registrationTokens.push(id);
});
console.log(registrationTokens);
// process the tokens
const message = {
data: { title: "Testing", body: "Test" },
tokens: registrationTokens
};
messaging.sendMulticast(message).then(response => {
console.log(
response.successCount + " messages were sent successfully"
);
});
});
});
sendMulticast wasn't introduced into the Firebase Admin SDK until very recently. Try upgrading your firebase-admin dependency to the latest (npm install firebase-admin#latest).