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

调用Microsoft Graph API获取访问令牌失败的技术咨询

问题描述

我尝试调用Microsoft Graph API读取用户邮件,但调用服务时遇到问题。目前已完成用户认证,但无法获取访问Graph API的令牌。我理解的流程为:

  • 重定向用户至Microsoft Identity Platform进行认证
  • 授权时需提供scopes、tenantId、clientId、clientSecret和授权码
  • 访问Graph API

控制器代码(Microsoft登录)

public IActionResult MicrosoftLogin()
{
    // Redirect user to Microsoft Identity Platform for authentication
    return Challenge(new AuthenticationProperties { RedirectUri = "/Home/MicrosoftCallback" }, OpenIdConnectDefaults.AuthenticationScheme);
}

回调代码

public async Task<IActionResult> MicrosoftCallback()
{
    try
    {

        var scopes = new[] { "User.Read" };

        // Multi-tenant apps can use "common",
        // single-tenant apps must use the tenant ID from the Azure portal
        var tenantId = "common";

        // Values from app registration
        var clientId = "YOUR_CLIENT_ID";
        var clientSecret = "YOUR_CLIENT_SECRET";

        var authorizationCode = "AUTH_CODE_FROM_REDIRECT";
  
        // using Azure.Identity;
        var options = new AuthorizationCodeCredentialOptions
        {
            AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
        };

        var authCodeCredential = new AuthorizationCodeCredential(
            tenantId, clientId, clientSecret, authorizationCode, options);

        var graphClient = new GraphServiceClient(authCodeCredential, scopes);

        var user = await graphClient.Users.GetAsync();
        var outlookmail = await graphClient.Me.Messages.GetAsync();
        ViewData["user"] = user;
        ViewData["mail"] = outlookmail;
        return View("MicrosoftHome");
    }
    catch (ServiceException ex)
    {
        Console.WriteLine("Error" + ex);
        return View();
    }
}

Program.cs代码

string[] initialScopes = configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
services
.AddAuthentication(options =>
{
    //options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    //options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
    options.DefaultScheme = "AppCookie";
    options.DefaultChallengeScheme = "MyAzureAdScheme";
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;

})
.AddCookie("AppCookie")
.AddGoogle(GoogleDefaults.AuthenticationScheme, googleOptions =>
{
    googleOptions.ClientId = builder.Configuration["Authentication:Google:ClientId"];
    googleOptions.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
    googleOptions.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");

    // Add the required scope for GMail API access
    googleOptions.Scope.Add("https://www.googleapis.com/auth/gmail.readonly");
    googleOptions.SaveTokens = true;

})
.AddMicrosoftIdentityWebApp(configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();

疑问

回调代码参考了Authorization Code Provider相关文档,不确定是否需要将scopes、tenantId、clientId、clientSecret替换为AAD注册应用的信息;若需要,请问如何获取授权码?


解决方案

1. 必须替换的配置项

是的,必须将代码中的占位符替换为AAD应用注册的真实信息:

  • scopes:当前仅配置了User.Read,不足以访问邮件,需添加Mail.Read,改为new[] { "User.Read", "Mail.Read" }
  • tenantId:多租户应用用common没问题,单租户应用需替换为Azure门户中你的租户ID
  • clientId:替换为AAD应用注册里的「应用程序(客户端)ID」
  • clientSecret:替换为AAD应用注册中生成的「客户端密码」(生成后无法再次查看,需妥善保存)

2. 授权码的正确获取方式

你当前手动写死AUTH_CODE_FROM_REDIRECT是错误的,授权码是用户完成认证后,Microsoft Identity Platform自动返回给回调地址的。结合你已使用Microsoft.Identity.Web库,推荐两种更简便的处理方式:

方式一:利用Microsoft.Identity.Web内置能力(推荐)

你已经在Program.cs中配置了AddMicrosoftGraph,可以直接在控制器中注入GraphServiceClient,无需手动处理令牌流程:

private readonly GraphServiceClient _graphServiceClient;

public HomeController(GraphServiceClient graphServiceClient)
{
    _graphServiceClient = graphServiceClient;
}

public async Task<IActionResult> MicrosoftCallback()
{
    try
    {
        var user = await _graphServiceClient.Me.GetAsync();
        var outlookmail = await _graphServiceClient.Me.Messages.GetAsync();
        ViewData["user"] = user;
        ViewData["mail"] = outlookmail;
        return View("MicrosoftHome");
    }
    catch (ServiceException ex)
    {
        Console.WriteLine("Error" + ex);
        return View();
    }
}

这种方式下,Microsoft.Identity.Web会自动处理令牌的获取、缓存和刷新,无需手动干预。

方式二:手动提取授权码(仅特殊场景使用)

如果一定要手动获取授权码,可从回调请求的HttpContext中提取:

var authorizationCode = await HttpContext.GetTokenAsync("code");

注意:授权码只能一次性使用,且需确保OpenIdConnect配置开启了令牌保存。

3. 额外配置检查

  • 确保AAD应用注册中已添加Mail.Read的委托权限,并完成管理员同意(租户内应用需此步骤)
  • 检查配置文件中的DownstreamApi:Scopes,确保包含Mail.Read,例如:"DownstreamApi:Scopes": "User.Read Mail.Read"
  • Program.cs中重复设置了DefaultChallengeScheme,建议删除冗余行,保留options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;即可

内容的提问来源于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.13 21:30:39