DEV Community

Cover image for Express.js integration guide for passwordless authentication with DID.app
Peter Saxton
Peter Saxton

Posted on • Originally published at did.app

Express.js integration guide for passwordless authentication with DID.app

Just want the code? Find this example on github

What is DID.app

DID.app is an Identity Provider, that authenticates users by verifying access to either an email address or securely stored private key.

This allows your users to sign in with just a single click.

Requirements

This tutorial will require you to have Node.js and Express installed.

New Express project

Use the Express generator to start a new project.

npx express-generator --view=pug myapp
cd myapp
npm install
Enter fullscreen mode Exit fullscreen mode

Install openid-client and cookie-session from npm.

npm install --save openid-client cookie-session
Enter fullscreen mode Exit fullscreen mode

Add sessions to the application

We will use cookie-session so that we can keep a user signed in after we have authenticated them.
To use it, require the module and add to the apps middleware in app.js.

// other dependencies
var cookieSession = require("cookie-session");

// other middleware
var { SESSION_SECRET } = process.env;
app.use(cookieSession({ name: "myapp", secret: SESSION_SECRET }));
Enter fullscreen mode Exit fullscreen mode

It is best practise to keep your session secret out of your source code.

Fetch OpenID Connect configuration

Only routes for handing authentication will require the OpenID Configuration for DID.app.
Create a routes file for sessions routes/session.js and configure the client library.

var express = require("express");
var router = express.Router();
var { Issuer } = require("openid-client");

var { CLIENT_ID, CLIENT_SECRET } = process.env;

var clientPromise = Issuer.discover("https://did.app").then(function(issuer) {
  console.log("Discovered issuer %s %O", issuer.issuer, issuer.metadata);
  return new issuer.Client({
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET
  });
});

// ...
Enter fullscreen mode Exit fullscreen mode

The client_id in client_secret are fetched from the environment,
we will generate them later.

Create File for sign in routes

Signing in, or up, using the OpenID connect flow requires two endpoints.

One endpoint redirects the user to the OpenID provider (in this case DID.app) to authenticate themselves.
A second callback endpoint is where the result of authenticating is handled.

Add both of these into the session routes file.

// ...

router.get("/authenticate", function(req, res, next) {
  clientPromise.then(function(client) {
    var authorizationUrl = client.authorizationUrl({
      scope: "openid",
      redirect_uri: "http://localhost:3000/session/callback"
    });
    res.redirect(authorizationUrl);
  });
});

router.get("/callback", function(req, res, next) {
  clientPromise
    .then(function(client) {
      var params = client.callbackParams(req);
      return client.callback("http://localhost:3000/session/callback", params);
    })
    .then(function(tokenSet) {
      var claims = tokenSet.claims();
      console.log(claims);
      req.session = { userId: claims.sub };
      res.redirect("/");
    });
});

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Add the session routes to the express app in app.js.

// other dependencies
var sessionRouter = require("./routes/session");

// ...

app.use("/", indexRouter);
app.use("/users", usersRouter);
// new routes
app.use("/session", sessionRouter);
Enter fullscreen mode Exit fullscreen mode

Display authentication status

Our users need a button that lets them sign in.
To add one to the homepage the route handler checks if there is already a user session,
if so we make this user id available to the view.
Make these changes to routes/index.js

router.get("/", function(req, res, next) {
  var session = req.session || {};
  res.render("index", { title: "Express", userId: session.userId });
});
Enter fullscreen mode Exit fullscreen mode

In the view we use the user id to show them some information about them, or if no user a sign in button this code to show a button
Add this snippet into views/index.pug.

if userId
  span User ID #{userId}
else
  a.button.action(href='/session/authenticate') Sign in
Enter fullscreen mode Exit fullscreen mode

Setup the App on DID

You will need a DID account. Sign up to create one now.

After signing up, you will be directed to set up your first app.
Because we will run on localhost we need to use test app, select test mode.

Screenshot of creating an app on DID

After setting the details for the app, copy the client id and secret for use in our application.

Try it out

Start Express, passing in the required configuration as environment variables.

CLIENT_ID=test_abc CLIENT_SECRET=test_abcdef SESSION_SECRET=somesecret npm start
Enter fullscreen mode Exit fullscreen mode

Visit localhost:3000,
you should see your new Express app with a shiny sign in button.

Any problems, see this commit for the complete set of changes.

Have a question?

If you have any further questions contact us at team@did.app.

Discussion (3)

Collapse
steelvoltage profile image
Brian Barbour

Hmm... Trying to understand the benefits of this, earnestly. I mean... they still kind have to use a password though--getting into their email. I mean, it's possible they're logged in--but not guaranteed. I guess the benefit is you don't have to hash and manage their password. At the same time, you're taking them away from your site to login to your site.

Collapse
crowdhailer profile image
Peter Saxton Author

Fair comment. The impression I am getting is that we need to get better at explaining how our device authentication works. You only need to access your emails up until you have set up a trusted device which you can then use as a key for your accounts indefinitely.

Not managing the password is an ancillary benefit, also not having to store email addresses can be a benefit as they are personally identifying information that you have to store securly

Collapse
victorioberra profile image
Victorio Berra

The point is it's OAuth2. So now you have a token that can be used to access an API on the users behalf and you centralized your identites. Right now, taking someone away from your site to login to your site is considered a best practice. How do you think "login with Facebook/Google" works?