A simple login/sign up platform using React + Auth0 with Heroku deployment (Part 2 or 3 — React)

Ian Khor
dev <tech> tips & tricks
5 min readFeb 20, 2017

--

In the previous article, we have dealt with setting up the Auth0 and Heroku environment. Now its time to dive into some React code !

React

  1. Use the boilerplate source code which i’ve created here by running
git clone https://github.com/iankhor/react-vanilla-boilerplate-with-router your-app-name

2. Go into your app folder, and in our case would be

cd your-app-name

3. Install all existing node modules by running

npm i

5. Install Auth0 and jwt-decode by running

npm i auth0-js jwt-decode --save

5. Copy over the example environmental variables file to .env by running

cp .env.example .env

6. In the .env file, create two environmental variables called REACT_APP_AUTH_DOMAIN_ADDRESS and REACT_APP_AUTH_CLIENT_ID. Populate the domain ID and client ID as noted in auth0 setup Step 12.

7. The following steps are similar to the Auth0 React tutorial here but modified for our purposes.

8. Under the folder src, create a subfolder called auth and three files named initAuth.js, AuthService.js and jwtHelper.js

7. In jwtHelper.js, copy and paste the following code and save. This is the helper file for auth0.

import decode from ‘jwt-decode’;export function getTokenExpirationDate(token){
const decoded = decode(token)
if(!decoded.exp) {
return null
}
const date = new Date(0) // The 0 here is the key, which sets the date to the epoch
date.setUTCSeconds(decoded.exp)
return date
}
export function isTokenExpired(token){
const date = getTokenExpirationDate(token)
if (date === null) {
return false
}
return !(date.valueOf() > new Date().valueOf())
}

7. In AuthService.js, copy and paste the following code and save.

import { EventEmitter } from 'events'
import { isTokenExpired } from './jwtHelper'
import auth0 from 'auth0-js'
export default class AuthService extends EventEmitter {
constructor(clientId, domain) {
super()
// Configure Auth0
this.auth0 = new auth0.WebAuth({
clientID: clientId,
domain: domain,
responseType: 'token id_token',
redirectUri: `${window.location.origin}/`
})
this.login = this.login.bind(this)
this.signup = this.signup.bind(this)
this.loginWithGoogle = this.loginWithGoogle.bind(this)
}
login(username, password) {
this.auth0.client.login({
realm: 'react-auth0',
username,
password
}, (err, authResult) => {
if (err) {
alert('Error: ' + err.description)
return
}
if (authResult && authResult.idToken && authResult.accessToken) {
this.setToken(authResult.accessToken, authResult.idToken)
window.location = window.location.origin //redirect to main page
}
})
}
signup(email, password){
this.auth0.redirect.signupAndLogin({
connection: 'react-auth0',
email,
password,
}, function(err) {
if (err) {
alert('Error: ' + err.description)
}
})
}
loginWithGoogle() {
this.auth0.authorize({
connection: 'google-oauth2',
})
}
parseHash(hash) {
this.auth0.parseHash({ hash }, (err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setToken(authResult.accessToken, authResult.idToken)
console.log('AuthService parseHash : code to transition to /')
this.auth0.client.userInfo(authResult.accessToken, (error, profile) => {
if (error) {
console.log('Error loading the Profile', error)
} else {
this.setProfile(profile)
}
})
} else if (authResult && authResult.error) {
alert('Error: ' + authResult.error)
}
})
}
loggedIn() {
// Checks if there is a saved token and it's still valid
const token = this.getToken()
return !!token && !isTokenExpired(token)
}
setToken(accessToken, idToken) {
// Saves user access token and ID token into local storage
localStorage.setItem('access_token', accessToken)
localStorage.setItem('id_token', idToken)
}
setProfile(profile) {
// Saves profile data to localStorage
localStorage.setItem('profile', JSON.stringify(profile))
// Triggers profile_updated event to update the UI
this.emit('profile_updated', profile)
}
getProfile() {
// Retrieves the profile data from localStorage
const profile = localStorage.getItem('profile')
return profile ? JSON.parse(localStorage.profile) : {}
}
getToken() {
// Retrieves the user token from localStorage
return localStorage.getItem('id_token')
}
logout() {
// Clear user token and profile data from localStorage
localStorage.removeItem('id_token')
localStorage.removeItem('profile')
}
}

8. In initAuth.js, copy and paste the following code and save. This is a helper file to create an auth0 instance.

import AuthService from ‘./AuthService’const auth = new AuthService(process.env.REACT_APP_AUTH_CLIENT_ID, process.env.REACT_APP_AUTH_DOMAIN_ADDRESS)export default auth

9. We are going to create the sign up and log in components. create two files called SignUp.js and Login.js in the components folder.

10. In SignUp.js, copy and paste the following code and save.

import React, { Component } from 'react'
import auth from './../auth/initAuth'
class SignUp extends Component {
constructor(props){
super(props)
this.state = {
email: null,
password: null
}
}

_handleSubmit = (e, data) => {
e.preventDefault()
auth.signup(this.state.email, this.state.password)
}
_handleEmailChange = (e) => {
this.setState( {email: e.target.value} )
console.log('email', this.state.email)
}
_handlePasswordChange = (e) => {
this.setState( {password: e.target.value} )
console.log('password', this.state.password)
}
render(){
return(
<form className="commentForm" onSubmit={this._handleSubmit}>
<input type="email" placeholder="Enter your email" onChange={this._handleEmailChange}/>
<input type="password" placeholder="Enter a password" onChange={this._handlePasswordChange}/>
<input type="submit" value="Sign up" />
</form>
)
}
}
export default SignUp

12. In Login.js, copy and paste the following code and save.

import React, { Component } from 'react'
import auth from './../auth/initAuth'
class Login extends Component {
constructor(props){
super(props)
this.state = {
email: null,
password: null
}
}
_handleSubmit = (e, data) => {
e.preventDefault()
auth.login(this.state.email, this.state.password)
}
_handleEmailChange = (e) => {
this.setState( {email: e.target.value} )
}
_handlePasswordChange = (e) => {
this.setState( {password: e.target.value} )
}
_logout = () => {
auth.logout()
this.props._refresh()
}
_renderLoginForm = () => {
return(
<form className="commentForm" onSubmit={this._handleSubmit}>
<input type="email" placeholder="Enter your email" onChange={this._handleEmailChange}/>
<input type="password" placeholder="Enter a password" onChange={this._handlePasswordChange}/>
<input type="submit" value="Login" />
</form>
)
}
_renderLogout = () => {
return(
<div>
<button onClick={ this._logout }>You are logged in ! Click to logout !</button>
</div>
)
}
render(){
return(
<div>
{ auth.loggedIn() ? this._renderLogout() : this._renderLoginForm() }
</div>
)
}
}
export default Login

13. Update the Routes.js in the shared folder to create private (or authenticated) pages.

import React from 'react'
import auth from './../../auth/initAuth'
//Routes
import NotFound from './NotFound'
import App from './../App';
import PageOne from './../PageOne'
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'const PrivateRoute = ({ component, ...rest }) => (
<Route {...rest} render={props => (
auth.loggedIn() ? (
React.createElement(component, props)
) : (
<Redirect to="/NotFound" />
)
)}/>
)
const Routes = (props) => {
return (
<BrowserRouter>
<Switch>
<Route path="/" exact component={App} />
<PrivateRoute path="/PageOne" component={PageOne} />
<Route path="/NotFound" component={NotFound} />
<Route component={NotFound} />
</Switch>
</BrowserRouter>
)
}
export default Routes

Great ! We are nearing the end of our hero’s journey.

Sit back, relax and when you have ready. Click here for the final part of this publication.

--

--

Ian Khor
dev <tech> tips & tricks

Web app developer, electronics engineer, business analyst & designer at heart. Concept, design, build and commercial acumen.