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

Time:04-22

I'm working on a web app that uses Azure AD for user authentication. However, I am struggling to redirect the user to the home page after they successfully signed out. I've tried following this documentation, but this isn't the solution I am looking for.

In SignOut() in HomeController.cs, is there an alternative way without returning

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

This is what I have:

Index.cshtml

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

<div >
    <h1 >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": "*"
}

CodePudding user response:

Please note that the order in the middleware is must be like below which is important.

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

Try to construct a sign out URI in your application so that when the user clicks on the Logout link or button, you redirect your users to that URI. The format of a sign out URI is:

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://localhost/myapp/logout/?&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.

  • If required also go to app registration in portal and set the logout url as reply url.

Please check AzureAD microsoft authentication library for-js issues

  • In AccountController , try to modify the callback redirect url. As a workaround try to build you own AccountController something like below and Redirect the user to: https://login.microsoftonline.com/common/oauth2/logout And then logout redirect back to your app home page.

     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");
         }
    

References:

  1. How to specify custom logout URL when using Azure AD authentication in .NET core - Stack Overflow
  2. asp.net mvc 4 - Clear session on Logout MVC 4 - Stack Overflow
  • Related