Sales: contact@tretainfotech.com | +91-917-317-5751
HR: hr@tretainfotech.com | +91-962-477-169

React JS Authentication with Auth0– Part 2

React readux
Oct 25, 2018 IdentityServer,React Js,Javascript 0 1993

React JS Authentication with Auth0– Part 2


ReactJS Authentication with Auth0– Part 2

In this, I'm going to explain and demonstrate how to configure your react app and adding authentication with auth0 to your react application.

First start editor of your choice, here I'm going to use VS Code. Visual Studio Code editor is a code editor developed by Microsoft for the cross-platform Operating systems. It provides features like syntax highlighting and refactoring your code, debugging, Git control, snippets. You can download it at https://code.visualstudio.com/download.

Let's get started, you will need to open your editor. In this case, I'm using Visual Studio Code. Now that you need reach to the path where you will need to create new react application using the vscode terminal window.

Currently, I'm using the latest ReactJS 16.6.0 version.

You can start your application by changing your directory and running npm(node package manager) start command as you can see in below screen-shot.

Once the application is created using create-react-app package and all the dependencies are added to your project. As you can see below.

Now, for the initial change, I'm going to make a change in the index.js file.

In this file, I have added a state variable as an object, used global window.setState() function to update the state and moved ReactDom.render() method into the setState function, so if any changes are encountered in state ReactDOM will be re-rendered.

I'll update state property in an immutable way so, I won't override existing properties in an object. After that, I've added the initialState object with one property “name” and updated the state using window.setState().

I've added a new folder named component in the src directory, also added main.js file as Main component and with that I've copied App.js Header tag Html into the main.js render function as JSX code. Another change that I've made is in the index.js file and modified initialState object by adding another property. The complete code here.

Index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

let state = {};
window.setState = change => {
    state = { ...state, change };

    ReactDOM.render(<App {...state} />, document.getElementById('root'));
};
/*eslint no-restricted-globals : 0*/
let initialState = {
    name: 'developer-1',
    location: location.pathname.replace(/^\/?|\/$/g, '')
};
window.setState(initialState);
serviceWorker.unregister();

src/component/main.js file

import React, { Component } from 'react';

export default class Main extends Component {
    render() {
        return (
            <div>
                <p>
                    Edit <code>src/App.js</code> and save to reload.
                </p>
                <a
                    className="App-link"
                    href="https://reactjs.org"
                    target="_blank"
                    rel="noopener noreferrer">
                    Learn React
                </a>
            </div>
        );
    }
}

App.js file

import React, { Component } from 'react';
import './App.css';
import Main from './component/main';
import AuthPage from './component/authPage';
import NotFound from './component/notFound';
import Callback from './component/callback';

class App extends Component {
    render() {
        let mainComponent = null;
        switch (this.props.location) {
            case '':
                mainComponent = <Main {...this.props} />;
                break;
            case 'secret':
                mainComponent = <AuthPage {...this.props} />;
                break;
            case 'callback':
                mainComponent = <Callback {...this.props} />;
                break;
            default:
                mainComponent = <NotFound {...this.props} />;
                break;
        }
        return (
            <div className="App">
                <header className="">Hello {this.props.name}</header>
                {mainComponent}
            </div>
        );
    }
}

export default App;

I have added a few more files notFound.js and authPage.js.

import React, { Component } from 'react';

export default class authPage extends Component {
    render() {
        return <div>this is auth page</div>;
    }
}

Now I'm going to add a new component called callback in the components folder and this can be used for authenticated users only.

callback.js file

import React, { Component } from 'react';

export default class Callback extends Component {
    render() {
        return (
            <div>
                <p>Call back success</p>
                you're authenticated user
            </div>
        );
    }
}

Now with this setup, I'm going to install a package called auth0-js and this is the package for adding authentication to react app. Below command will add the dependency to package.json file.

npm install  --save auth0-js 

auth0-js library is just a wrapper around the auth0 APIs.

Now with this changes, I'm going to one new file in the root directory that is auth.js. In this file, I'm going to define my app's domain, clientID, redirectUri, audience, responseType, scope. auth0 package has several methods which will help us to create a secure authentication process.

Auth.js

import auth0 from 'auth0-js';
import jwtDecode from 'jwt-decode';

const LOGIN_SUCCESS_PAGE = '/secret';
const LOGIN_FAILED_PAGE = '/';
export default class Auth {
    auth0 = new auth0.WebAuth({
        domain: 'reactjs-auth0.auth0.com',
        clientID: '8LDCDSPRlI00PuXdEoVipV3026cATV6L',
        redirectUri: 'http://localhost:3000/callback',
        audiance: 'https://reactjs-auth0.auth0.com/userinfo',
        responseType: 'token id_token',
        scope: 'openid profile'
    });

    constructor() {
        this.login = this.login.bind(this);
    }
    login() {
        this.auth0.authorize();
    }

    /*eslint no-restricted-globals : 0*/
    handleAuthentication() {
        this.auth0.parseHash((err, authResult) => {
            if (authResult && authResult.accessToken && authResult.idToken) {
                let expiresAt = JSON.stringify(
                    authResult.expiresIn * 1000 + new Date().getTime()
                );
                localStorage.setItem('access_token', authResult.accessToken);
                localStorage.setItem('id_token', authResult.idToken);
                localStorage.setItem('expires_at', expiresAt);
                location.hash = '';
                location.pathname = LOGIN_SUCCESS_PAGE;
            } else {
                location.pathname = LOGIN_FAILED_PAGE;
            }
        });
    }

    isAuthenticated() {
        let expiresAt = JSON.parse(localStorage.getItem('expires_at'));
        return new Date().getTime() < expiresAt;
    }
    logout() {
        localStorage.removeItem('access_token');
        localStorage.removeItem('id_token');
        localStorage.removeItem('expires_at');
        location.hash = '';
        location.pathname = LOGIN_FAILED_PAGE;
    }

    getProfile() {
        if (localStorage.getItem('id_token')) {
            return jwtDecode(localStorage.getItem('id_token'));
        } else {
            return {};
        }
    }
}

I have added a few methods which will help me to check Authenticated user, log in to auth0 for authentication, handling the verified response.

import auth0 from 'auth0-js';

This line will make available auth0 methods in js file. I have to create a variable Auth class with name auth0 and this will be used to initialize auth0 object with WebAuth() method.

auth0 = new auth0.WebAuth({
        domain: 'YOUR-DOMAIN',
        clientID: 'YOUR-CLIENT-ID',
        redirectUri: 'http://localhost:3000/callback',
        audiance: 'https://YOUR-DOMAIN.auth0.com/userinfo',
        responseType: 'token id_token',
        scope: 'openid profile'
    });

With this change, I have added another method to login into the application. It has this.auth0.authorize() will lead to auth0 server login page.

 login() {
        this.auth0.authorize();
  }

handleAuthentication(): This method will handle all the response related to authentication. It will store all the secret information in the localStorage.

/*eslint no-restricted-globals : 0*/

    handleAuthentication() {
        this.auth0.parseHash((err, authResult) => {
            if (authResult && authResult.accessToken && authResult.idToken) {
                let expiresAt = JSON.stringify(
                    authResult.expiresIn * 1000 + new Date().getTime()
                );
                localStorage.setItem('access_token', authResult.accessToken);
                localStorage.setItem('id_token', authResult.idToken);
                localStorage.setItem('expires_at', expiresAt);
                location.hash = '';
                location.pathname = LOGIN_SUCCESS_PAGE;
            } else {
                location.pathname = LOGIN_FAILED_PAGE;
            }
        });
    }

isAuthenticated(): This method will check expiration Time of id_token.

isAuthenticated() {
        let expiresAt = JSON.parse(localStorage.getItem('expires_at'));
        return new Date().getTime() < expiresAt;
    }

logout(): This method will remove all the local storage and redirect a user to the initial page.

 logout() {
        localStorage.removeItem('access_token');
        localStorage.removeItem('id_token');
        localStorage.removeItem('expires_at');
        location.hash = '';
        location.pathname = LOGIN_FAILED_PAGE;
    }

getProfile(): This method will fetch the user info from the id_token and this can be decoded by the jwtDecode method. For that, we need to install a package called jwt-decode from npm.

/* run this command in terminal */
npm install --save jwt-decode


/*.js function*/
getProfile() {
        if (localStorage.getItem('id_token')) {
            return jwtDecode(localStorage.getItem('id_token'));
        } else {
            return {};
        }
    }

with these changes, I've modified several other files as given below.

AuthPage.js: Here, I have changed the code a bit added a click handler for logout() from the app.

import React, { Component } from 'react';

export default class authPage extends Component {
    render() {
        return (
            <div>
                this is auth page
                <hr />
                <button onClick={this.props.auth.logout}>Logout</button>
            </div>
        );
    }
}

callback.js: In this, I have added componentDidMount() method for calling handleAuthentication() method.

import React, { Component } from 'react';
import Auth from '../auth';

export default class Callback extends Component {
    componentDidMount() {
        const auth = new Auth();
        auth.handleAuthentication();
    }
    render() {
        return (
            <div>
                <p>Call back success</p>
                you're authenticated user
            </div>
        );
    }
}

main.js: Now, JSX is bit changed the user can have logged in button only when he is not logged in.

import React, { Component } from 'react';

export default class Main extends Component {
    render() {
        return (
            <div>
                <p>
                    Edit <code>src/App.js</code> and save to reload.
                </p>
                <a className="App-link" href="/secret">
                    click here to get authenticated
                </a>

                {!this.props.auth.isAuthenticated() ? (
                    <div>
                        please login <hr />
                        <button onClick={this.props.auth.login}> login</button>
                    </div>
                ) : null}
            </div>
        );
    }
}

notFound.js : 

import React, { Component } from 'react';

export default class NotFound extends Component {
    render() {
        return (
            <div>
                <p>Oops !! </p> <hr />
                You're not authenticated to view this page
            </div>
        );
    }
}

app.js

import React, { Component } from 'react';
import './App.css';
import Main from './component/main';
import AuthPage from './component/authPage';
import NotFound from './component/notFound';
import Callback from './component/callback';

class App extends Component {
    render() {
        let mainComponent = null;
        switch (this.props.location) {
            case '':
                mainComponent = <Main {...this.props} />;
                break;
            case 'secret':
                mainComponent = this.props.auth.isAuthenticated() ? (
                    <AuthPage {...this.props} />
                ) : (
                    <NotFound {...this.props} />
                );
                break;
            case 'callback':
                mainComponent = <Callback {...this.props} />;
                break;
            default:
                mainComponent = <NotFound {...this.props} />;
                break;
        }
        return (
            <div className="App">
                <header className="">Hello {this.props.name}</header>
                {mainComponent}
            </div>
        );
    }
}

export default App;

With all these changes I have added an authentication layer to the application and we can check the entire flow.

As given screenshots.

Once anyone clicks on the login button, it'll be redirected to the auth0 login page.

 

Now once the user selected any of the login options, let say google account. It will ask for user permission to access its profile.

If the user successfully logged in, the user will be redirected to the authenticated page.

 

Blog Author

Comments

Leave a Comment