From bc23cf0e407a0e0aa228ba20f218eeb1d53f654f Mon Sep 17 00:00:00 2001 From: sinedied Date: Mon, 9 Oct 2023 17:50:41 +0200 Subject: [PATCH 1/6] feat(search): configure CORS --- README.md | 7 +++++++ infra/core/host/container-app.bicep | 6 +++++- infra/main.bicep | 6 ++++++ infra/main.parameters.json | 3 +++ packages/search/src/app.ts | 3 --- packages/search/src/plugins/config.ts | 2 ++ packages/search/src/plugins/cors.ts | 15 +++++++++++++++ 7 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 packages/search/src/plugins/cors.ts diff --git a/README.md b/README.md index fecd05f9..bbbb5a4c 100644 --- a/README.md +++ b/README.md @@ -206,6 +206,13 @@ By default, the deployed Azure web app will have no authentication or access res To then limit access to a specific set of users or groups, you can follow the steps from [Restrict your Azure AD app to a set of users](https://learn.microsoft.com/azure/active-directory/develop/howto-restrict-your-app-to-a-set-of-users) by changing "Assignment Required?" option under the Enterprise Application, and then assigning users/groups access. Users not granted explicit access will receive the error message -AADSTS50105: Your administrator has configured the application to block users unless they are specifically granted ('assigned') access to the application.- +### Enabling CORS for an alternate frontend + +By default, the deployed search API will only allow requests from the same origin as the deployed web app origin. To enable [CORS](https://developer.mozilla.org/docs/Web/HTTP/CORS) for a frontend hosted on a different origin, run: + +1. Run `azd env set ALLOWED_ORIGIN https://` +2. Run `azd up` + ## Running locally You can only run locally **after** having successfully run the `azd up` command. diff --git a/infra/core/host/container-app.bicep b/infra/core/host/container-app.bicep index 08dd7318..1a50c447 100644 --- a/infra/core/host/container-app.bicep +++ b/infra/core/host/container-app.bicep @@ -12,6 +12,7 @@ param imageName string param keyVaultName string = '' param managedIdentity bool = !empty(keyVaultName) param targetPort int = 80 +param allowedOrigins array = [] @description('CPU cores allocated to a single container instance, e.g. 0.5') param containerCpuCoreCount string = '0.5' @@ -19,7 +20,7 @@ param containerCpuCoreCount string = '0.5' @description('Memory allocated to a single container instance, e.g. 1Gi') param containerMemory string = '1.0Gi' -resource app 'Microsoft.App/containerApps@2022-03-01' = { +resource app 'Microsoft.App/containerApps@2023-05-01' = { name: name location: location tags: tags @@ -32,6 +33,9 @@ resource app 'Microsoft.App/containerApps@2022-03-01' = { external: external targetPort: targetPort transport: 'auto' + corsPolicy: { + allowedOrigins: empty(allowedOrigins) ? ['*'] : allowedOrigins + } } secrets: concat(secrets, [ { diff --git a/infra/main.bicep b/infra/main.bicep index 8e0705f2..8240d299 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -76,12 +76,15 @@ param principalId string = '' @description('Use Application Insights for monitoring and performance tracing') param useApplicationInsights bool = false +param allowedOrigin string + // Only needed for CD due to internal policies restrictions param aliasTag string = '' var abbrs = loadJsonContent('abbreviations.json') var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = union({ 'azd-env-name': environmentName }, empty(aliasTag) ? {} : { alias: aliasTag }) +var allowedOrigins = empty(allowedOrigin) ? [webApp.outputs.uri] : [webApp.outputs.uri, allowedOrigin] // Organize resources in a resource group resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { @@ -154,6 +157,7 @@ module searchApi './core/host/container-app.bicep' = { containerAppsEnvironmentName: containerApps.outputs.environmentName containerRegistryName: containerApps.outputs.registryName managedIdentity: true + allowedOrigins: allowedOrigins containerCpuCoreCount: '1.0' containerMemory: '2.0Gi' secrets: useApplicationInsights ? [ @@ -528,3 +532,5 @@ output AZURE_STORAGE_RESOURCE_GROUP string = storageResourceGroup.name output WEBAPP_URI string = webApp.outputs.uri output SEARCH_API_URI string = searchApi.outputs.uri output INDEXER_API_URI string = indexerApi.outputs.uri + +output ALLOWED_ORIGINS string = join(allowedOrigins, ',') diff --git a/infra/main.parameters.json b/infra/main.parameters.json index c85324ac..4fec5f94 100644 --- a/infra/main.parameters.json +++ b/infra/main.parameters.json @@ -68,6 +68,9 @@ "useApplicationInsights": { "value": "${AZURE_USE_APPLICATION_INSIGHTS=false}" }, + "allowedOrigin": { + "value": "${ALLOWED_ORIGIN}" + }, "aliasTag": { "value": "${AZURE_ALIAS}" } diff --git a/packages/search/src/app.ts b/packages/search/src/app.ts index 7acb844b..5063e4eb 100644 --- a/packages/search/src/app.ts +++ b/packages/search/src/app.ts @@ -2,7 +2,6 @@ import path, { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { type FastifyPluginAsync } from 'fastify'; import AutoLoad, { type AutoloadPluginOptions } from '@fastify/autoload'; -import cors from '@fastify/cors'; export type AppOptions = { // Place your custom options for app below here. @@ -17,8 +16,6 @@ const __dirname = path.dirname(__filename); const app: FastifyPluginAsync = async (fastify, options_): Promise => { // Place here your custom code! - fastify.register(cors, {}); - // Do not touch the following lines // This loads all plugins defined in plugins diff --git a/packages/search/src/plugins/config.ts b/packages/search/src/plugins/config.ts index 94b9ed4f..24ca8a89 100644 --- a/packages/search/src/plugins/config.ts +++ b/packages/search/src/plugins/config.ts @@ -15,6 +15,7 @@ export interface AppConfig { azureOpenAiEmbeddingModel: string; kbFieldsContent: string; kbFieldsSourcePage: string; + allowedOrigins: string; } const camelCaseToUpperSnakeCase = (string_: string) => string_.replaceAll(/[A-Z]/g, (l) => `_${l}`).toUpperCase(); @@ -38,6 +39,7 @@ export default fp( azureOpenAiEmbeddingModel: process.env.AZURE_OPENAI_EMBEDDING_MODEL || 'text-embedding-ada-002', kbFieldsContent: process.env.KB_FIELDS_CONTENT || 'content', kbFieldsSourcePage: process.env.KB_FIELDS_SOURCEPAGE || 'sourcepage', + allowedOrigins: process.env.ALLOWED_ORIGINS || '*', }; // Check that all config values are set diff --git a/packages/search/src/plugins/cors.ts b/packages/search/src/plugins/cors.ts new file mode 100644 index 00000000..f529e46b --- /dev/null +++ b/packages/search/src/plugins/cors.ts @@ -0,0 +1,15 @@ +import fp from 'fastify-plugin'; +import cors from '@fastify/cors'; + +export default fp( + async (fastify) => { + const allowedOrigins = fastify.config.allowedOrigins.split(',').map((origin) => origin.trim()); + fastify.register(cors, { + origin: allowedOrigins, + }); + }, + { + name: 'cors', + dependencies: ['config'], + }, +); From 948805d7f5d0d74f1b20adeab8b4551898ae36ce Mon Sep 17 00:00:00 2001 From: sinedied Date: Mon, 9 Oct 2023 17:55:49 +0200 Subject: [PATCH 2/6] docs: clarify roles permissions --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bbbb5a4c..e1e9491b 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,9 @@ The application is made from multiple components, including: - **Azure account**. If you're new to Azure, [get an Azure account for free](https://azure.microsoft.com/free/cognitive-search/) and you'll get some free Azure credits to get started. - **Azure subscription with access enabled for the Azure OpenAI service**. You can request access with [this form](https://aka.ms/oaiapply). -- **Azure account permissions**: Your Azure account must have `Microsoft.Authorization/roleAssignments/write` permissions, such as [Role Based Access Control Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview), [User Access Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#user-access-administrator), or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner). If you don't have subscription-level permissions, you must be granted [RBAC](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview) for an existing resource group and [deploy to that existing group](#existing-resource-group). +- **Azure account permissions**: + * Your Azure account must have `Microsoft.Authorization/roleAssignments/write` permissions, such as [Role Based Access Control Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview), [User Access Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#user-access-administrator), or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner). If you don't have subscription-level permissions, you must be granted [RBAC](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview) for an existing resource group and [deploy to that existing group](#existing-resource-group). + * Your Azure account also needs `Microsoft.Resources/deployments/write` permissions on the subscription level. ## Azure deployment From 060df808e61afcb265da0cf3feb3cd29776a8a04 Mon Sep 17 00:00:00 2001 From: Yohan Lasorsa Date: Tue, 10 Oct 2023 07:34:04 +0000 Subject: [PATCH 3/6] chore: remove invalid command --- azure.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/azure.yaml b/azure.yaml index 20b468fb..74251301 100644 --- a/azure.yaml +++ b/azure.yaml @@ -10,8 +10,6 @@ services: dist: dist language: ts host: staticwebapp - build: - command: echo "webapp will be built before deployment" hooks: predeploy: windows: From 8517a36d6b40b771a1b585297fe56a59407ce51f Mon Sep 17 00:00:00 2001 From: Yohan Lasorsa Date: Tue, 10 Oct 2023 07:35:37 +0000 Subject: [PATCH 4/6] chore: add copilot extension to devcontainer --- .devcontainer/devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 320db4d4..a5034b04 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -31,7 +31,8 @@ "ms-azuretools.vscode-bicep", "ms-azuretools.vscode-docker", "esbenp.prettier-vscode", - "humao.rest-client" + "humao.rest-client", + "GitHub.copilot" ] } }, From 1af860136500d331d0d61d67215549ef78f324b9 Mon Sep 17 00:00:00 2001 From: Yohan Lasorsa Date: Tue, 10 Oct 2023 07:50:35 +0000 Subject: [PATCH 5/6] chore: add logs for allowed origins --- packages/search/src/plugins/cors.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/search/src/plugins/cors.ts b/packages/search/src/plugins/cors.ts index f529e46b..6707d1bc 100644 --- a/packages/search/src/plugins/cors.ts +++ b/packages/search/src/plugins/cors.ts @@ -4,6 +4,7 @@ import cors from '@fastify/cors'; export default fp( async (fastify) => { const allowedOrigins = fastify.config.allowedOrigins.split(',').map((origin) => origin.trim()); + fastify.log.info(`CORS allowed origins: ${allowedOrigins.join(', ')}`); fastify.register(cors, { origin: allowedOrigins, }); From 71497856232c14b8e8186ab25374fd27a786a0a6 Mon Sep 17 00:00:00 2001 From: Natalia Venditto Date: Wed, 11 Oct 2023 13:29:28 +0200 Subject: [PATCH 6/6] docs: update README.md --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e1e9491b..520e1b44 100644 --- a/README.md +++ b/README.md @@ -56,22 +56,22 @@ The application is made from multiple components, including: ## Getting started -## Azure account requirements +## Azure account prerequisites -**IMPORTANT:** In order to deploy and run this example, you'll need: +**IMPORTANT:** In order to deploy and run this sample, you'll need: -- **Azure account**. If you're new to Azure, [get an Azure account for free](https://azure.microsoft.com/free/cognitive-search/) and you'll get some free Azure credits to get started. +- **Azure account**. If you're new to Azure, [get an Azure account for free](https://azure.microsoft.com/free/cognitive-search/) to get free Azure credits to get started. - **Azure subscription with access enabled for the Azure OpenAI service**. You can request access with [this form](https://aka.ms/oaiapply). - **Azure account permissions**: - * Your Azure account must have `Microsoft.Authorization/roleAssignments/write` permissions, such as [Role Based Access Control Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview), [User Access Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#user-access-administrator), or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner). If you don't have subscription-level permissions, you must be granted [RBAC](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview) for an existing resource group and [deploy to that existing group](#existing-resource-group). - * Your Azure account also needs `Microsoft.Resources/deployments/write` permissions on the subscription level. + * Your Azure account must have `Microsoft.Authorization/roleAssignments/write` permissions, such as [Role Based Access Control Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview), [User Access Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#user-access-administrator), or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner). If you don't have subscription-level permissions, they must be granted to you with [RBAC](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview) for an existing resource group and [deploy to that existing group](#existing-resource-group). + * Your Azure account also needs `Microsoft.Resources/deployments/write` permissions at a subscription level. ## Azure deployment ### Cost estimation -Pricing varies per region and usage, so it isn't possible to predict exact costs for your usage. -However, you can try the [Azure pricing calculator](https://azure.com/e/8ffbe5b1919c4c72aed89b022294df76) for the resources below. +Pricing may vary per region and usage. Exact costs cannot be estimated. +You may try the [Azure pricing calculator](https://azure.com/e/8ffbe5b1919c4c72aed89b022294df76) for the resources below. - Azure Container Apps: Pay-as-you-go tier. Costs based on vCPU and memory used. [Pricing](https://azure.microsoft.com/pricing/details/container-apps/) - Azure Static Web Apps: Free Tier. [Pricing](https://azure.microsoft.com/pricing/details/app-service/static/) @@ -86,9 +86,10 @@ either by deleting the resource group in the Portal or running `azd down`. ### Project setup -You have a few options for setting up this project. -The easiest way to get started is GitHub Codespaces, since it will setup all the tools for you, -but you can also [set it up locally](#local-environment) if desired. +There are multiple ways to successfully setup this project. + +The easiest way to get started is with GitHub Codespacesm that provides preconfigurations to setup all the tools for you. [Read more below](#github-codespaces). +Alternatively you can [set up your local environment](#local-environment) follwing the instructions below. #### GitHub Codespaces @@ -98,7 +99,7 @@ You can run this repo virtually by using GitHub Codespaces, which will open a we #### VS Code Remote Containers -A related option is VS Code Remote Containers, which will open the project in your local VS Code using the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers): +A similar option to Codespaces is VS Code Remote Containers, that will open the project in your local VS Code instance using the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers): [![Open in Remote - Containers](https://img.shields.io/static/v1?style=for-the-badge&label=Remote%20-%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/azure-samples/azure-search-openai-javascript) @@ -110,7 +111,7 @@ A related option is VS Code Remote Containers, which will open the project in yo - [Powershell 7+ (pwsh)](https://github.com/powershell/powershell) - For Windows users only. - **Important**: Ensure you can run `pwsh.exe` from a PowerShell command. If this fails, you likely need to upgrade PowerShell. -Then bring down the project code: +Then get the project code: 1. Create a new folder and switch to it in the terminal 1. Run `azd auth login`