Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(search): configure CORS #71

Merged
merged 6 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"ms-azuretools.vscode-bicep",
"ms-azuretools.vscode-docker",
"esbenp.prettier-vscode",
"humao.rest-client"
"humao.rest-client",
"GitHub.copilot"
]
}
},
Expand Down
32 changes: 21 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +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).
- **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, 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/)
Expand All @@ -84,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

Expand All @@ -96,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)

Expand All @@ -108,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`
Expand Down Expand Up @@ -206,6 +209,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 <app_name> 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://<your-domain.com>`
2. Run `azd up`

## Running locally

You can only run locally **after** having successfully run the `azd up` command.
Expand Down
2 changes: 0 additions & 2 deletions azure.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ services:
dist: dist
language: ts
host: staticwebapp
build:
command: echo "webapp will be built before deployment"
hooks:
predeploy:
windows:
Expand Down
6 changes: 5 additions & 1 deletion infra/core/host/container-app.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ 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'

@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
Expand All @@ -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, [
{
Expand Down
6 changes: 6 additions & 0 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -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' = {
Expand Down Expand Up @@ -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 ? [
Expand Down Expand Up @@ -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, ',')
3 changes: 3 additions & 0 deletions infra/main.parameters.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
"useApplicationInsights": {
"value": "${AZURE_USE_APPLICATION_INSIGHTS=false}"
},
"allowedOrigin": {
"value": "${ALLOWED_ORIGIN}"
},
sinedied marked this conversation as resolved.
Show resolved Hide resolved
"aliasTag": {
"value": "${AZURE_ALIAS}"
}
Expand Down
3 changes: 0 additions & 3 deletions packages/search/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -17,8 +16,6 @@ const __dirname = path.dirname(__filename);
const app: FastifyPluginAsync<AppOptions> = async (fastify, options_): Promise<void> => {
// Place here your custom code!

fastify.register(cors, {});

// Do not touch the following lines

// This loads all plugins defined in plugins
Expand Down
2 changes: 2 additions & 0 deletions packages/search/src/plugins/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand Down
16 changes: 16 additions & 0 deletions packages/search/src/plugins/cors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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.log.info(`CORS allowed origins: ${allowedOrigins.join(', ')}`);
fastify.register(cors, {
origin: allowedOrigins,
});
},
{
name: 'cors',
dependencies: ['config'],
},
);
Loading