Firebase Auth - Set session expiration - javascript

How can I set session expiration for a Firebase auth session?
By default the session never expires.
I wish for the session to expire after 8 hours of inactivity.
I have read the documentation but cannot figure out how to set session expiration.
My code for signing in the user and performing tasks on sign in and sign out
firebase.auth().signInWithEmailAndPassword(data.email, data.password)
firebase.auth().onAuthStateChanged((user) => {
if (user) {
//Signed in
}else{
//Signed out
}
}
Thanks for all replies!
I have tried but cannot seem to get Firebase-admin to work.
Firebase-db.js
const admin = require('firebase-admin')
const databaseConnection = {
serviceAccountFile: './serviceAccount.json',
databaseURL: 'https://myProject.firebaseio.com/'
}
const serviceAccount = require(databaseConnection.serviceAccountFile)
const app = admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: databaseConnection.databaseURL
}, 'test')
const database = admin.database(app)
module.exports = database
sessionSignout.js
const functions = require('firebase-functions')
const database = require('../../firebase-db')
const admin = database.admin
exports.sessionSignout = functions
.region('europe-west1')
.pubsub
.schedule('*/15 * * * *')
.timeZone('Europe/Stockholm')
.onRun(async (event) => {
database.ref(`users`)
.once('value', (usersSnapshots) => {
usersSnapshots.forEach((snapshot) => {
const uid = snapshot.key
admin.auth().revokeRefreshTokens(uid)
})
})
}
I get error
Error: function execution failed. Details: Cannot read property 'auth' of undefined

The documentation you linked says that you can use the Firebase Admin SDK to revoke a user's refresh tokens in order to terminate their session. This code must run on a backend you control, which means that you won't be able to do it in the client app. The backend will need to know when the user became "inactive", by whatever definition of that you choose. Wiring this all up is non-trivial, but possible.

Thanks for all the answers!
I just wanted to share my code for others to use.
I already had code in place to accommodate presence awareness.
index.js
import database from './firebase/firebase' //Firebase setup for client
firebase.auth().onAuthStateChanged((user) => {
//Handle login and redirect
if (user) {
//We are logged in
addPresenceAwarenessListener()
}else{
...
}
}
const addPresenceAwarenessListener = () => {
// Create a reference to the special '.info/connected' path in
// Realtime Database. This path returns `true` when connected
// and `false` when disconnected.
database.ref('.info/connected').on('value', (snapshot) => {
// If we're not currently connected, don't do anything.
if (snapshot.val() == false) {
return
}
const uid = firebase.auth().currentUser.uid
//Push last login/logout to user profile
const userLastLoginOutRef = database.ref(`users/${uid}`)
userLastLoginOutRef.onDisconnect().update({lastLoginOut: firebase.database.ServerValue.TIMESTAMP})
.then(() => { userLastLoginOutRef.update({lastLoginOut: firebase.database.ServerValue.TIMESTAMP}) })
})
}
Session handling - expire sessions after n hours (setting "sessExp" in database)
firebase-db.js - Basic Firebase setup for cloud functions
const admin = require('firebase-admin')
const databaseConnection = {
serviceAccountFile: './my-project.json',
databaseURL: 'https://my-project.firebaseio.com/'
}
const serviceAccount = require(databaseConnection.serviceAccountFile)
const app = admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: databaseConnection.databaseURL
}, 'remindMiNotifier')
const database = admin.database(app)
module.exports = database
sessionSignout.js - Signout user after a specific time period - if specified. Default to eternal session.
const functions = require('firebase-functions')
const moment = require('moment')
const database = require('../../firebase-db')
const admin = database.app
//Import enviroment variable config (.env)
require('dotenv').config()
//Export cron job - deploy: firebase deploy --only functions:sessionSignout
exports.sessionSignout = functions
.region('europe-west1')
.pubsub
.schedule('*/15 * * * *')
.timeZone('Europe/Stockholm')
.onRun(async (event) => {
//Start execution
const now = moment()
const defaultSessionTime = 0 //Eternal session
//Get all users and calculate inactive time - time since last login
let logoutUsersArray = []
await database.ref(`users`)
.once('value', (usersSnapshots) => {
usersSnapshots.forEach((snapshot) => {
const userData = snapshot.val()
const lastLoginOut = (userData.lastLoginOut) ? userData.lastLoginOut : 0
//Only process users that has a login/out time stamp
if(lastLoginOut > 0){
const userSessionTime = (userData.sessExp) ? userData.sessExp : defaultSessionTime
const hoursSinceLastLoginOut = now.diff(lastLoginOut, 'hours')
const logoutUser = ( userSessionTime > 0 && (hoursSinceLastLoginOut > userSessionTime) )
if(logoutUser){
const userId = snapshot.key
const userName = (userData.alias) ? userData.alias : userData.displayName
const email = (userData.email) ? userData.email : ''
const userObject = {
userId,
userName,
email,
lastLoginOut,
diffHours: now.diff(lastLoginOut, 'hours')
}
logoutUsersArray.push(userObject)
}
}
})
})
console.log('logoutUsersArray', logoutUsersArray)
//Collect all promises to carry out
let myPromises = []
// Revoke all refresh tokens for each user
logoutUsersArray.forEach((logoutUser) => {
const uid = logoutUser.userId
myPromises.push(
admin.auth().revokeRefreshTokens(uid)
.then(() => {
return admin.auth().getUser(uid)
})
.then((userRecord) => {
return new Date(userRecord.tokensValidAfterTime).getTime() / 1000
})
.then((timestamp) => {
// Retrieve the timestamp of the revocation, in seconds since the epoch.
console.log('Tokens revoked at: ', timestamp)
return Promise.resolve(true)
})
.catch((err) => {
console.error('Error', err)
return Promise.reject(err)
})
)
})
//Execute promises
console.log('Execute promises')
return Promise.all(myPromises)
.then(() => Promise.resolve(true))
.catch((err) => {
console.error('Error', err)
return Promise.reject(err)
})
})//End sessionSignout
Documentation on firebase-admin can be found here.

Related

Is there a library for promptForCredentials with firebase?

I am trying to learn how to delete users from the Firebase Auth provided, but when I click on the delete button on my html it gives me Uncaught ReferenceError: promptForCredentials is not defined
I can't find any information on if I am suppose to import it anywhere. I'm using plain vanilla javascript.
Here is my code:
my html includes for scripts:
<script src="https://www.gstatic.com/firebasejs/8.2.7/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.2.7/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.2.7/firebase-database.js"></script>
javascript:
const firebaseConfig = {//my app information}
firebase.initializeApp(firebaseConfig);
const auth = firebase.auth();
const database = firebase.database();
const fname = document.querySelector('#fname');
const lname = document.querySelector('#lname');
const email = document.querySelector('#email');
const password = document.querySelector('#password');
const confirm_password = document.querySelector('#confirmpassword');
const deleteConfirmed = document.querySelector('.deleteConfirmed');
const passConfirmed = document.querySelector('#deletePass');
auth.onAuthStateChanged(user => {
if(user) {
const user = auth.currentUser
const database_ref = database.ref('users/' + user.uid).on('value', (snapshot) => {
fname.value = snapshot.val().first_name;
lname.value = snapshot.val().last_name;
email.value = snapshot.val().email;
});
deleteConfirmed.addEventListener('click', () => {
const credential = promptForCredentials();
user.reauthenticateWithCredential(credential).then(() => {
// User re-authenticated.
}).catch((error) => {
// An error ocurred
// ...
});
})
} else {
location.href = "/login";
}
});
It looks like you copied code from the Firebase documentation on re-authenticating a user, which shows:
const user = firebase.auth().currentUser;
// TODO(you): prompt the user to re-provide their sign-in credentials
const credential = promptForCredentials();
user.reauthenticateWithCredential(credential).then(() => {
// User re-authenticated.
}).catch((error) => {
// An error occurred
// ...
});
That TODO there means that you have to implement the prompt for the credentials of the user in whatever way works for your app, and then pass them to the reauthenticateWithCredential API.

stripe webhook with firebase showing error

it is showing Unexpected value for STRIPE_SIGNING_SECRET error even after checking it many times in the env file
the terminal shows everything created but it does not reach firebase database I am thinking there is a error in the code
the stripe dashboard also says connected
I am using the forward to local host line in git terminal
webhook code
import { buffer } from "micro";
import * as admin from 'firebase-admin'
//secure a connection to Firebase from backend
const serviceAccount = require('../../../permissions.json');
const app = !admin.apps.length ? admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
})
: admin.app();
// establish connection to stripe
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const endpointSecret = process.env.STRIPE_SIGNING_SECRET;
if (typeof endpointSecret !== "string") {
console.error("Unexpected value for STRIPE_SIGNING_SECRET");
// potentially throw an error here
}
const fulfillOrder = async (session) => {
//console.log('Fulfilling order', session)
return app
.firestore()
.collection("user")
.doc(session.metadata.email)
.collection("orders")
.doc(session.id)
.set({
amount: session.amount_total / 100,
amount_shipping: session.amount_total_details.amount_shipping / 100,
images: JSON.parse(session.metadata.images),
timestamp: admin.firestore.FieldValue.serverTimestamp(),
})
.then(() => {
console.log(`success: order ${session.id} had been added to db`);
});
};
export default async (req, res) =>{
if(req.method === 'post'){
const requestBuffer = await buffer(req);
const payload = requestBuffer.toString();
const sig = req.headers["stripe-signature"];
let event;
// verify that the event posted came from stripe
try{
event = stripe.webhooks.constructEvent(
payload,
sig,
endpointSecret);
} catch (err) {
console.log('ERROR', err.message)
return res.status(400).send(`Webhook error: ${err.message}`)
}
//handle the checkout event
if (event.type === 'checkout.session.completed') {
const session = event .data.object;
//fulfill the order...
return fulfillOrder(session)
.then(() => res.status(200))
.catch((err) => res.status(400).send(`Webhook error: ${err.message}`));
}
}
};
export const config = {
api: {
bodyParser: false,
externalResolver: true,
},
};
firebase rules
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow write: if false;
allow read: if true;
}
}
}
const endpointSecret = process.env.STRIPE_SIGNNING_SECRET;
Typo: STRIPE_SIGNNING_SECRET
To avoid the next issue, fix the other typo:
const sig = req.headers["stripe-signatur"];
stripe-signature

Firebase TypeError: Cannot read property 'val' of undefined

I have tried Firebase cloud function for sending a notification.My project structure
and this is the index.js,
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.pushNotification = functions.database.ref('/messages').onWrite( event => {
console.log('Push notification event triggered');
const message = event.data.val();
const user = event.data.val();
console.log(message);
console.log(user);
const topic = "myTopic";
const payload = {
"data": {
"title": "New Message from " + user,
"detail":message,
}
};
return admin.messaging().sendToTopic(topic, payload);
});
The above code is misconfigured, when I deploy in Node.js, LOG in Function shows:
"TypeError: Cannot read property 'val' of undefined".
What Actually I am trying to do :
I am trying to extract info from snapshot load into that index.js so that when a new child gets added to Real-time database, it should trigger a notification payload with a title and body.
In Android, I use a child listener, for listening when a new record is added
FirebaseDatabase.getInstance().getReference().child("messages")
OnChildAdded(.....){
if (dataSnapshot != null) {
MessageModel messageModel = dataSnapshot.getValue(MessageModel.class);
if (messageModel != null) {
// do whatever
}
}
But in index.js, I could not able to parse that.
A bit guidance how to fixate index.js according to my database structure would be immensely appreciated.
PS- I have never done coding in JS
If you want more context, I'd be happy to provide it.
Change this:
exports.pushNotification = functions.database.ref('/messages').onWrite( event => {
const message = event.data.val();
const user = event.data.val();
});
to this:
exports.pushNotification = functions.database.ref('/messages').onWrite(( change,context) => {
const message = change.after.val();
});
Please check this:
https://firebase.google.com/docs/functions/beta-v1-diff#realtime-database
The cloud functions were changed and now onWrite has two parameters change and context
The change has two properties before and after and each of these is a DataSnapshot with the methods listed here:
https://firebase.google.com/docs/reference/admin/node/admin.database.DataSnapshot
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/NOTIFICATIONS/{UserId}/{{notification_id}').onWrite((change, context) =>
{
const UserId = context.params.UserId;
const notification = context.params.notification;
console.log('The user Id is : ', UserId);
if(!change.after.exists())
{
return console.log('A Notification has been deleted from the database : ', notification_id);
}
if (!change.after.exists())
{
return console.log('A notification has been deleted from the database:', notification);
return null;
}
const deviceToken = admin.database().ref(`/USER/${UserId}/device_token`).once('value');
return deviceToken.then(result =>
{
const token_id = result.val();
const payload = {
notification : {
title : "Friend Request",
body : "You've received a new Friend Request",
icon : "default"
}
};
return admin.messaging().sendToDevice(token_id, payload).then(response => {
console.log('This was the notification Feature');
});
});
});

Return Promise<String> from Cloud Function

I want to return the myref as the promise as the reponse from this function to the calling end.Please help i am new at this.
const functions = require("firebase-functions");
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.createService = functions.https.onRequest((req, res) => {
// Grab the text parameter.
const serviceName = req.query.serviceName;
const ttlHour = req.query.ttlHour;
const promise = [];
const myref = admin.database().ref("/root/events");
promise.push(myref);
// Push the new message into the Realtime Database using the Firebase
Admin SDK.
myref.set({ name: serviceName, ttl: ttlHour, startTs: 0 }).then(snapshot => {
res.send("Write succeeded!");
});
});

How do I add a user to my Firebase DB with it's unique id when I first create a user for authentication

This may seem like an obvious question, but i'm having trouble creating a user inside of my Firebase db. I'm very new to firebase and coding for that matter.
How I understand it to be is when a user clicks the sign up button, a user with the newly authenticated uid will be created under 'object/users". However the user is always null. In the google documentation it says that sometimes the user is null because it hasn't finished initializing the user. And to put it inside of onAuthStateChanged. This works, however, if I did this a user would be created/overwritten every time they log in or log out.
I suppose what I'm looking for is some solution to create a user inside of my firebase db when a user first signs up with valid information.
Here is my code:
function LoginCtrl($scope) {
$scope.txtEmail = document.getElementById('txtEmail');
$scope.txtPassword = document.getElementById('txtPassword');
$scope.btnLogin = document.getElementById('btnLogin');
$scope.btnSignUp = document.getElementById('btnSignUp');
$scope.btnLogout = document.getElementById('btnLogout');
btnLogin.addEventListener('click', e => {
const email = txtEmail.value;
const pass = txtPassword.value;
const auth = firebase.auth();
const promise = auth.signInWithEmailAndPassword(email, pass);
promise.catch(e => console.log(e.message));
});
btnSignUp.addEventListener('click', e => {
const email = txtEmail.value;
const pass = txtPassword.value;
const auth = firebase.auth();
const promise = auth.createUserWithEmailAndPassword(email, pass);
promise.catch(e => console.log(e.message));
createUser();
});
function createUser() {
var user = firebase.auth().currentUser;
if (user != null) {
const dbRefObject = firebase.database().ref().child('object');
const dbRefList = dbRefObject.child("users/" + user.uid);
dbRefList.set({
name: "jim",
});
}
}
btnLogout.addEventListener('click', e => {
firebase.auth().signOut();
});
firebase.auth().onAuthStateChanged(firebaseUser => {
if (firebaseUser) {
console.log(firebaseUser);
btnLogout.classList.remove('hide');
btnLogin.classList.add('hide');
} else {
console.log('not logged in');
btnLogout.classList.add('hide');
btnLogin.classList.remove('hide');
}
})
}
The culprit is in these three lines:
const promise = auth.createUserWithEmailAndPassword(email, pass);
promise.catch(e => console.log(e.message));
createUser();
You are firing off createUser() too soon. In the above code fragment, this happens right after starting the (asynchronous) registration process – the SDK never had a chance to actually create the user in the auth database!
The createUserWithEmailAndPassword returns a promise, as reflected in your local variable. What you need to do here is chaining the two calls, i.e. start the database manipulation after the promise is fulfilled.
Try this:
// inside the click handler
btnSignUp.addEventListener('click', e => {
const email = txtEmail.value;
const pass = txtPassword.value;
const auth = firebase.auth();
auth.createUserWithEmailAndPassword(email, pass)
.then(user => createUser(user)) // <-- important!
.catch(e => console.log(e.message));
});
function createUser(user) {
// no need for currentUser if you pass it in as an argument
if (user) {
...
}
}

Categories