Windows authentication for remote user in ASP.NET Core application with Entity Framework

2

Background

I have an ASP.NET Core application running in Http.sys to provide Windows authentication.

The client side of my application works with Windows authentication and users are prompted for their Windows login credentials when first accessing the site. All good with that.

I am using a SQL Server database (hosted on the same server as the app) which is built by Entity Framework using the code first approach. In the startup.cs file I am running a dbContext.migrate() to ensure the database is up to date and then the app continues as normal.

The Issue

When a remote user is interacting with the site and performs an action that requires database access, I would like that database transaction to be performed under their username.

For instance, if I run the Asp.net core application on my server as User1 and then access the webapp from a user's machine who is logged in as User2 the database interactions are still being performed by User1 as that is the user the main application is running under.

I would like for it to be possible to make the database interactions be performed by User2 instead of User1 in this instance. Mainly for the purpose of logging and analysis.

When retrieving an instance of the dbContext I am supplying it with a connection string that states to use SSPI for it's authentication. Is there a way to include the remote user's username into this connection in order for them to connect to the database as themselves? I can easily retrieve the remote user's credentials/username from my controller. I believe I can use something called Impersonation but I am unsure of the best practices of implementing that or if it is a recommended solution.

Any assistance will be greatly appreciated.

Update

Thanks to some useful feedback I have tried to implement the following:

My connection string is set as so in the startup.cs:

services.AddDbContext<MyDbContext>(options => options.UseSqlServer(@"Server=MyServer\MSSQLSERVER;Database=MyDatabase;Trusted_Connection=True;"));

Then I perform an action such as this in my controller/class:

        var values = new List<Values>();

        WindowsIdentity.RunImpersonated(user.AccessToken, () =>
        {
            var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
            optionsBuilder.UseSqlServer(@"Server=MyServer\MSSQLSERVER;Database=MyDatabase;Trusted_Connection=True;");

            using (var ctx = new MyDbContext(optionsBuilder.Options))
            {
                values = (from row in ctx.Table select row).OrderBy(x => x.Id).ToList();
            }
        });

The user variable is passed into the function and is of type WindowsIdentity.

Executing the code from localhost works fine but then when a remote user tries it returns the following error: System.Data.SqlClient.SqlException: 'A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - No such host is known.)

Update 2

I managed to resolve this error by using an IP address for the connection string instead of the server/machine name. So now my connection string looks like this: @"Server=<IP address>\MSSQLSERVER;Database=ConfigDb;Trusted_Connection=True

Now I am facing another issue where the SQL Server is stating that No connection could be made because the target machine actively refused it. I can connect to the SQL Server using this user through SQL Management Studio but not through my app.

I found in EventViewer that the actual error is related to SSPI: SSPI handshake failed with error code 0x8009030c, state 14 while establishing a connection with integrated security; the connection has been closed. Reason: AcceptSecurityContext failed. The operating system error code indicates the cause of failure. The logon attempt failed [CLIENT: <named pipe>]. Not really sure why this would be failing.

c#
entity-framework
asp.net-core
windows-authentication
http.sys
asked on Stack Overflow Nov 28, 2018 by Weagertron • edited Nov 30, 2018 by Vishal Parmar

2 Answers

0

ASP.NET Core Docs cover this exact topic.

Simply put, ASP.NET Core does not implement impersonation. It has an example of a workaround using WindowsIdentity but it has its limitations and should only be used for relatively simple operations.

There is also a NuGet package which may help you get some more functionality, but still is limited.

answered on Stack Overflow Nov 28, 2018 by Adam Vincent
0

Is there a way to include the remote user's username into this connection in order for them to connect to the database as themselves?

It would not work with providing only username for connectionstring, you also need the password for the current user.

I believe I can use something called Impersonation but I am unsure of the best practices of implementing that or if it is a recommended solution.

For RunImpersonated, follow steps below:

  • Configure connectionstring with Windows Credential instead of username and password.

     services.AddDbContext<IISWindowsDbContext>(options =>options.UseSqlServer(@"Server=localhost\MSSQLSERVER01;Database=IISWindows;Trusted_Connection=True;"));
    
  • Useage

        public IActionResult About()
    {
        IList<Blog> blogs = new List<Blog>();
        var user = (WindowsIdentity)User.Identity;
        WindowsIdentity.RunImpersonated(user.AccessToken, () =>
        {
            var impersonatedUser = WindowsIdentity.GetCurrent();
            blogs = _context.Blogs.ToList();
        });
        return Ok(blogs);
    }
    
answered on Stack Overflow Nov 29, 2018 by Edward

User contributions licensed under CC BY-SA 3.0