Mastering session authentication. a complete walkthrough on how to build… | by christian cashiola | itnext

Assign a port for the application

We are ready to finish server.js by assigning a port.

// assign port
const port = 3000;
app.listen(port, () => console.log(`This app is listening on port ${port}`));

Our completed server.js file should look like the code below:

Authentication integration completed

That’s it! In this tutorial, you learned how Passport.js works, how to configure it, and how to integrate it with Node.js and Auth0 to add authentication to web applications. You also learned about security and identity best practices and how an identity platform such as Auth0 lets you delegate to a team of experts the giant responsibility of keeping logins secure.

All that is left is for you to continue building this application as you may like. Feel free to dive deeper into the Auth0 Documentation to learn more about how Auth0 helps you save time on implementing and managing identity. Use the comments below this blog post to ask questions or join the Auth0 Community to connect with other developers like yourself.

Thank you for your time!

I ran into an issue

Authentication with cookie parser

After installing cookie-parser, we will include it as a middleware with the following line of code.

We passed in a string to the cookieParser method, because we want our cookie to be signed. The string passed is used the secret in signing a cookie. Once the server has set cookies in the browser, the cookies information will be contained in the signedCookies attribute of each subsequent request.Our auth.js file now looks like this.

Authorization with express.js middleware

Authorization in web apps usually means restricting certain functions to privileged clients. These functions can either be methods, pages, or REST API endpoints.

Express.js middleware allows us to apply certain rules seamlessly to all routes, groups of routes (namespacing), or individual routes.

For example, if we want to protect all /api/ endpoints, we utilize the following middleware with *:

Basic authentication

We are using express, express-session, ejs is used as templating engine.

Let’s implement basic authentication. Here we will signup and login.

app.post(“/signup”, async (req, res, next) => {

letname = req.body.name;

letemail = req.body.email;

letpassword = req.body.password;

letnewUser = newUser({

name:name,

email:email,

password:password

})

try {

letuser = awaitnewUser.save();

res.render(‘signup-result’, {

user:user,

message:“Account Created”,

message2:false,

success:true

});

} catch (error) {

res.render(‘signup-result’, {

message:“Account Creation Failed”,

message2:‘The Email may have been already used.’,

success:false

});

}

})

Here, we simply get the name, email, and password from the form and save it to the database.

Bearer authentication / jwt authentication

Or we can say API authentication. 

Compatible session stores

The following modules implement a session store that is compatible with this
module. Please make a PR to add additional modules 🙂

★ aerospike-session-store A session store using Aerospike.★ better-sqlite3-session-store A session store based on better-sqlite3.★ cassandra-store An Apache Cassandra-based session store.★ cluster-store A wrapper for using in-process / embedded
stores – such as SQLite (via knex), leveldb, files, or memory – with node cluster (desirable for Raspberry Pi 2
and other multi-core embedded devices).★ connect-arango An ArangoDB-based session store.★ connect-azuretables An Azure Table Storage-based session store.★ connect-cloudant-store An IBM Cloudant-based session store.★ connect-couchbase A couchbase-based session store.★ connect-datacache An IBM Bluemix Data Cache-based session store.★ @google-cloud/connect-datastore A Google Cloud Datastore-based session store.★ connect-db2 An IBM DB2-based session store built using ibm_db module.★ connect-dynamodb A DynamoDB-based session store.★ @google-cloud/connect-firestore A Google Cloud Firestore-based session store.★ connect-hazelcast Hazelcast session store for Connect and Express.★ connect-loki A Loki.js-based session store.★ connect-lowdb A lowdb-based session store.★ connect-memcached A memcached-based session store.★ connect-memjs A memcached-based session store using
memjs as the memcached client.★ connect-ml A MarkLogic Server-based session store.★ connect-monetdb A MonetDB-based session store.★ connect-mongo A MongoDB-based session store.★ connect-mongodb-session Lightweight MongoDB-based session store built and maintained by MongoDB.★ connect-mssql-v2 A Microsoft SQL Server-based session store based on connect-mssql.★ connect-neo4j A Neo4j-based session store.★ connect-pg-simple A PostgreSQL-based session store.★ connect-redis A Redis-based session store.★ connect-session-firebase A session store based on the Firebase Realtime Database★ connect-session-knex A session store using
Knex.js, which is a SQL query builder for PostgreSQL, MySQL, MariaDB, SQLite3, and Oracle.★ connect-session-sequelize A session store using
Sequelize.js, which is a Node.js / io.js ORM for PostgreSQL, MySQL, SQLite and MSSQL.★ connect-sqlite3 A SQLite3 session store modeled after the TJ’s connect-redis store.★ connect-typeorm A TypeORM-based session store.★ couchdb-expression A CouchDB-based session store.★ dynamodb-store A DynamoDB-based session store.★ express-etcd An etcd based session store.★ express-mysql-session A session store using native
MySQL via the node-mysql module.★ express-nedb-session A NeDB-based session store.★ express-oracle-session A session store using native
oracle via the node-oracledb module.★ express-session-cache-manager
A store that implements cache-manager, which supports
a variety of storage types.★ express-session-etcd3 An etcd3 based session store.★ express-session-level A LevelDB based session store.★ express-session-rsdb Session store based on Rocket-Store: A very simple, super fast and yet powerfull, flat file database.

Configure express-session

🛠️️ Next, open index.js and add the following under the Required External Modules section:

const express =require("express");const path =require("path");const expressSession =require("express-session");const passport =require("passport");const Auth0Strategy =require("passport-auth0");require("dotenv").config();

You are adding imports for express-session, passport, and passport-auth0, which you’ll configure in the next sections.

🛠️️ Between the App Variables and App Configuration sections, create two new sections, Session Configuration and Passport Configuration:

const app =express();const port = process.env.PORT||"8000";

Order matters in Express. Please ensure that you add the sections in the right order.

🛠️️ Under Session Configuration, configure expressSession as follows:

const session ={
  secret: process.env.SESSION_SECRET,
  cookie:{},
  resave:false,
  saveUninitialized:false};if(app.get("env")==="production"){
  session.cookie.secure =true;}

expressSession takes a configuration object, session, that defines what options to enable in a session. Here, you are configuring the following options:

Configure passport with the application settings

In this section, you’ll focus on wiring up Passport.js with your Express app.

🛠️️ With Auth0Strategy already imported, proceed to define this strategy under the Passport Configuration section in index.js:

const strategy =newAuth0Strategy({
    domain: process.env.AUTH0_DOMAIN,
    clientID: process.env.AUTH0_CLIENT_ID,
    clientSecret: process.env.AUTH0_CLIENT_SECRET,
    callbackURL: process.env.AUTH0_CALLBACK_URL},function(accessToken, refreshToken, extraParams, profile, done){returndone(null, profile);});

Here, the Auth0Strategy method takes your Auth0 credentials and initializes the strategy. It’s essential to understand what the Auth0Strategy is doing for you:

Cookie.domain

Specifies the value for the DomainSet-Cookie attribute. By default, no domain
is set, and most clients will consider the cookie to apply to only the current
domain.

Cookie.expires

Specifies the Date object to be the value for the ExpiresSet-Cookie attribute.
By default, no expiration is set, and most clients will consider this a
“non-persistent cookie” and will delete it on a condition like exiting a web browser
application.

Cookie.maxage

Alternatively req.session.cookie.maxAge will return the time
remaining in milliseconds, which we may also re-assign a new value
to adjust the .expires property appropriately. The following
are essentially equivalent

For example when maxAge is set to 60000 (one minute), and 30 seconds
has elapsed it will return 30000 until the current request has completed,
at which time req.session.touch() is called to reset
req.session.cookie.maxAge to its original value.

Cookie.originalmaxage

The req.session.cookie.originalMaxAge property returns the original
maxAge (time-to-live), in milliseconds, of the session cookie.

Cookie.path

Specifies the value for the PathSet-Cookie. By default, this is set to ‘/’, which
is the root path of the domain.

Cookie.secure

Specifies the boolean value for the SecureSet-Cookie attribute. When truthy,
the Secure attribute is set, otherwise it is not. By default, the Secure
attribute is not set.

Create a server.js

Now we are ready to create our application’s root server.js file.

We start by including our dependencies.

const express = require('express'); // server software
const bodyParser = require('body-parser'); // parser middleware
const session = require('express-session');  // session middleware
const passport = require('passport');  // authentication
const connectEnsureLogin = require('connect-ensure-login'); //authorization

Debugging

This module uses the debug module
internally to log information about session operations.

To see all the internal logs, set the DEBUG environment variable to
express-session when launching your app (npm start, in this example):

$ DEBUG=express-session npm start

On Windows, use the corresponding command;

Define get routes

We are ready to append the server.js code with the GET routes.

We start with the route to our homepage. It is accessible to everybody.

app.get('/', (req, res) => {
  res.sendFile(__dirname   '/static/index.html');
})

Next, we create the route to our application’s login page. It is also accessible to everybody.

app.get('/login', (req, res) => {
  res.sendFile(__dirname   '/static/login.html');
});

Express-session options and how to use them

To set up the session, you need to set a couple of Express-session options, as shown below.

  • secret – a random unique string key used to authenticate a session. It is stored in an environment variable and can’t be exposed to the public. The key is usually long and randomly generated in a production environment.

  • resave – takes a Boolean value. It enables the session to be stored back to the session store, even if the session was never modified during the request. This can result in a race situation in case a client makes two parallel requests to the server. Thus modification made on the session of the first request may be overwritten when the second request ends. The default value is true. However, this may change at some point. false is a better alternative.

  • saveUninitialized – this allows any uninitialized session to be sent to the store. When a session is created but not modified, it is referred to as uninitialized.

  • cookie: { maxAge: oneDay } – this sets the cookie expiry time. The browser will delete the cookie after the set duration elapses. The cookie will not be attached to any of the requests in the future. In this case, we’ve set the maxAge to a single day as computed by the following arithmetic.

Check the documentation for all possible options and learn more about these options.

Genid

Function to call to generate a new session ID. Provide a function that returns
a string that will be used as a session ID. The function is given req as the
first argument if you want to use some value attached to req when generating
the ID.

The default value is a function which uses the uid-safe library to generate IDs.

NOTE be careful to generate unique IDs so your sessions do not conflict.

Installation

This is a Node.js module available through the
npm registry. Installation is done using the
npm install command:

$ npm install express-session

License

MIT

Mastering session authentication

Original photo by Samuel Zeller

Node.js very basic local authentication using express sessions

Background – I have been building a basic website using Node.js and now that I have got it up and running I need to establish a very basic form of local authentication to make the content secure to only a handful of trusted users (who I might choose to accept – I don’t want a sign up feature). I have looked into a range of options including JSON Web Tokens, Cookies, etc, and have settled on using sessions, as they seem the simplest to setup. (This only has to be a basic short term security feature; it doesn’t have to scale well or be a new standard in online security).

I have looked at a variety of online guides, and most of these rely on using a MongoDB database. I don’t want to use this, as eventually I need to use PostgreSQL. A common theme has been to use passport:

It seems though that I may be able to avoid these, as done by the below example:

Using express-session and cookie-parser with Express

I have been trying to follow/modify the example from Simple usage of express-session and cookie-parser with Express and have the following in my app.js:

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var expressSession = require('express-session');


var routes = require('./routes/index');
var users = require('./routes/users');
var private_info = require('./routes/private_info');

var app = express();

app.use(cookieParser());

app.use(expressSession({secret:'somesecret'}));

app.use(bodyParser());

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));


app.get('/', function(req, res){
  var html = '<form action="/" method="post">'  
      'Your name: <input type="text" name="userName"><br>'  
      '<button type="submit">Submit</button>'  
      '</form>';
  if (req.session.userName) {
    html  = '<br>Your username from your session is: '   req.session.userName;
  }
  res.send(html);
});


app.get('/private_info', function(req, res){
  var html = "You are not allowed to see this private info.";
  if (req.session.userName === "oliver") {
    html = 'See all this private info...<br/>DATA<br/>DATA<br/>User = '   req.session.userName;
    res.render('private_info', { title: 'Private Info' });
  }
  res.send(html);
});

app.post('/', function(req, res){
  req.session.userName = req.body.userName;
  res.redirect('/');
});

app.use('/', routes);
app.use('/users', users);
app.use('/private_info', private_info);


// error handlers
    ... etc ...   

module.exports = app;

This works fairly well for my purposes (albeit there is an error message Error: Can't set headers after they are sent., but I can worry about that later). I would now like to incorporate this method into my main website. However, the main website collects live data, and I don’t know when/where/how to incorporate this into my current structure. The page I would like to secure has the following structure (found in routes/predictions.js):

var express = require('express');
var router = express.Router();
var async = require("async");
var papa = require("papaparse");
var request_retry = require('requestretry');

// long series of function declarations and constructing a chain of call backs
function request_longshot_rob(first_data_list, res) {
    //Long series of functions producing a data object called data_for_client.
    res.render("predictions", {data: data_for_client});
}

router.get('/', function (req, res, next) {
    request_longshot_rob(first_data_list, res);
});

module.exports = router;

How is the best way to incorporate this authentication request, especially if I want to secure maybe 5 pages and don’t want to have to do something complicated on a case by case basis. I.e. I want the user’s session ID/username to be verified, and if accepted, I would like the perform the script in routes/predictions.js as normal.

Any help would be greatly appreciated.

EDIT


A typical routes/ file, e.g. routes/private_info can generally have the following structure:

var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
    console.log("private info page function");
    res.render('private_info', { title: 'Private Info' });
});

module.exports = router;

Project: adding e-mail and password login to blog

To enable session-based authentication in Blog, we need to do the following:

Req.session.id

Each session has a unique ID associated with it. This property is an
alias of req.sessionID and cannot be modified.
It has been added to make the session ID accessible from the session
object.

Req.sessionid

To get the ID of the loaded session, access the request property
req.sessionID. This is simply a read-only value set when a session
is loaded/created.

Security and auth in node.js

You know that security is an important aspect of any real-world web application. This is especially true nowadays, because our apps don’t function in silos anymore. What if I tell you that you don’t have to spend days studying for security certifications or read sketchy dark-web hacker forums to implement a secure Node app? I’ll show you a few tricks.

Session store implementation

Every session store must be an EventEmitter and implement specific
methods. The following methods are the list of required, recommended,
and optional.

For an example implementation view the connect-redis repo.

Session.destroy(callback)

Destroys the session and will unset the req.session property.
Once complete, the callback will be invoked.

Session.regenerate(callback)

To regenerate the session simply invoke the method. Once complete,
a new SID and Session instance will be initialized at req.session
and the callback will be invoked.

Session.reload(callback)

Reloads the session data from the store and re-populates the
req.session object. Once complete, the callback will be invoked.

Session.save(callback)

Save the session back to the store, replacing the contents on the store with the
contents in memory (though a store may do something else–consult the store’s
documentation for exact behavior).

Session.touch()

Updates the .maxAge property. Typically this is
not necessary to call, as the session middleware does this for you.

Set the cookie-parser

Define Cookie-parser usage so that the server can access the necessary option to save, read and access a cookie.

Set up real-world authentication for node.js

This tutorial’s core objective is to teach you how to set up real-world authentication in a Node.js Express app. For that reason, you’ll start by setting up Auth0 to interact with a real authentication server throughout the tutorial. Otherwise, Passport.js gives you a ton of error messages in the terminal, and the app won’t run.

Auth0 is a global leader in Identity-as-a-Service (IDaaS). Its extensible platform seamlessly authenticates and secures more than 2.5 billion logins per month, making it loved by developers and trusted by global enterprises.

The best part of the Auth0 platform is how streamlined it is to get started by following these three easy steps.

Setting up the development stage

First off we will install the cookie-parser node package, which will be used to manage cookies on our express server. We do so by running the following command from the terminal(Make sure the terminal is opened in your working folder ). Since this a continuation of our previous post, we work from the same directory. We will run $ npm install cookie-parser –save to install our package.

Step 1: sign up and create an auth0 application

🛠️️ If you are new to Auth0, sign up for a free Auth0 account here. A free account offers you:

🛠️️ During the sign-up process, you’ll create something called a Tenant, representing the product or service to which you are adding authentication — more on this in a moment.

🛠️️ Once you are signed in, you are welcomed into the Auth0 Dashboard. In the left sidebar menu, click on “Applications”.

Step 3: add auth0 configuration variables to node.js

🛠️️ Under the project directory, create a hidden file called .env to store configuration variables and secrets that your app needs.

touch .env

Make sure to add this file to .gitignore so that it isn’t committed to version control.

🛠️️ Add the following to .env:

AUTH0_CLIENT_ID=AUTH0_DOMAIN=AUTH0_CLIENT_SECRET=

🛠️️ Head back to your Auth0 application Settings tab and populate each property of the .env hidden file with its corresponding Auth0 application value:

Step 3: mongodb/mongoose integration

It’s time for a field trip! Let’s create our MongoDB Atlas account. I remember the first time I used Atlas I felt very lost… So we will do this together. (If you are an Atlas expert, skip ahead).

Step 5: the session

Let’s get right to business. In our config.js, we need to add a few more lines of code:

--- backend/config.js --
...
SESS_NAME = 'sid',
SESS_SECRET = 'secret!session',
SESS_LIFETIME = 1000 * 60 * 60 * 2

} = process.env;

Here we destructure/define a session name, secret, and lifetime (I’m only using two days). This SESS_SECRET is used to hash the sessionID with HMAC. This secret should normally be something esoteric and defined in your environment (e.g., Heroku, etc), but for development, this is ok. The SESS_NAME will be used to reference our session later.

Ok, let’s head back over to our server.js file. I’ll split it in two:

Step 6: react/redux setup

Before continuing, if this is your first time using React and Redux, these concepts might sound a little confusing, but don’t worry! They’re very repetitive and just take practice. As always, I’ll explain things at a high level but if your head is spinning I highly recommend the React and Redux documentation. They’re fantastic! Lastly, mind the implicit returns. We’ll be using a lot of them. Ready?

Let’s change directories up one level to our root project directory, SessionAuth: cd ..Now run this command: npx create-react-app frontendThis sets up a React application without any build configuration.

This creates quite a bit for us, so we’ll need to do a little house-cleaning.Feel free to keep any files you want/need. I’m going bare-bones for this app so it can be re-used for future applications without any baggage. I deleted: public/favicon.ico, manifest.json, src/App.css, App.test.js, index.css, logo.svg, serviceWorker.js and rearranged my file structure like so:

SessionAuth/
|— backend/
|— frontend/
|— node_modules/
|— public/
|— index.html
|— src/
|— actions/
|— components/
|— App.js <= moved
|— reducers/

|— errors/
|— session/
|— store/
|— util/
|— index.js
|— .gitignore
|— package-lock.json
|— package.json

I gave the index.html a trim as well:

--- frontend/public/index.html ---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<title>Session Auth</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

Next stop is our App.js:

--- frontend/src/components/App.js ---
import React from 'react';
export default () => (
<>
<h1>Hello world</h1>
</>
);

At a very high-level, components are just functions that return JSX, which produces React elements, which are then rendered to the DOM. To keep things simple we won’t be using any stateful components.

Now for our index.js:

--- frontend/src/index.js ---
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
// delete old imports we don't need
ReactDOM.render(
<App />,
document.getElementById('root')
);

We will be adding a lot more here later, but for now, this is good. We are rendering the App component inside the <div id=”root”></div> from our index.html, which in turn renders that h1.

Store.all(callback)

Optional

This optional method is used to get all sessions in the store as an array. The
callback should be called as callback(error, sessions).

Store.clear(callback)

Optional

This optional method is used to delete all sessions from the store. The
callback should be called as callback(error) once the store is cleared.

Store.destroy(sid, callback)

Required

This required method is used to destroy/delete a session from the store given
a session ID (sid). The callback should be called as callback(error) once
the session is destroyed.

Store.get(sid, callback)

Required

This required method is used to get a session from the store given a session
ID (sid). The callback should be called as callback(error, session).

The session argument should be a session if found, otherwise null or
undefined if the session was not found (and there was no error). A special
case is made when error.code === ‘ENOENT’ to act like callback(null, null).

Store.length(callback)

Optional

This optional method is used to get the count of all sessions in the store.
The callback should be called as callback(error, len).

Store.set(sid, session, callback)

Required

This required method is used to upsert a session into the store given a
session ID (sid) and session (session) object. The callback should be
called as callback(error) once the session has been set in the store.

Store.touch(sid, session, callback)

Recommended

This recommended method is used to “touch” a given session given a
session ID (sid) and session (session) object. The callback should be
called as callback(error) once the session has been touched.

This is primarily used when the store will automatically delete idle sessions
and this method is used to signal to the store the given session is active,
potentially resetting the idle timer.

Summary

In this chapter, we learned how to implement standard email and password authentication, and used Express.js middleware to protect sensitive pages and endpoints in Blog. Then, we covered OAuth 1.0 and OAuth 2.0 with Everyauth and OAuth modules, respectively.

Use middleware for authentication

As explained in the “Using middleware” section of the Express docs, an Express application is essentially a series of middleware function calls that execute during the request-response cycle. Each function can modify the request and response objects as needed and then either pass control to the next middleware function or end the request-response cycle.

What you will build

You’ll secure the login portal for a restaurant named WHATABYTE using Passport.js with Auth0:

We tested this tutorial using Node.js v12.16.0 and npm v6.13.

🏆 mission accomplished 🏆

I know this article is quite lengthy, and this is my first time writing anything online, but I really enjoyed this experience.Of course, more than anything I hope YOU enjoyed this!

See you next time!

View the codebase on GitHub.More about me.

Create express authentication endpoints

🛠️️ In this section, you will create three endpoints that handle the application’s authentication flow:

GET /login

GET /logout

GET /callback

To manage these endpoints better, you will create them within an authentication module and export them through an Express router so that your Express application can use them.

🛠️️ To start, create an auth.js file under the project directory.

touch auth.js

Populate it with this template to define its structure:

🛠️️ Next, add the following under the Required External Modules section to import packages that are needed and load your environmental variables:

const express =require("express");const router = express.Router();const passport =require("passport");const querystring =require("querystring");require("dotenv").config();

Here’s an overview of the new modules you are using:

You’ll soon see how these modules streamline your route controller logic.

🛠️️ The first endpoint you’ll create is the GET/login one. Update the Routes Definitions section of your auth.js file as follows:



router.get("/login",
  passport.authenticate("auth0",{
    scope:"openid email profile"}),(req, res)=>{
    res.redirect("/");});

Install expressjs and dependencies

Before we start writing code, we must install the necessary dependencies.

Let’s start by installing ExpressJS (server framework), body-parser (parses incoming request bodies), and express-session (cookie-based session middleware).

npm install express body-parser express-session

Next, we install Passport and passport-local. Passport is the authentication library and passport-local is our core authentication strategy.

npm install passport passport-local

Add the endpoints

We have to make three routes here:

Похожее:  Веб-сервисы и 1С: «Здравствуй, Name», время-деньги и «Жизнь»

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *