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

@azure-iot/authentication

v1.0.0-rc.2

Published

Provides Authentication support for Azure IOT microservices

Downloads

22

Readme

@azure-iot/authentication

This library provides support for user authentication in Azure IoT microservices.

Developer Setup

1. Install Node

Node can be found here

2. npm install

This will download and install all dependencies required to build this script.

3. npm run build

This will build the project, putting the output into the src folder.

Usage

This library provides the Authentication module, which takes in an express application and a few configuration values, and returns a middleware that the application can use on routes to ensure the user is authenticated.

Internally, the library sets up an express session and a passport session, both backed by mongodb, to ensure the req.user object contains the authenticated user information correctly when the route is executed.

This module does not actually authenticate against an OAuth providers directly: it merely sets up passport so the user is redirected to a specified login URL (typically hosted on a different service), which will then perform the authentication and redirect back to the page the user was originally trying to visit.

Example

import {Authentication} from '@azure-iot/authentication';

const app = express();
const auth = await Authentication.initialize(
    app, // an express application
    'http://shell/login', // URL of the login page
    'keyboard cat', // password to encrypt user sessions
    'mongodb://localhost:27071'); // URI of the mongodb instance

app.get('/', auth.ensureAuthentication, (req, res) => res.sendStatus(200));

Control flow

  1. Authentication.initialize initializes express and passport sessions backed by mongodb.
  2. User requests a service route (e.g., http://localhost:3000/) that specifies auth.ensureAuthentication as a middleware.
  3. Express creates a new session for the user and stores it in mongo.
  4. The Auth middleware sees that Passport hasn't authenticated the user, stores the current URL (http://localhost:3000/) in the session, and redirects the user to the login page (in our example, http://shell/login).
  5. Passport (running in the Shell service as a middleware to the /login route) sees that the user is not authenticated, and redirects to the OAuth provider (in our case, AAD) to actually authenticate the user.
  6. AAD asks for the user's credentials (or redirects to a federated AD instance which will ask for the credentials), and redirects back to a callback page in Shell with the user's tokens (e.g., http://shell/auth/aad/return?code=foo&id_token=...)
  7. Passport parses the returned tokens (which contains information like the user's name, unique id, and email), and gives it to Shell.
  8. Shell creates a new User entry in mongo (if one with the specified unique id doesn't already exist), and gives it to Passport.
  9. Passport stores the user's unique id in the express session (using serialization code specified in the Authentication module.)
  10. Shell redirects back to the user's original URL (stored in the session in step 4.)
  11. Back in the original service, Express picks up the session id stored in the cookie, and retrieves the full session information from mongo.
  12. Passport retrieves the user information specified in the session, and makes it available to other request handlers in the req.user property.
  13. The Auth middleware sees that Passport has authenticated the user, and calls the next request handler.

Full example

This library will most likely be used only in a production environment, with the configuration fetched from the Config service. The following code demonstrates a helper module that a service can use to query the config service and initialize authentication with retry logic (for production environment, in case the config service hasn't been initialized yet), and fallback logic (for dev purposes, when the config needs to be fetched from a file instead of the config service.)

import * as express from 'express';
import * as request from 'request';
import {Authentication} from '@azure-iot/authentication';

export class Config {
    constructor(
        public IotHubConnectionString: string,
        public EnsureAuthentication: express.RequestHandler) {}
        
    public static async initialize(app: express.Express): Promise<Config> {
        const waitForConfig = process.env.NODE_ENV === 'production';
        const configUrl: string = process.env.CONFIG_URL || 'http://localhost:3009';
        
        let result: Config = null;
        do {
            try {
                const discovery = await getHal<void>(configUrl + '/api/discovery');
                
                const settingsLink = discovery._links['settings:list'];
                if (!settingsLink) throw new Error('Config service does not provide settings:list');
        
                const configSettings = await getHal<ConfigSettings>(configUrl + settingsLink.href);
                if (!configSettings.iotHubConnStr) throw new Error('Config service does not provide setting "iotHubConnStr"');
                if (!configSettings.loginUrl) throw new Error('Config service does not provide setting "loginUrl"');
                if (!configSettings.mongoUri) throw new Error('Config service does not provide setting "mongoUri"');
                if (!configSettings.sessionSecret) throw new Error('Config service does not provide setting "sessionSecret"');
                
                const auth = await Authentication.initialize(
                    app,
                    configSettings.loginUrl,
                    configSettings.sessionSecret,
                    configSettings.mongoUri);
                
                return new Config(
                    configSettings.iotHubConnStr,
                    auth.ensureAuthenticated);
            } catch (err) {
                process.stderr.write(`WARNING: Could not initialize from Config Service: ${err}.\n`);
                if (waitForConfig) {
                    // wait for 5 seconds before retrying:
                    await new Promise((resolve, reject) => setTimeout(resolve, 5000));         
                } else {
                    // we're in dev mode. Get config from file, and use empty 
                    // middleware for authentication.
                    const userConfigFile = path.join(__dirname, '../user-config.json');
                    if (!fs.existsSync(userConfigFile)) {
                        console.log('Unable to find the user configuration: please fill out the information in ' + userConfigFile);
                        process.exit(1);
                    }

                    let userConfig: {
                        IotHubConnectionString: string;
                    } = require(userConfigFile);

                    return new Config(
                        userConfig.IotHubConnectionString,
                        (req, res, next) => next()); // empty middleware
                }
            }
        } while (!result);
    }
}

interface ConfigSettings {
    iotHubConnStr: string;
    loginUrl: string;
    mongoUri: string;
    sessionSecret: string;
    'device-management': {
        logLevel: string;
        consoleReporting: string;
    };
}

interface HalLink {
    href: string;
}

interface HalResponse {
    _links: {
        self: HalLink;
        [rel: string]: HalLink;
    };
}

async function getHal<T>(uri: string) {
    return new Promise<T & HalResponse>((resolve, reject) => {        
        request.get(uri, {json: true}, (err, response, body) => {
            err ? reject(err) : resolve(body);
        });
    });
}