Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
Security is hard. Often when we build applications we want to allow only registered users to access the application. We want to be able to manage user accounts, see when they last logged in, be able to disable suspicious accounts and have a dashboard to view and manage all this data. We might also decide to support multi-factor authentication and social login.
But security isnât just hard, it also takes a while to implement. What if thereâs a service that could take away this part of the development hassle from you? Why spend weeks or months rolling your own auth? This is where Auth0 shines. In this tutorial, Iâll show you how to build a chat application with Pusher, add user authentication with Auth0 Lock, and manage users from the Auth0 dashboard.
Introduction to Auth0 and Pusher
Auth0 is an Authentication-as-a-Service (or Identity-as-a-Service) provider focused on encapsulating user authentication and management, which provides an SDK to allow developers to easily add authentication and manage users. Its user management dashboard allows for breach detection and multi-factor authentication, and Passwordless login.
Pusherâs APIâs and hosted infrastructure allow us to build scalable and reliable realtime applications. Pusher has a concept of channels and events which are fundamental to it. Channels provide a way of filtering data and controlling access to different streams of information, while events are the primary method of packaging messages in the Pusher system and form the basis of all communication.
Building the application
We will be building a chat application thatâll allow users to communicate with each other where everyone sees every other personâs messages. Itâll work similarly to how channels work in Slack: just one channel for everybody to communicate.
Hereâs what weâll be building:
Final AppSetting up the backend
Weâll start by building the backend which will facilitate receiving and broadcasting chat messages, serving static files, and also setting up Auth0 and Pusher.
First, youâll need to signup for a Pusher and Auth0 account. Go to pusher.com and auth0.com and sign up for an account. To use Pusher API we have to signup and create a Pusher app from the dashboard. We can create as many applications as we want and each one will get an application id and secret key which weâll use to initialise a Pusher instance on client or server side code.
Create a new Pusher account
To create a new Pusher app, click the Your apps side menu, then click the Create a new app button below the drawer. This brings up the setup wizard.
1. Enter a name for the application. In this case Iâll call it âchatâ.2. Select a cluster.3. Select the option âCreate app for multiple environmentsâ if you want to have different instances for development, staging and production.4. Select Vanilla JS as the frontend and NodeJS as the backend.5. Complete the process by clicking `Create App` button to set up your app instance.
Since weâre building our backend in Node using Express, letâs initialise a new Node app and install the needed dependencies. Run the following command:
1. npm init and select the default options2. npm iâââsave body-parser express pusher to install express and the Pusher node package
Add a new file called server.js which will contain logic to authenticate the Pusher client and also render the static files weâll be adding later. This file will contain the content below:
var express = require(âexpressâ);var bodyParser = require(âbody-parserâ);var Pusher = require(âpusherâ);
var app = express();app.use(bodyParser.json());app.use(bodyParser.urlencoded({ extended: false }));
var pusher = new Pusher({ appId: APP_ID, key: APP_KEY, secret: APP_SECRET, cluster: eu });
app.post(â/pusher/authâ, function(req, res) { var socketId = req.body.socket_id; var channel = req.body.channel_name; var auth = pusher.authenticate(socketId, channel); res.send(auth);});
app.post(â/messageâ, function(req, res) { var message = req.body.message; var name = req.body.name; pusher.trigger( âprivate-chatâ, âmessage-addedâ, { message, name }); res.sendStatus(200);});
app.get(â/â,function(req,res){ res.sendFile(â/public/index.htmlâ, {root: __dirname });});
app.use(express.static(__dirname + â/publicâ));
var port = process.env.PORT || 5000;app.listen(port, function () { console.log(`app listening on port ${port}!`)});
We instantiate Pusher by passing in an object that contains the details of our app ID and secret key, which can be found on the App Keys tab in your Pusher dashboard. Pusher also provides a mechanism for authenticating users to a channel at the point of subscription. To do this, we expose an endpoint on the server that will validate the request and respond with a success or failure. This endpoint will be called by Pusher client libraries and can be named anything. We used the default name for this endpoint on Pusher, which is /pusher/auth. The line var auth = pusher.authenticate(socketId, channel); authenticates the client with Pusher and returns an authentication code to the calling client.
To allow this file to run when we start npm, we update package.json with the following value:
âscriptsâ: { âstartâ: ânode server.jsâ, âtestâ: âecho \âError: no test specified\â && exit 1"}
Create an Auth0 clientTo create an Auth0Â client
1. Select Clients from the side menu.2. On the new page, click the Create Client button3. Enter a name for the app and select Single Page App as an option4. Click the Create button to create the client.
An Auth0 client provides us with Client Id and Secret which weâll use to interact with Auth0 from the code. On the settings tab, we can see the Name, Client Id, Secret, Client Type and many more. I want to enable CORS for my domain http://localhost:5000, set the log out URL and the URL to redirect to after the user has been authenticated with Auth0. Update the following settings with http://localhost:5000
1. Allowed Callback URLs2. Allowed Logout URLs3. Allowed Origins (CORS)
Building the frontend
With the backend all good to go, we build the web page that will facilitate messaging. Create a folder named public which will contain the html and javascript file. Create two new files style.css and index.html with the following content:
style.css
@import url("http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css"); .chat { list-style: none; margin: 0; padding: 0; } .chat li { margin-bottom: 10px; padding-bottom: 5px; border-bottom: 1px dotted #B3A9A9; } .chat li.left .chat-body { margin-left: 60px; } .chat li.right .chat-body { margin-right: 60px; } .chat li .chat-body p { margin: 0; color: #777777; } .panel .slidedown .glyphicon, .chat .glyphicon { margin-right: 5px; } .body-panel { overflow-y: scroll; height: 250px; } ::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); background-color: #F5F5F5; } ::-webkit-scrollbar { width: 12px; background-color: #F5F5F5; } ::-webkit-scrollbar-thumb { -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); background-color: #555; }
index.html
<!-- template from http://bootsnipp.com/snippets/6eWd --> <!DOCTYPE html> <html> <head> <!-- Latest compiled and minified CSS --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <!-- Optional theme --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"> <script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script> <!-- Latest compiled and minified JavaScript --> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> <link rel="stylesheet" href="style.css"> <script src="https://cdn.auth0.com/js/lock/10.18.0/lock.min.js"></script> <script src="https://js.pusher.com/4.0/pusher.min.js"></script> <script src="index.js"></script> </head> <body> <div class="container"> <div class="row form-group"> <div class="col-xs-12 col-md-offset-2 col-md-8 col-lg-8 col-lg-offset-2"> <div class="panel panel-primary"> <div class="panel-heading"> <span class="glyphicon glyphicon-comment"></span> <span id="username"></span> <div class="btn-group pull-right"> <button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown"> <span class="glyphicon glyphicon-chevron-down"></span> </button> <ul class="dropdown-menu slidedown"> <li><a><span class="glyphicon glyphicon-refresh"> </span>Refresh</a></li> <li><a><span class="glyphicon glyphicon-ok-sign"> </span>Available</a></li> <li><a><span class="glyphicon glyphicon-remove"> </span>Busy</a></li> <li><a><span class="glyphicon glyphicon-time"></span> Away</a></li> <li class="divider"></li> <li><a id="logout"><span class="glyphicon glyphicon-off"></span> Sign Out</a></li> </ul> </div> </div> <div class="panel-body body-panel"> <ul class="chat"> </ul> </div> <div class="panel-footer clearfix"> <textarea id="message" class="form-control" rows="3"></textarea> <span class="col-lg-6 col-lg-offset-3 col-md-6 col-md-offset-3 col-xs-12" style="margin-top: 10px"> <button class="btn btn-warning btn-lg btn-block" id="btn-chat">Send</button> </span> </div> </div> </div> </div> </div> <script id="new-message" type="text/template"> <li id="{{id}}" class="right clearfix"> <div class="chat-body clearfix"> <div class="header"> <small class="text-muted">{{name}}</small> </div> <p> {{body}} </p> </div> </li> </script> </body> </html>
This file uses template from bootsnip and also includes a script reference to Auth0 Lock <script src=âhttps://cdn.auth0.com/js/lock/10.18.0/lock.min.js"></script>. Lock is a drop-in authentication widget that provides a standard set of behaviours required for login and a customisable user interface. It provides a simple way to integrate with Auth0 with very minimal configuration.
We want to allow users to sign in when they enter the application and be able to send messages once theyâre authenticated. Add a new file index.js with the following content:
$(document).ready(function(){ // Initiating our Auth0Lock let lock = new Auth0Lock( âCLIENT_IDâ, âCLIENT_DOMAINâ,//example: lotus.auth0.com { auth: { params: { scope: âopenid profileâ } }, autoclose: true, closable: false, rememberLastLogin: true } ); let profile = JSON.parse(localStorage.getItem(âprofileâ)); let isAuthenticated = localStorage.getItem(âisAuthenticatedâ); function updateValues(userProfile, authStatus) { profile = userProfile; isAuthenticated = authStatus; } if(!isAuthenticated && !window.location.hash){ lock.show();//show Lock widget } // Listening for the authenticated event lock.on(âauthenticatedâ, function(authResult) { // Use the token in authResult to getUserInfo() and save it to localStorage lock.getUserInfo(authResult.accessToken, function(error, profile) { if (error) { // Handle error return; } localStorage.setItem(âaccessTokenâ, authResult.accessToken); localStorage.setItem(âprofileâ, JSON.stringify(profile)); localStorage.setItem(âisAuthenticatedâ, true); updateValues(profile, true); $(â#usernameâ).html(profile.name); }); }); });
We initialise Lock by passing it the Client Id of the app, your user domain which starts with your username followed by .auth0.com or {YOUR_SELECTED_REGION}.auth0.com e.g lotus.eu.auth0.com. The widget is configurable and we can send in configuration options like closable, autoClose, and auth. Within the auth option we tell it to return the openid and profile claims.
We check if the user is authenticated and show the widget when theyâre not. Once the user is authenticated, Lock emits the `authenticated` event which weâve subscribed to. When itâs raised, we store the user profile and other credentials to localStorage and set the userâs name to be displayed on the page. Once the user is authenticated, we want to connect to Pusher and send messages across. Update index.js with the following code:
if(!isAuthenticated && !window.location.hash){ lock.show(); } else{ // Enable pusher logging â donât include this in production Pusher.logToConsole = true; var pusher = new Pusher(âAPP_SECRETâ, { cluster: âe.g euâ, encrypted: false }); var channel = pusher.subscribe(âprivate-chatâ); channel.bind(âmessage-addedâ, onMessageAdded); } function onMessageAdded(data) { let template = $(â#new-messageâ).html(); template = template.replace(â{{body}}â, data.message); template = template.replace(â{{name}}â, data.name); $(â.chatâ).append(template); }
Pusher is initialised with the APP_SECRET and CLUSTER which you can get from the app dashboard on Pusher. We subscribe to a channel called private-chat. Pusher has 3 types of channels: Public, Private and Presence channel. Private and Presence channels let your server control access to the data you are broadcasting. Presence channels go further to force subscribers to register user information when subscribing. Private channels are named starting with private- and authenticated in the server when subscribing.
And finally we want to send the message to the user when they click send and also log them out when they select signout. Update index.js with the code below
$(â#btn-chatâ).click(function(){ const message = $(â#messageâ).val(); $(â#messageâ).val(ââ); //send message $.post( âhttp://localhost:5000/messageâ, { message, name: profile.name } ); }); $(â#logoutâ).click((e) => { e.preventDefault(); logout(); }); function logout(){ localStorage.clear(); isAuthenticated = false; lock.logout({ returnTo: âhttp://localhost:5000â }); }
When the user clicks the send button, we take the message and put it in an object with the userâs profile name and send it to the /message endpoint on the server. When the logout button is clicked, it calls the logout function which clears the data stored in localStorage and call lock.logout() which logs the user out on Auth0 and redirects them back to our website. With all these additions, index.js should have the following content:
$(document).ready(function(){ // Initiating our Auth0Lock let lock = new Auth0Lock( âCLIENT_IDâ, âCLIENT_DOMAINâ, { auth: { params: { scope: âopenid profileâ } }, autoclose: true, closable: false, rememberLastLogin: true }); // Listening for the authenticated event lock.on(âauthenticatedâ, function(authResult) { // Use the token in authResult to getUserInfo() and save it to localStorage lock.getUserInfo(authResult.accessToken, function(error, profile) { if (error) { // Handle error console.log(error); return; } localStorage.setItem(âaccessTokenâ, authResult.accessToken); localStorage.setItem(âprofileâ, JSON.stringify(profile)); localStorage.setItem(âisAuthenticatedâ, true); updateAuthenticationValues(profile, true); $(â#usernameâ).html(profile.name); }); }); let profile = JSON.parse(localStorage.getItem(âprofileâ)); let isAuthenticated = localStorage.getItem(âisAuthenticatedâ); function updateAuthenticationValues(userProfile, authStatus) { profile = userProfile; isAuthenticated = authStatus; } $(â#logoutâ).click((e) => { e.preventDefault(); logout(); }); function logout(){ localStorage.clear(); isAuthenticated = false; lock.logout({ returnTo: âhttp://localhost:5000â }); } function onMessageAdded(data) { let template = $(â#new-messageâ).html(); template = template.replace(â{{body}}â, data.message); template = template.replace(â{{name}}â, data.name); $(â.chatâ).append(template); } if(!isAuthenticated && !window.location.hash){ lock.show(); } else{ if(profile){ $(â#usernameâ).html(profile.name); } // Enable pusher logging â donât include this in production Pusher.logToConsole = true; var pusher = new Pusher(âAPP_SECRETâ, { cluster: âeuâ, encrypted: false }); var channel = pusher.subscribe(âprivate-chat'); channel.bind(âmessage-addedâ, onMessageAdded); $(â#btn-chatâ).click(function(){ const message = $(â#messageâ).val(); $(â#messageâ).val(ââ); //send message $.post( âhttp://localhost:5000/messageâ, { message, name: profile.name } ); }); } });
To test the app, run `npm start` on the terminal and open http://localhost:5000 on two separate browsers. Hereâs a run through of it:
Wrap Up
This is an app to show how you can use Pusher to send messages in real-time and secure the channels, add user authentication and account management with Auth0, and easily integrate to Auth0 using Auth0 Lock. On your auth0 dashboard you can see the total number of users, logins and new signups.
You can also see all your users when you click on the **Users** side menu. On this page you can see the list of your users and their mode of login.
Selecting a user takes you to a more detailed page where you can take various actions on the account, for example, blocking an account or sending a verification email.
Also on Pusher, you can go to your application dashboard, under the Stats, where youâll see statistics concerning your application, such as connection frequency and how many messages were sent through that app. The combination of these two technologies makes it faster and easier to build real-time secured applications. You can find the code here on GitHub.
This was originally published on Pusher
Build a Secure Chat Web Application with JavaScript, Auth0 and Pusher was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.