在本快速入门中,你将使用 Quickstart 6 On-Behalf-Of Flow 示例,通过用户委托身份验证运行数据 API 生成器(DAB)。 Web 应用通过 Microsoft Entra ID 对用户进行登录验证,将承载令牌发送给 DAB,而 DAB 会将每个令牌交换成对应已登录用户的 Azure SQL 令牌。
此示例使用Azure SQL,因为本地SQL Server不接受Microsoft Entra令牌。 执行SELECT SUSER_NAME()的WhoAmI视图证明,SQL 看到的是实际调用方,而不是 DAB 的托管标识。
先决条件
- .NET 8 或更高版本
- Docker Desktop
- PowerShell
- 用于构建编排的 .NET Aspire 工具
- 用于 Microsoft Entra 设置和 Azure 部署的 Azure CLI
- 如果部署数据库项目,请使用 sqlpackage
- 具有创建 Azure SQL、Azure 容器应用、Azure 容器注册表、Log Analytics 和资源组权限的 Azure 订阅
- 创建Microsoft Entra应用注册的权限、创建 API 应用客户端密码、添加Azure SQL 数据库委派的
user_impersonation权限,以及授予管理员同意 - 可以成为Azure SQL Microsoft Entra管理员的Microsoft Entra用户或组
示例显示的内容
- 使用 MSAL 浏览器登录的静态 Web 应用。
- 从 Web 应用到 DAB 的持有者令牌调用。
- DAB 配置为使用 Microsoft Entra ID
EntraId身份验证提供程序。 - 已配置用于 OBO 令牌交换的 DAB
user-delegated-auth。 - 用于 OBO 交换的带有客户端密码的 API 应用注册。
- Azure SQL 数据库 具有经管理员同意的委派
user_impersonation权限。 - 不包含
User ID、Password或Authentication关键字的纯 Azure SQL 连接字符串。 - 不允许匿名访问的已通过身份验证的 DAB 实体。
- 已登录呼叫者的包含Azure SQL用户。
- 一种返回
SUSER_NAME()以验证 SQL 调用方身份的WhoAmI视图。 - 在
azure-infra中通过 PowerShell 脚本进行 Azure 部署和清理。
身份验证流程
| 跳 | Authentication |
|---|---|
| 用户到 Web 应用 | 使用 Microsoft Entra ID 进行 MSAL 浏览器登录 |
| Web 应用到 DAB API | DAB API 受众的持有者令牌 |
| DAB API 的作用 | authenticated |
| DAB 到 Azure SQL | 实际登录用户的 OBO 令牌 |
与该系列比较
| Step | 哪些更改 |
|---|---|
| 上一个 | 使用 SQL 行级安全性来筛选 SQL 数据中的行,但 SQL 仍会对 DAB 服务标识进行身份验证。 |
| 本快速入门 | 使用 OBO,使 Azure SQL 能够对实际已登录的用户进行身份验证,以便进行审核并实施基于用户的策略。 |
| 下一步 | 配置 OBO 身份验证 详细介绍了 OBO 配置属性。 |
Azure 独有的行为
OBO 需要使用 Microsoft Entra 身份验证的 Azure SQL。 本地的 SQL Server 容器无法接受 Microsoft Entra 令牌,因此完整的 OBO 路径仅限于 Azure。
使用未附加参数的 Azure SQL 连接字符串,以便 DAB 能够在每个经过身份验证的请求中注入每用户的 OBO 令牌。
Server=tcp:<server>.database.windows.net,1433;Database=<database>;Encrypt=True;TrustServerCertificate=False;
请勿在 OBO 连接字符串中包含这些值:
User IDPasswordAuthentication
Important
如果连接字符串包含 Authentication=,则当 DAB 还提供访问令牌时,SQL 客户端库将拒绝请求。
使用样本
克隆示例存储库。
git clone https://github.com/Azure-Samples/dab-2.0-quickstart-web_entra-api_entra-db_entra-obo.git
cd dab-2.0-quickstart-web_entra-api_entra-db_entra-obo
恢复本地工具。
dotnet tool restore
登录到Azure。
az login
将示例部署到Azure。
pwsh ./azure-infra/azure-up.ps1
部署脚本为 DAB、Web 应用、MCP 检查器和 SQL 指挥官预配Azure SQL和Azure 容器应用资源。 它还运行Microsoft Entra设置,创建 API 应用客户端密码,添加Azure SQL 数据库委派的user_impersonation权限,授予管理员同意,部署数据库,创建包含的用户,并为 OBO 配置 DAB。
部署后,打开脚本打印的 Web 应用 URL。 登录并验证 SQL Server 将你识别为 徽章中显示的是你的用户主体名称。 该徽章显示为由SELECT SUSER_NAME()支持的WhoAmI实体。
匿名 API 请求应返回 401 Unauthorized。
完成后,清理Azure资源和应用注册。
pwsh ./azure-infra/azure-down.ps1
密钥文件
| 路径 | Purpose |
|---|---|
data-api/dab-config.json |
启用 user-delegated-auth、禁用缓存、配置 EntraId和公开 WhoAmI 视图实体。 |
database/Views/WhoAmI.sql |
定义用于身份验证的 SELECT SUSER_NAME() AS UserName。 |
web-app/index.html |
显示已登录用户和 SQL 身份徽章。 |
web-app/app.js |
协调登录、页面更新和标识刷新。 |
web-app/dab.js |
将持有者令牌请求发送到 DAB 并读取 WhoAmI。 |
azure-infra/entra-setup.ps1 |
创建 Microsoft Entra 应用注册项、创建 API 客户端机密、添加 Azure SQL 数据库 委派权限 user_impersonation,并授予管理员同意权限。 |
azure-infra/resources.bicep |
定义 Azure 资源,并将原始 Azure SQL 连接字符串和 OBO 设置传递给 DAB。 |
azure-infra/post-provision.ps1 |
部署数据库、设置Azure SQL Microsoft Entra管理员、创建包含的用户并配置 OBO 环境值。 |
使用GitHub Copilot重新创建此示例
打开要在Visual Studio Code中创建示例的工作区,将GitHub Copilot切换到代理模式,然后粘贴此提示。
You are GitHub Copilot running in agent mode. Recreate the Data API builder Quickstart 6 On-Behalf-Of Flow sample as a complete Azure-only project in the current VS Code workspace under `quickstart-06-on-behalf-of`. Build a static SPA with MSAL browser sign-in, DAB with Microsoft Entra bearer-token validation and OBO user-delegated authentication, Azure SQL, REST, GraphQL, MCP, .NET Aspire build orchestration, SQL Commander, MCP Inspector, and Azure Container Apps deployment scripts. DAB is the only API, GraphQL, and MCP layer over SQL. SQL must authenticate the actual signed-in user, not the DAB managed identity or service principal.
Source repository: https://github.com/Azure-Samples/dab-2.0-quickstart-web_entra-api_entra-db_entra-obo. If internet access is available, inspect or clone this repository before you create files. Reuse and adapt its files as closely as possible, especially `web-app/`, `data-api/`, `database/`, `aspire-apphost/`, `mcp-inspector/`, `azure-infra/`, scripts, and README patterns. The goal is to implement the published quickstart, not to invent a different sample. If the repository differs from this prompt or the current Data API builder docs, prefer the current docs for product behavior.
Minimize user interaction. Use the defaults in this prompt and make reasonable best guesses for noncritical choices. Do not ask for a root folder or project folder name; use the current VS Code workspace and the default subfolder. Ask only when you need approval for resource changes, secrets, permissions, materially higher cost, external account choices, or an ambiguous requirement that affects the architecture.
Azure-only constraint: do not build a local SQL Server OBO path. Local SQL Server cannot accept Microsoft Entra tokens. Use local tooling only for project generation, web app development, DAB config validation where possible, container builds, and database package builds.
Start with a short plan and proceed with safe defaults before you create files or run commands. Use the default `WhoAmI` view unless the user explicitly asks for additional schema. Ask only these questions if the values aren't already available from the environment or prior context:
- Which Azure subscription, primary region, fallback region, resource group, and tenant should the sample use? Default fallback region: `westus2` if the primary region can't provision Azure SQL or Container Apps.
- Should I create new SPA and API app registrations or reuse existing registrations?
- Confirm that the API app can use a client secret. OBO requires a confidential client.
- Confirm that the API app should receive Azure SQL Database delegated `user_impersonation` permission and admin consent.
- Which Microsoft Entra user or group should become the Azure SQL Microsoft Entra admin?
- Which signed-in users or groups should become contained database users for validation?
- Do you approve creating billable Azure resources, app registrations, and an API app client secret if deployment starts?
After the answers, show a checklist and ask for approval before implementation. Include phases for scaffold, Entra setup, database package, Azure infrastructure, post-provision, validation, and cleanup. Do not run `az`, `az ad`, `azd`, or Azure deployment commands that create or change resources until the user explicitly approves the exact command set.
After approval, continue working without asking status-check questions. If a command, build, container, endpoint, or validation step fails, inspect the error, adjust the project, rerun the step, and continue. Keep iterating until the sample runs end-to-end or you hit a blocker that requires user action.
Use cost-first Azure defaults. Choose the cheapest option that satisfies the quickstart requirements: use a free Azure SQL database offer when the subscription and region support it and it supports Microsoft Entra/OBO validation; otherwise choose the lowest-cost SQL option that supports user-delegated authentication. Use Azure Container Apps consumption, minimal CPU and memory, Basic Azure Container Registry, minimal Log Analytics retention, and no always-on or dedicated plans unless required. Prioritize finishing the project. Treat regional provisioning limits as expected adjustment points, not failures: if the primary region can't provision a required service or free SQL option, use the approved fallback region such as `westus2`, and continue the deployment. Ask the user only when both the primary and fallback regions can't satisfy the requirements, when a change would materially increase cost, when a new permission is required, or when you need approval for Azure commands that create or change resources beyond the already-approved plan. Keep every resource minimal, but make the web interface neat and approachable: small code footprint, responsive layout, clear status messages, accessible labels, and simple styling that is polished rather than austere.
Verify prerequisites and report only missing items: .NET SDK, Docker Desktop running, PowerShell, Azure CLI signed in, permission to create app registrations and grant admin consent, `sqlpackage`, .NET Aspire tooling, and the DAB CLI. Use these docs while building:
- DAB CLI reference: https://learn.microsoft.com/azure/data-api-builder/command-line/
- `dab configure` OBO options: https://learn.microsoft.com/azure/data-api-builder/command-line/dab-configure
- `dab validate`: https://learn.microsoft.com/azure/data-api-builder/command-line/dab-validate
- DAB MCP overview: https://learn.microsoft.com/azure/data-api-builder/mcp/overview
- OBO concept: https://learn.microsoft.com/azure/data-api-builder/concept/security/authenticate-on-behalf-of
- User-delegated auth configuration: https://learn.microsoft.com/azure/data-api-builder/configuration/data-source#user-delegated-auth
Create this structure under the sample folder:
- `azure-infra/` for Bicep, `azure-up.ps1`, `azure-down.ps1`, `entra-setup.ps1`, `entra-teardown.ps1`, `resources.bicep`, and `post-provision.ps1`.
- `data-api/` for `dab-config.json` and a DAB Dockerfile that bakes the config into the image.
- `database/` for a SQL Database Project, seed data, and `Views/WhoAmI.sql`.
- `web-app/` for static HTML, CSS, and JavaScript with MSAL browser support.
- `aspire-apphost/` for build orchestration only.
- `mcp-inspector/` for MCP Inspector container assets and nginx same-origin proxy config.
Handle secrets first. Add `.env`, `**/bin`, and `**/obj` to `.gitignore` before writing secrets or local configuration. Store the API client secret only in local `.env` files for local preparation and in Azure Key Vault or Azure Container Apps secrets for Azure. Never inline secret values in Bicep, PowerShell scripts, generated JSON, logs, or reports. Generate secret references for Container Apps instead of plaintext environment values. Never print tokens, passwords, or client secret values. Redact all secret values as `***redacted***`.
Configure DAB CORS before you start or deploy the web app. Do not leave `runtime.host.cors.origins` as `[]`. Set it to include the exact web app origins, including scheme and port: any local web origin used for development and the deployed Azure Container Apps web FQDN. Keep `allow-credentials` set to `false` because this SPA sends bearer tokens, not browser credentials or cookies. Direct REST, GraphQL, or Swagger requests can succeed even when the browser blocks JavaScript fetch calls, so browser-origin CORS must be configured and validated separately.
Use this DAB CLI workflow for config shaping and validation where possible:
```dotnetcli
dab init --database-type mssql --connection-string "@env('DATABASE_CONNECTION_STRING')" --auth.provider EntraID --auth.audience "@env('ENTRA_AUDIENCE')" --auth.issuer "@env('ENTRA_ISSUER')" --rest.enabled true --graphql.enabled true --mcp.enabled true
dab configure --data-source.user-delegated-auth.enabled true --data-source.user-delegated-auth.provider EntraId --data-source.user-delegated-auth.database-audience "https://database.windows.net"
dab add WhoAmI --source dbo.vw_WhoAmI --source.type view --source.key-fields "UserName" --permissions "authenticated:read" --mcp.dml-tools true
dab validate --config data-api/dab-config.json
```
Use a bare Azure SQL connection string so DAB can inject the per-user OBO access token. Do not include `User ID`, `Password`, or `Authentication`.
```text
Server=tcp:<server>.database.windows.net,1433;Database=<database>;Encrypt=True;TrustServerCertificate=False;
```
Use this DAB data-source shape for OBO:
```json
{
"data-source": {
"database-type": "mssql",
"connection-string": "@env('DATABASE_CONNECTION_STRING')",
"user-delegated-auth": {
"enabled": true,
"provider": "EntraId",
"database-audience": "https://database.windows.net"
}
}
}
```
Create `database/Views/WhoAmI.sql` to prove SQL sees the signed-in user.
```sql
CREATE VIEW dbo.vw_WhoAmI AS
SELECT CAST(SUSER_NAME() AS nvarchar(256)) AS UserName;
```
Implement the SPA with MSAL browser. `web-app/dab.js` must send bearer tokens to DAB on every protected request.
```javascript
export async function getAuthHeaders() {
const token = await acquireAccessToken();
return { Authorization: `Bearer ${token}` };
}
```
For Azure, bake `dab-config.json` into the DAB image. Do not rely on volume mounts in Azure Container Apps.
```dockerfile
FROM mcr.microsoft.com/azure-databases/data-api-builder:latest
COPY dab-config.json /App/dab-config.json
```
Before any Azure post-provision command, list the exact `az`, `az acr`, `az containerapp`, and `sqlpackage` commands you intend to run and wait for explicit user approval. Post-provision in this order: deploy dacpac, set the Azure SQL Microsoft Entra admin, create contained database users or groups for validation, grant access to demo objects and `WhoAmI`, replace placeholders, build and push the DAB image, then update Container Apps.
```powershell
dotnet build database/database.sqlproj -c Release
sqlpackage /Action:Publish /SourceFile:database/bin/Release/database.dacpac /TargetConnectionString:"$sqlConn" /p:BlockOnPossibleDataLoss=false
az acr build --registry $acrName --image dab-api:latest --file ./data-api/Dockerfile ./data-api/
az containerapp update --name $dabAppName --resource-group $resourceGroup --image "$acrName.azurecr.io/dab-api:latest"
```
Deploy MCP Inspector with a same-origin proxy pattern and set `MCP_SERVER_URL` to the DAB `/mcp` endpoint.
```nginx
location /mcp {
proxy_pass http://127.0.0.1:6277;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
}
```
Deploy SQL Commander with env var `ConnectionStrings__db` and ensure the connection string includes `TrustServerCertificate=true`.
```text
ConnectionStrings__db=Server=<server>.database.windows.net;Database=<database>;User Id=<user>;Password=<password>;TrustServerCertificate=true
```
Validation must prove OBO, not only API authentication:
- A direct Azure SQL query confirms the database is reachable, the deployed objects exist, and required contained users or groups exist.
- DAB `/health` returns a 2xx response.
- The web site returns a successful HTTP response.
- A browser-origin request from each web app origin receives an `Access-Control-Allow-Origin` response header that matches that origin.
- The web app signs in with Microsoft Entra ID.
- Signed-in REST, GraphQL, and MCP calls include bearer headers and reach DAB under the `authenticated` role.
- The `WhoAmI` entity returns the signed-in user's UPN from `SUSER_NAME()`.
- `WhoAmI` does not return the DAB managed identity, service principal, or Container App identity.
- Anonymous REST and GraphQL calls return `401`.
- The DAB Container App database connection string contains no SQL password, no `User ID`, and no `Authentication` keyword.
- The API client secret exists only as a secret reference or redacted local value.
- MCP Inspector connects to DAB MCP with streamable HTTP.
- SQL Commander can browse the deployed schema.
- Required contained users or groups exist in Azure SQL.
Do not report final URLs, asset locations, or a success summary until you directly verify database connectivity and query results, a 2xx DAB health response, and a successful web site response. This validation ensures the sample works without requiring the developer to check.
Troubleshoot with these checks:
- OBO token exchange fails: verify the API app has Azure SQL Database delegated `user_impersonation` permission and admin consent.
- SQL login fails for a token-identified principal: add the signed-in user or group as a contained user in the database.
- DAB returns 401 for valid bearer tokens: verify audience and issuer values in `dab-config.json`.
- SQL sees the service identity instead of the user: verify `user-delegated-auth`, the API client secret, and the bare SQL connection string.