Slide 1

Slide 1 text

Securing AI Apps on Azure Date Topic Speakers July 2 5-6PM UTC Using Keyless Auth with Azure AI Services Marlene Mhangami Pamela Fox July 8 5-6PM UTC Add User Login to AI Apps using Built-in Auth James Casey Pamela Fox July 9 7-8PM UTC Add User Login to AI Apps using MSAL SDK Ray Luo Pamela Fox July 10 7-8PM UTC Handling User Auth for a SPA App on Azure Matt Gotteiner July 17 7-8PM UTC Data Access Control for AI RAG Apps on Azure Matt Gotteiner Pamela Fox July 25 11PM-12PM Deploying an AI App to a Private Network on Azure Matt Gotteiner Anthony Shaw https://aka.ms/S-1355

Slide 2

Slide 2 text

Securing AI Apps on Azure: Add User Login to AI Apps using MSAL SDK Pamela Fox Python Cloud Advocacy github.com/pamelafox James Casey Product Manager, Identity Developer Experience github.com/jamesc TODO: new aka

Slide 3

Slide 3 text

Goal: Require authentication for our AI app WITH AUTOMATION! aka.ms/azai/msi aka.ms/azai/auth-builtin aka.ms/azai/auth-local

Slide 4

Slide 4 text

Identity and Access Management: A Primer

Slide 5

Slide 5 text

What is Identity and Access Management? Ensures the right user gets access to the right resource

Slide 6

Slide 6 text

How do Authentication and Authorization Work? Authenticate users through the Open ID Connect protocol (OIDC) Authorize users using OAuth 2.0 protocol Terminology: Auth Flow ▪ Authentication / Authorization Exchange Authorization Server ▪ Issues tokens for apps to access protected resources Client ▪ App requesting access to a protected resource Resource Owner ▪ Owns protected resource client is trying to access Resource Server ▪ Provides access to protected data ▪ Relies on authorization server for authentication, uses token for authorization

Slide 7

Slide 7 text

Securing a web API A secure API requires an “access token” that the client must fetch from the authorization server (via OAuth2/OpenID Connect) before calling the API. Client App Microsoft Entra ID​ Client app requests access token Access token returned to client app Resource Server (API endpoint) Call API HTTP Get + access token Data returned to client 1 2 3 4

Slide 8

Slide 8 text

Before returning data, the resource API must perform authentication and authorization checks. Client App Microsoft Entra ID​ Client app requests access token Access token returned to client app Call API HTTP Get + access token Data returned to client 1 2 3 4 Resource Server (API endpoint) Authorization Securing a web API continued

Slide 9

Slide 9 text

What is an access token? • Commonly a JSON blob that contains claims about the subject • Resource API needs to validate these claims, make authorization decisions based on these claims and potentially other information • It is passed to the API inside request headers "Accept": "application/json, text/plain, */*​ " "Accept-Encoding": "gzip,deflate,sdch​ " "Accept-Language": "en-GB, en;g=0,8,en- US;0=6,de;g=0,4​ " "Authorization": "bearer EyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1hiIsImtpZ CI6InU0T2ZORlBId0VCb3NIanRy...​ " "Cache-Control": "no-cache​ " "Connection": "keep-alive​ " "Host": "localhost:8080"​ "..." Request Headers

Slide 10

Slide 10 text

Access Token Claims "aud": "416684a7-0b52-4fa3-9918-e76d16542be2", "iss": "https://login.microsoftonline.com/c72a295d-d7a5-41ea-a351-b15dd9f67215/v2.0", "iat": 1563883336, "nbf": 1563883336, "exp": 1563887236, "azp": "bb764c21-49b8-49de-aa24-6c76d7dc800f", "oid": "0e748cd0-5d2a-4918-a351-9549e75fd1dd", "scp": "Catalog.View.Published", "roles": "Catalog.View", "wids": "9c6df0f2-1e7c-4dc3-b195-66dfbd24aa8f", "sub": "0X7PvET4orHRnRYndMvA4CYlYxg_CowsE1BGTIAK6hE", "tid": "c72a295d-d7a5-41ea-a351-b15dd9f67215", "preferred_username": [email protected], "email": [email protected]", "name": "Jane Bowen", Key: Authentication: know the subject Authorization: should subject have access Display-only https://learn.microsoft.com/entra/identity-platform/access-token-claims-reference

Slide 11

Slide 11 text

Built-in Authentication abstracts away the details https://learn.microsoft.com/azure/app-service/overview-authentication-authorization#how-it-works Azure App Service and Container Apps provide built-in authentication and authorization capabilities (sometimes referred to as "Easy Auth"), so you can sign in users and access data by writing minimal or no code in your web app

Slide 12

Slide 12 text

Adding user authentication: A code-first approach

Slide 13

Slide 13 text

Registering with the Microsoft identity platform To request tokens from the Microsoft identity platform, you need to register a Microsoft Entra application and create a service principal for it. Microsoft Entra Application Object Microsoft Graph Service Principal Microsoft identity platform

Slide 14

Slide 14 text

Registering Entra applications with Graph SDK graph_client = GraphServiceClient(credentials=credential, scopes=scopes) graph_client.applications.post(Application( display_name=f"ChatGPT Sample Client App {identifier}", sign_in_audience="AzureADMyOrg", web=WebApplication( redirect_uris=["http://YOUR-APP-URL/.auth/login/aad/callback"], implicit_grant_settings=ImplicitGrantSettings(enable_id_token_issuance=True)), required_resource_access=[ RequiredResourceAccess( resource_app_id="00000003-0000-0000-c000-000000000000", resource_access=[ ResourceAccess(id="e1fe6dd8-ba31-4d61-89e7-88639da4683d", type="Scope"), # Graph User.Read ResourceAccess(id="7427e0e9-2fba-42fe-b0c0-848c9e6a8182", type="Scope"), # offline_access ResourceAccess(id="37f7f235-527c-4136-accd-4a02d197296e", type="Scope"), # openid ResourceAccess(id="14dad69e-099b-42c9-810b-d002981feec1", type="Scope"), # profile ])])) Graph SDKs available in C#, Go, Java, JavaScript, PHP, Powershell, Python auth_init.py aka.ms/azai/auth-builtin

Slide 15

Slide 15 text

Setting Entra application credentials with Graph SDK request_password = AddPasswordPostRequestBody( password_credential=PasswordCredential(display_name="WebAppSecret"), ) graph_client.applications.by_application_id(app_id) .add_password.post(request_password) Currently, app registrations can use either password or certificate credentials. (Stay tuned for a better way!) auth_init.py aka.ms/azai/auth-builtin

Slide 16

Slide 16 text

OAuth2 authentication flow with OIDC App backend Microsoft Entra servers Browser OAuth2 Leg 1 Initiate the authorization code flow Returns authorization URI User Signs in Returns redirect to redirectURI OAuth2 Leg 2 Exchange authorization code for token Returns redirect to URI Visits webapp Returns access token Render webpage &scope=openid email name and ID token

Slide 17

Slide 17 text

Implementing the authentication flow Option 1: For auth on Azure App Service or Container Apps Option 2: For auth on any host (including local) Use MSAL packages to orchestrate OIDC flow using app registration Configure built-in authentication and authorization with Microsoft identity platform as the provider Join tomorrow for a deep dive session! Today's topic!

Slide 18

Slide 18 text

Configuring built-in authentication on Container Apps

Slide 19

Slide 19 text

Configuring built-in authentication for Container Apps • Set clientID to the app ID of the Entra app registration • Store app password in secrets and point clientSecretSettingName to that secret • Set openIdIssuer to the Microsoft idP endpoint var loginEndpoint = environment().authentication.loginEndpoint var openIdIssuer = '${loginEndpoint}${tenant().tenantId}/v2.0' resource auth 'Microsoft.App/containerApps/authConfigs@2023-05-01' = { parent: app name: 'current' properties: { platform: { enabled: true } globalValidation: { redirectToProvider: 'azureactivedirectory' unauthenticatedClientAction: 'RedirectToLoginPage' } identityProviders: { azureActiveDirectory: { registration: { clientId: clientId clientSecretSettingName: clientSecretName openIdIssuer: openIdIssuer } } } } } appauth.bicep aka.ms/azai/auth-builtin

Slide 20

Slide 20 text

A successfully configured built-in authentication

Slide 21

Slide 21 text

Demo: built-in authentication with Entra ID

Slide 22

Slide 22 text

Extracting user details from headers def extract_username(headers, default_username="You"): if "X-MS-CLIENT-PRINCIPAL" not in headers: return default_username token = json.loads(base64.b64decode(headers.get("X-MS-CLIENT-PRINCIPAL"))) claims = {claim["typ"]: claim["val"] for claim in token["claims"]} return claims.get("name", default_username) @app.get("/") async def index(): username = extract_username(request.headers) return await render_template("index.html", username=username) https://learn.microsoft.com/azure/app-service/configure-authentication-user-identities The built-in authentication service injects headers into request headers with details about the authenticated user, like their username. app.py aka.ms/azai/auth-builtin

Slide 23

Slide 23 text

Using External ID

Slide 24

Slide 24 text

What is Entra External ID?

Slide 25

Slide 25 text

Tenant Architecture – External ID Subscription AppCreator Service Principal App Registration User Flow (Sign-in/Sign-up) App Users Entra Tenant (external) Entra Tenant (workforce) App Roles

Slide 26

Slide 26 text

Tenant Architecture – External ID Subscription > azd auth login --tenant-id EXTERNAL-TENANT Get Token (user) Entra Tenant (external) Entra Tenant (workforce)

Slide 27

Slide 27 text

Tenant Architecture – External ID Subscription > ./scripts/setup_for_external_id.sh Creates AppCreator Service Principal Entra Tenant (external) Entra Tenant (workforce) App Roles

Slide 28

Slide 28 text

Tenant Architecture – External ID Subscription > azd auth login --tenant-id AZURE-TENANT-ID AppCreator Service Principal Get Token Entra Tenant (external) Entra Tenant (workforce) App Roles

Slide 29

Slide 29 text

Tenant Architecture – External ID Subscription > azd up (pre-hook – auth_init.py) Creates AppCreator Service Principal App Registration User Flow (Sign-in/Sign-up) Entra Tenant (external) Entra Tenant (workforce) App Roles

Slide 30

Slide 30 text

Tenant Architecture – External ID Subscription > azd up (infra) Configures AppCreator Service Principal App Registration User Flow (Sign-in/Sign-up) Creates Entra Tenant (external) Entra Tenant (workforce) App Roles

Slide 31

Slide 31 text

Tenant Architecture – External ID Subscription Entra Tenant (workforce) Entra Tenant (external) > azd up (post-hook – auth_update.py) AppCreator Service Principal App Registration User Flow (Sign-in/Sign-up) Configure App Roles

Slide 32

Slide 32 text

Tenant Architecture – External ID Subscription Entra Tenant (workforce) Entra Tenant (external) AppCreator Service Principal App Registration User Flow (Sign-in/Sign-up) App Users New Users are now in the external configuration tenant App Roles

Slide 33

Slide 33 text

Using different credentials in different tenants def get_credential(tenant_id: str) -> AsyncTokenCredential: client_id = os.getenv("AZURE_AUTH_EXTID_APP_ID", None) if client_id is None: logger.info(f"Using Azd CLI Credential for tenant_id {tenant_id}") return AzureDeveloperCliCredential(tenant_id=tenant_id) client_secret = os.getenv("AZURE_AUTH_EXTID_APP_SECRET", None) logger.info(f"Using Client Secret Credential for client ID {client_id}") return ClientSecretCredential(tenant_id=tenant_id, client_id=client_id, client_secret=client_secret) We use azd environment variables to use different credentials • AzureDeveloperCliCredential – for workforce tenant (Azure) • ClientSecretCredential – for external configuration tenant auth_init.py aka.ms/azai/auth-builtin

Slide 34

Slide 34 text

Granting permissions to the App Registration async def get_or_create_permission_grant(graph_client: GraphServiceClient, sp_id: str, graph_sp_id: str): permission_scopes = " ".join(["User.Read", "offline_access", "openid", "profile"]) grant_id = await get_permission_grant(graph_client, sp_id, graph_sp_id, permission_scopes) if grant_id: logger.info("Permission grant already exists, not creating new one") else: logger.info("Creating permission grant") await create_permission_grant(graph_client, sp_id, graph_sp_id, permission_scopes) Pre-grant permissions for Entra External ID app registration to request claims auth_init.py aka.ms/azai/auth-builtin https://learn.microsoft.com/entra/identity/enterprise-apps/what-is-access-management

Slide 35

Slide 35 text

async def get_or_create_userflow_app(graph_client_beta: GraphServiceClientBeta, userflow_id: str, app_id: str) -> bool: logger.info(f"Possibly setting up association between userflow {userflow_id} and client app {app_id}...") flow_app_exists = await userflow_has_app(graph_client_beta, userflow_id, app_id) if flow_app_exists: logger.info("User flow is already associated with client app.") else: logger.info("Adding user flow to client app.") await add_app_to_userflow(graph_client_beta, userflow_id, app_id) Configure sign-in and sign-up with a User Flow https://learn.microsoft.com/entra/external-id/customers/how-to-user-flow-sign-up-sign-in-customers Create user flow, and attach it to the application auth_init.py aka.ms/azai/auth-builtin

Slide 36

Slide 36 text

External ID Pricing Consumption based pricing per Monthly Active User (MAU) • Free Tier • Base price per user with add-ons have additional per-MAU pricing https://learn.microsoft.com/entra/external-id/customers/faq-customers for all pricing details

Slide 37

Slide 37 text

Microsoft Entra External ID - Visual Studio Code extension Create a tenant with a guided walkthough Browse your resources in explorer view Public preview https://aka.ms/ciam-hub-vscode-quickstart

Slide 38

Slide 38 text

Azure Portal - External configuration tenant in App Service Auth Create or use existing tenant and application Customize sign-in branding Public preview https://aka.ms/ciam-hub-appservice-docs

Slide 39

Slide 39 text

Demo: built-in authentication with Entra External ID

Slide 40

Slide 40 text

Upcoming improvements

Slide 41

Slide 41 text

Registering Entra applications with Graph Bicep template resource clientApp 'Microsoft.Graph/[email protected]' = { uniqueName: clientAppName displayName: clientAppDisplayName signInAudience: 'AzureADMyOrg' web: { redirectUris: ['${webAppEndpoint}/.auth/login/aad/callback'] implicitGrantSettings: {enableIdTokenIssuance: true}} requiredResourceAccess: [{ resourceAppId: '00000003-0000-0000-c000-000000000000' resourceAccess: [ // User.Read {id: 'e1fe6dd8-ba31-4d61-89e7-88639da4683d', type: 'Scope'} // offline_access {id: '7427e0e9-2fba-42fe-b0c0-848c9e6a8182', type: 'Scope'} // openid {id: '37f7f235-527c-4136-accd-4a02d197296e', type: 'Scope'} // profile {id: '14dad69e-099b-42c9-810b-d002981feec1', type: 'Scope'} ]} ]} resource clientSp 'Microsoft.Graph/servicePrincipals@beta' = { appId: clientApp.appId } Create a Graph application and associated service principal in Bicep (vs. SDK) Public preview appreg.bicep https://aka.ms/graphbicep aka.ms/graph-bicep-mi-fic

Slide 42

Slide 42 text

Using managed identity as federated identity credential var openIdIssuer = '${loginEndpoint}${tenant().tenantId}/v2.0' resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { name: '${name}-id' location: location } resource clientAppFic 'federatedIdentityCredentials@beta' = { name: '${clientApp.uniqueName}/msiAsFic' audiences: ['api://AzureADTokenExchange'] issuer: openIdIssuer subject: webIdentity.properties.principalId } App registrations can go password-less! More secure than secrets/certificates since no strings need to be stored securely or rotated. This will be eventually supported by Graph Bicep, MSAL SDKs, and Built-in Auth. appreg.bicep aka.ms/graph-bicep-mi-fic

Slide 43

Slide 43 text

Configuring built-in authentication for App Service Just change clientSecretSettingName to this exact value instead → var loginEndpoint = environment().authentication.loginEndpoint var openIdIssuer = '${loginEndpoint}${tenant().tenantId}/v2.0' resource configAuth 'Microsoft.Web/sites/config@2022-03-01' = { parent: appService name: 'authsettingsV2' properties: { globalValidation: { requireAuthentication: true unauthenticatedClientAction: 'RedirectToLoginPage' redirectToProvider: 'azureactivedirectory' } identityProviders: { azureActiveDirectory: { enabled: true registration: { clientId: clientId clientSecretSettingName: 'OVERRIDE_USE_MI_FIC_ASSERTION_CLIENTID' openIdIssuer: openIdIssuer }}} login: { tokenStore: { enabled: true } }}} appauth.bicep aka.ms/graph-bicep-mi-fic

Slide 44

Slide 44 text

Next steps

Slide 45

Slide 45 text

Get started with our samples Azure OpenAI + AI Search + Entra + MSAL + App Service Built-in Auth aka.ms/ragchat Azure OpenAI + Entra + Container Apps Built-in Auth aka.ms/azai/auth-builtin Find more samples at: aka.ms/azai Java JavaScript Python .NET OpenAI Assistants Fine-tuning ...and more!

Slide 46

Slide 46 text

Learn more Microsoft Entra https://entra.microsoft.com Microsoft Entra developer center https://aka.ms/dev/ms-entra Microsoft Graph Bicep https://aka.ms/graphbicep Built-in Authentication on Azure Container Apps https://learn.microsoft.com/azure/container-apps/authentication Built-in Authentication on Azure App Service https://learn.microsoft.com/azure/app-service/overview-authentication-authorization

Slide 47

Slide 47 text

Securing AI Apps on Azure Date Topic Speakers July 2 5-6PM UTC Using Keyless Auth with Azure AI Services Marlene Mhangami Pamela Fox July 8 5-6PM UTC Add User Login to AI Apps using Built-in Auth James Casey Pamela Fox July 9 7-8PM UTC Add User Login to AI Apps using MSAL SDK Ray Luo Pamela Fox July 10 7-8PM UTC Handling User Auth for a SPA App on Azure Matt Gotteiner July 17 7-8PM UTC Data Access Control for AI RAG Apps on Azure Matt Gotteiner Pamela Fox July 25 11PM-12PM Deploying an AI App to a Private Network on Azure Matt Gotteiner Anthony Shaw https://aka.ms/S-1355