如何在 C# MVC 中使用 Azure AD 实现 post-注销

How to implement a post-logout with Azure AD in C# MVC

我正在开发一个使用 Azure AD 进行用户身份验证的 Web 应用程序。但是,我很难在用户成功注销后将其重定向到主页。我试过按照这个 documentation,但这不是我正在寻找的解决方案。

In SignOut() in HomeController.cs,有没有不返回的替代方法

SignOut(new AuthenticationProperties { RedirectUri = callbackUrl }, 
        CookieAuthenticationDefaults.AuthenticationScheme, 
        OpenIdConnectDefaults.AuthenticationScheme)

这是我的:

Index.cshtml

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
    <p>@ViewBag.test</p>
    @if (User?.Identity?.IsAuthenticated ?? false)
    {
        <h2>User is login</h2>
        <a asp-controller="Home" asp-action="SignOut">Sign out</a>
    }
    else
    {
        <h2>User is not login</h2>
    }
</div>

HomeController.cs

using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web;
using System.Net.Http.Headers;
using Microsoft.AspNetCore.Authorization;
using HealthAD.Models;
using HealthAD.Graph;

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

namespace HealthAD.Controllers;

[Authorize(Policy = "OpenIdConnect")]
[AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
public class HomeController : Controller
{
    private readonly GraphProfileClient _graphProfileClient;
    private readonly ITokenAcquisition _tokenAcquisition;
    private readonly ILogger<HomeController> _logger;
    public string UserDisplayName { get; private set; } = "";

    public HomeController(
                        ILogger<HomeController> logger,
                        GraphProfileClient graphProfileClient,
                        ITokenAcquisition tokenAcquisition
                        )
    {
        _logger = logger;
        _graphProfileClient = graphProfileClient;
        _tokenAcquisition = tokenAcquisition;
    }

    public async Task<ActionResult<Microsoft.Graph.User?>> Test()
    {
        // await OnGetAsync();
        var test = await _graphProfileClient.GetUserProfile();
        return test;
    }

    [AllowAnonymous]
    public IActionResult Index()
    {
        ViewBag.test = "Test";
        return View();
    }

    [HttpGet]
    public IActionResult SignOut()
    {
        var callbackUrl = Url.Action(nameof(SignedOut), "Home", values: null, protocol: Request.Scheme);
        return SignOut
        (
            new AuthenticationProperties { RedirectUri = callbackUrl },
            CookieAuthenticationDefaults.AuthenticationScheme,
            OpenIdConnectDefaults.AuthenticationScheme
        );
    }

    [HttpGet]
    public IActionResult SignedOut()
    {
        if (User?.Identity?.IsAuthenticated ?? false)
        {
            return RedirectToAction(nameof(HomeController.Index), "Index");
        }
        return RedirectToAction(nameof(HomeController.Index), "Index");
    }

    public async Task<IActionResult> Privacy()
    {
        var test = await _graphProfileClient.GetUserProfile();

        ViewBag.name = test.DisplayName;

        return View();
    }

    public async Task OnGetAsync()
    {
        var user = await _graphProfileClient.GetUserProfile();
        UserDisplayName = user.DisplayName;
    }
}

Program.cs

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Identity.Web;
using HealthAD.Graph;

var builder = WebApplication.CreateBuilder(args);

var initialScopes = builder.Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(" ");

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services
    .AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
    .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
    .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
    .AddInMemoryTokenCaches();

builder.Services.AddAuthorization(configurations =>
{
    configurations.AddPolicy("OpenIdConnect", new AuthorizationPolicyBuilder()
        .AddAuthenticationSchemes(OpenIdConnectDefaults.AuthenticationScheme)
        .RequireAuthenticatedUser().Build()
    );
});

builder.Services.AddScoped<GraphProfileClient>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();
app.UseAuthentication();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

appsettings.json

{
    "AzureAd": {
        "Instance": "https://login.microsoftonline.com/",
        "ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "TenantId": "xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "Domain": "domain.com",
        "CallbackPath": "/signin-oidc",
        "SignedOutCallbackPath ": "/signout-oidc",
        "ClientSecret": "xxxxx~xxxxxx-xxxxxx-xxxxxxxxxxxxxxxxx"
    },
    "DownstreamApi": {
        "BaseUrl": "https://graph.microsoft.com/v1.0",
        "Scopes": "user.read"
    },
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft.AspNetCore": "Warning"
        }
    },
    "AllowedHosts": "*"
}

请注意,中间件中的顺序必须如下所示,这一点很重要。

 app.UseAuthentication();
    app.UseAuthorization();

尝试在您的应用程序中构建注销 URI,以便当用户单击注销 link 或按钮时,您将用户重定向到该 URI。 注销 URI 的格式为:

https://login.microsoftonline.com/{0}/oauth2/logout?post_logout_redirect_uri=

Also note that without session the logout redirect failsi.e; If we would use the request "https://login.microsoftonline.com/common/oauth2/v2.0/logout?post_logout_redirect_uri=https%3A%2F%2Flocalhost%2Fmyapp%2Flogout%2F%3F&client_id=;" without a session, it would like take you to the page which shows, "Successfully logged out", but it won't redirect, as AzureAD, won't just redirect without a proper session since that's not a safe practice.

  • 如果需要,还可以转到门户中的应用程序注册并设置 注销 url 作为回复 url.

请检查AzureAD microsoft authentication library for-js issues

  • 在 AccountController 中,尝试修改回调重定向 url。作为一个 解决方法尝试建立你自己的 AccountController 之类的东西 下面并将用户重定向到: https://login.microsoftonline.com/common/oauth2/logout然后 注销重定向回您的应用主页。

     public class AccountController : Controller
        {
            [HttpGet]
           public IActionResult SignIn()
          {
             ...
           }
    
         [HttpGet]
         public IActionResult SignOut()
         {
             var callbackUrl = Url.Action(nameof(SignedOut), "Account", values: null, protocol: Request.Scheme);
             return SignOut(
                 new AuthenticationProperties { RedirectUri = callbackUrl },
                 CookieAuthenticationDefaults.AuthenticationScheme,
                 OpenIdConnectDefaults.AuthenticationScheme);
         }
    
    
         [HttpGet]
         public IActionResult SignedOut()
         {
             if (User.Identity.IsAuthenticated)
             {
                 // Redirect to home page if the user is authenticated.
                 return RedirectToAction(nameof(HomeController.Index), "Home");
             }
    
             return RedirectToAction(nameof(HomeController.Index), "pathtoberedirectedto");
         }
    

参考文献:

  1. 如何在使用 Azure AD 身份验证时指定自定义注销 URL 在 .NET 核心中 - 堆栈溢出