In this post, I share what I have learned about integrated windows authentication and how to enable it in a web application which consists of an angular front-end and ASP.NET core 3 backend.
Let me explain by giving an example. At work, my computer is joined to a domain controller, which is basically a server that runs active directory. Joining a domain controller means the domain controller manages my credentials, not my computer. When I login using my windows credentials, my computer communicates with the domain controller to validate my credentials and allow access. We have .NET applications running on IIS on a set of servers that are joined to the domain controller. IIS can check against the domain controller to ensure I have authenticated before granting access. Furthermore, it can work with the browser do so seamlessly without requiring me to enter my credentials because it has built in integrated windows authentication. This is possible because both the server on which IIS runs and the browser on my machine are joined to a same domain controller, and the browser supports the Negotiate authentication scheme. From the document, this is an advantage of integrated windows authentication.
Built into IIS. – Does not send the user credentials in the request. – If the client computer belongs to the domain (for example, intranet application), the user does not need to enter credentials
Integrated Windows Authentication
Hopefully, you now have some ideas about integrated windows authentication. Next, let’s look at how it works.
Per the document, integrated windows authentication
works with any browser that supports the Negotiate authentication scheme, which includes most major browsers.
Integrated Windows Authentication
The Negotiate authentication scheme is Microsoft’s authentication mechanism which uses Kerberos which is a system that validates a user’s identity based on shared secrets and provides access by issuing tickets.
Here is how it works.
To access a protected resource, the client must present a valid ticket to the server. To obtain the ticket, the client sends a request to a Key Distribution Center (KDC). The client encrypts the request using the user’s credentials. Upon receiving the encrypted request, the KDC retrieves the user’s password from active directory given the username, and uses the password to decrypt the request. By way of encrypting and decrypting the request using the user’s password which the KDC can get from the database, the KDC can verify the user’s identity without having the client sending over the password. Once the client receives the ticket, which the KDC encrypts using a key that it shares with the resource server, the client sends over the ticket to the resource server, which in turn validates the ticket against the KDC using the shared key. Once all the validations are done, the server returns the resource to the client.
The above is just a high level summary. If you want to learn more about Kerberos and see examples, I suggest you watch this short video, read this blog and IETF article.
Hopefully, you now have some ideas about how integrated windows authentication works, let’s discuss when should you use it.
As a summarize, you should consider using integrated windows authentication if:
The document mentions integrated windows authentication is susceptible to cross-site request forgery, so just keep this in mind.
Now that you know about integrated windows authentication and how it works, let’s look at how you can implement it in your ASP.NET core application.
In my case, it turns out to be not difficult to configure my application and IIS to use integrated windows authentication. I just have to make a few changes in the app, and enable Windows authentication in IIS.
Set <WindowsAuthentication
> to true in applicationhost.config, which is under .vs -> {PROJECT_NAME} -> config directory. The .vs directory is hidden by default, so I enabled the option to show the hidden folders.
<windowsAuthentication enabled="true"> <providers> <add value="Negotiate" /> <add value="NTLM" /> </providers> </windowsAuthentication>
See this link for instructions on how to view hidden folder in Windows 10.
In launchSettings.json, which is under Properties folder of the ASP.NET core project, enable WindowsAuthentication under iisSettings:
{ "iisSettings": { "windowsAuthentication": true, "anonymousAuthentication": false, "iisExpress": { "applicationUrl": "http://localhost:61863/", "sslPort": 44378 } } }
Configure(...)
method, add these middlewares: app.UseAuthentication(); app.UseAuthorization();
Since the app is an ASP.NET core 3 app, per the document, I put the above middlewares between app.UseRouting()
and app.UseEndpoints()
.
If the app uses authentication/authorization features such as
Migrate from ASP.NET Core 2.2 to 3.0AuthorizePage
or[Authorize]
, place the call toUseAuthentication
andUseAuthorization
: after,UseRouting
andUseCors
, but beforeUseEndpoints
:
If you want to learn more, checkout this post on StackOverflow.
In ConfigureServices()
method, I added the following:
services.AddAuthentication(IISDefaults.AuthenticationScheme); services.AddAuthorization();
Technically, you don’t need to make any changes in angular for integrated windows authentication to work. Some tutorials online I looked at suggest to add to the header the key and value: withCredentials: true
. However, I realized that this is not necessary, and the authentication still work even after I removed the codes.It appears the browser automatically handles the process by the Negotiate authentication scheme.
It seems as if there is not a way to get info about the windows user from the client app. Therefore, to get the username and status of the windows user, I make the call to the backend.
[Route("GetAuthenticatedUser")] [HttpGet("[action]")] public IdentityUser GetUser() { return new IdentityUser() { Username = User.Identity?.Name, IsAuthenticated = User.Identity != null ? User.Identity.IsAuthenticated : false, AuthenticationType = User.Identity?.AuthenticationType }; } public class IdentityUser { public string Username { get; set; } public bool IsAuthenticated { get; set; } public string AuthenticationType { get; set; } }
@Injectable() export class AuthGuardService implements CanActivate { constructor(public router: Router, private apiService: ApiService) { } canActivate(): Observable<boolean> | Promise<boolean> | boolean { return this.apiService.User.pipe( take(1), switchMap(currentUser => { if (!currentUser) { return this.apiService.loadUser().pipe( take(1), switchMap(user => { return of(user && user.isAuthenticated); }), catchError((err: HttpErrorResponse) => { if (err) { console.error("Failed to load user: " + JSON.stringify(err)); if (err.status === 403) { this.apiService.setErrorMessage("You don't have access to use this application."); } else { this.apiService.setErrorMessage("Something went wrong! :("); } } return of(false); })); } return of(currentUser.isAuthenticated); })); } }
Integrated Windows Authentication
Enable Windows Authentication In Web API And Angular App
What is the difference between Negotiate and NTLM authentication?
Kerberos – authentication protocol
SPNEGO-based Kerberos and NTLM HTTP Authentication in Microsoft Windows
How to enable Windows authentication for a Web site, Web application, or Web service
Enhancing ASP.NET Core/Blazor App Security and Reusability with HttpMessageHandler and Named HttpClient
Why you need to register authentication middleware even if your ASP.NET core web API does not handle authentication.
Web scraping in C# using HtmlAgilityPack
Building multitenant application – Part 2: Storing value into database session context from ASP.NET core web API
Common frameworks, libraries and design patterns I use
Build and deploy a WebJob alongside web app using azure pipelines
Authenticate against azure ad using certificate in a client credentials flow
Notes on The Clean Architecture