You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Azure/混合加入设备以自身身份认证调用Azure受保护API?

以Azure AD设备身份调用API的实现方案

需求背景

我们需要在Azure加入/混合加入的Windows及Mac设备上运行服务或计划任务,以设备自身身份而非终端用户身份调用API——这和传统域环境中工作站用NetworkService账户访问Web服务、数据库的场景完全一致。

当前困境

设备注册流程会返回绑定本地私钥的设备专属证书,但找不到使用该证书获取API访问/ID令牌的文档或库方法;而与设备绑定的Primary Refresh Tokens (PRTs)因关联具体用户,不符合无用户上下文的需求。我们本质上需要适用于普通工作站(而非IoT设备)的X509证书设备认证方案。

实现方案

认证逻辑确认

你的判断正确:使用设备证书获取令牌的请求构造,和应用程序通过证书凭据认证的逻辑基本一致,核心是基于设备证书完成OAuth 2.0客户端证书认证流程,向Azure AD令牌端点请求令牌,步骤如下:

  1. 从设备本地存储(Windows证书库/Mac钥匙串)中获取注册生成的设备证书(含私钥)
  2. 用证书私钥签名生成JWT断言,断言需包含设备ID、租户ID等设备身份声明
  3. 向Azure AD令牌端点发送POST请求,携带client_id(设备的Azure AD对象ID)、grant_type=client_credentials、client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer、client_assertion(生成的JWT断言)、scope(目标API的权限范围)等参数

跨平台代码示例

Windows平台(MSAL.NET)

using Microsoft.Identity.Client;
using System.Security.Cryptography.X509Certificates;

// 从LocalMachine\My证书库获取设备证书
X509Certificate2 GetDeviceCertificate()
{
    using var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    store.Open(OpenFlags.ReadOnly);
    // 根据设备证书的主题或指纹筛选
    var certs = store.Certificates.Find(X509FindType.FindBySubjectContains, "Azure AD Device", validOnly: true);
    return certs.Count > 0 ? certs[0] : throw new InvalidOperationException("未找到设备证书");
}

// 初始化客户端并获取令牌
var certificate = GetDeviceCertificate();
var app = ConfidentialClientApplicationBuilder
    .Create("设备的Azure AD对象ID")
    .WithTenantId("你的租户ID")
    .WithCertificate(certificate)
    .Build();

var result = await app.AcquireTokenForClient(new[] { "api://目标API的客户端ID/.default" })
    .ExecuteAsync();
var accessToken = result.AccessToken;

Mac平台(MSAL Swift)

import MSAL
import Security

// 从钥匙串获取设备证书
func getDeviceCertificate() throws -> SecCertificate {
    let query: [CFString: Any] = [
        kSecClass: kSecClassCertificate,
        kSecAttrLabel: "Azure AD Device",
        kSecReturnRef: kCFBooleanTrue!,
        kSecMatchLimit: kSecMatchLimitOne
    ]
    var ref: AnyObject?
    let status = SecItemCopyMatching(query as CFDictionary, &ref)
    guard status == errSecSuccess, let cert = ref as? SecCertificate else {
        throw NSError(domain: "证书获取失败", code: Int(status))
    }
    return cert
}

// 初始化客户端并获取令牌
do {
    let certificate = try getDeviceCertificate()
    let clientId = "设备的Azure AD对象ID"
    let tenantId = "你的租户ID"
    let authority = "https://login.microsoftonline.com/\(tenantId)"
    
    let confidentialClient = try MSALConfidentialClientApplication(
        clientId: clientId,
        authority: authority,
        certificate: certificate
    )
    
    let parameters = MSALAcquireTokenForClientParameters(scopes: ["api://目标API的客户端ID/.default"])
    confidentialClient.acquireToken(with: parameters) { result, error in
        guard let authResult = result else {
            print("令牌获取失败: \(error?.localizedDescription ?? "未知错误")")
            return
        }
        let accessToken = authResult.accessToken
        // 使用令牌调用API
    }
} catch {
    print("初始化失败: \(error.localizedDescription)")
}

关键注意点

  • 权限配置:需在Azure AD中为设备身份分配目标API的应用权限(需管理员同意)
  • 证书管理:确保设备证书未过期,私钥未被篡改或从设备存储中删除
  • 库版本:使用最新版MSAL库,保证跨平台兼容性和功能完整性

内容的提问来源于stack exchange,提问作者John Gasper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 04:17:20