c# - HttpClient calling a Windows-Authenication ApiController Method...but no WindowsIdentity coming along for the ride -


is there way api controller iidentity of account initiated call api controller when api-controller using windows-authentication ?

my "castcontroller.user.identity" (of type) windowsidentity. "empty". empty, : isauthenticated = false, , empty username. isn't null, "empty".

my "webtier" iis application running custom apppool , iidentity runs custom apppool "mydomain\myserviceaccount". i'm trying "castcontroller.user.identity.name" value service account.

(i guess client able connect webapitier valid windows-account, i'm mentioning in case throwing weird monkey wrench)

my "webtier" (mvc application) has method:

you'll notice 2 ways i'm using usedefaultcredentials. (aka, i've been trying figure out 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 */              {                 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;         /* above "empty" */          ////string username = actioncontext.requestcontext.principal;/*  web api v2  */         string username = string.empty;         apicontroller castcontroller = actioncontext.controllercontext.controller apicontroller;         if (null != castcontroller)         {             username = castcontroller.user.identity.name;              /* above "empty" */         }          return true;     } } 

}

again. i'm not doing "double hop" (that i've read in few places).
both tiers on same domain (and local development, they're on same machine)....

the funny thing i've read ( how httpclient pass credentials along request? ) , "problem" reported there how want mine work. (?!?!).

for development, "webapitier" running under full iis. "webtier", i've tried under iis-express , full-fledge iis.

i ran console app program 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 null.  not expected.");         }     }     else     {         throw new httprequestexception(response.reasonphrase + " " + response.requestmessage);     } 

same result other code. "empty" windows identity.

i went through this

http://www.iis.net/configreference/system.webserver/security/authentication/windowsauthentication

just sanity check.

ok. figured out issue. post.

how windows user name when identity impersonate="true" in asp.net?

//start quote//

with in application , anonymous access enabled in iis, see following results:

system.environment.username: computer name page.user.identity.name: blank system.security.principal.windowsidentity.getcurrent().name: computer name 

//end quote

so i'll include full answer.......to show issue , possible settings need tweaked.

go , download mini example.

https://code.msdn.microsoft.com/asp-net-web-api-tutorial-8d2588b1

this give quick "webapitier" called productsapp (productsapp.csproj).

if want yourself....just create webapi controller...that returns products.

public class productscontroller : apicontroller {     product[] products = new product[]      {          new product { id = 1, name = "tomato soup", category = "groceries", price = 1 },          new product { id = 2, name = "yo-yo", category = "toys", price = 3.75m },          new product { id = 3, name = "hammer", category = "hardware", price = 16.99m }      };      [identitywhitelistauthorization]     public ienumerable<product> getallproducts()     {         return products;     }  } 

open above .sln.

add new "class library" csproj called "webapiidentitypoc.domain.csproj".

create new class in library.

namespace webapiidentitypoc.domain {     public class product     {         public int id { get; set; }         public string name { get; set; }         public string category { get; set; }         public decimal price { get; set; }     } } 

delete (or comment out)

\productsapp\models\product.cs

add (project) reference in productsapp webapiidentitypoc.domain.

fix namespace issue in

\productsapp\controllers\productscontroller.cs

//using productsapp.models; using webapiidentitypoc.domain;  namespace productsapp.controllers {     public class productscontroller : apicontroller     { 

(you're moving "product" object library server , client can share same object.)

you should able compile @ point.

..........

add new "console application" projec solution.

webapiidentitypoc.consoleone.csproj

use nuget add "newtonsoft.json" reference/library webapiidentitypoc.consoleone.csproj.

add references (framework or extensions using right-click/add references on "/references folder in csproj)

system.net.http system.net.http.formatting system.net.http.webrequest (this 1 may not needed) 

add project reference webapiidentitypoc.domain.

in "program.cs" in console app, paste code: .............

namespace webapiidentitypoc.consoleone {     using system;     using system.collections.generic;     using system.linq;     using system.net;     using system.net.http;     using system.net.http.headers;     using system.security.principal;     using system.text;     using system.threading.tasks;     using newtonsoft.json;     using webapiidentitypoc.domain;      public class program     {         private static readonly string webapiexampleurl = "http://localhost:47503/api/products/getallproducts"; /* check productsapp.csproj properties, "web" tab, "iis express" settings if there issue */          public static void main(string[] args)         {             try             {                 system.security.principal.windowsidentity ident = system.security.principal.windowsidentity.getcurrent();                 if (null != ident)                 {                     console.writeline("will identity '{0}' show in identitywhitelistauthorizationattribute ???", ident.name);                 }                  runhttpclientexample();                 runwebclientexample();                 runwebclientwicexample();             }             catch (exception ex)             {                 system.text.stringbuilder sb = new system.text.stringbuilder();                 exception exc = ex;                 while (null != exc)                 {                     sb.append(exc.gettype().name + system.environment.newline);                     sb.append(exc.message + system.environment.newline);                     exc = exc.innerexception;                 }                  console.writeline(sb.tostring());             }              console.writeline("press enter exit");             console.readline();         }          private static void runwebclientexample()         {             /* articles said httpclient not pass on credentials because of async operations, these "experiments" using older webclient.  stick httpclient if can */             webclient webclient = new webclient();             webclient.usedefaultcredentials = true;             string serviceurl = webapiexampleurl;             string json = webclient.downloadstring(serviceurl);             ienumerable<product> returnitems = jsonconvert.deserializeobject<ienumerable<product>>(json);             showproducts(returnitems);         }          private static void runwebclientwicexample()         {             /* articles said httpclient not pass on credentials because of async operations, these "experiments" using older webclient.  stick httpclient if can */             system.security.principal.windowsidentity ident = system.security.principal.windowsidentity.getcurrent();             windowsimpersonationcontext wic = ident.impersonate();             try             {                 webclient webclient = new webclient();                 webclient.usedefaultcredentials = true;                 string serviceurl = webapiexampleurl;                 string json = webclient.downloadstring(serviceurl);                 ienumerable<product> returnitems = jsonconvert.deserializeobject<ienumerable<product>>(json);                 showproducts(returnitems);             }                         {                 wic.undo();             }         }          private static void runhttpclientexample()         {             ienumerable<product> returnitems = null;              httpclienthandler handler = new httpclienthandler()             {                 usedefaultcredentials = true, 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 (httpclient client = new httpclient(handler))             {                 client.defaultrequestheaders.accept.add(new mediatypewithqualityheadervalue("application/json"));                  string serviceurl = webapiexampleurl;                  httpresponsemessage response = client.getasync(new uri(serviceurl)).result;                  var temp1 = response.tostring();                 var temp2 = response.content.readasstringasync().result;                  if (response.issuccessstatuscode)                 {                     task<ienumerable<product>> wrap = response.content.readasasync<ienumerable<product>>();                     if (null != wrap)                     {                         returnitems = wrap.result;                     }                     else                     {                         throw new argumentnullexception("task<ienumerable<product>>.result null.  not expected.");                     }                 }                 else                 {                     throw new httprequestexception(response.reasonphrase + " " + response.requestmessage);                 }             }              showproducts(returnitems);         }          private static void showproducts(ienumerable<product> prods)         {             if (null != prods)             {                 foreach (product p in prods)                 {                     console.writeline("{0}, {1}, {2}, {3}", p.id, p.name, p.price, p.category);                 }                  console.writeline(string.empty);             }         }     } } 

you should able compile , run , see products display in console app.

.....

in "productsapp.csproj", add new folder.

/webapiextensions/

under folder, add new file:

identitywhitelistauthorizationattribute.cs

paste in code:

using system; using system.collections.generic; using system.linq; using system.web; using system.web.http; using system.web.http.controllers;  namespace productsapp.webapiextensions {     public class identitywhitelistauthorizationattribute : system.web.http.authorizeattribute     {         public identitywhitelistauthorizationattribute()         {         }          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;             var test2 = system.security.principal.windowsidentity.getcurrent();              ////string username = actioncontext.requestcontext.principal.name;/*  web api v2  */             string dingdingdingusername = string.empty;             apicontroller castcontroller = actioncontext.controllercontext.controller apicontroller;             if (null != castcontroller)             {                 dingdingdingusername = castcontroller.user.identity.name;             }          string status = string.empty;         if (string.isnullorempty(dingdingdingusername))         {             status = "not good.  no dingdingdingusername";         }         else         {             status = "finally!";         }              return true;         }     } } 

decorate webapimethod attribute.

    [identitywhitelistauthorization]     public ienumerable<product> getallproducts()     {         return products;     } 

(you'll have resolve namespace).

at point, should able compile....and run.

but dingdingdingusername string.empty. (the original issue spanned post).

ok..

click (left-click once) productsapp.csproj in solution explorer.

look @ properties tab. (this not "right-click / properties ::: properties show (default in bottom right of vs) when left-click productsapp.csproj.

you'll see several settings, there 2 of interest:

anonymous authentication | enabled windows authentication | enabled 

(note, above how these settings show in vs gui. show in .csproj file)

           <iisexpressanonymousauthentication>enabled</iisexpressanonymousauthentication> <iisexpresswindowsauthentication>enabled</iisexpresswindowsauthentication> 

if set

anonymous authentication | disabled 

(which shows in .csproj this:

<iisexpressanonymousauthentication>disabled</iisexpressanonymousauthentication> <iisexpresswindowsauthentication>enabled</iisexpresswindowsauthentication> 

)

voila! "dingdingdingname" value should show up.

the link have above .. points anonymous-authenication-enabled being issue.

but here long example show direct effects...in regards httpclient.

one more caveat learned along way.

if cannot alter

anonymous authentication enabled/disabled windows authentication enabled/disabled 

settings, need adjust "master settings".

in iis express, in file like:

c:\users\myusername\documents\iisexpress\config\applicationhost.config

the “master settings” need allow local settings overridden.

<sectiongroup name="security">     <section name="anonymousauthentication" overridemodedefault="allow" />     <!-- other stuff -->     <section name="windowsauthentication" overridemodedefault="allow" />     </sectiongroup> 

the authentications need turned on @ master level.

<security>     <authentication>      <anonymousauthentication enabled="true" username="" />      <windowsauthentication enabled="true">                     <providers>                                     <add value="negotiate" />                                     <add value="ntlm" />                     </providers>     </windowsauthentication>      </authentication> 

(full iis have similar settings in

c:\windows\system32\inetsrv\config\applicationhost.config

)

bottom line:

httpclient can send on windowsidentity of process running httpclient code....using httpclienthandler and if webapitier set windowsauthentication and anonymous-authentication turned off.

ok. hope helps in future.


Comments

Popular posts from this blog

yii2 - Yii 2 Running a Cron in the basic template -

asp.net - 'System.Web.HttpContext' does not contain a definition for 'GetOwinContext' Mystery -

c# - MSDN OneNote Api: Navigate to never before opened page without opening a OneNote Application Window -