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

@makerx/express-msal

v1.3.0

Published

Simple server-side cookie-session based Azure AD authentication for Express.js apps.

Downloads

409

Readme

Express MSAL

Simple server-side cookie-session based Azure AD authentication for Express.js apps.

What does this do?

This library wraps @azure/msal-node to provide a simple way to run interactive authentication on Express JS hosted UIs (web pages) that need to call APIs using Bearer token authentication, without writing and deploying client-side MSAL code.

As an example use case, we host GraphQL Playground or Apollo Sandbox and Voyager UIs alongside our GraphQL APIs from server-side middleware. This package allows us to add authentication across all those UIs without building, packaging and deploying custom UI code.

For Bearer token authentication, see @makerxstudio/express-bearer.

Usage

npm install @azure/msal-node express cookie-session @makerxstudio/express-msal
import { ConfidentialClientApplication } from '@azure/msal-node'
import { AuthConfig, pkceAuthenticationMiddleware, copySessionJwtToBearerHeader } from '@makerxstudio/express-msal'
import cookieSession from 'cookie-session'

const app = express()
app.use(cookieSession(cookieSessionOptions))

const msalApp = new ConfidentialClientApplication(msalConfig)
const authConfig: AuthConfig = {
  app,
  msalApp,
  scopes: ['profile', 'api://my-api/.default'],
}

// trigger pkce auth on GET requests (iteractive users accessing UIs)
app.get('*', pkceAuthenticationMiddleware(authConfig))
// set a Bearer {token} auth header on POST request to '/graphql'
app.post('/graphql', copySessionJwtToBearerHeader)
  • pkceAuthenticationMiddleware starts the PKCE auth flow (redirect) when there is no session, creates a cookie-session containing an accessToken.
  • copySessionJwtToBearerHeader: takes the accessToken from the session cookie and adds a Bearer {token} header onto the request, useful for supporting API access from your session-based auth (will not overwrite existing authorization header on the request).

Is this secure?

Auth configuration security

We recommend you use the Web platform auth configuration in Azure and use ConfidentialClientApplication (which requires a client secret). Using this auth configuration which requires a client secret prevents your auth client ID being used outside of your controlled server-side application.

Session security

We only use cookie-session with this library, along with secure-as-possible cookie configuration.

The logic here is that access tokens are already persisted in browser storage and are freely available in the browser, however it's definitely possible to configure an insecure session cookie that does not restrict access to the cookie and access token adequately.

We strongly recommend configuring your cookie-session options carefully, applying the most restrictive policy, setting path, domain, sameSite, secure, httpOnly etc.

const cookieSessionOptions = {
  name: '<session-name>',
  maxAge: 1000 * 60 * 60,
  sameSite: 'strict',
  secure: true,
  httpOnly: true,
}
app.use(cookieSession(cookieSessionOptions))

Config

AuthConfig:

| Option | Description | | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | app | The Express JS app on which the auth reply handler is set up (see authReplyRoute). | | msalClient | The @azure/msal-node ConfidentialClientApplication instance. | | scopes | The scopes to use to aquire the accessToken. | | authReplyRoute | The route on which the auth completion handler is be set up, which must be configured in the Azure App Registration, default: /auth. | | augmentSession | Optional function to add additional info to the session from the msal AuthenticationResult. | | logger | Optional logger implementation to log token validation errors, handler setup info entry etc. | | authorizationUrlRequestOverride | Optional per request override of the authorisation URL request configuration, allows for things like dynamic authority for multi-tenanted apps, etc. |

Detailed usage examples

// set up cookie-session
const cookieSessionOptions = {
  name: '<session-name>',
  maxAge: 1000 * 60 * 60, // match session lifetime to the access token
  sameSite: 'strict',
  secure: true,
  httpOnly: true,
}
app.use(cookieSession(cookieSessionOptions))

// set up msal client
const msalApp = new ConfidentialClientApplication({
  auth: {
    clientId: '<client-ID>',
    clientSecret: '<client-secret>'
    authority: 'https://login.microsoftonline.com/<tenant-ID>',
  },
})

// configure all config options
const authConfig: AuthConfig = {
  app,
  msalApp,
  scopes: ['profile', 'api://my-api/.default'],
  // specify non-default reply url
  replyUrl: '/auth-callback',
  // add some additional info to the session from the msal `AuthenticationResult`
  augmentSession: (response) => {
    return { username: response.account?.username }
  },
  // specify a logger
  logger,
  // specify an authority override
  authorizationUrlRequestOverride: (req) => ({authority: authorityLookup[req.host]})
}

// use pkce auth on everything apart from ./api*
app.use(/^\/(?!api).*/, pkceAuthenticationMiddleware(authConfig))

// set a Bearer {token} auth header on request to '/api*
app.use('/api*', copySessionJwtToBearerHeader)

// add a logout endpoint for GET requests to /logout
app.get('/logout', logout)

// return the currently logger in user's username from GET requests to /user
app.get('/user', (req, res) => {
  if (!isAuthenticatedSession(req.session)) return res.status(400).send('Not logged in').end()
  res.send(req.session.username).end()
})

Logging

Set the logger implementation to an object that fulfills the Logger definition:

type Logger = {
  error(message: string, ...optionalParams: unknown[]): void
  warn(message: string, ...optionalParams: unknown[]): void
  info(message: string, ...optionalParams: unknown[]): void
  verbose(message: string, ...optionalParams: unknown[]): void
  debug(message: string, ...optionalParams: unknown[]): void
}

Note: this type is compatible with winston loggers.

The following example uses console logging:

const logger: Logger = {
  error: (message: string, ...params: unknown[]) => console.error
  warn: (message: string, ...params: unknown[]) => console.warn
  info: (message: string, ...params: unknown[]) => console.info
  verbose: (message: string, ...params: unknown[]) => console.trace
  debug: (message: string, ...params: unknown[]) => console.debug
}

const pkceAuthConfig: AuthConfig = {
  /* other config */
  logger,
}

@azure/msal-node

Use the MSAL system loggerOptions config to control logging from the @azure/msal package:

const config: Partial<Configuration> = {
  system: {
    loggerOptions: {
      loggerCallback(loglevel: LogLevel, message: string, containsPii: boolean) {
        console.log(message)
      },
      piiLoggingEnabled: false,
      logLevel: LogLevel.Verbose,
    },
  },
}

To assist supplying a log function to MSAL, this module exports toNpmLogLevel which maps MSAL LogLevel values to npm log level labels, making it easy to integrate a winston Logger instance:

const msalLoggerConfig: Partial<Configuration> = {
  system: {
    loggerOptions: {
      loggerCallback: (level, message) => logger.log(toNpmLogLevel(level), message),
    },
  },
}