0

I have a ASP Core MVC application which uses AD authentication which is working well. I want to add an extra step to the signin, after the user is authenticated on AD, I want to verify if the user is authorized on a database, as my application can only be used by authenticated and authorized users.

This is want I have currently on Startup.cs:

        // Authentication:
        services.AddAuthentication(sharedOptions =>
        {
            sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddAzureAd(options =>
        {
            Configuration.Bind("AzureAd", options);
            AzureAdOptions.Settings = options;
        })
        .AddCookie(options =>
        {
            options.EventsType = typeof(CustomCookieAuthenticationEvents);
        });

I've implemented a custom class to override the sign in:

public override async Task SigningIn(CookieSigningInContext context)
    {
        var email = context.Principal.Claims.FirstOrDefault(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")?.Value;

        // Verifies if the user is configured on the database, if it's not access must be denied.
        try
        {
            var user = _managementDbContext.Users.Single(u => u.Email == email);

            // Add tenant and role information to the claims list
            context.Principal.Claims.Append(new Claim("organization", user.Tenant.Name));
            context.Principal.Claims.Append(new Claim("role", user.Role.Name));

            await base.SigningIn(context);
        }
        catch (InvalidOperationException)
        {
            //TODO redirect to another page
        }
    }

When I run the code (which database is configured to return an error and fail the authentication) the flow falls into the exception, but the authentication cookie is created either-way.

I was expecting to be able to prevent that. I'm wondering if I'm overriding the correct event.

Thank you in advance.

PS: I'm using .NET Core 2.2

1 Answer 1

1

The CookieAuthenticationEvents.SigningIn event is not actually useful to interrupt the sign-in process. There is no mechanism that stops it, and even if you set a redirect result on the HttpContext, the remainder of the CookieAuthenticationHandler will still establish the authentication cookie.

A better option is to prevent the cookie scheme from being called to sign in completely. So you want to hook into the process earlier, to fail the challenge that happens by AzureAD and the underlying OpenID Connect scheme.

Use the TicketReceived event of the OIDC scheme to hook into the process right before the sign-in happens. At that point, the user principal is fully constructed so you can access all of its properties. And then just validate that user, and fail accordingly if the user should not be signed in.

Something like this (untested):

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAd(options =>
{
    Configuration.Bind("AzureAd", options);
});

services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
    // TicketReceived event is called when the authentication process
    // is completed but right before the SignIn happens
    options.Events.OnTicketReceived = async (context) =>
    {
        var user = context.Principal;

        userValidator = context.HttpContext.Services.GetService<IUserValidator>();
        var isValid = await userValidator.ValidateUser(user);

        if (!isValid)
        {
            context.Fail("User is not allowed");
            context.Response.Redirect("/error/user-not-allowed");
            return;
        }

        context.Success();
    };
});
Sign up to request clarification or add additional context in comments.

3 Comments

Okay so I had to remove the configuration of AzureAD authentication and replace it by OpenIDConnect. I was able to see the code somehow working, but it seems that the is still up. I'm wondering if I have to force an sign out.
You shouldn’t remove the AzureAD authentication. AzureAD sets up both Cookie and OpenIDConnect by itself, you just might need to add some additional configuration on top of it.
I found that adding `context.HandleResponse();' after the redirect prevented the cookie from being created. The idea came from this solution. However, I'm not entirely sure if this is the proper way to prevent sign-in.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.