0
votes

I would like, that the users in my reactjs application to be automatically logged out after 1 hour. (Login session should be 1 hour, then users need to login again).

I have no idea how to do this. Currently, the users are able to login using the firebase function '.signInWithEmailAndPassword(email, password)', but then have to manually click the logout button (which triggers the firebase '.signOut()' function). Also, this is the data that will be stored in my browsers local storage, when a user is logged in (removed after signout of course):

enter image description here

withAuthentication.js code

import React from 'react'
import { connect } from 'react-redux';
import { compose } from 'recompose';
import { withFirebase } from '../Firebase';

const withAuthentication = (Component) => {
  class WithAuthentication extends React.Component {

    constructor(props) {
        super(props);

        this.props.onSetAuthUser(JSON.parse(localStorage.getItem('authUserLocalStorage')));
    }

    componentDidMount() {
        this.listener = this.props.firebase.onAuthUserListener(
            //next
            authUser => {
                localStorage.setItem('authUserLocalStorage', JSON.stringify(authUser));
                this.props.onSetAuthUser(authUser)
            },
            //fallback
            () => {
                localStorage.removeItem('authUserLocalStorage');
                this.props.onSetAuthUser(null)
            }
        );
    }

    componentWillUnmount() { 
        this.listener();
    }

    render() {
        return (
            <Component {...this.props} />
        );
    }
}

const mapDispatchToProps = dispatch => ({ //used for dispatching actions to the store (redux)
    onSetAuthUser: authUser =>
    dispatch({ type: 'AUTH_USER_SET', authUser }),
});

return compose(
    withFirebase,
    connect(null, mapDispatchToProps)
)(WithAuthentication);
};
export default withAuthentication;

Firebase.js code

import firebaseApp from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import 'firebase/storage';

const firebaseConfig = {
 apiKey: process.env.REACT_APP_API_KEY,
 authDomain: process.env.REACT_APP_AUTH_DOMAIN,
 databaseURL: process.env.REACT_APP_DATABASE_URL,
 projectId: process.env.REACT_APP_PROJECT_ID,
 storageBucket: process.env.REACT_APP_STORAGE_BUCKET,
 messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
 appId: process.env.REACT_APP_APP_ID,
};

class Firebase {
constructor() {
    firebaseApp.initializeApp(firebaseConfig); //initialize firebase with the configuration

    this.fieldValue = firebaseApp.firestore.FieldValue; //(using Cloud Firestore)

    //we implement the authentication & database API for our Firebase class
    this.firebaseAuth = firebaseApp.auth(); //instantiate the firebase auth package
    this.firestoreDb = firebaseApp.firestore(); //instantiate Cloud Firestore (using Cloud Firestore)
    this.storage = firebaseApp.storage(); //instantiate firebase storage
}

/*****************************************************************
 ********* Authentication API (authentication interface) *********
 ****************************************************************/

// sign up function (registration)
doCreateUserWithEmailAndPassword = (email, password) =>
    this.firebaseAuth.createUserWithEmailAndPassword(email, password); //call the Firebase 'createUserWithEmailAndPassword' to create a user

// login/sign-in function
doSignInWithEmailAndPassword = (email, password) =>
    this.firebaseAuth.signInWithEmailAndPassword(email, password);

// Sign out function (Firebase knows about the currently authenticated user. If no user is authenticated, nothing will happen when this function is called)
doSignOut = () => 
    this.firebaseAuth.signOut();

// Reset password for an authenticated user
doPasswordReset = (email) =>
    this.firebaseAuth.sendPasswordResetEmail(email);

// Change password for an authenticated user
doPasswordUpdate = password =>
    this.firebaseAuth.currentUser.updatePassword(password);


// send a verification email
doSendEmailVerification = () =>
    this.firebaseAuth.currentUser.sendEmailVerification({
        url: process.env.REACT_APP_CONFIRMATION_EMAIL_REDIRECT,
    });


/*****************************************************************
 ******************* User API (User interface) *******************
 ****************************************************************/

 //we create a function, that makes use of the firebase function 'onAuthStateChanged()', so we can call it from other components
onAuthUserListener = (next, fallback) =>

    this.firebaseAuth.onAuthStateChanged(authUser => {
        if (authUser) { //if user is not null

            this.user(authUser.uid)
            .get() //(using Cloud Firestore)
            .then(snapshot => {
                const dbUser = snapshot.data(); //(using Cloud Firestore)

                    //default empty roles
                    if (!dbUser.roles) {
                        dbUser.roles = {};
                    }

                    //merge auth and db user
                    //we merge everything from the database user with the unique identifier and email from the authenticated user
                    authUser = {
                        uid: authUser.uid,
                        email: authUser.email,

                        //To find out if a user has a verified email, you can retrieve this information from the authenticated user
                        emailVerified: authUser.emailVerified,
                        providerData: authUser.providerData,

                        ...dbUser
                    };

                    next(authUser); //implement the specific implementation details of every higher-order component (local state for authentication, redirect for authorization)
                });
        } else {
            fallback(); //implement the specific implementation details of every higher-order component (local state for authentication, redirect for authorization)
        }
    });
}
export default Firebase;
1

1 Answers

2
votes

I also struggled with that issue. Unfortunately, with the default implementation of Firebase Auth it isn't possible anymore to change the duration of the refresh token.

You could solve it by using a timeout for an hour and then logging the user manually out.

this.firebaseAuth.onAuthStateChanged((user) => {
    let userSessionTimeout = null;

    if (user === null && userSessionTimeout) {
      clearTimeout(userSessionTimeout);
      userSessionTimeout = null;
    } else {
      user.getIdTokenResult().then((idTokenResult) => {
        const authTime = idTokenResult.claims.auth_time * 1000;
        const sessionDurationInMilliseconds = 60 * 60 * 1000; // 60 min
        const expirationInMilliseconds = sessionDurationInMilliseconds - (Date.now() - authTime);
        userSessionTimeout = setTimeout(() => this.firebaseAuth.signOut(), expirationInMilliseconds);
      });
    }
  });