From 88c77b4caa61e26889ea8c865a9b2a9edca40778 Mon Sep 17 00:00:00 2001 From: TJ Durnford Date: Sun, 16 Feb 2020 11:25:55 -0800 Subject: [PATCH] Added OBO sample --- lerna.json | 3 +- .../component/src/hooks/useSetNotification.js | 1 + .../.dockerignore | 6 + .../Dockerfile | 47 + .../Dockerfile-run | 28 + .../README.md | 274 + .../app/.env | 1 + .../app/.gitignore | 23 + .../app/package-lock.json | 15167 ++++++++++++++++ .../app/package.json | 36 + .../app/public/favicon.ico | Bin 0 -> 3870 bytes .../public/images/BotServices-Translucent.svg | 1 + .../app/public/images/BotServices.png | Bin 0 -> 3953 bytes .../app/public/images/BotServices.svg | 1 + .../app/public/images/Microsoft-32px.png | Bin 0 -> 1208 bytes .../app/public/images/Microsoft-64px.png | Bin 0 -> 1300 bytes .../images/Microsoft-Graph-64px-DDD-White.png | Bin 0 -> 2322 bytes .../images/Microsoft-Graph-64px-EEE-White.png | Bin 0 -> 2269 bytes .../public/images/Microsoft-Graph-64px.png | Bin 0 -> 1543 bytes .../app/public/index.html | 56 + .../app/public/manifest.json | 15 + .../app/serve.json | 10 + .../app/src/App.css | 7 + .../app/src/App.js | 17 + .../app/src/index.css | 4 + .../app/src/index.js | 13 + .../app/src/oauth/Composer.js | 148 + .../app/src/oauth/Context.js | 4 + .../app/src/oauth/fetchProfileDisplayName.js | 15 + .../src/oauth/fetchProfilePhotoAsBase64.js | 24 + .../app/src/serviceWorker.js | 128 + .../app/src/setupProxy.js | 6 + .../MicrosoftGraphProfileMenu/ProfileMenu.css | 114 + .../src/ui/MicrosoftGraphProfileMenu/index.js | 81 + .../OnBehalfOfAttachment/index.css | 71 + .../Attachments/OnBehalfOfAttachment/index.js | 112 + .../WebChat/Notifications/AppSignInToast.js | 43 + .../WebChat/Notifications/BotSignInToast.js | 125 + .../TraditionalBotAuthenticationToast.js | 35 + .../src/ui/WebChat/Notifications/index.css | 53 + .../app/src/ui/WebChat/Notifications/index.js | 3 + .../app/src/ui/WebChat/index.css | 10 + .../app/src/ui/WebChat/index.js | 86 + .../app/src/utils/fetchJSON.js | 15 + .../app/src/utils/requiresInteraction.js | 11 + .../azure-pipelines.yml | 50 + .../bot/.eslintrc.js | 17 + .../bot/.gitignore | 2 + .../bot/README.md | 109 + .../bot/bots/authBot.js | 36 + .../bot/bots/dialogBot.js | 45 + .../new-rg-parameters.json | 42 + .../preexisting-rg-parameters.json | 39 + .../template-with-new-rg.json | 183 + .../template-with-preexisting-rg.json | 154 + .../bot/dialogs/logoutDialog.js | 42 + .../bot/dialogs/mainDialog.js | 129 + .../bot/index.js | 74 + .../bot/oAuthHelpers.js | 100 + .../bot/package-lock.json | 5459 ++++++ .../bot/package.json | 34 + .../bot/simple-graph-client.js | 110 + .../docker-compose.yml | 16 + .../e.soo-on-behalf-of-authentication/init.sh | 9 + .../rest-api/.gitignore | 2 + .../rest-api/package-lock.json | 1287 ++ .../rest-api/package.json | 22 + .../rest-api/src/encodeBase64URL.js | 7 + .../rest-api/src/exchangeAccessToken.js | 42 + .../rest-api/src/generateDirectLineToken.js | 17 + .../rest-api/src/index.js | 55 + .../src/routes/aad/oauth/authorize.js | 30 + .../rest-api/src/routes/aad/oauth/callback.js | 45 + .../aad/oauth/createPKCECodeChallenge.js | 14 + .../aad/oauth/createPKCECodeVerifier.js | 15 + .../rest-api/src/routes/aad/settings.js | 12 + .../rest-api/src/routes/botMessages.js | 28 + .../rest-api/src/routes/directLine/token.js | 9 + .../src/utils/createHTMLWithPostMessage.js | 4 + .../rest-api/src/utils/fetchJSON.js | 18 + .../rest-api/src/utils/generateOAuthState.js | 12 + .../sshd_config | 16 + 82 files changed, 25078 insertions(+), 1 deletion(-) create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/.dockerignore create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/Dockerfile create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/Dockerfile-run create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/README.md create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/.env create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/.gitignore create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/package-lock.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/package.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/favicon.ico create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/images/BotServices-Translucent.svg create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/images/BotServices.png create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/images/BotServices.svg create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/images/Microsoft-32px.png create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/images/Microsoft-64px.png create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/images/Microsoft-Graph-64px-DDD-White.png create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/images/Microsoft-Graph-64px-EEE-White.png create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/images/Microsoft-Graph-64px.png create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/index.html create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/public/manifest.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/serve.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/App.css create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/App.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/index.css create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/index.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/oauth/Composer.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/oauth/Context.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/oauth/fetchProfileDisplayName.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/oauth/fetchProfilePhotoAsBase64.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/serviceWorker.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/setupProxy.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/MicrosoftGraphProfileMenu/ProfileMenu.css create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/MicrosoftGraphProfileMenu/index.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/WebChat/Attachments/OnBehalfOfAttachment/index.css create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/WebChat/Attachments/OnBehalfOfAttachment/index.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/WebChat/Notifications/AppSignInToast.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/WebChat/Notifications/BotSignInToast.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/WebChat/Notifications/TraditionalBotAuthenticationToast.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/WebChat/Notifications/index.css create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/WebChat/Notifications/index.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/WebChat/index.css create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/ui/WebChat/index.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/utils/fetchJSON.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app/src/utils/requiresInteraction.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/azure-pipelines.yml create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/.eslintrc.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/.gitignore create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/README.md create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/bots/authBot.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/bots/dialogBot.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/deploymentTemplates/new-rg-parameters.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/deploymentTemplates/preexisting-rg-parameters.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/deploymentTemplates/template-with-new-rg.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/deploymentTemplates/template-with-preexisting-rg.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/dialogs/logoutDialog.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/dialogs/mainDialog.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/index.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/oAuthHelpers.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/package-lock.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/package.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/bot/simple-graph-client.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/docker-compose.yml create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/init.sh create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/.gitignore create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/package-lock.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/package.json create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/encodeBase64URL.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/exchangeAccessToken.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/generateDirectLineToken.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/index.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/routes/aad/oauth/authorize.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/routes/aad/oauth/callback.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/routes/aad/oauth/createPKCECodeChallenge.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/routes/aad/oauth/createPKCECodeVerifier.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/routes/aad/settings.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/routes/botMessages.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/routes/directLine/token.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/utils/createHTMLWithPostMessage.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/utils/fetchJSON.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/utils/generateOAuthState.js create mode 100644 samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/sshd_config diff --git a/lerna.json b/lerna.json index 74ed1239e6..bccf06bfd7 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,8 @@ { "lerna": "3.19.0", "packages": [ - "packages/*" + "packages/*", + "samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/app" ], "version": "0.0.0" } diff --git a/packages/component/src/hooks/useSetNotification.js b/packages/component/src/hooks/useSetNotification.js index 5c98e252db..f5062c4a40 100644 --- a/packages/component/src/hooks/useSetNotification.js +++ b/packages/component/src/hooks/useSetNotification.js @@ -1,5 +1,6 @@ import useWebChatUIContext from './internal/useWebChatUIContext'; export default function useSetNotification() { + console.log(useWebChatUIContext()); return useWebChatUIContext().setNotification; } diff --git a/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/.dockerignore b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/.dockerignore new file mode 100644 index 0000000000..5fc54870bb --- /dev/null +++ b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/.dockerignore @@ -0,0 +1,6 @@ +/app/build +/app/node_modules +/bot/.env +/bot/node_modules +/rest-api/.env +/rest-api/node_modules diff --git a/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/Dockerfile b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/Dockerfile new file mode 100644 index 0000000000..cdb9f32104 --- /dev/null +++ b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/Dockerfile @@ -0,0 +1,47 @@ +# This container is for simplifying CI when using Azure Pipelines + +# The first builder image will build HTML and JavaScript code out of the create-react-app project +FROM node:12 AS builder-react +WORKDIR /var/build/react/ + +# Copy the web app code to /var/build/react/ +# We excluded /.env and /node_modules/ via .dockerignore +ADD app/ /var/build/react/ + +# Doing a fresh "npm install" on build to make sure the image is reproducible +RUN npm ci + +# Build the web app code via create-react-app and react-scripts +RUN npm run build + +# The second builder image will aggregate all code into a single Docker image for export +FROM node:12 + +# Copy the bot code to /var/bot/ +ADD bot/ /var/build/bot/ + +# Copy the web server code to /var/web/ +ADD rest-api/ /var/build/web/ + +# Copy SSH configuration and startup script to /var/ +# Adopted from https://github.com/Azure-App-Service/node/blob/master/10.14/sshd_config +ADD init.sh /var/build/ +ADD sshd_config /var/build/ + +# Copy static React app to /var/web/public/, to be consumed by web server +COPY --from=builder-react /var/build/react/build/ /var/build/web/public/ + +# Doing a fresh "npm install" on build to make sure the image is reproducible +WORKDIR /var/build/bot/ +RUN npm ci + +# Doing a fresh "npm install" on build to make sure the image is reproducible +WORKDIR /var/build/web/ +RUN npm ci + +# Pack "concurrently" to make sure the image is reproducible +WORKDIR /var/build/ +RUN npm install concurrently@5.0.0 + +# Pack the build content as a "build.tgz" and export it out +RUN tar -cf build.tgz * diff --git a/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/Dockerfile-run b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/Dockerfile-run new file mode 100644 index 0000000000..62e630c836 --- /dev/null +++ b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/Dockerfile-run @@ -0,0 +1,28 @@ +# This is the container for running the demo under Azure Web App +FROM node:12 AS builder-web + +# Expose both port 80 and 2222 (SSH for Azure Web App) +EXPOSE 80 2222 + +WORKDIR /var/ + +# Extract build image to /var/ +ADD build.tgz /var/ + +# Setup OpenSSH for debugging thru Azure Web App +# https://docs.microsoft.com/en-us/azure/app-service/containers/app-service-linux-ssh-support#ssh-support-with-custom-docker-images +# https://docs.microsoft.com/en-us/azure/app-service/containers/tutorial-custom-docker-image +ENV SSH_PASSWD "root:Docker!" +ENV SSH_PORT 2222 +RUN \ + apt-get update \ + && apt-get install -y --no-install-recommends dialog \ + && apt-get update \ + && apt-get install -y --no-install-recommends openssh-server \ + && echo "$SSH_PASSWD" | chpasswd \ + && mv /var/sshd_config /etc/ssh/ \ + && mv /var/init.sh /usr/local/bin/ \ + && chmod u+x /usr/local/bin/init.sh + +# Set up entrypoint +ENTRYPOINT init.sh diff --git a/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/README.md b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/README.md new file mode 100644 index 0000000000..62b0e76564 --- /dev/null +++ b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/README.md @@ -0,0 +1,274 @@ +# Single sign-on demo for On Behalf Of Authentication using OAuth + + +# Description + +In this demo, we will show you how to authorize a user to access resources on an enterprise app with a bot using [Microsoft Graph](https://developer.microsoft.com/en-us/graph/). + +> When dealing with personal data, please respect user privacy. Follow platform guidelines and post your privacy statement online. + +## Background + +Different companies may use different access delegation technologies to protect their resources. In our demo, we are targeting authorization through [OAuth 2.0](https://tools.ietf.org/html/rfc6749)). + +Although OAuth and [OpenID](https://openid.net/) are often related to each other, they solve different problems. OAuth is for authorization and access delegation, while OpenID is for authentication and user identity. + +Instead of OpenID, most enterprise apps use OAuth plus a user profile API to identify an individual user. In this demo, we will demonstrate how to use OAuth to obtain access to user profile API and use the API to identifying the accessor. + +This demo does not include any threat models and is designed for educational purposes only. When you design a production system, threat-modelling is an important task to make sure your system is secure and provide a way to quickly identify potential source of data breaches. IETF [RFC 6819](https://tools.ietf.org/html/rfc6819) and [OAuth 2.0 for Browser-Based Apps](https://tools.ietf.org/html/draft-ietf-oauth-browser-based-apps-01#section-9) is a good starting point for threat-modelling when using OAuth 2.0. + +# Test out the hosted sample + +- [Try out MockBot](https://webchat-sample-obo.azurewebsites.net/) + +# How to run locally + +This demo integrates with multiple services. There are multiple services you need to setup in order to host the demo. + +1. [Clone the code](#clone-the-code) +2. [Setup OAuth via Azure Active Directory for the Web App](#setup-oauth-via-azure-active-directory-for-the-web-app) +3. [Setup OAuth via Azure Active Directory for the Bot](#setup-oauth-via-azure-active-directory-for-the-bot) +4. [Setup Azure Bot Services](#setup-azure-bot-services) +5. [Prepare and run the code](#prepare-and-run-the-code) + +## Clone the code + +To host this demo, you will need to clone the code and run locally. + +1. Clone this repository +1. Create two files for environment variables, `/bot/.env` and `/rest-api/.env` + - In `/rest-api/.env`: + - Write `AAD_OAUTH_REDIRECT_URI=http://localhost:3000/api/aad/oauth/callback` + - When Azure Active Directory completes the authorization flow, it will send the browser to this URL. This URL must be accessible by the browser from the end-user machine + +## Setup OAuth via Azure Active Directory for the Web App + +- Go to your [Azure Active Directory](https://ms.portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview) +- Create a new application + 1. Select "App registrations" + 2. Click "New registration" + 3. Fill out "Name", for example, "Web Chat On-Behalf-Of Sample" + 4. In "Redirect URI (optional)" section, add a new entry + 1. Select "Web" as type + 2. Enter `http://localhost:3000/api/aad/oauth/callback` as the redirect URI + - This must match `AAD_OAUTH_REDIRECT_URI` in `/rest-api/.env` we saved earlier + - Click "Register" +- Save the client and tenant ID + 1. Select the "Overview" blade + 1. On the main pane, copy the content of "Application (client) ID" and "Directory (tenant) ID" to `/rest-api/.env`, it should looks be a GUID + - `AAD_OAUTH_CLIENT_ID=12345678abcd-1234-5678-abcd-12345678abcd` + - `AAD_OAUTH_TENANT_ID=abcd1234-abcd-1234-efgh-5678abcdefgh` +- Update App Registration Manifest + 1. Select the "Manifest" blade + 1. Set `accessTokenAcceptedVersion` to `2` + 2. Set `oauth2AllowImplicitFlow` to `true` +- Enable ID Tokens + 1. Select the "Authentication" Blade + 1. Towards the bottom of the "Web" section, check the box next to "ID tokens" under "Implicit grant" + +## Setup OAuth via Azure Active Directory for the Bot +- Go to your [Azure Active Directory](https://ms.portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/Overview) +- Create a new application + 1. Select "App registrations" + 2. Click "New registration" + 3. Fill out "Name", for example, "Web Chat On-Behalf-Of Sample" + 4. In "Redirect URI (optional)" section, add a new entry + 1. Select "Web" as type + 2. Enter `https://token.botframework.com/.auth/web/redirect` as the redirect URI + - Click "Register" +- Update App Registration Manifest + 1. Select the "Manifest" blade + 1. Set `accessTokenAcceptedVersion` to `2` +- Create a new client secret + 1. Select the "Certificates & secretes" blade + 2. Click the "New client secret" +- Add a scope and client application + 1. Select the "Expose an API" blade + 1. Add a new scope for the bot + 1. Click the "Add a scope" button under "Scopes defined by this API" + 1. Add a scope name + 2. Set "Who can consent?" to "Admins and users" + 3. Add an admin consent display name + 4. Add an admin consent description + 5. Click "Add scope" + 2. Add a client application + 1. Click the "Add a client application" under "Authorized client applications" + 1. Set the client id to the Web Applications client id + 2. Check the box next to the scope we added in the previous step under "Authorized scopes" + 3. Click "Add application" + + + +## Setup Azure Bot Services + +> We prefer using [Bot Channel Registration](https://ms.portal.azure.com/#create/Microsoft.BotServiceConnectivityGalleryPackage) during development. This will help you diagnose problems locally without deploying to the server and speed up development. + +You can follow our instructions on how to [setup a new Bot Channel Registration](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration?view=azure-bot-service-3.0). + +1. Save the Microsoft App ID and password to `/bot/.env` + - `MICROSOFT_APP_ID=12345678-1234-5678-abcd-12345678abcd` + - `MICROSOFT_APP_PASSWORD=a1b2c3d4e5f6` +1. Save the Web Chat secret to `/rest-api/.env` + - `DIRECT_LINE_SECRET=a1b2c3.d4e5f6g7h8i9j0` + +> When you are building your production bot, never expose your Web Chat or Direct Line secret to the client. Instead, you should use the secret to generate a limited token and send it to the client. For information, please refer [to this page on how to generate a Direct Line token](https://docs.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-direct-line-3-0-authentication?view=azure-bot-service-4.0#generate-token) and [Enhanced Direct Line Authentication feature](https://blog.botframework.com/2018/09/25/enhanced-direct-line-authentication-features/). + +During development, you will run your bot locally. Azure Bot Services will send activities to your bot through a public URL. You can use [ngrok](https://ngrok.com/) to expose your bot server on a public URL. + +1. Run `ngrok http -host-header=localhost:3978 3978` +1. Update your Bot Channel Registration. You can use [Azure CLI](https://aka.ms/az-cli) or [Azure Portal](https://portal.azure.com) + - Via Azure CLI + - Run `az bot update --resource-group --name --subscription --endpoint "https://a1b2c3d4.ngrok.io/api/messages"` + - Via Azure Portal + - Browse to your Bot Channel Registration + - Select "Settings" + - In "Configuration" section, set "Messaging Endpoint" to `https://a1b2c3d4.ngrok.io/api/messages` + +## Prepare and run the code + +1. Under `app`, `bot`, and `rest-api` folder, run the following: + 1. `npm install` + 1. `npm start` +1. Browse to http://localhost:3000/ to start the demo + +# Things to try out + +- Notice there are two sign-in buttons on top-right hand corner + - After signed in, both the website and the bot get your sign in information +- Type, "where are my packages" in Web Chat + - If not signed in, the bot will present a sign-in button + - If signed in, the bot will answer the question +- Type, "bye" in Web Chat + - If signed in, the bot will sign you out + +# Code + +- `/app/` is the React app built using `create-react-app` scaffold +- `/bot/` is the bot server +- `/rest-api/` is the REST API for handling OAuth requests + - `GET /api/aad/oauth/authorize` will redirect to Azure AD OAuth authorize page at https://login.microsoftonline.com/12345678-1234-5678-abcd-12345678abcd/oauth2/v2.0/authorize + - `GET /api/aad/oauth/callback` will handle callback from Azure AD OAuth + - `GET /api/aad/settings` will send Azure AD OAuth settings to the React app + - `GET /api/directline/token` will generate a new Direct Line token for the React app + - It will serve React app as a static content + - During development-time, it will also serve the bot server via `/api/messages` + - To enable this feature, add `PROXY_BOT_URL=http://localhost:3978` to `/web/.env` + - This will forward all traffic from `https://a1b2c3d4.ngrok.io/api/messages` to `https://localhost:3978/api/messages` + +# Overview + +This sample includes multiple parts: + +- A basic web page with sign in and sign out button, coded with React +- Web Chat integrated, coded with pure JavaScript +- Wiring between the web page and Web Chat through DOM events + - When web page sign in, it should emit DOM event `accesstokenchange` with `{ data: { accessToken, provider } }` + - When the bot ask for sign out, Web Chat will emit DOM event `signout` +- For bot, OAuth access token is piggybacked on every user-initiated activity through `channelData.oauthAccessToken` and `channelData.oauthProvider` + +## Assumptions + +- Developer has an existing enterprise web app that uses OAuth to access protected resources + - We assume the OAuth access token lives in the browser's memory and is accessible through JavaScript + - Access token can live in browser memory but must be secured during transmit through the use of TLS + - More about security considerations can be found at [IETF RFC 6749 Section 10.3](https://tools.ietf.org/html/rfc6749#section-10.3) +- Developer know how to alter existing JavaScript code around their existing UI for OAuth + +## Goals + +- Website and bot conversation supports both anonymous and authenticated access + - Forced page refresh and/or new conversation is not mandated +- End-user is able to sign in through the web page, and is recognized by the bot immediately + - Vice versa, end-user is able to sign in through the bot, and is recognized by the web page immediately +- End-user is able to sign in through the web page and sign out though the bot + - Vice versa, end-user is able to sign in through the bot and sign out through the web page + +## Organization of JavaScript code + +In our demo, we built an enteprise single-page app using React. Then, we use a ``; diff --git a/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/utils/fetchJSON.js b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/utils/fetchJSON.js new file mode 100644 index 0000000000..9814c30216 --- /dev/null +++ b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/utils/fetchJSON.js @@ -0,0 +1,18 @@ +const fetch = require('node-fetch'); + +// Helper function for fetching network resource as JSON +module.exports = async function fetchJSON(url, options) { + const res = await fetch(url, { + ...options, + headers: { + ...options.headers, + accept: 'application/json' + } + }); + + if (!res.ok) { + throw new Error(`Failed to fetch JSON from server due to ${res.status}`); + } + + return await res.json(); +}; diff --git a/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/utils/generateOAuthState.js b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/utils/generateOAuthState.js new file mode 100644 index 0000000000..2b5b681e66 --- /dev/null +++ b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/rest-api/src/utils/generateOAuthState.js @@ -0,0 +1,12 @@ +const crypto = require('crypto'); + +// For additional security, GitHub recommends using a "state" parameter that is only known to the servers and never passed to the client. +// We are using a well-known salt to create the "state" parameter for distributed knowledge. +module.exports = function generateOAuthState(seed, salt) { + const hash = crypto.createHash('sha384'); + + hash.update(seed); + hash.update(salt); + + return hash.digest('hex').substr(0, 10); +}; diff --git a/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/sshd_config b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/sshd_config new file mode 100644 index 0000000000..04b53f9524 --- /dev/null +++ b/samples/07.advanced-web-chat-apps/e.soo-on-behalf-of-authentication/sshd_config @@ -0,0 +1,16 @@ +# This is ssh server systemwide configuration file. +# +# /etc/sshd_config + +Port SSH_PORT +ListenAddress 0.0.0.0 +LoginGraceTime 180 +X11Forwarding yes +Ciphers aes128-cbc,3des-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr +MACs hmac-sha1,hmac-sha1-96 +StrictModes yes +SyslogFacility DAEMON +PasswordAuthentication yes +PermitEmptyPasswords no +PermitRootLogin yes +Subsystem sftp internal-sftp