proper implementation of "windows" authentication in web api? - c#

I've created a Web Api 2 app which will only be used on the corporate network. I've read about Windows authentication in Web API so it seems to be possible. But I need to figure out the proper implementation for this. I've included the following xml in my Web.config:
<system.web>
<authentication mode="Windows" />
</system.web>
I seem to remember some type of event hook in old school webforms app. Something like BeginRequest() where a security check could be made before rendering a page. I included the following line of code as the first line in one of my controller methods but the returned value appears to just be an empty object without any meaningful info:
var identity = HttpContext.Current.User.Identity as WindowsIdentity;
Does Web API 2 support Windows authentication? Am I missing a step? Should Windows authentication work if I submit a general request from Postman for testing? I also tried this code but got a similar empty object:
var x = RequestContext.Principal;
I vaguely recall an IIS setting like "Enable Integrated Security." Can you please specify the exact setting? And would I be able to accomplish this if I'm running the app on IIS Express?
UPDATE
I followed the steps for IIS Express mentioned in one of the answers below but the code samples that I provided in my original post still didn't get a populated user object. I also updated applicationhost.config file to turn off anonymous authentication:
<anonymousAuthentication enabled="false" userName="" />
After I made that updated I resubmitted my test request via Postman but I get the following error:
<h3>HTTP Error 401.2 - Unauthorized</h3>
<h4>You are not authorized to view this page due to invalid authentication headers.</h4>
</div>
<div class="content-container">
<fieldset>
<h4>Most likely causes:</h4>
<ul>
<li>No authentication protocol (including anonymous) is selected in IIS.</li>
<li>Only integrated authentication is enabled, and a client browser was used that does not support integrated authentication.</li>
<li>Integrated authentication is enabled and the request was sent through a proxy that changed the authentication headers before they reach the Web server.</li>
<li>The Web server is not configured for anonymous access and a required authorization header was not received.</li>
<li>The "configuration/system.webServer/authorization" configuration section may be explicitly denying the user access.</li>
</ul>
</fieldset>
</div>
<div class="content-container">
<fieldset>
<h4>Things you can try:</h4>
<ul>
<li>Verify the authentication setting for the resource and then try requesting the resource using that authentication method.</li>
<li>Verify that the client browser supports Integrated authentication.</li>
<li>Verify that the request is not going through a proxy when Integrated authentication is used.</li>
<li>Verify that the user is not explicitly denied access in the "configuration/system.webServer/authorization" configuration section.</li>
<li>Check the failed request tracing logs for additional information about this error. For more information, click
here.
</li>
</ul>
</fieldset>
</div>
Do I need to configure my Postman request with some type of special header in order for this to work?

In addition to the previous answers, we also need to Pass credentials in cross-origin requests.
Server Side (Web API):
Set the SupportsCredentials property to true on the [EnableCors] attribute:
[EnableCors(origins: "http://exampleclient.com", headers: "*",
methods: "*", SupportsCredentials = true)]
Client Side (UI):
Set XMLHttpRequest.withCredentials to true.
jQuery:
$.ajax({
type: 'get',
url: 'http://www.example.com/api/auth',
xhrFields: {
withCredentials: true
}
Angular:
this.http.get('http://www.example.com/api/auth', { withCredentials: true }).subscribe((resp: any) => {
console.log(resp)
}
XMLHttpRequest:
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://www.example.com/api/auth');
xhr.withCredentials = true;

If you are using IIS Express, you need to update applicationhost.config file.
This is the file version of the IIS configuration tool where you can configure the web server itself. you can find this file in the following directory:
%userprofile%\documents\iisexpress\config\applicationhost.config
or
%userprofile%\my documents\iisexpress\config\applicationhost.config
When you find it, update it as:
<windowsAuthentication enabled="true">
<providers>
<add value="Negotiate" />
<add value="NTLM" />
</providers>
</windowsAuthentication>
For IIS:
Select your Application
Double Click - 'Authentication'
Enable Windows Authentication
Restart IIS Server
Check this for more details

Windows authentication that uses the local domain user and that is intended for intranet sites.
Example :
I implemented a TestAuthentication method/action with a fixed route path. For the demo I do not include Authorize attributes yet. The code checks the User property of the ApiController. This contains the same data as Thread.CurrentPrincipal or HttpContext.Current.User. Make sure Anonymous Authentication in IIS is disabled otherwise the Identity.Name will be empty.
public class WinAuthController : ApiController
{
[HttpGet]
[Route("api/testauthentication")]
public IHttpActionResult TestAutentication()
{
Debug.Write("AuthenticationType:" + User.Identity.AuthenticationType);
Debug.Write("IsAuthenticated:" + User.Identity.IsAuthenticated);
Debug.Write("Name:" + User.Identity.Name);
if (User.Identity.IsAuthenticated)
{
return Ok("Authenticated: " + User.Identity.Name);
}
else
{
return BadRequest("Not authenticated");
}
}
}
In Web.config file :
<system.web>
<authentication mode="Windows" />
</system.web>
In IE you can check the setting with Tools > Internet Options > Advanced and look for a setting Enable Windows Integrated Authentication. When you go to the tab Security and then Intranet and Custom Level, then you will find a setting at the bottom to specify if IE should logon automatically or prompt for the username and password.
Please visit below link, it has proper steps to follow for WEP API Windows authentication :
http://www.scip.be/index.php?Page=ArticlesNET38&Lang=EN

Below are the steps to configure windows authentication in web api for both local and server (IIS).
1) For Local:
a) To create a web api project in windows authentication mode, follow below steps:
After choosing ASP.Net Web Application, select Web API template and from the right side click Change Authentication button and select Windows Authentication.
b) For an existing web api project, just add the following lines in your applicationhost.config file.
<location path="YourProjectName">
<system.webServer>
<security>
<authentication>
<anonymousAuthentication enabled="false" />
<windowsAuthentication enabled="true" />
</authentication>
</security>
</system.webServer>
</location>
2) For Server (IIS)
To run windows authentication after hosting the application in IIS just add following lines in your web.config file inside system.web node:
<authentication mode="Windows" />
<authorization>
<allow verbs="OPTIONS" users="?" />
<deny users="?" />
</authorization>
In both the cases, just use the following lines in your code that windows authentication is working properly:
if(User.Identity.IsAuthenticated)
{
//do work
}

Related

Automatic Redirect on Authorize Failure Using IIS Automatic Authentication with ASP.NET Core MVC

I am creating a website in ASP.Net Core MVC and using IIS's automatic authentication to have users of the company automatically be validated through active directory. This is working as it should at the moment, however, I'd like to redirect users to a log in page if they can not be validated through IIS. I've seen other posts describing how to do this when using a normal authentication cookie, but not with this automatic authentication.
This is my current Configure Services method:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.Configure<IISServerOptions>(options =>
{
options.AutomaticAuthentication = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
So at the moment it is successfully locking out users who do not have AD authorization on a page marked [Authorize], I'd just like to redirect that locked out traffic to another page marked [AllowAnonymous]. Is there a way to modify the services.Configure method? So far I do not see anything.
What you're using is called Windows Authentication. The reference to AutomaticAuthentication just tells IIS to automatically set HttpContext.User with the user details (otherwise it doesn't, even if the authentication succeeded).
What you're looking for is really two authentication methods: For people with AD accounts (who are logged in with those accounts), log them in automatically using Windows Authentication. Otherwise, show a login screen and you can handle the authentication yourself.
I've done this before in ASP.NET MVC, but I haven't (yet) done it in ASP.NET Core. I'll describe what I did, but this isn't a complete solution since there's a missing piece for ASP.NET Core.
Use cookies for your main authentication method. Set it up like normal, with a login page where you validate the credentials and set the cookie.
Setup one controller action (lets say, /Login/SSO) that uses Windows Authentication. I did that using a location tag in the web.config. That is still likely the same in ASP.NET Core, I'm just not entirely sure how you pick up on that in the code. But the part in web.config would look something like this:
<location path="Login/SSO">
<system.webServer>
<security>
<authentication>
<windowsAuthentication enabled="true"/>
<anonymousAuthentication enabled="false"/>
</authentication>
</security>
</system.webServer>
<system.web>
<authorization>
<allow users="?"/>
</authorization>
</system.web>
</location>
In the code for that action, assuming you can figure out how to grab the username from the Windows Authentication, you set the cookie and return a 200 OK (you don't need to return a view). If Windows Authentication fails, IIS will return a 401 on its own. (I think the <allow users="?"/> was just to prevent the cookie authentication from trying to redirect - but I can't remember)
The fancy trick is on the login page. Hide everything on the page by default. On page load, do an AJAX request to /Login/SSO. If it succeeds, then the user is logged in and you can forward them (via javascript) to the page they want. If it fails, just show the contents of the login page. That would look something like this (assuming you're using jQuery):
$(function() {
$.get("#Url.Action("SSO", "Login")").done(function(data) {
//success! forward to the page they want
window.location.replace(returnUrl);
}).fail(function() {
//failed - show login prompt
$("#loginBox").show();
});
});
With this setup, a user who can be authenticated using Windows authentication will hit the site, get redirected to the login page, the login page does a successful AJAX request that creates their cookie, and they get redirected back to the original page they wanted. Pretty seamless.

IIS MVC/WCF Certificate authentication on specified pages

The problem I am facing is that I only want the clients (end users) to present a certificate on specified pages within the MVC/WCF application.
When I set the IIS options "Client certificates = Accept" the site always requires a certificate when browsed, shouldn't be this way when setting the option to "Required" ?
Googled for a while but web.config configurations to override the IIS certificate check and only do certificate check on certain pages does not turn up many results.
In a simplified way what I want to accomplish is:
User browses Home page
Reads some news articles
Goes to a page that requires authentication
Make the user/client present a certificate
Here on I will make some checks to the certificate programatically (this bit I have solved)
I don't provide any code cause I don't have any, everything I have is the certificate option in IIS.
Figured out the answer.
Location tag and in the applicationHost.config allow override the "access" section
applicationHost.config
<section name="access" overrideModeDefault="Allow" />
Application web.config
<location path="PathToSvcFile.svc">
<system.webServer>
<security>
<authentication>
<anonymousAuthentication enabled="true"/>
</authentication>
<access sslFlags="Ssl, SslNegotiateCert"/>
</security>
</system.webServer>

Asp.Net Forms Authentication with Subdomains

I am running a single asp.net 4.5.2 application using mvc 5. I have custom routines made to handle subdomains for each Area of the application.
I have my user auth within one of the Areas (Profile), which is it's own subdomain. In the navigation bar, there is a login form that POSTs to the Login() action of the Profile controller. Since this is a subdomain, I am setting the domain info for the auth manually to have it work across all subdomains.
For the life of me, I cannot figure out how to get it to work. I've tried setting the Form Auth domain to the TLD, the TLD with a . in front, with the forms info in webconfig, and without.
Here are the important bits concerning forms auth:
Web.Config
<system.web>
<authentication mode="Forms">
<forms domain=".teknik.io" protection="All" enableCrossAppRedirects="true" name="TeknikAuth" />
</authentication>
</system.web>
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
</modules>
</system.webServer>
Profile Controller
public ActionResult Login(LoginViewModel model)
{
...
authcookie.Name = "TeknikAuth";
authcookie.HttpOnly = true;
authcookie.Secure = true;
authcookie.Domain = string.Format(".{0}", Config.Host); // ".teknik.io"
Response.Cookies.Add(authcookie);
...
}
Update 1
I have determined that it is working on my dev domain (single domain), and when I then visit the main domain, the cookie is still working. The only difference between the two is that on dev, the login request is on the same subdomain, while on production, it is sending the request to another subdomain.
So I figured out what was wrong. When logging in (and setting the cookie), I was sending a post request to a different domain than the one I was currently on (profile.teknik.io/Login). This for some reason was not setting the proper cookie, so no auth was occurring. Once I moved the login to the parent domain, the auth works correctly across subdomains.
Update 1
The real issue was the ajax request for logging in. It did not have CORS enabled, so once I did that, and added the appropriate allow headers, the request would work and the cookies would be saved correctly.

WCF - Disable browser authentication for client app

I have created a WCF OData Service using .NET4.5 with C#. In the application, there is a form Basic authentication implemented that uses ASP.NET Identity which works well.
Following is my code in the service app, where I intercept the authorization header and authenticate the request.
protected override void OnStartProcessingRequest(ProcessRequestArgs args)
{
bool isAuthenticated = CustomBasicAuth.Authenticate(HttpContext.Current);
if (!isAuthenticated)
{
args.OperationContext.ResponseHeaders.Add("WWW-Authenticate", "Basic");
HttpContext.Current.Response.SuppressFormsAuthenticationRedirect = true;
throw new DataServiceException(401, "Invalid login or password");
}
base.OnStartProcessingRequest(args);
}
So whenever I access the service app in browser, it would prompt for the browser login. This is great for testing purpose.
Then I have a web based client which calls the service. On invalid login, it returns the 401 error, which is expected and I can handle it. But it also prompts the browser login form. How can I suppress this form in client app. (Disabling browser properties is not an option)
You can deny access to the service / location for un-authenticated users, through web.config:
<configuration>
<location path="Path/To/Folder/YourService.svc">
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>
...
</configuration>
The same pattern can be applied to any aspx page, asmx classic web service, etc.

IIS Password prompt for given folder ASP.NET MVC

How can I configure IIS to prompt for passwords on a web application running on ASP.NET MVC in IIS?
I want to password protect the Views\ApplicationLog\ folder, so that browsers ask for username and password when users try to access this folder.
How could I accomplish this? Can this be done directly from IIS, or do I need to set something in web.config?
Please try to be thorough, I don't know IIS all that well.
If you're using MVC, you shouldn't have anyone directly accessing anything under your Views folder at all. Instead, your Controllers (or their actions) will assert whatever authentication is required.
In this specific case, it looks like your controller is named ApplicationLogController. So you'd add an [Authorize] attribute on the controller as follows, and it will automatically ensure that anyone accessing the controller methods is authorized.
[Authorize] // You can also do [Authorize(Roles="MyRole,YourRole")], etc...
public class ApplicationLogController : Controller
{
...
}
In the IIS 6 manager, go to the "Directory Security" tab, click on "Edit" then check "Integrated Windows Authentication" and / or "Basic Authentication" depending on the modes you want to support.
In system.web within your app's web.config:
<authentication mode="Windows">
<authorization>
<deny users="?" />
</authorization>

Categories