6

I am using Azure AAD B2C to manage my users and auth flow. I am trying to create a user experience where the user can log in under a different account. The user should be able to Log Out of their account and then click Sign In and be able to provide a different username and password.

However, currently I sign out. I can confirm through F12 debugger that all cookies are cleared. Then I click sign in and it signs me back into my previous account without challenging me for a username and password.

I'm not sure what is happening or why. Here is my Sign In code.

public IActionResult SignIn([FromRoute] string scheme)
    {
        scheme ??= OpenIdConnectDefaults.AuthenticationScheme;
        var redirectUrl = Url.Content("~/");
        var properties = new AuthenticationProperties { RedirectUri = redirectUrl, AllowRefresh = true };
        properties.Items["policy"] = "B2C_1_SignUpIn";
        return Challenge(properties, scheme);
    }

Here is my Sign Out code.

public async Task<IActionResult> SignOutAsync([FromRoute] string scheme)
    {
        HttpContext.Session.Clear();
        if (HttpContext.Request.Cookies.Count > 0)
        {
            var test = HttpContext.Request.Cookies.ToList();
            var siteCookies = HttpContext.Request.Cookies.Where(c => c.Key.Contains(".AspNetCore.") || c.Key.Contains("Microsoft.Authentication"));
            foreach (var cookie in siteCookies)
            {
                Response.Cookies.Delete(cookie.Key);
            }
        }

        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);

        return Redirect("~/");
    }

My Startup.cs looks like this.

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.AddDistributedMemoryCache();
        services.AddMicrosoftIdentityWebAppAuthentication(
            Configuration, 
            "AzureAdB2C", 
            OpenIdConnectDefaults.AuthenticationScheme, 
            CookieAuthenticationDefaults.AuthenticationScheme);

        services.AddMvc();
        services.AddSession();  
        services.AddRazorPages();
        services.AddControllersWithViews();

        services.Configure<OpenIdConnectOptions>(Configuration.GetSection("AzureAdB2C"));
    }

    // 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();
            app.UseMigrationsEndPoint();
        }
        else
        {
            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.UseSession();
        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

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

        app.UseEndpoints(endpoints =>
        {
                //endpoints.MapAreaControllerRoute(  
                //                  name: "Identity",  
                //                  areaName: "Identity",  
                //                  pattern: "Identity/{controller=Home}/{action=Index}");  
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}");
            endpoints.MapRazorPages();
        });
    }
}

My UI references the User.Identity.IsAuthenicated to determine if user information is present. Any help why it is behaving this way by automatically signing me in after I have signed out would be very helpful. I am new to OIDC and Azure B2C but I feel like I'm missing something very basic. Thanks in advance.

Rob S.
  • 1,044
  • 7
  • 25
  • Looks to be a duplicate of https://stackoverflow.com/questions/56874202/logout-redirect-with-azure-b2c-and-net-core – evan Aug 10 '22 at 16:51
  • I disagree. My redirect works fine. – Rob S. Aug 11 '22 at 01:19
  • Are you redirecting to Azure B2C's `end_session_endpoint`? It looks like you are redirecting to "~/". – evan Aug 11 '22 at 18:24
  • The redirect is where it redirects to after hitting the end_session_endpoint which is achieved through the built in SignOutAsync function. This is my understanding, but I may be wrong. I will try this and get back. – Rob S. Aug 11 '22 at 22:01

3 Answers3

2

Diving deep into Microsoft docs for over a month I finally found this document.

Microsoft Docs - OIDC send a sign-out request

I had actually read through this doc a few times and then today I finally noticed a little asterisk.

client-id requirement

Up until this point I had only been sending required parameters in the query string of my get request. Even though client_id is marked as not required, it is in my case because I am using Application isolation SSO and require ID token on logout request is set to "No".

I changed my redirect uri by adding the client_id query parameter to the logout uri. My final SignOut Action looks like the following.

public IActionResult SignOut([FromRoute] string scheme)
    {
        HttpContext.Session.Clear();
        if (HttpContext.Request.Cookies.Count > 0)
        {
            var test = HttpContext.Request.Cookies.ToList();
            var siteCookies = HttpContext.Request.Cookies.Where(c => c.Key.Contains(".AspNetCore.") || c.Key.Contains("Microsoft.Authentication"));
            foreach (var cookie in siteCookies)
            {
                Response.Cookies.Delete(cookie.Key);
            }
        }
        var redirectUrl = Url.Content($"https://mytenantname.b2clogin.com/mytenantname.onmicrosoft.com/myuser_flow_policy_name/oauth2/v2.0/logout?post_logout_redirect_uri=https://localhost:44370/&client_id=my_app_registraion_client_guid");
        var properties = new AuthenticationProperties { RedirectUri = redirectUrl };
        
        return SignOut(properties, scheme);
    }
Rob S.
  • 1,044
  • 7
  • 25
  • 1
    Yikes, I hate to think how long I would have been struggling with this if not for this answer. Thanks so much for following up OP. – ajbeaven Sep 12 '22 at 05:35
  • 1
    Spent the past 2 days looking for a way to solve this issue not sure how I missed this but thank you so much! – Jackington Apr 20 '23 at 15:48
0

Are you also clearing the logged in status on the Azure side? As Azure B2C is your Identity provider you will need to log the user out from both your application and B2C to force them to have to re enter their credentials.

This can be done by redirecting your user to the sign out URL from the OpenId config to clear the auth cookies from the B2C side as well after you have cleared the auth cookies for your application.

You can even include a redirect URL as a query parameter to redirect the user back to a specific page on your website.

Alex B
  • 1
  • I believe this is what the line HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme) does. If not please provide instructions on how to do that properly. If it solves my problem I'll count this as the solution. Thanks. – Rob S. Aug 11 '22 at 12:06
  • I redirected to a different page on sign out and the next time i clicked sign in it authenticates my previous user without challenging a password. – Rob S. Aug 18 '22 at 13:09
0

Add the below method to AccountController and the signout should clear the local as well as remote login. This should also handle the signout redirect. Hope this helps.

[HttpGet("{scheme?}")]
    public IActionResult SignOut(
        [FromRoute] string scheme, 
        [FromQuery] string redirectUri)
    {
        scheme ??= OpenIdConnectDefaults.AuthenticationScheme;
        string redirect;
        if (!string.IsNullOrEmpty(redirectUri) && Url.IsLocalUrl(redirectUri))
        {
            redirect = redirectUri;
        }
        else
        {
            redirect = Url.Content("~/")!;
        }
        return SignOut(
             new AuthenticationProperties
             {
                 RedirectUri = redirect,
             },
             CookieAuthenticationDefaults.AuthenticationScheme,
             scheme);
    }
Jeevan
  • 43
  • 5
  • This behaves the exact same way. When I click sign in it auto authenticates. – Rob S. Aug 18 '22 at 13:08
  • If you are using b2c custom policy, can you try adding SM-AAD as session provider in the technical profiles you are using? – Jeevan Aug 19 '22 at 10:09