We need to activate Google Sign In option from the Firebase console. Click on Authentication from the side menu.
Enable Google Sign In
Now let’s create a function for Sign In.
In ../src/firebase/firebase.js add
var provider = new firebase.auth.GoogleAuthProvider();export const logIn = () => {return new Promise((resolve, reject) => {firebase.auth().signInWithPopup(provider).then(function(result) {var token = result.credential.accessToken;var user = result.user;resolve({token: token,user: user,});});});}
We need to add a provider first to authenticate. Here we are using GoogleAuthProvider to enable Sign In using Google account. There are different providers for different Sign In methods.
We create a function logIn which returns a promise that resolves with the token and user data of the authenticated user.
The signInWithPopup creates a popup for the user to Sign In
After Sign In, it returns the object with access token and user details like email, user id, etc. Console log the Object to find out what it returns.
For our authentication to work, we need another page to show to the user. For this we can create a Home Page.
For that let’s create a new component Home.js in ../src/components/Home.js
import React, { Component } from 'react';import AppBar from '@material-ui/core/AppBar';import Toolbar from '@material-ui/core/Toolbar';import AccountCircle from '@material-ui/icons/AccountCircle';import Grid from '@material-ui/core/Grid';import screenshot from '../assets/screenshot.png';import '../css/Home.css';class Home extends Component {constructor(props) {super(props);this.state = {uDetails: {},};}render () {console.log(this.state);return(<div className="Home"><Grid container spacing={0}><Grid item xs={12}><AppBar position="static"><Toolbar><h1>Notes App</h1><AccountCircle style={{ position: 'absolute', right: '30px'}} /></Toolbar></AppBar></Grid><br /><br /><br /><Grid item xs={12} className="Screenshot" style={{margin: '20px'}}><p id="desc">Our notes app!</p><img src={ screenshot } id="screenshot" alt="screenshot" /></Grid></Grid></div>);}}export default Home;
The home page looks like this.
This is just a very simple component that has an AppBar with an account button with which the user can Sign In. Feel free to unleash your creativity on making the home page more beautiful but for our purpose, this is enough.
../src/css/Home.css
#screenshot {max-width: 60%;max-height: 60%;margin-left: 40%;border-style: groove;}#desc {font-size: 30px;margin-left: 80px;margin-top: 150px;font-weight: 700;}
Add the route to Home component in the ../src/App.js file
import React from 'react';import { BrowserRouter as Router, Route } from 'react-router-dom';import Dashboard from './components/Dashboard';import Home from './components/Home';const App = () => (<Router><div><Route exact path="/" component={ Home } /><Route exact path="/dashboard" component={ Dashboard } /></div></Router>);export default App;
Add a new Route to the Home component. We used the exact attribute so the component will be mounted only at that exact path. If we did not give exact attribute here, both Home component and Dashboard component will be displayed on the /dashboard page since Dashboard component’s path is an extension of Home component’s path. You can see that for yourself by trying both ways.
Now let’s add the functionality for the Accounts Icon.
First we need to import the Sign In function in our Home component.
import { logIn } from '../firebase/firebase';
Then let’s create a function which changes the state on receiving the user’s details:
signIn = () => {logIn().then(details => {this.setState({uDetails: details,});});}
To use state, initialize it using the component’s constructor function.
constructor(props) {super(props);this.state = {uDetails: {},};
We need to redirect the user to the /dashboard page on Signing In. React Router provides a property called history for every component listed as Route. We also need a way to check if the user is authenticated. For that lets keep the users uid in the browser’s localStorage. This uid will uniquely identify the user.
- If the uid field is null, then the user is not logged in.
- If the uid field is not null, we fetch the notes of the respective user.
For that we need to add the uid to the localStorage on Sign In and remove the localStorage on Sign Out.
Add this function to Home.js to set localStorage on Sign In
signIn = () => {let mainComp = this;logIn().then(details => {localStorage.setItem("LOCAL_UID", details.user.uid);mainComp.setState({uDetails: details,});this.props.history.push('/dashboard');});}
This would push ‘/dashboard’ into the component’s props which gets passed on to the subsequent components and store the user’s uid inside the session’s localStorage.
You can see the localStorage now has a new field LOCAL_UID in the Applications tab of Chrome’s developer tools.
For logging out, lets create a logout function in firebase.js
export const logOut = () => {firebase.auth().signOut().then( () => {console.log(‘Successfully Logged Out');});}
A button for Logging out in Dashboard.js
<Button variant="contained" onClick={ this.logOut } style={{position:'fixed', right:'12px'}}>Log Out</Button>
Add this function to Dashboard.js
logOut = () => {localStorage.removeItem('LOCAL_UID');logOut();this.props.history.push('/');}
We have successfully added authentication in our app. All the notes are stored under the main notes/ node. Anyone accessing the dashboard can view all the stored notes.
Now we need to create a separate entry in firebase database for each user. So notes of a particular user will be stored under his/her uid.
For this, we need to change how we store notes into the Firebase database.
Modify these functions in firebase.js
export const addToDB = (note) => {const NoteRef = firebase.database().ref('notes/' + localStorage.getItem('LOCAL_UID')).push({title: note.title,timestamp: note.timestamp,content : note.content});return NoteRef.key;}export const removeFromDB = (noteKey) => {firebase.database().ref('notes/' + localStorage.getItem('LOCAL_UID') + '/' + noteKey).remove();}export const fetchFromDB = () => {return new Promise((resolve, reject) => {firebase.database().ref('notes/' + localStorage.getItem('LOCAL_UID')).once('value').then( function(snapshot) {if(snapshot.val()) {var notes = [];Object.keys(snapshot.val()).forEach(key => {var note = {};note = {...snapshot.val()[key]};note['key'] = key;notes.push(note);});resolve(notes);}else {resolve([]);}});})}
localStorage.getItem('LOCAL_UID')
Will return the user’s uid stored in the local storage. Adding it to the ref traverses into the node with user’s uid thus we can separate user’s notes based on their user ids.
To prevent unauthenticated users from accessing dashboard, we modify its return function. Thus Dashboard component returns the note’s dashboard if user id is logged in the local state. Else, it shows the Please login to continue message.
../src/components/Dashboard.js
return (<div>{localStorage.getItem('LOCAL_UID') != undefined ?(<div className="Dashboard"><Grid container spacing={0}><Grid item xs={12} className="NavBar"><AppBar className="AppBar" position="fixed"><Toolbar><h1>My Notes</h1><Button variant="contained" onClick={ this.logOut } style={{position:'fixed', right:'12px'}}>Log Out</Button></Toolbar></AppBar></Grid><br /><br /><br /><Grid item xs={12} className="Notes" style={{ marginTop:'20px' }}>{ this.state.notes.map(note => <Container removeNote={ this.removeNote } note={ note } /> )}<br /><br /></Grid></Grid><Button className='AddButton' onClick={this.handleClickOpen} style={ AddButtonStyle } variant="fab" color="primary" aria-label="Add"><AddIcon /></Button><Dialogopen={this.state.open}TransitionComponent={Transition}keepMountedonClose={this.handleClose}aria-labelledby="alert-dialog-slide-title"aria-describedby="alert-dialog-slide-description"><DialogTitle id="alert-dialog-slide-title"><TextFieldid="itle"label="Title"placeholder="Note Title"className={classes.textField}value={this.state.title}onChange={this.handleChange('title')}margin="normal"/></DialogTitle><DialogContent><DialogContentText id="alert-dialog-slide-description"><TextFieldid="content"label="Content"placeholder="Add your note here"multilinevalue={this.state.content}className={classes.textField}onChange={this.handleChange('content')}margin="normal"/></DialogContentText></DialogContent><DialogActions><Button onClick={this.handleClose} color="secondary">Cancel</Button><Button onClick={this.addNote} color="primary">Add</Button></DialogActions></Dialog></div>):(<h1>Please log in to continue</h1>)}</div>);