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

ASP.NET MVC集成Entra登录后Request.IsAuthenticated返回false问题

OWIN集成Entra身份验证后Request.IsAuthenticated返回false的问题

在ASP.NET表单身份验证场景下使用Microsoft OWIN Cookie身份验证时,Request.IsAuthenticated会正常返回true。但集成Entra(原Azure AD)身份验证后,尽管登录流程能成功完成且可以获取到用户详情,Request.IsAuthenticated仍返回false。

相关代码

public partial class Startup
{
    private static string clientId = ConfigurationManager.AppSettings["AzureAd:ClientId"];
    private static string tenant = ConfigurationManager.AppSettings["AzureAd:TenantId"];
    private static string clientSecret = ConfigurationManager.AppSettings["AzureAd:ClientSecret"];
    private static string authority = $"https://login.microsoftonline.com/common/v2.0";

    private static string redirectUri = ConfigurationManager.AppSettings["AzureAd:RedirectUri"];

    private static string returnUri = ConfigurationManager.AppSettings["AzureAd:ReturnUri"];
    private static string graphScopes = ConfigurationManager.AppSettings["AzureAd:AppScope"];

    public void ConfigureAuth(IAppBuilder app)
    {
        try
        {
            app.CreatePerOwinContext(ApplicationDbContext.Create);
            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
            app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new Microsoft.Owin.PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
                }
            });

            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
            app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
            
            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                ClientId = clientId,
                Authority = authority,
                Scope = $"openid email profile offline_access User.Read Calendars.Read",
                RedirectUri = redirectUri,
                PostLogoutRedirectUri = redirectUri,
                
                TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    IssuerValidator = (issuer, token, tvp) =>
                    {
                        return issuer;
                    }
                },
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    AuthenticationFailed = OnAuthenticationFailedAsync,
                    AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync
                }
            });

            var cookieOptions = new CookieAuthenticationOptions();
            cookieOptions.CookieManager = new SystemWebCookieManager();
            app.UseCookieAuthentication(cookieOptions);
        }
        catch (Exception ex)
        {
            Logger.Log($"/Home/Error?debug={ex.Message}");
            throw ex;
        }
    }

    private async Task OnAuthorizationCodeReceivedAsync(AuthorizationCodeReceivedNotification notification)
    {
        notification.HandleCodeRedemption();

        var idClient = ConfidentialClientApplicationBuilder.Create(clientId)
            .WithRedirectUri(redirectUri)
            .WithClientSecret(clientSecret)
            .Build();

        var signedInUser = new ClaimsPrincipal(notification.AuthenticationTicket.Identity);
        HttpContext context = HttpContext.Current;
        var tokenStore = new SessionTokenStore(idClient.UserTokenCache, context, signedInUser);

        try
        {
            string[] scopes = graphScopes.Split(' ');

            var result = await idClient.AcquireTokenByAuthorizationCode(
                scopes, notification.Code).ExecuteAsync();

            var userDetails = await GraphHelper.GetUserDetailsAsync(result.AccessToken);

            tokenStore.SaveUserDetails(userDetails);
            notification.HandleCodeRedemption(null, result.IdToken);

        }
        catch (MsalException ex)
        {
            string message = "AcquireTokenByAuthorizationCodeAsync threw an exception";
            notification.HandleResponse();
            Logger.Log($"/Home/Error?message={message}&debug={ex.Message}");
            //notification.Response.Redirect($"/Home/Error?message={message}&debug={ex.Message}");
        }
        catch (Microsoft.Graph.ServiceException ex)
        {
            string message = "GetUserDetailsAsync threw an exception";
            notification.HandleResponse();
            Logger.Log($"/Home/Error?message={message}&debug={ex.Message}");
            //notification.Response.Redirect($"/Home/Error?message={message}&debug={ex.Message}");
        }
    }

    private static Task OnAuthenticationFailedAsync(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
    {
        notification.HandleResponse();
        string redirect = $"/Home/Error?message={notification.Exception.Message}";

        if (notification.ProtocolMessage != null && 
            !string.IsNullOrEmpty(notification.ProtocolMessage.ErrorDescription))
        {
            redirect += $"&debug={notification.ProtocolMessage.ErrorDescription}";
        }

        notification.Response.Redirect(redirect);

        return Task.FromResult(0);
    }
}

问题原因及修复方案

1. 移除重复的Cookie身份验证中间件

代码末尾额外注册了无配置的Cookie中间件,会干扰之前的应用Cookie配置。将SystemWebCookieManager整合到第一个Cookie中间件配置中,并删除重复注册:

// 修改第一个Cookie中间件
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new Microsoft.Owin.PathString("/Account/Login"),
    Provider = new CookieAuthenticationProvider
    {
        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
            validateInterval: TimeSpan.FromMinutes(30),
            regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
    },
    CookieManager = new SystemWebCookieManager() // 新增这一行
});

// 删除以下重复代码
// var cookieOptions = new CookieAuthenticationOptions();
// cookieOptions.CookieManager = new SystemWebCookieManager();
// app.UseCookieAuthentication(cookieOptions);

2. 添加外部身份到应用身份的转换逻辑

Entra登录生成的是外部身份,需要转换为应用Cookie身份才能让Request.IsAuthenticated返回true。在OpenIdConnectAuthenticationOptions的Notifications中添加SecurityTokenValidated处理:

Notifications = new OpenIdConnectAuthenticationNotifications
{
    AuthenticationFailed = OnAuthenticationFailedAsync,
    AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync,
    SecurityTokenValidated = async (context) =>
    {
        var externalIdentity = context.AuthenticationTicket.Identity;
        // 可根据需求添加自定义声明
        // externalIdentity.AddClaim(new Claim("CustomClaim", "Value"));

        var signInManager = context.OwinContext.Get<ApplicationSignInManager>();
        await signInManager.SignInAsync(null, null, externalIdentity, isPersistent: false);
        
        context.HandleResponse();
        context.Response.Redirect("/");
    }
}

3. 修正HandleCodeRedemption的调用方式

OnAuthorizationCodeReceivedAsync中重复调用HandleCodeRedemption会导致身份票证生成异常,移除第一个无参数调用:

private async Task OnAuthorizationCodeReceivedAsync(AuthorizationCodeReceivedNotification notification)
{
    // 移除这一行:notification.HandleCodeRedemption();

    var idClient = ConfidentialClientApplicationBuilder.Create(clientId)
        .WithRedirectUri(redirectUri)
        .WithClientSecret(clientSecret)
        .Build();

    // 其余代码保持不变
}

4. 简化Issuer验证逻辑

当前自定义IssuerValidator未做实际验证,可移除该配置使用默认逻辑:

TokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true
    // 移除IssuerValidator配置
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 22:03:13