在 .net core 3.1.1 Web 应用程序中更改 AzureAD 身份验证的 redirectURI 和 CallbackPath

Change redirectURI and CallbackPath for AzureAD authentication in .net core 3.1.1 web app

我使用 Razor Pages 框架创建了一个新的 .net core 3.1.1 Web 应用程序。 创建应用程序时,我将默认身份验证设置为 AzureAd。 当我 运行 应用程序时,身份验证工作正常。 生成的 appsettings 文件如下所示:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "myDomain",
    "TenantId": "myTenantId",
    "ClientId": "myClientId",
    "CallbackPath": "/signin-oidc"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

我在我的应用程序中创建了一个新的控制器,它看起来非常简单,就像:

namespace WebApplication1.Controllers
{
    public class AccountController : Controller
    {
        [HttpGet]
        public void SignIn()
        {
           //here comes the logic which checks in what role is the logged User
           //the role management stuff will be implemented in the app
        }
    }
}

这是我的 Startup.cs 的样子:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
            .AddAzureAD(options => Configuration.Bind("AzureAd", options));
        services.AddMvc(options =>
        {
            options.EnableEndpointRouting = false;
        });
        services.AddRazorPages().AddMvcOptions(options =>{});
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/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.UseAuthentication();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
            endpoints.MapControllers();
        });

        app.UseMvc(routes =>
       {
            routes.MapRoute(
            name: "default",
            template: "{controller=Account}/{action=SignIn}");
       });
    }
}

我希望能够将 AzureAd/CallbackPath 更改为与“/signin-oidc”不同的内容,例如。我想将其更改为 Account/SignIn。然后我想捕获来自 azure 的回调调用,并根据记录的用户电子邮件地址修改令牌以添加一些系统角色,并根据用户角色重定向到适当的仪表板页面。管理员和客户端可以有不同的仪表板。

所以我尝试更改 "CallbackPath": "/Account/SignIn" 并且还在 Azure 中更新了 RedirectURI:

然后我再次 运行 应用程序,在 void SignIn() 中设置断点,我再次登录,而不是点击 /Account/SignIn 我只是被重定向到主页,the https://localhost:44321。 我还尝试在浏览器中手动 运行 和 https://localhost:44321/Account/SignIn,我看到以下错误消息:

An unhandled exception occurred while processing the request.
Exception: OpenIdConnectAuthenticationHandler: message.State is null or empty.

我试图检查 documentation 中是否有任何东西,但我没有找到任何有用的东西。 关于我应该怎么做才能让它发挥作用的任何想法? 干杯

编辑:

我也用Microsoft.AspNetCore.Authentication.AzureAD.UI框架。

CallbackPath 是服务器在身份验证期间重定向的路径。它由 OIDC 中间件本身自动处理,这意味着我们无法通过创建新的 controller/action 并为其设置 CallbackPath 来控制逻辑。以下是大致流程:

在身份验证过程中,整个过程由 OpenID Connect 中间件控制,用户在 Azure 的登录页面验证凭据后,Azure Ad 会将用户重定向回应用程序的重定向 url,这是在 OIDC 的配置中设置的,所以您可以获得授权代码(如果使用代码流)并完成身份验证过程。身份验证后,用户将被重定向到重定向 url .

based on the logged user email address I'd like to modify the token to add some system roles and make a redirect to the appropriate dashboard page based on the user role. There can be a different dashboard for admin and a client.

首先,您不能修改令牌,也不需要修改它。

您可以在 OIDC OWIN 中间件中使用通知事件,它会调用以使开发人员能够控制身份验证过程。 OnTokenValidated 为您提供了修改从传入令牌中获取的 ClaimsIdentity 的机会,您可以根据本地数据库中的用户 id 查询用户的角色并添加到用户的声明中:

 services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
            .AddAzureAD(options => Configuration.Bind("AzureAd", options));


services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
    options.Events = new OpenIdConnectEvents
    {
        OnTokenValidated = ctx =>
        {
            //query the database to get the role

            // add claims
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Role, "Admin")
            };
            var appIdentity = new ClaimsIdentity(claims);

            ctx.Principal.AddIdentity(appIdentity);

            return Task.CompletedTask;
        },
    };
});

然后在 controller 中,你可以得到这样的声明:

var role = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;

然后您可以根据特定声明过滤操作。

如果您想在身份验证后将用户重定向到特定的 route/page,请将 url 设置为 AuthenticationProperties :

if (!User.Identity.IsAuthenticated)
{
    return Challenge(new AuthenticationProperties() { RedirectUri = "/home/redirectOnRole" } , AzureADDefaults.AuthenticationScheme);
}  

并且在该路径中,您可以根据用户的角色重定向用户。

多亏了 的回答,我在通过 AzureAd 登录后设法建立了到我想要的路由的重定向,并且我还设法根据登录的角色重定向到正确的页面用户。

我将 _LoginPartial 修改为:

@using HighElo.Web.Extensions
<ul class="navbar-nav">
    @if (User.Identity.IsAuthenticated)
    {
        <li class="nav-item">
            <span class="navbar-text text-dark">Logged as: <b>@User.GetEmail()</b></span>
        </li>
        <li class="nav-item">
            <a class="nav-link text-dark" asp-area="AzureAD" asp-controller="Account" asp-action="SignOut">Sign out</a>
        </li>
    }
    else
    {
        <li class="nav-item">

            <a class="nav-link text-dark" href="/Account/SignIn">Sign in</a>
        </li>
    }
</ul>

我在我的根目录中创建了一个新控制器,如下所示:

[AllowAnonymous]
public class AccountController : Controller
{
    [Route("Account/SignIn")]
    public IActionResult SignIn()
    {
        if (!User.Identity.IsAuthenticated)
        {
            return Challenge(new AuthenticationProperties() { RedirectUri = "/Account/Callback" }, AzureADDefaults.AuthenticationScheme);
        }

        return Forbid();
    }

    [Authorize]

    public IActionResult Callback()
    {
        if (User.IsInRole("Client"))
        {
            //redirect to the Clients area
            return LocalRedirect("/Clients/Dashboard");
        }
        //here comes other role checks

        return Forbid();
    }
}