如何让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令牌端点请求令牌,步骤如下:
- 从设备本地存储(Windows证书库/Mac钥匙串)中获取注册生成的设备证书(含私钥)
- 用证书私钥签名生成JWT断言,断言需包含设备ID、租户ID等设备身份声明
- 向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
相关产品推荐
相关产品推荐

