Skip to content

Commit e5d7790

Browse files
authored
Merge pull request #8 from Azure-Samples/builtinauth
Add built-in auth feature
2 parents f3aeda3 + a4a6682 commit e5d7790

File tree

7 files changed

+202
-4
lines changed

7 files changed

+202
-4
lines changed

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ since the local app needs credentials for Azure AI to work properly.
2424

2525
## Important Security Notice
2626

27-
This template, the application code and configuration it contains, has been built to showcase Microsoft Azure specific services and tools. We strongly advise our customers not to make this code part of their production environments without implementing or enabling additional security features. When you deploy this app, it will be **publicly accessible on the internet**. See [Security Guidelines](#security-guidelines) for more information on how to secure your deployment.
27+
This template, the application code and configuration it contains, has been built to showcase Microsoft Azure specific services and tools. We strongly advise our customers not to make this code part of their production environments without implementing or enabling additional security features. See [Security Guidelines](#security-guidelines) for more information on how to secure your deployment.
2828

2929
## Features
3030

3131
* A Python [Quart](https://quart.palletsprojects.com/en/latest/) that uses the [Azure AI Inference SDK](https://learn.microsoft.com/python/api/overview/azure/ai-inference-readme?view=azure-python-preview) package to generate responses to user messages.
32-
* A basic HTML/JS frontend that streams responses from the backend using [JSON Lines](http://jsonlines.org/) over a [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
32+
* A basic HTML/JS frontend that streams responses from the backend using [JSON Lines](http://jsonlines.org/) over a [ReadableStream](https://developer.mozilla.org/docs/Web/API/ReadableStream).
3333
* [Bicep files](https://docs.microsoft.com/azure/azure-resource-manager/bicep/) for provisioning Azure resources, including Azure AI Services, Azure Container Apps, Azure Container Registry, Azure Log Analytics, and RBAC roles.
3434

3535
![Screenshot of the chat app](docs/screenshot_chatapp.png)
@@ -126,7 +126,7 @@ Once you've opened the project in [Codespaces](#github-codespaces), in [Dev Cont
126126
azd up
127127
```
128128

129-
It will prompt you to provide an `azd` environment name (like "chat-app"), select a subscription from your Azure account, and select a [location where DeepSeek-R1 is available](https://learn.microsoft.com/en-us/azure/ai-studio/how-to/deploy-models-serverless-availability#deepseek-models-from-microsoft) (like "westus"). Then it will provision the resources in your account and deploy the latest code. If you get an error or timeout with deployment, changing the location can help, as there may be availability constraints for the Azure AI resource.
129+
It will prompt you to provide an `azd` environment name (like "chat-app"), select a subscription from your Azure account, and select a [location where DeepSeek-R1 is available](https://learn.microsoft.com/azure/ai-studio/how-to/deploy-models-serverless-availability#deepseek-models-from-microsoft) (like "westus"). Then it will provision the resources in your account and deploy the latest code. If you get an error or timeout with deployment, changing the location can help, as there may be availability constraints for the Azure AI resource.
130130

131131
3. When `azd` has finished deploying, you'll see an endpoint URI in the command output. Visit that URI, and you should see the chat app! 🎉
132132
4. Remember to take down your app once you're no longer using it, either by deleting the resource group in the Portal or running this command:
@@ -197,9 +197,11 @@ either by deleting the resource group in the Portal or running `azd down`.
197197
198198
This template uses [Managed Identity](https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/overview) for authenticating to the Azure OpenAI service.
199199
200+
This template also enables the Container Apps [built-in authentication feature](https://learn.microsoft.com/azure/container-apps/authentication) with a Microsoft Entra ID identity provider. The Bicep files use the new [Microsoft Graph extension (public preview)](https://learn.microsoft.com/graph/templates/overview-bicep-templates-for-graph) to create the Entra application registration using [managed identity with Federated Identity Credentials](https://learn.microsoft.com/azure/container-apps/managed-identity), so that no client secrets or certificates are necessary.
201+
200202
Additionally, we have added a [GitHub Action](https://github.com/microsoft/security-devops-action) that scans the infrastructure-as-code files and generates a report containing any detected issues. To ensure continued best practices in your own repository, we recommend that anyone creating solutions based on our templates ensure that the [Github secret scanning](https://docs.github.com/code-security/secret-scanning/about-secret-scanning) setting is enabled.
201203
202204
You may want to consider additional security measures, such as:
203205
204206
* Protecting the Azure Container Apps instance with a [firewall](https://learn.microsoft.com/azure/container-apps/waf-app-gateway) and/or [Virtual Network](https://learn.microsoft.com/azure/container-apps/networking?tabs=workload-profiles-env%2Cazure-cli).
205-
* Adding user login to the app, to restrict access only to users within your organization. See [this example for adding user login with the built-in auth feature of Container Apps](https://github.com/Azure-Samples/openai-chat-app-entra-auth-builtin).
207+
* Enabling Microsoft Defender for Cloud on the resource group and setting up [security policies](https://learn.microsoft.com/azure/defender-for-cloud/security-policy-concept).

infra/aca.bicep

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ module app 'core/host/container-app-upsert.bicep' = {
4747
containerRegistryName: containerRegistryName
4848
env: env
4949
targetPort: 50505
50+
secrets: {
51+
'override-use-mi-fic-assertion-client-id': acaIdentity.properties.clientId
52+
}
5053
}
5154
}
5255

infra/appregistration.bicep

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
extension microsoftGraphV1
2+
3+
@description('Specifies the name of cloud environment to run this deployment in.')
4+
param cloudEnvironment string = environment().name
5+
6+
// NOTE: Microsoft Graph Bicep file deployment is only supported in Public Cloud
7+
@description('Audience uris for public and national clouds')
8+
param audiences object = {
9+
AzureCloud: {
10+
uri: 'api://AzureADTokenExchange'
11+
}
12+
AzureUSGovernment: {
13+
uri: 'api://AzureADTokenExchangeUSGov'
14+
}
15+
USNat: {
16+
uri: 'api://AzureADTokenExchangeUSNat'
17+
}
18+
USSec: {
19+
uri: 'api://AzureADTokenExchangeUSSec'
20+
}
21+
AzureChinaCloud: {
22+
uri: 'api://AzureADTokenExchangeChina'
23+
}
24+
}
25+
26+
@description('Specifies the ID of the user-assigned managed identity.')
27+
param webAppIdentityId string
28+
29+
@description('Specifies the unique name for the client application.')
30+
param clientAppName string
31+
32+
@description('Specifies the display name for the client application')
33+
param clientAppDisplayName string
34+
35+
@description('Specifies the scopes that the client application requires.')
36+
param clientAppScopes array = ['User.Read', 'offline_access', 'openid', 'profile']
37+
38+
param serviceManagementReference string = ''
39+
40+
param issuer string
41+
42+
param webAppEndpoint string
43+
44+
// Get the MS Graph Service Principal based on its application ID:
45+
// https://learn.microsoft.com/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in
46+
var msGraphAppId = '00000003-0000-0000-c000-000000000000'
47+
resource msGraphSP 'Microsoft.Graph/[email protected]' existing = {
48+
appId: msGraphAppId
49+
}
50+
51+
var graphScopes = msGraphSP.oauth2PermissionScopes
52+
resource clientApp 'Microsoft.Graph/[email protected]' = {
53+
uniqueName: clientAppName
54+
displayName: clientAppDisplayName
55+
signInAudience: 'AzureADMyOrg'
56+
serviceManagementReference: empty(serviceManagementReference) ? null : serviceManagementReference
57+
web: {
58+
redirectUris: [
59+
'http://localhost:50505/.auth/login/aad/callback'
60+
'${webAppEndpoint}/.auth/login/aad/callback'
61+
]
62+
implicitGrantSettings: { enableIdTokenIssuance: true }
63+
}
64+
requiredResourceAccess: [
65+
{
66+
resourceAppId: msGraphAppId
67+
resourceAccess: [
68+
for (scope, i) in clientAppScopes: {
69+
id: filter(graphScopes, graphScopes => graphScopes.value == scope)[0].id
70+
type: 'Scope'
71+
}
72+
]
73+
}
74+
]
75+
76+
resource clientAppFic '[email protected]' = {
77+
name: '${clientApp.uniqueName}/miAsFic'
78+
audiences: [
79+
audiences[cloudEnvironment].uri
80+
]
81+
issuer: issuer
82+
subject: webAppIdentityId
83+
}
84+
}
85+
86+
resource clientSp 'Microsoft.Graph/[email protected]' = {
87+
appId: clientApp.appId
88+
}
89+
90+
output clientAppId string = clientApp.appId
91+
output clientSpId string = clientSp.id

infra/appupdate.bicep

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
metadata description = 'Creates an Azure Container Apps Auth Config using Microsoft Entra as Identity Provider.'
2+
3+
@description('The name of the container apps resource within the current resource group scope')
4+
param containerAppName string
5+
6+
@description('The client ID of the Microsoft Entra application.')
7+
param clientId string
8+
9+
param openIdIssuer string
10+
11+
@description('Enable token store for the Container App.')
12+
param includeTokenStore bool = false
13+
14+
@description('The URI of the Azure Blob Storage container to be used for token storage.')
15+
param blobContainerUri string = ''
16+
@description('The resource ID of the managed identity to be used for accessing the Azure Blob Storage.')
17+
param appIdentityResourceId string = ''
18+
19+
resource app 'Microsoft.App/containerApps@2023-05-01' existing = {
20+
name: containerAppName
21+
}
22+
23+
resource auth 'Microsoft.App/containerApps/authConfigs@2024-10-02-preview' = {
24+
parent: app
25+
name: 'current'
26+
properties: {
27+
platform: {
28+
enabled: true
29+
}
30+
globalValidation: {
31+
redirectToProvider: 'azureactivedirectory'
32+
unauthenticatedClientAction: 'RedirectToLoginPage'
33+
}
34+
identityProviders: {
35+
azureActiveDirectory: {
36+
enabled: true
37+
registration: {
38+
clientId: clientId
39+
clientSecretSettingName: 'override-use-mi-fic-assertion-client-id'
40+
openIdIssuer: openIdIssuer
41+
}
42+
validation: {
43+
defaultAuthorizationPolicy: {
44+
allowedApplications: []
45+
}
46+
}
47+
}
48+
}
49+
login: {
50+
// https://learn.microsoft.com/azure/container-apps/token-store
51+
tokenStore: {
52+
enabled: includeTokenStore
53+
azureBlobStorage: includeTokenStore
54+
? {
55+
blobContainerUri: blobContainerUri
56+
managedIdentityResourceId: appIdentityResourceId
57+
}
58+
: {}
59+
}
60+
}
61+
}
62+
}

infra/bicepconfig.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"experimentalFeaturesEnabled": {
3+
"extensibility": true
4+
},
5+
// specify an alias for the version of the v1.0 dynamic types package you want to use
6+
"extensions": {
7+
"microsoftGraphV1": "br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:0.1.8-preview"
8+
}
9+
}

infra/main.bicep

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ param disableKeyBasedAuth bool = true
3636
// Parameters for the specific Azure AI deployment:
3737
param aiServicesDeploymentName string = 'DeepSeek-R1'
3838

39+
@description('Service Management Reference for the Entra app registration')
40+
param serviceManagementReference string = ''
41+
3942
var resourceToken = toLower(uniqueString(subscription().id, name, location))
4043
var tags = { 'azd-env-name': name }
4144

@@ -125,6 +128,31 @@ module aca 'aca.bicep' = {
125128
}
126129
}
127130

131+
var issuer = '${environment().authentication.loginEndpoint}${tenant().tenantId}/v2.0'
132+
module registration 'appregistration.bicep' = {
133+
name: 'reg'
134+
scope: resourceGroup
135+
params: {
136+
clientAppName: '${prefix}-entra-client-app'
137+
clientAppDisplayName: 'DeepSeek Entra Client App'
138+
webAppEndpoint: aca.outputs.uri
139+
webAppIdentityId: aca.outputs.identityPrincipalId
140+
issuer: issuer
141+
serviceManagementReference: serviceManagementReference
142+
}
143+
}
144+
145+
module appupdate 'appupdate.bicep' = {
146+
name: 'appupdate'
147+
scope: resourceGroup
148+
params: {
149+
containerAppName: aca.outputs.name
150+
clientId: registration.outputs.clientAppId
151+
openIdIssuer: issuer
152+
includeTokenStore: false
153+
}
154+
}
155+
128156
module aiServicesRoleBackend 'core/security/role.bicep' = {
129157
scope: resourceGroup
130158
name: 'aiservices-role-backend'

infra/main.parameters.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
},
2020
"disableKeyBasedAuth": {
2121
"value": "${DISABLE_KEY_BASED_AUTH=true}"
22+
},
23+
"serviceManagementReference": {
24+
"value": "${AZURE_SERVICE_MANAGEMENT_REFERENCE}"
2225
}
2326
}
2427
}

0 commit comments

Comments
 (0)