npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

msal-node-custom

v1.0.0

Published

This project illustrates a simple wrapper around the [ConfidentialClientApplication](https://azuread.github.io/microsoft-authentication-library-for-js/ref/classes/_azure_msal_node.confidentialclientapplication.html) class of the [Microsoft Authentication

Downloads

9

Readme

MSAL Node wrapper for Express.js

This project illustrates a simple wrapper around the ConfidentialClientApplication class of the Microsoft Authentication Library for Node.js (MSAL Node) for web applications built with Express.js, in order to streamline routine authentication tasks, such as login, logout and token acquisition, as well as securing routes and controlling access.

This is an open source project. Suggestions and contributions are welcome!

Features

Prerequisites

Installation

    npm install azure-samples/ms-identity-javascript-nodejs-tutorial 

or download and extract the repository .zip file.

Build

    git clone https://github.com/Azure-Samples/ms-identity-javascript-nodejs-tutorial.git
    cd ms-identity-javascript-nodejs-tutorial/Common/msal-node-custom
    npm install
    npm run build

Getting started

Initialization

Import the package and initialize the WebAppAuthProvider. The initialize() method takes a configuration object.

Below is a basic web application using WebAppAuthProvider:

const express = require('express');
const session = require('express-session');
const { WebAppAuthProvider } = require('msal-node-custom');

const SERVER_PORT = process.env.PORT || 4000;

async function main() {

    // initialize express
    const app = express();

    /**
     * Using express-session middleware. Be sure to familiarize yourself with available options
     * and set them as desired. Visit: https://www.npmjs.com/package/express-session
     */
    app.use(session({
        secret: 'ENTER_YOUR_SECRET_HERE',
        resave: false,
        saveUninitialized: false,
        cookie: {
            httpOnly: true,
            secure: false, // set this to true on production
        }
    }));

    app.use(express.urlencoded({ extended: false }));
    app.use(express.json());

    try {
        // initialize the wrapper
        const authProvider = await WebAppAuthProvider.initialize({
            auth: {
                authority: "https://login.microsoftonline.com/Enter_the_Tenant_Info_Here",
                clientId: "Enter_the_Application_Id_Here",
                clientSecret: "Enter_the_Client_Secret_Here", // use certificates instead for enhanced security
                redirectUri: "/redirect",
            }
        });
    
        app.use(authProvider.authenticate({
            protectAllRoutes: true, // force user to authenticate for all routes
            acquireTokenForResources: { // acquire an access token for this resource
                "graph.microsoft.com": { // you can specify the resource name as you like
                    scopes: ["User.Read"], // scopes for the resource that you want to acquire a token for
                    routes: ["/profile"] // acquire a token before the user hits these routes
                },
            }
        }));
    
        // add your routers/controllers here...
    
        app.use(authProvider.interactionErrorHandler()); // this middleware handles interaction required errors
    
        app.listen(SERVER_PORT, () => console.log(`Msal Node Auth Code Sample app listening on port ${SERVER_PORT}!`));
    } catch (error) {
        console.log(error);
        process.exit(1);
    }
}

main();

You can access the current authentication context via req.authContext variable:

  • req.authContext.isAuthenticated(): indicates if the current user is authenticated or not.
  • req.authContext.getAccount(): MSAL.js account object containing useful information like ID token claims (see AccountInfo)
  • req.authContext.getCachedTokenForResource(<resourceName>): returns the access token for the given resource from cache, if exists and not expired.

Middleware

Authentication

Add login() and logout() middleware to routes that you want to trigger a login/logout with Azure AD:

app.get(
    '/signin',
    (req, res, next) => {
        return req.authContext.login({
            postLoginRedirectUri: "/",
        })(req, res, next);
    }
);
app.get(
    '/signout',
    (req, res, next) => {
        return req.authContext.logout({
            postLogoutRedirectUri: "/",
        })(req, res, next);
    }
);

Alternatively, you can require authentication for all routes in your application using the authenticate() middleware:

    app.use(authProvider.authenticate({
        protectAllRoutes: true,
    }));

Acquiring tokens

acquireToken() can be used in controllers to acquire a token silently from cache or network using the refresh token. If this is not possible, acquireToken will throw an interaction required error. To handle this error, make sure you have added the interactionErrorHandler to the end of your middleware chain.

If you have configured the authenticate() middleware before, you can also use the getCachedTokenForResource() method to retrieve a non-expired access token for the resource from cache directly.

exports.getProfilePage = async (req, res, next) => {
    try {
        /**
         * If you have configured your authenticate middleware accordingly, you should be 
         * able to get the access token for the Microsoft Graph API from the cache. If not, 
         * you will need to acquire and cache the token yourself.
         */
        let accessToken = req.authContext.getCachedTokenForResource("graph.microsoft.com");

        if (!accessToken) {

            /**
             * You can acquire a token for the Microsoft Graph API as shown below. Note that
             * if an interaction required error is thrown, you need to catch it and pass it 
             * to the interactionErrorHandler middleware.
             */
            const tokenResponse = await req.authContext.acquireToken({
                scopes: ["User.Read"],
                account: req.authContext.getAccount(),
            })(req, res, next);

            accessToken = tokenResponse.accessToken;
        }

        // ...

    } catch (error) {
        next(error); // pass errors to error middleware for handling
    }
}

Securing routes

Simply add the guard() middleware before the controller that serves the page you would like to secure:

    app.get('/id',
        authProvider.guard({
            forceLogin: true // ensure that the user is authenticated before accessing this route
        }),
        (req, res, next) => {
            res.render('id', {
                isAuthenticated: req.authContext.isAuthenticated(), 
                idToken: req.authContext.getAccount().idTokenClaims
            });
        }
    );

Controlling access

Use the guard() middleware to control access for a certain claim or claims in the user's ID token:

    app.get(
        '/todolist',
        authProvider.guard({
            forceLogin: true, // ensure that the user is authenticated before accessing this route
            idTokenClaims: {
                roles: ["TaskUser", "TaskAdmin"], // grant access to the route only if user has one of these claims
            },
        }),
    );

    app.get(
        '/dashboard',
        authProvider.guard({
            forceLogin: false, // if user is not authenticated, an error will be thrown instead
            idTokenClaims: {
                roles: ["TaskAdmin"],
            },
        })
    );

Remarks

Session support

We recommend using express-session to add session support to your apps. package using in-memory session store. in-memory session store is unfit for production, and you should either use a compatible session store or implement your own storage solution.

Caching

MSAL Node has an in-memory cache by default. This wrapper adds support for storing MSAL cache in user session. As such, a session middleware is necessary for enabling cache persistence.

Information