|
| 1 | +# Sample: Azure SignalR Service integration with Event Grid and Azure Function |
| 2 | + |
| 3 | +A step by step tutorial to build a chat room with real-time online counting using Azure Functions, Event Grid, App Service Authentication, and SignalR Service. |
| 4 | + |
| 5 | +- [Prerequisites](#prerequisites) |
| 6 | +- [Initialize the function app](#initialize-function-app) |
| 7 | +- [Deploy and run function app on Azure](#deploy-to-azure) |
| 8 | +- [Enable authentication on Azure](#enable-authentication) |
| 9 | +- [Build the sample locally](#build-locally) |
| 10 | + |
| 11 | +<a name="prerequisites"></a> |
| 12 | +## Prerequisites |
| 13 | + |
| 14 | +The following softwares are required to build this tutorial. |
| 15 | + |
| 16 | +* [Node.js](https://nodejs.org/en/download/) (Version 10.x) |
| 17 | +* [.NET SDK](https://www.microsoft.com/net/download) (Version 2.x, required for Functions extensions) |
| 18 | +* [Azure Functions Core Tools](https://github.com/Azure/azure-functions-core-tools) (Version 2) |
| 19 | +* [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) |
| 20 | + |
| 21 | +### Create an Azure SignalR Service instance |
| 22 | + |
| 23 | +No matter you want to build and test the Azure Functions app on Azure or locally. The app will access a SignalR Service instance in Azure that needs to be created ahead of time. |
| 24 | + |
| 25 | +1. Create Azure SignalR Service using `az cli` |
| 26 | + |
| 27 | + ```bash |
| 28 | + az signalr create -n <signalr-name> -g <resource-group-name> --sku Free_F1 |
| 29 | + ``` |
| 30 | + |
| 31 | + For more details about creating Azure SignalR Service, see the [tutorial](https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-quickstart-azure-functions-javascript#create-an-azure-signalr-service-instance). |
| 32 | + |
| 33 | +1. After the instance is deployed, open it in the portal and locate its `Settings` page. Change the Service Mode setting to **Serverless**. |
| 34 | + |
| 35 | +  |
| 36 | + |
| 37 | +### Create a Storage account |
| 38 | + |
| 39 | +An Azure Storage account is required by a function app using Event Grid trigger. You will also host the web page for the chat UI using the static websites feature of Azure Storage if you try to deploy the application to Azure. |
| 40 | + |
| 41 | +- Create a storage with kind `StorageV2` using `az cli` |
| 42 | + |
| 43 | + ```bash |
| 44 | + az storage account create -n <storage-account-name> -g <resource-group-name> -l <location> --sku Standard_LRS --kind StorageV2 |
| 45 | + ``` |
| 46 | + |
| 47 | +<a name="initialize-function-app"></a> |
| 48 | +## Initialize the function app |
| 49 | + |
| 50 | +- Git clone the project and open the folder with VS Code |
| 51 | + |
| 52 | + ```bash |
| 53 | + git clone [email protected]:aspnet/AzureSignalR-samples.git |
| 54 | +
|
| 55 | + cd AzureSignalR-samples/samples/EventGridIntegration/javascript |
| 56 | + ``` |
| 57 | + |
| 58 | +### Configure application settings |
| 59 | + |
| 60 | +When running and debugging the Azure Functions runtime locally, application settings are read from **local.settings.json**. Also, you can upload there settings to remote when you try to deploy Function App to Azure. Update this file with the connection string of the SignalR Service instance that you created earlier. |
| 61 | + |
| 62 | +1. Open the file **local.settings.json** and update the settings. |
| 63 | + |
| 64 | + ```json |
| 65 | + { |
| 66 | + "IsEncrypted": false, |
| 67 | + "Values": { |
| 68 | + "AzureSignalRConnectionString": "<signalr-connection-string>", |
| 69 | + "WEBSITE_NODE_DEFAULT_VERSION": "10.14.1", |
| 70 | + "FUNCTIONS_WORKER_RUNTIME": "node", |
| 71 | + "AzureWebJobsStorage": "<Azure-storage-connection-string>", |
| 72 | + "AZURE_STORAGE_CONNECTION_STRING": "<Azure-storage-connection-string>" |
| 73 | + }, |
| 74 | + "Host": { |
| 75 | + "LocalHttpPort": 7071, |
| 76 | + "CORS": "http://127.0.0.1:5500", |
| 77 | + "CORSCredentials": true |
| 78 | + } |
| 79 | + } |
| 80 | + ``` |
| 81 | + |
| 82 | + - Replace `AzureSignalRConnectionString` with Azure SignalR Service connection string. Obtain the value from the **Keys** page in the Azure SignalR Service resource in the Azure portal; either the primary or secondary connection string can be used. |
| 83 | + |
| 84 | +  |
| 85 | + |
| 86 | + - Replace `AzureWebJobsStorage` and `AZURE_STORAGE_CONNECTION_STRING` with connection string of storage created previously. `AzureWebJobsStorage` is used by Event Grid trigger and the `AZURE_STORAGE_CONNECTION_STRING` is used by storage client in codes. |
| 87 | + |
| 88 | +1. Save the file. |
| 89 | + |
| 90 | +1. Open the VS Code command palette (`Ctrl-Shift-P`, macOS: `Cmd-Shift-P`) and select **Azure Functions: Initialize Project for Use with VSCode**. |
| 91 | + |
| 92 | +1. Open the terminal and run `func extensions install` to install all the dependencies. |
| 93 | + |
| 94 | +<a name="deploy-to-azure"></a> |
| 95 | +## Deploy and run function app on Azure |
| 96 | + |
| 97 | +### Deploy function app to Azure |
| 98 | + |
| 99 | +1. In the VS Code, install the [Azure Functions Extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions). |
| 100 | + |
| 101 | +1. Open the VS Code command palette (`Ctrl-Shift-P`, macOS: `Cmd-Shift-P`) and select **Azure Functions: Deploy to Function App**. |
| 102 | + |
| 103 | +1. When prompted, provide the following information. |
| 104 | + |
| 105 | + | Name | Value | |
| 106 | + |---|---| |
| 107 | + | Folder to deploy | Select the main project folder | |
| 108 | + | Subscription | Select your subscription | |
| 109 | + | Function app | Select **Create New Function App** | |
| 110 | + | Function app name | Enter a unique name | |
| 111 | + | Resource group | Select the same resource group as the SignalR Service instance | |
| 112 | + | Storage account | Select the storage account you created earlier | |
| 113 | + |
| 114 | + A new function app is created in Azure and the deployment begins. Wait for the deployment to complete. |
| 115 | + |
| 116 | +### Upload function app local settings |
| 117 | + |
| 118 | +1. Open the VS Code command palette (`Ctrl-Shift-P`, macOS: `Cmd-Shift-P`). |
| 119 | + |
| 120 | +1. Search for and select the **Azure Functions: Upload local settings** command. |
| 121 | + |
| 122 | +1. When prompted, provide the following information. |
| 123 | + |
| 124 | + | Name | Value | |
| 125 | + |---|---| |
| 126 | + | Local settings file | local.settings.json | |
| 127 | + | Subscription | Select your subscription | |
| 128 | + | Function app | Select the previously deployed function app | |
| 129 | + |
| 130 | + Local settings are uploaded to the function app in Azure. If prompted to overwrite existing settings, select **Yes to all**. |
| 131 | + |
| 132 | +### Configure static websites |
| 133 | + |
| 134 | +We use **Static Website** to host the web page. Find the [document](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website) for more details. |
| 135 | + |
| 136 | +1. Enable the static website feature |
| 137 | + |
| 138 | + ```bash |
| 139 | + az storage blob service-properties update --account-name <storage-account-name> --static-website --index-document index.html |
| 140 | + ``` |
| 141 | + |
| 142 | +1. Get the url of function app |
| 143 | + |
| 144 | + ```bash |
| 145 | + hostname=$(az functionapp show -n <function-app-name> -g <resource-group-name> -o tsv --query defaultHostName) |
| 146 | + ``` |
| 147 | + |
| 148 | + Replace the `apiBaseUrl` in `index.html` with the url `https://$(hostname)` |
| 149 | + |
| 150 | + Upload the `index.html` to Azure Storage |
| 151 | + |
| 152 | + ```bash |
| 153 | + az storage blob upload-batch -s content -d \$web --account-name <storage-account-name> |
| 154 | + ``` |
| 155 | + |
| 156 | + Get the primary url of website and save it for future use. |
| 157 | + |
| 158 | + ```bash |
| 159 | + az storage account show -n <storage-account-name> -g <resource-group-name> --query "primaryEndpoints.web" --output tsv |
| 160 | + ``` |
| 161 | + |
| 162 | +### Subscribe Azure SignalR events |
| 163 | + |
| 164 | +Subscribe to Azure SignalR OnConnected and OnDisconnected events and let Event Grid trigger be enabled. |
| 165 | + |
| 166 | +1. Open the VS Code command palette (`Ctrl-Shift-P`, macOS: `Cmd-Shift-P`). |
| 167 | + |
| 168 | +1. Search for and select the **Azure Functions: Open in portal** command. |
| 169 | + |
| 170 | +1. Select the Function `OnConnection` in the left panel. After the function shown, click `Add Event Grid subscription` and choose the Azure SignalR Service. |
| 171 | +  |
| 172 | + |
| 173 | +1. Fill the page as shown below. |
| 174 | +  |
| 175 | + |
| 176 | +### Enable function app cross origin resource sharing (CORS) |
| 177 | + |
| 178 | +Although there is a CORS setting in **local.settings.json**, it is not propagated to the function app in Azure. You need to set it separately. |
| 179 | + |
| 180 | +1. Open the function app in the Azure portal. |
| 181 | + |
| 182 | +1. Under the **Platform features** tab, select **CORS**. |
| 183 | + |
| 184 | +1. In the *Allowed origins* section, add an entry with the static website *primary endpoint* as the value (remove the trailing */*). |
| 185 | + |
| 186 | +1. In order for the SignalR JavaScript SDK call your function app from a browser, support for credentials in CORS must be enabled. Select the "Enable Access-Control-Allow-Credentials" checkbox. |
| 187 | + |
| 188 | +1. Click **Save** to persist the CORS settings. |
| 189 | + |
| 190 | +### Try the application |
| 191 | + |
| 192 | +1. In a browser, navigate to the storage account's primary web endpoint. |
| 193 | +
|
| 194 | +1. When you're connected, the online connection count is shown and you will get a welcome message. |
| 195 | + |
| 196 | +1. Send public messages by entering them into the main chat box. |
| 197 | + |
| 198 | +  |
| 199 | + |
| 200 | +<a name="enable-authentication"></a> |
| 201 | +## Enable authentication on Azure |
| 202 | + |
| 203 | +### Enable App Service Authentication |
| 204 | + |
| 205 | +App Service Authentication supports authentication with Azure Active Directory, Facebook, Twitter, Microsoft account, and Google. |
| 206 | + |
| 207 | +1. In the function app that was opened in the portal, locate the **Platform features** tab, select **Authentication/Authorization**. |
| 208 | + |
| 209 | +1. Turn **On** App Service Authentication. |
| 210 | + |
| 211 | +1. In **Action to take when request is not authenticated**, select "Allow Anonymous requests (no action)". |
| 212 | +
|
| 213 | +1. In **Allowed External Redirect URLs**, enter the URL of your storage account primary web endpoint that you previously noted. |
| 214 | +
|
| 215 | +1. Follow the documentation for the login provider of your choice to complete the configuration. |
| 216 | +
|
| 217 | + - [Azure Active Directory](https://docs.microsoft.com/azure/app-service/configure-authentication-provider-aad) |
| 218 | + - [Facebook](https://docs.microsoft.com/azure/app-service/configure-authentication-provider-facebook) |
| 219 | + - [Twitter](https://docs.microsoft.com/azure/app-service/configure-authentication-provider-twitter) |
| 220 | + - [Microsoft account](https://docs.microsoft.com/azure/app-service/configure-authentication-provider-microsoft) |
| 221 | + - [Google](https://docs.microsoft.com/azure/app-service/configure-authentication-provider-google) |
| 222 | +
|
| 223 | +  |
| 224 | +
|
| 225 | +### Update the web app |
| 226 | +
|
| 227 | +1. Open **index.html** and update the value of `isAuthNeeded` to `true`. |
| 228 | +
|
| 229 | +1. The application can be configured with authentication using Azure Active Directory, Facebook, Twitter, Microsoft account, or Google. Select the authentication provider that you will use by setting the value of `authProvider`. |
| 230 | +
|
| 231 | +1. Use **Azure Storage: Deploy to Static Website** command to upload the **index.html** to Azure Storage |
| 232 | +
|
| 233 | +### Update negotiate function |
| 234 | +
|
| 235 | +1. Update in `userId` in `negotiate/function.json` to `"{headers.x-ms-client-principal-name}"` |
| 236 | +
|
| 237 | +1. Deploy the function to Azure again |
| 238 | +
|
| 239 | +### Try the application with authentication |
| 240 | +
|
| 241 | +1. In a browser, navigate to the storage account's primary web endpoint. |
| 242 | +
|
| 243 | +1. Select **Login** to authenticate with your chosen authentication provider. |
| 244 | +
|
| 245 | +1. When you're connected, the online connection count is shown and you will get a welcome message. |
| 246 | +
|
| 247 | +1. Send public messages by entering them into the main chat box. |
| 248 | +
|
| 249 | +1. Send private messages by clicking on a username in the chat history. Only the selected recipient will receive these messages. |
| 250 | +
|
| 251 | +  |
| 252 | +
|
| 253 | +<a name="build-locally"></a> |
| 254 | +## Build and run the sample locally |
| 255 | +
|
| 256 | +### Prerequisites |
| 257 | +
|
| 258 | +* [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) - serve web pages locally for testing |
| 259 | +* [ngrok](https://ngrok.com/) - Public URLs for exposing your local Event Grid trigger |
| 260 | +
|
| 261 | +### Create an ngrok endpoint |
| 262 | +
|
| 263 | +When running Event Grid trigger locally, you need a tool to proxy events to your local endpoint like [ngrok](https://ngrok.com/). For more details about running Event Grid trigger locally, go to the [document](https://docs.microsoft.com/en-us/azure/azure-functions/functions-debug-event-grid-trigger-local). |
| 264 | +
|
| 265 | +Download *ngrok.exe* from [ngrok](https://ngrok.com/), and run with the following command: |
| 266 | +
|
| 267 | +``` |
| 268 | +ngrok http -host-header=localhost 7071 |
| 269 | +``` |
| 270 | +
|
| 271 | +The -host-header parameter is needed because the functions runtime expects requests from localhost when it runs on localhost. 7071 is the default port number when the runtime runs locally. |
| 272 | +
|
| 273 | +The command creates output like the following: |
| 274 | +
|
| 275 | +``` |
| 276 | +Session Status online |
| 277 | +Version 2.2.8 |
| 278 | +Region United States (us) |
| 279 | +Web Interface http://127.0.0.1:4040 |
| 280 | +Forwarding http://263db807.ngrok.io -> localhost:7071 |
| 281 | +Forwarding https://263db807.ngrok.io -> localhost:7071 |
| 282 | +
|
| 283 | +Connections ttl opn rt1 rt5 p50 p90 |
| 284 | + 0 0 0.00 0.00 0.00 0.00 |
| 285 | +``` |
| 286 | +
|
| 287 | +You'll use the `https://{subdomain}.ngrok.io` URL for your Event Grid subscription. |
| 288 | +
|
| 289 | +### Run the Event Grid trigger function |
| 290 | +
|
| 291 | +The ngrok URL doesn't get special handling by Event Grid, so your function **must be running** locally when the subscription is created. If it isn't, the validation response doesn't get sent and the subscription creation fails. |
| 292 | +
|
| 293 | +### Create a subscription |
| 294 | +
|
| 295 | +Create an Event Grid subscription of SignalR Service, and give it your ngrok endpoint. |
| 296 | +
|
| 297 | +Use this endpoint pattern for Functions 2.x: |
| 298 | +
|
| 299 | +``` |
| 300 | +https://{SUBDOMAIN}.ngrok.io/runtime/webhooks/eventgrid?functionName={FUNCTION_NAME} |
| 301 | +``` |
| 302 | +
|
| 303 | +The `{FUNCTION_NAME}` parameter must be the name specified in the `FunctionName` attribute. |
| 304 | +
|
| 305 | +Here's an example to integrate with Azure SignalR Service using the Azure CLI: |
| 306 | +
|
| 307 | +```azurecli |
| 308 | +az eventgrid event-subscription create --resource-id <signalr-service-resource-id> --name <event-grid-subscription-name> --endpoint https://263db807.ngrok.io/runtime/webhooks/eventgrid?functionName=OnConnection |
| 309 | +``` |
| 310 | +
|
| 311 | +## Run the client page and test |
| 312 | +
|
| 313 | +The chat application's UI is a simple single page application (SPA) created with the Vue JavaScript framework. It will be hosted separately from the function app. Locally, you will run the web interface using the Live Server VS Code extension. |
| 314 | +
|
| 315 | +1. Press **F5** to run the function app locally and attach a debugger. |
| 316 | +
|
| 317 | +1. With **index.html** open, start Live Server by opening the VS Code command palette (`Ctrl-Shift-P`, macOS: `Cmd-Shift-P`) and selecting **Live Server: Open with Live Server**. Live Server will open the application in a browser. |
| 318 | + - The `Host` section in **local.settings.json** configures the port and CORS settings for the local Functions host (this setting has no effect when running in Azure). |
| 319 | +
|
| 320 | + > [!NOTE] |
| 321 | + > Live Server is typically configured to serve content from `http://127.0.0.1:5500`. If you find that it is using a different URL or you are using a different HTTP server, change the `CORS` setting to reflect the correct origin. |
| 322 | +
|
| 323 | +1. The application opens. You will get a welcome message from `Function` and real-time connected connection counting. Also, you can broadcast message in the chat sample. |
0 commit comments