Is there a way for my api controller to get the IIdentity of the account who initiated the call to the api controller when the api-controller is using windows-authentication ?
My "castController.User.Identity" is (of type) WindowsIdentity. But it is "empty". Empty, as is : IsAuthenticated = false, and an empty UserName. It isn't null, it is "empty".
My "WebTier" is an IIS application running with an custom AppPool and the IIdentity which runs the custom AppPool is something like "mydomain\myServiceAccount". I'm trying to get the "castController.User.Identity.Name" value to be this service account.
(I guess it could be any client who is able to connect to my WebApiTier with a valid windows-account, but I'm mentioning this just in case it could be throwing a weird monkey wrench)
My "WebTier" (Mvc Application) has this method:
You'll notice 2 ways I'm using UseDefaultCredentials. (Aka, I've been trying to figure this out for a bit)
private async Task<HttpResponseMessage> ExecuteProxy(string url)
{
HttpClientHandler handler = new HttpClientHandler()
{
UseDefaultCredentials = true
};
handler.PreAuthenticate = true;
WebRequestHandler webRequestHandler = new WebRequestHandler();
webRequestHandler.UseDefaultCredentials = true;
webRequestHandler.AllowPipelining = true;
webRequestHandler.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequired;
webRequestHandler.ImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Identification;
using (var client = new HttpClient(handler)) /* i've tried webRequestHandler too */
{
Uri destinationUri = new Uri("http://localhost/MyVirtualDirectory/api/mycontroller/mymethod");
this.Request.RequestUri = destinationUri;
return await client.SendAsync(this.Request);
}
}
"WebApiTier" Setup.
web.config
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<authentication mode="Windows" />
"WebApiTier" Code
public MyController : ApiController
{
[ActionName("MyMethod")]
[MyCustomAuthorization]
public IEnumerable<string> MyMethod()
{
return new string[] { "value1", "value2" };
}
}
public class MyCustomAuthorizationAttribute : System.Web.Http.AuthorizeAttribute
{
private string CurrentActionName { get; set; }
public override void OnAuthorization(HttpActionContext actionContext)
{
this.CurrentActionName = actionContext.ActionDescriptor.ActionName;
base.OnAuthorization(actionContext);
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var test1 = System.Threading.Thread.CurrentPrincipal;
/* the above is "empty" */
////string userName = actionContext.RequestContext.Principal;/* Web API v2 */
string userName = string.Empty;
ApiController castController = actionContext.ControllerContext.Controller as ApiController;
if (null != castController)
{
userName = castController.User.Identity.Name;
/* the above is "empty" */
}
return true;
}
}
}
Again. I'm not doing a "double hop" (that I've read about in a few places).
Both tiers are on the same domain (and local development, they're on the same machine)....
The funny thing is that I've read this ( How to get HttpClient to pass credentials along with the request? ) and the "problem" reported there is EXACTLY how I want mine to work. (?!?!).
For development, the "WebApiTier" is running under full IIS. For "WebTier", I've tried it under IIS-Express and full-fledge IIS.
I also ran a console app program with this code:
Console App
IEnumerable<string> returnItems = null;
HttpClientHandler handler = new HttpClientHandler()
{
UseDefaultCredentials = true
};
handler.PreAuthenticate = true;
WebRequestHandler webRequestHandler = new WebRequestHandler();
webRequestHandler.UseDefaultCredentials = true;
webRequestHandler.AllowPipelining = true;
webRequestHandler.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequired;
webRequestHandler.ImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Identification;
HttpClient client = new HttpClient(handler);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string serviceUrl = "http://localhost/MyVirtualDirectory/api/mycontroller/mymethod";
HttpResponseMessage response = client.GetAsync(new Uri(serviceUrl)).Result;
var temp1 = (response.ToString());
var temp2 = (response.Content.ReadAsStringAsync().Result);
if (response.IsSuccessStatusCode)
{
Task<IEnumerable<string>> wrap = response.Content.ReadAsAsync<IEnumerable<string>>();
if (null != wrap)
{
returnItems = wrap.Result;
}
else
{
throw new ArgumentNullException("Task<IEnumerable<string>>.Result was null. This was not expected.");
}
}
else
{
throw new HttpRequestException(response.ReasonPhrase + " " + response.RequestMessage);
}
Same result as the other code. An "empty" Windows Identity.
I also went through this
http://www.iis.net/configreference/system.webserver/security/authentication/windowsauthentication
just as a sanity check.