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

botbuilder-simple-authentication

v1.3.0

Published

Authentication middleware for the Microsoft Bot Framework v4 designed to simplify the auth process.

Downloads

34

Readme

botbuilder-simple-authentication

botbuilder-simple-authentication exposes middleware designed to make authentication implementation quick and easy for Bot Framework Adapters.

Table of Contents

  1. Basic Usage
  2. Samples
  3. Configuration Properties
  4. Usage with Express
  5. Using Environment Variables
  6. Custom Scopes
  7. Custom Button Text
  8. Custom Authentication Card
  9. Custom Magic Code HTML
  10. Custom Azure AD Tenant and Resource

Basic Usage

BotAuthenticationMiddleware assumes control of the conversation flow when a user is not authenticated and provides the user's access token and profile after successful login.

The middleware minimally requires 3 configuration properties:

  1. A method that returns whether the user is authenticated or not.
  2. A method that is triggered after the user has logged in successfully that receives the user's access token and profile.
  3. At least one clientId/clientSecret for an application created with a supported provider.

Installation

npm install botbuilder-simple-authentication

Import the botbuilder-simple-authentication module

let simpleAuth = require('botbuilder-simple-authentication');

Create a BotAuthenticationConfiguration

The following example uses in memory storage, but any storage service can be used to store and manage authentication data. BotAuthenticationConfiguration methods can be synchronous or asynchronous.

let storage = new builder.MemoryStorage();
const conversationState = new builder.ConversationState(storage);
adapter.use(conversationState);

const authenticationConfig = {
	isUserAuthenticated: (context) => {
		//if this method returns false, the middleware will take over
		const state = conversationState.get(context);
		return state.authData;
	},
	onLoginSuccess: async (context, accessToken, profile, provider) => {
		//the middleware passes over the access token and profile retrieved for the user
		const state = conversationState.get(context);
		state.authData = { accessToken, profile, provider };
		await context.sendActivity(`Hi there ${profile.displayName}!`);
	},
	facebook: {
		clientId: 'FACEBOOK_CLIENT_ID',
		clientSecret: 'FACEBOOK_CLIENT_SECRET'
	},
	azureADv1: {
		clientId: 'AZURE_AD_V1_CLIENT_ID',
		clientSecret: 'AZURE_AD_V1_CLIENT_SECRET'
	},
	azureADv2: {
		clientId: 'AZURE_AD_V2_CLIENT_ID',
		clientSecret: 'AZURE_AD_V2_CLIENT_SECRET'
	},
	google: {
		clientId: 'GOOGLE_CLIENT_ID',
		clientSecret: 'GOOGLE_CLIENT_SECRET'
	},
	twitter: {
		consumerKey: 'TWITTER_CONSUMER_KEY',
		consumerSecret: 'TWITTER_CONSUMER_SECRET'
	},
	github: {
		clientId: 'GITHUB_CLIENT_ID',
		clientSecret: 'GITHUB_CLIENT_SECRET'
	}
};

Implement the BotAuthenticationMiddleware

Create a new instance of the middleware, passing in the BotAuthenticationConfiguration along with your server (compatible with Restify or Express).

adapter.use(new simpleAuth.BotAuthenticationMiddleware(server, authenticationConfig));

Create an Application with a Supported Provider

Navigate to a supported provider's developer site listed below and create a new application. Add the appropriate Redirect URL to your app's approved redirect urls, then copy the clientId and clientSecret used to create the BotAuthenticationConfiguration.

| Supported Providers | Redirect URL | Developer Site | | ------------------- | ---------------------------------------- | -------------------------------------- | | Facebook | {BASE_URL}/auth/facebook/callback | https://developers.facebook.com/apps | | AzureADv1 | {BASE_URL}/auth/azureAD/callback | https://apps.dev.microsoft.com | | AzureADv2 | {BASE_URL}/auth/azureAD/callback | https://apps.dev.microsoft.com | | Google | {BASE_URL}/auth/google/callback | https://console.cloud.google.com/home | | Twitter | {BASE_URL}/auth/twitter/callback | https://apps.twitter.com | | GitHub | {BASE_URL}/auth/github/callback | https://github.com/settings/developers |

Samples

The samples folder contains basic examples with minimal configuration and advanced examples that implement each of the optional configuration properties, for both JavaScript and TypeScript.

Configuration Properties

BotAuthenticationConfiguration

| Property | Constraint | Type | Description | | ---------------------------------- | ------------- | ----------------------------------------------------------------------------------- | -----------------------------| | isUserAuthenticated | Required | (context: TurnContext) => boolean | Runs each converation turn. The middleware will prevent the bot logic from running when it returns false. | | onLoginSuccess | Required | (context: TurnContext, accessToken: string, profile: any, provider: string) => void | Runs when the user inputs the correct magic code. The middleware passes the user's access token and profile. | | onLoginFailure | Optional | (context: TurnContext, provider: string) => void | Runs when the user inputs an incorrect magic code. The middleware will force another login attempt by default. | | customAuthenticationCardGenerator | Optional | (context: TurnContext, authorizationUris: {}[]) => Partial< Activity > | Overrides the default Authentication Card. The middleware supplies the authorization uris necessary to build the card. | | customMagicCodeRedirectEndpoint | Optional | string | Overrides the default magic code display page. The server endpoint provided will receive a redirect with the magic code in the query string. | | noUserFoundMessage | Optional | string | Message sent on first conversation turn where the user is not authenticated, immediately prior to the Authentication Card. | | facebook | Optional | DefaultProviderConfiguration | Configuration object that enables Facebook authentication. | | azureADv1 | Optional | AzureADConfiguration | Configuration object that enables AzureADv1 authentication. | | azureADv2 | Optional | AzureADConfiguration | Configuration object that enables AzureADv2 authentication. | | google | Optional | DefaultProviderConfiguration | Configuration object that enables Google authentication. | | twitter | Optional | TwitterConfiguration | Configuration object that enables Twitter authentication. | | github | Optional | DefaultProviderConfiguration | Configuration object that enables GitHub authentication. |

DefaultProviderConfiguration

| Property | Constraint | Type | Description | | ------------------------------- | ------------- | --------------------- | ------------------------------------------------------------------------------------ | | clientId | Required | string | Client Id taken from the provider's authentication application. | | clientSecret | Required | string | Client Secret taken from the provider's authentication application. | | scopes | Optional | string[] | Scopes that the user will be asked to consent to as part of the authentication flow. | | buttonText | Optional | string | Text displayed inside the button that triggers the provider's authentication flow. |

AzureADConfiguration

| Property | Constraint | Type | Description | | ------------------------------- | ------------- | --------------------- | ------------------------------------------------------------------------------------ | | clientId | Required | string | Application Id taken from the Microsoft Application Registration Portal. | | clientSecret | Required | string | Application Secret taken from the Microsoft Application Registration Portal. | | scopes | Optional | string[] | Scopes that the user will be asked to consent to as part of the authentication flow. | | buttonText | Optional | string | Text displayed inside the button that triggers the provider's authentication flow. | | tenant | Optional | string | Organizational tenant domain. | | resource | Optional | string | Identifier of the WebAPI that your client wants to access on behalf of the user |

TwitterConfiguration

| Property | Constraint | Type | Description | | ------------------------------- | ------------- | --------------------- | ------------------------------------------------------------------------------------ | | consumerKey | Required | string | Consumer Key taken from the Twitter Application Management page. | | consumerSecret | Required | string | Consumer Secret taken from the Twitter Application Management page. | | buttonText | Optional | string | Text displayed inside the button that triggers the provider's authentication flow. |

Usage With Express

IMPORTANT - For use with Express, the middleware must be instantiated before the adapter.processActivity() statement.

Express Application

Create an Express Application

let express = require('express');
let app = express();

Implement the BotAuthenticationMiddleware

adapter.use(new simpleAuth.BotAuthenticationMiddleware(app, authenticationConfig));

Express Router

Create an Express Application

let express = require('express');
let app = express();

Create a Router

let router = express.Router();
app.use('/', router);

Implement the BotAuthenticationMiddleware

adapter.use(new simpleAuth.BotAuthenticationMiddleware(router, authenticationConfig));

Using Environment Variables

Provider clientIds and clientSecrets can be set via environment variables and do not have to be set in provider configuration objects.

| BotAuthenticationConfiguration Property | Environment Variable | | ---------------------------------------- | ------------------------------------------ | | facebook.clientId | FACEBOOK_CLIENT_ID | | facebook.clientSecret | FACEBOOK_CLIENT_SECRET | | azureADv1.clientId | AZURE_AD_V1_CLIENT_ID | | azureADv1.clientSecret | AZURE_AD_V1_CLIENT_SECRET | | azureADv2.clientId | AZURE_AD_V2_CLIENT_ID | | azureADv2.clientSecret | AZURE_AD_V2_CLIENT_SECRET | | google.clientId | GOOGLE_CLIENT_ID | | google.clientSecret | GOOGLE_CLIENT_SECRET | | twitter.consumerKey | TWITTER_CONSUMER_KEY | | twitter.consumerSecret | TWITTER_CONSUMER_SECRET | | github.clientId | GITHUB_CLIENT_ID | | github.clientSecret | GITHUB_CLIENT_SECRET |

Example .env

FACEBOOK_CLIENT_ID = {VALUE}
FACEBOOK_CLIENT_SECRET = {VALUE}
AZURE_AD_V2_CLIENT_ID = {VALUE}
AZURE_AD_V2_CLIENT_SECRET = {VALUE}

Example BotAuthenticationConfiguration

let storage = new builder.MemoryStorage();
const conversationState = new builder.ConversationState(storage);
adapter.use(conversationState);

const authenticationConfig = {
	isUserAuthenticated: (context) => {
		//if this method returns false, the middleware will take over
		const state = conversationState.get(context);
		return state.authData;
	},
	onLoginSuccess: async (context, accessToken, profile, provider) => {
		//the middleware passes over the access token and profile retrieved for the user
		const state = conversationState.get(context);
		state.authData = { accessToken, profile, provider };
		await context.sendActivity(`Hi there ${profile.displayName}!`);
	},
};

Custom Scopes

Each provider declared in the BotAuthenticationConfiguration object except for Twitter has an optional scope property that accepts an array of strings (Twitter scopes are set in the Twitter Application Management page). If custom scopes aren't provided, the following scopes are used by default:

| Provider | Scopes | | ------------------------ | -------------------------------------------- | | Facebook | public_profile | | AzureADv1 | User.Read | | AzureADv2 | profile | | Google | https://www.googleapis.com/auth/plus.login | | GitHub | user |

Default Scopes

facebook: {
	clientId: 'FACEBOOK_CLIENT_ID',
	clientSecret: 'FACEBOOK_CLIENT_SECRET'
}

Example Custom Scopes

facebook: {
	clientId: 'FACEBOOK_CLIENT_ID',
	clientSecret: 'FACEBOOK_CLIENT_SECRET'
	scopes: ['public_profile', 'email', 'user_likes']
}

Custom Button Text

Each provider declared in the BotAuthenticationConfiguration object has an optional buttonText property that accepts a string. If custom button text isn't provided, the following strings are used by default:

| Provider | Button Text | | ------------------------ | ------------------------------------------ | | Facebook | Log in with Facebook | | AzureADv1 | Log in with Microsoft | | AzureADv2 | Log in with Microsoft | | Google | Log in with Google+ | | Twitter | Log in with Twitter | | GitHub | Log in with GitHub |

Default Button Text

facebook: {
	clientId: 'FACEBOOK_CLIENT_ID',
	clientSecret: 'FACEBOOK_CLIENT_SECRET'
}

Example Custom Button Text

facebook: {
	clientId: 'FACEBOOK_CLIENT_ID',
	clientSecret: 'FACEBOOK_CLIENT_SECRET'
	buttonText: 'Facebook'
}

Custom Authentication Card

The customAuthenticationCardGenerator property is used to override the default card. The method receives the authorization uris for each provider set in the BotAuthenticationConfiguration and is responsible for navigating the user to one of them.

Default Authentication Card

Example Custom Authentication Card

customAuthenticationCardGenerator: async (context, authorizationUris) => {
	let cardActions = [];
	let buttonTitle;
	authorizationUris.map((auth) => {
		if (auth.provider === 'azureADv1' || auth.provider === 'azureADv2') {
			buttonTitle = 'Microsoft';
		} else if (auth.provider === 'facebook') {
			buttonTitle = 'Facebook';
		} else if (auth.provider === 'google') {
			buttonTitle = 'Google';
		} else if (auth.provider === 'twitter') {
			buttonTitle = 'Twitter';
		} else if (auth.provider === 'github') {
			buttonTitle = 'GitHub';
		}
		cardActions.push({ type: 'openUrl', value: auth.authorizationUri, title: buttonTitle });
	});
	let card = builder.CardFactory.heroCard('', ['https://qualiscare.com/wp-content/uploads/2017/08/default-user.png'], cardActions);
	let authMessage = builder.MessageFactory.attachment(card);
	return authMessage;
}

Custom Magic Code HTML

The BotAuthenticationConfiguration object has an optional customMagicCodeRedirectEndpoint property used to override the default magic code HTML display and create a custom page.

In order to fully implement a custom page, the server passed to the middleware will need to expose an endpoint that is referenced by the customMagicCodeRedirectEndpoint property. BotAuthenticationMiddleware adds the queryParser middleware to the restify server and redirects to this endpoint with the magic code in the query string, so the code is accessible via req.query.magicCode. The server is responsible for serving an HTML page capable of displaying the magic code.

Default Magic Code HTML

Example Custom Magic Code HTML

In the example below, restify exposes an endpoint that serves up an html file expecting a magic code in the URL's hash. The customMagicCodeRedirectEndpoint property is set to another endpoint that parses the magic code and sends it in the hash to the html file.

/app.js

let storage = new builder.MemoryStorage();
const conversationState = new builder.ConversationState(storage);
adapter.use(conversationState);

const authenticationConfig = {
	isUserAuthenticated: (context) => {
		//if this method returns false, the middleware will take over
		const state = conversationState.get(context);
		return state.authData;
	},
	onLoginSuccess: async (context, accessToken, profile, provider) => {
		//the middleware passes over the access token and profile retrieved for the user
		const state = conversationState.get(context);
		state.authData = { accessToken, profile, provider };
		await context.sendActivity(`Hi there ${profile.displayName}!`);
	},
	facebook: {
		clientId: 'FACEBOOK_CLIENT_ID',
		clientSecret: 'FACEBOOK_CLIENT_SECRET'
	},
	customMagicCodeRedirectEndpoint: '/customCode'
};

server.get('/customCode', (req, res, next) => {
	//simple redirect where we set the code in the hash and pull it down on the webpage that restify will serve at this endpoint
	let magicCode = req.query.magicCode;
	let hashedUrl = `/renderCustomCode#${magicCode}`;
	res.redirect(302, hashedUrl, next);
});

server.get('/renderCustomCode', restify.plugins.serveStatic({
	//need a public folder in the same directory as this file that contains an index.html page expecting a hash
	'directory': path.join(__dirname, 'public'),
	'file': 'index.html'
}));

/public/index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Authentication Bot - Login Success</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
        crossorigin="anonymous">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp"
        crossorigin="anonymous">
    <style>
        body {
            -moz-user-select: none;
            -webkit-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }
        .navbar {
            border-bottom: 3px solid #FFDB00;
            font-variant: small-caps
        }
        body {
            background-color: #EEEEEE;
        }
        .jumbotron {
            border: 3px solid #FFDB00;
            background-color: #333333;
            color: white;
        }
        h3 {
            font-variant: small-caps;
        }
        logo {
            margin-bottom: 1em;
        }
        #magic_code {
            font-size: 2em;
            font-family: monospace;
            font-weight: bold;
            -moz-user-select: all;
            -webkit-user-select: text;
            -ms-user-select: text;
            user-select: all;
        }
        .jumbotron {
            text-align: center;
        }
        .title {
            color: white !important;
        }
    </style>
</head>
<body>
    <nav class="navbar navbar-inverse navbar-static-top">
        <div class="container">
            <div class="navbar-header">
                <a class="navbar-brand logo">
                    <kbd><img src="http://icons.iconarchive.com/icons/paomedia/small-n-flat/256/key-icon.png" width="27" height="25" alt="">
                </a>
                <a class="navbar-brand title">Authentication Bot</a>
            </div>
        </div>
    </nav>
    <div class="container">
        <div class="jumbotron">
            <h3>Please type the magic code below into your conversation with the bot</h3>
        </div>
        <div class="jumbotron">
            <div id="magic_code"></div>
        </div>
    </div>
    <script type="text/javascript">
        document.getElementById("magic_code").innerText = (window.location.hash || '').replace('#', '');
    </script>
</body>
</html>

Custom Azure AD Tenant and Resource

The AzureADv1 and AzureADv2 providers declared in the BotAuthenticationConfiguration object have an optional tenant property that accepts a string. If a custom tenant isn't provided, the common endpoint is used by default:

Default Tenant

azureADv2: {
	clientId: 'AZURE_AD_V2_CLIENT_ID',
	clientSecret: 'AZURE_AD_V2_CLIENT_SECRET'
}

Example Custom Tenant (V1 or V2)

azureADv2: {
	clientId: 'AZURE_AD_V2_CLIENT_ID',
	clientSecret: 'AZURE_AD_V2_CLIENT_SECRET',
	tenant: 'microsoft.onmicrosoft.com'
}

The AzureADv1 provider declared in the BotAuthenticationConfiguration object has an optional resource property that accepts a string. If a custom resource isn't provided, the Microsoft Graph is used by default:

Default Resource

azureADv1: {
	clientId: 'AZURE_AD_V1_CLIENT_ID',
	clientSecret: 'AZURE_AD_V1_CLIENT_SECRET'
}

Example Custom Resource (V1 Only)

azureADv1: {
	clientId: 'AZURE_AD_V1_CLIENT_ID',
	clientSecret: 'AZURE_AD_V1_CLIENT_SECRET',
	//VSTS API resource
	resource: '499b84ac-1321-427f-aa17-267ca6975798'
}