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

在DeviceCodeCallback中在视图展示Microsoft Graph API设备代码凭证的问题

问题描述

尝试使用设备代码凭证(Device Code Credential)调用Microsoft Graph API,但无法在DeviceCode视图中显示设备代码。点击登录按钮触发的控制器代码如下:

public async Task<IActionResult> MicrosoftLogin()
{            
    var tenantId = "my-tenant-id";            
    var clientId = "my-client-id";
    var message = "";
    var options = new DeviceCodeCredentialOptions
    {
        AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
        ClientId = clientId,
        TenantId = tenantId,
        DeviceCodeCallback = (code, cancellation) =>
        {
            message = code.Message;
            return Task.FromResult(0);
        },
    };
    _deviceCodeCredential = new DeviceCodeCredential(options);           
    ViewData["message"] = message;
    return View("DeviceCode");
}

期望在MicrosoftLogin控制器中获取设备代码并传递至视图,但由于DeviceCodeCallback的触发时机问题,无法实现。尝试为回调添加async/await也无效,只有在触发MicrosoftCallback控制器调用Graph API时,code.Message才会在控制台显示:

public async Task<IActionResult> MicrosoftCallback(string userEmail)
{            
    var scopes = new[] { "Mail.Read", "User.Read" };
    graph = new GraphServiceClient(_deviceCodeCredential, scopes);
    var messages = await graph.Me.Messages
        .GetAsync(requestConfiguration =>
        {
            // 配置细节
        });
}

需要实现在DeviceCode视图中直接显示设备代码的需求。

解决方案

核心问题分析

DeviceCodeCredential的回调只有在实际发起令牌请求时才会被触发,仅实例化DeviceCodeCredential不会启动设备代码流。之前的代码中,只有在MicrosoftCallback里调用Graph API方法时,才会触发令牌请求,进而执行DeviceCodeCallback,导致设备代码无法提前传递到视图。

优化实现方案

通过主动触发令牌请求,并使用TaskCompletionSource等待设备代码生成,同时将令牌请求放在后台执行,避免阻塞控制器返回视图:

public async Task<IActionResult> MicrosoftLogin()
{
    var tenantId = "my-tenant-id";
    var clientId = "my-client-id";
    // 提前声明需要请求的权限范围
    var scopes = new[] { "Mail.Read", "User.Read" };
    string deviceCodeMessage = string.Empty;
    // 用于等待设备代码回调执行完成
    var codeReceivedTcs = new TaskCompletionSource<string>();

    var options = new DeviceCodeCredentialOptions
    {
        AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
        ClientId = clientId,
        TenantId = tenantId,
        DeviceCodeCallback = (code, cancellation) =>
        {
            deviceCodeMessage = code.Message;
            // 标记设备代码已获取
            codeReceivedTcs.SetResult(deviceCodeMessage);
            return Task.CompletedTask;
        },
    };

    _deviceCodeCredential = new DeviceCodeCredential(options);

    // 后台执行令牌请求,不阻塞控制器返回视图
    _ = Task.Run(async () =>
    {
        try
        {
            await _deviceCodeCredential.GetTokenAsync(
                new TokenRequestContext(scopes),
                CancellationToken.None);
            // 可在此处添加认证完成后的逻辑,比如记录状态、跳转提示等
        }
        catch (OperationCanceledException)
        {
            // 处理用户取消认证的情况
        }
        catch (Exception ex)
        {
            // 处理其他异常,比如超时、权限错误等
        }
    });

    // 等待设备代码生成完成
    await codeReceivedTcs.Task;

    ViewData["message"] = deviceCodeMessage;
    return View("DeviceCode");
}

方案说明

  1. 提前声明权限范围:GetTokenAsync需要指定请求的权限范围,需与后续Graph API调用的范围一致。
  2. 使用TaskCompletionSource:确保控制器等待DeviceCodeCallback返回设备代码后,再将代码传递到视图。
  3. 后台执行令牌请求:通过Task.Run将GetTokenAsync放在后台执行,避免控制器动作阻塞,用户能立即看到显示设备代码的视图。
  4. 异常处理:捕获认证过程中的取消、超时等异常,避免后台任务报错影响主流程。

后续回调优化

用户完成设备代码认证后,可通过页面自动刷新、按钮点击等方式触发MicrosoftCallback动作,此时_deviceCodeCredential已持有有效令牌,可直接调用Graph API获取数据。

内容的提问来源于stack exchange,提问作者Wu Bang Zheng

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 20:42:11