Use App Pool Credentials for WebClient Request - c#

I would like to use the app pool credentials to avoid a double-hop issue from a web API method. However, I do not want all requests to be impersonated but just this one particular request. The code currently looks something like this:
[Route("api/mycontroller/mymethod")]
public string GetDataFromOtherInternalSystem(int id)
{
var client = new WebClient ( Credentials = CredentialCache.DefaultNetworkCredentials);
return client.DownloadString('http://internaldomain/api/method/id')
}
From what I understand of MSDN, the user context is the logged in user for that browser session (i.e. my account going through Active Directory and not the app pool's account).
The credentials returned by DefaultNetworkCredentials represents the
authentication credentials for the current security context in which
the application is running. For a client-side application, these are
usually the Windows credentials (user name, password, and domain) of
the user running the application. For ASP.NET applications, the
default network credentials are the user credentials of the logged-in
user, or the user being impersonated.
This then creates the double-hop issue which could be eliminated if the request comes cleanly from the web application as the service account (without me having to construct credentials on the fly).
Any ideas on how to impersonate the app pool without me specifying user credentials as follows:
var cred = new NetworkCredential("myusername", "mypassword")
Again I'm trying to avoid the other web service being properly set up for Kerberos or CORS.

This can be accomplished by passing a null pointer (IntPtr.Zero) to the static Impersonate method of the WindowsIdentity class. Here is how it is described in the MSDN document for the Impersonate method:
Calling the Impersonate(IntPtr) method with a userToken value of Zero is equivalent to calling the Win32 RevertToSelf function. If another user is currently being impersonated, control reverts to the original user.
Usage would look something like the following:
using (var impersonationContext = WindowsIdentity.Impersonate(IntPtr.Zero))
{
try
{
// this code is now using the application pool indentity
}
finally
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}
}

Related

Obtaining a token for a Web API using user credentials

I've created a new user account in my test AAD tenant, say testuser1#mytenant.onmicrosoft.com and set a password for it. This new account is a member of a security group that can access a specific Web API. I'm trying to write a test (a console program) that non-interactively obtains an access token using the user credentials and the app id as audience and then calls an endpoint.
How do I do that?
Update:
I'm trying to write a set of integration security tests for my Web API application. The application uses AAD groups it gets as a set of claims and treats them as roles. So I want a set of test user accounts with a known password with different roles to test behavior of an endpoint under different security contexts. The approach worked for me for years with classic AD (where I could impersonate a user using login/password pair and perform a SOAP call to a service with Windows Auth enabled).
Updated2:
I could use a set of app registrations instead of test user accounts and get a token without no problem using client_id/client_secret pair but assigning an enterprise application to a security group requires premium AAD tier which is very expensive :(
This is basically what Resource Owner Password Credentials (ROPC) grant flow is for.
You give Azure AD your app's credentials with a user's credentials and you get an access token.
This flow should not be used for authentication typically, as it exists in the standard mainly as a legacy upgrade path.
And it does not work with federated users, users with MFA or with an expired password.
However, your case of an automated test is one of the scenarios where I think its usage is acceptable.
Here is an example of the call in C#:
string tokenUrl = $"https://login.microsoftonline.com/joonasapps.onmicrosoft.com/oauth2/token";
var req = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
req.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "password",
["client_id"] = "23d3be1b-a671-4452-a928-78fb842cb969",
["client_secret"] = "REDACTED",
["resource"] = "https://graph.windows.net",
["username"] = "testuser#joonasapps.onmicrosoft.com",
["password"] = "REDACTED"
});
using (var client = new HttpClient())
{
var res = await client.SendAsync(req);
string json = await res.Content.ReadAsStringAsync();
}
ADAL.NET does not expose an overload for doing this AFAIK so you need to do it manually like this.
You'll need to replace the parameters with your app's credentials + your user's credentials of course.
The token URL also needs your tenant id or domain name.
Change the resource parameter to your API's client id/app ID URI.
By "non-interactively" are you referring to the login window? If so given the flow and architecture you've described, this is not possible. How else would you get the users credentials?
You should use this article as a reference when building your solution so you understand the various OAuth 2.0 flows and options, including those for a native application.
https://learn.microsoft.com/en-us/azure/active-directory/develop/authentication-scenarios#native-application-to-web-api

How to use Windows User Login to authenticate with Webservice

I have a client-server application (both Windows, client is WPF, non-UWP) and i want to do authentication via active directory. My idea is to take the credentials (or a token) from the windows machine that the client is on and send that information to the server (via webservice, IIS, asp.net). the server then checks with ad if the credentials are valid and does authorization...
So the key points would be:
extract credentials/token from client-windows
send it via vebservice to server (that part should be simple)
validation on server against active directory
How can I achieve that?
If you want to check for a windows user authentication in a desktop application you can simpy use the
Environment.UserName
Variable, it provides the username of the current logged in user.
if you want to check if it is an active directory user you can call a function like this:
public bool UserExists(string username)
{
// create your domain context
using (PrincipalContext domain = new PrincipalContext(ContextType.Domain))
{
// find the user
UserPrincipal foundUser = UserPrincipal.FindByIdentity(domain, IdentityType.Name, username);
return foundUser != null;
}
}
With your new request you can split the code above:
In the client you can get the AD username and domain using Environment variable, pass it to the server and check if the user exist using the UserExist() function

LDAP and ActiveDirectory authentication in C#

I am new to LDAP and active directory authentication , I just studied few things about LDAp authentication and done with sample application
I just checking Does the user exist in ActiveDirectory or not
public static bool DoesUserExist()
{
using (var domainContext = new PrincipalContext(ContextType.Domain,Environment.UserDomainName))
{
using (var foundUser = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, Environment.UserName))
{
return foundUser != null;
}
}
}
in our local system its working fine , But when i hosted in ActiveDirectory Server and i am trying to access this with server IP address, i am facing some issue like
ContextType.Domain,Environment.UserDomainName and Environment.UserName
for these three values are coming from server Information not the users who accessing this application
So please help me how to get the User information(who accessing this application) so that i need to pass those info to server and need check for user is activedirectory user or not
Environment.UserDomainName returns the domain part of Environment.UserName, e.g. "mydomain.com", so you don't want that.
Environment.UserName itself will return the user who is currently "logged in to Windows", i.e. the app pool user - see MSDN.
You are better off checking the identity of the current web request, so in a MVC Controller or WebForms Page, use this.User.
Or if you are using Windows Authentication or hooking Forms Authentication into AD, the current Thread Principal should be the current request user, so you can use Thread.CurrentPrincipal.Identity.

Sharepoint Impersonate external web service call

I created a web service running on different machine/IIS than my SharePoint (2013) application node (the double hop issue kicks in). I expose this web service to other services in our company.
Below code snippet will successfully retrieve a SharePoint list using dedicated credentials (i.e. "sp_admin_user").
In my web service I can retrieve the username (w/o password ofc) of a user calling it which also exists in SharePoint by rule.
My question: How do I need to change below code to facilitate impersonation with above username?
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public string get_sp_list()
{
SPLists.Lists myservice = new SPLists.Lists();
myservice.Credentials = new System.Net.NetworkCredential( "sp_admin_user", "password123", "domain" );
myservice.Url = "https://sharepoint.company.com/sites/testground/_vti_bin/Lists.asmx";
[.. setting of variables ..]
System.Xml.XmlNode n = myservice.GetListItems(
sidList,
sidView,
query,
viewFields,
rowLimit,
queryOptions,
null
);
[.. compose json ..]
return json;
}
The only user you can impersonate without password is SharePoint IIS Application Pool user (its often NETWORK SERVICE). To impersonate user which is accessing SharePoint pages the computer where SharePoint is running need to have delegation rights to the external service. You can read about it here. This is all about security restrictions.
I advise you to reject impersonation approach, its much more easier to deal with passwords, make anonymous web service call or invent something else.
You can impersonate user like this:
using (var ctx = WindowsIdentity.Impersonate(IntPtr.Zero))
{
//external web service call as "COMPUTER_NAME\NETWORK_SERVICE" user
}
You can also get real user token (not IntPtr.Zero) if you have password.

HttpClient & Windows Auth: Pass logged in User of Consumer to Service

I am struggling to understand and set up a Service and Consumer where the Service will run as the user logged into the Consumer.
My consumer is an MVC application. My Service is a Web Api application. Both run on separate servers within the same domain. Both are set to use Windows Auth.
My consumer code is:
private T GenericGet<T>(string p)
{
T result = default(T);
HttpClientHandler handler = new HttpClientHandler() { PreAuthenticate = true, UseDefaultCredentials = true };
using (HttpClient client = new HttpClient(handler))
{
client.BaseAddress = new Uri(serviceEndPoint);
HttpResponseMessage response = client.GetAsync(p).Result;
if (response.IsSuccessStatusCode)
result = response.Content.ReadAsAsync<T>().Result;
}
return result;
}
In my Service I call User.Identity.Name to get the caller ID but this always comes back as the consumer App Pool ID, not the logged in user. The consumer App Pool is running as a Network Service, the server itself is trusted for delegation. So how do I get the logged in User? Service code:
// GET: /Modules/5/Permissions/
[Authorize]
public ModulePermissionsDTO Get(int ModuleID)
{
Module module= moduleRepository.Find(ModuleID);
if (module== null)
throw new HttpResponseException(HttpStatusCode.NotFound);
// This just shows as the App Pool the MVC consumer is running as (Network Service).
IPrincipal loggedInUser = User;
// Do I need to do something with this instead?
string authHeader = HttpContext.Current.Request.Headers["Authorization"];
ModulePermissionsDTO dto = new ModulePermissionsDTO();
// Construct object here based on User...
return dto;
}
According to this question, Kerberos is required to make this set up work because the HttpClient runs in a separate thread. However this confuses me because I thought the request sends an Authorization header and so the service should be able to use this and retrieve the user token. Anyway, I have done some testing with Kerberos to check that this correctly works on my domain using the demo in "Situation 5" here and this works but my two applications still wont correctly pass the logged in user across.
So what do I need to do to make this work? Is Kerberos needed or do I need to do something in my Service to unpack the Authorisation header and create a principal object from the token? All advice appreciated.
The key is to let your MVC application (consumer) impersonate the calling user and then issue the HTTP requests synchronously (i.e. without spawning a new thread). You should not have to concern yourself with low-level implementation details, such as NTLM vs Kerberos.
Consumer
Configure your MVC application like so:
Start IIS Manager
Select your MVC web application
Double click on 'Authentication'
Enable 'ASP.NET Impersonation'
Enable 'Windows Authentication'
Disable other forms of authentication (unless perhaps Digest if you need it)
Open the Web.config file in the root of your MVC application and ensure that <authentication mode="Windows" />
To issue the HTTP request, I recommend you use the excellent RestSharp library. Example:
var client = new RestClient("<your base url here>");
client.Authenticator = new NtlmAuthenticator();
var request = new RestRequest("Modules/5/Permissions", Method.GET);
var response = client.Execute<ModulePermissionsDTO>(request);
Service
Configure your Web API service like so:
Start IIS Manager
Select your Web API service
Double click on 'Authentication'
Disable 'ASP.NET Impersonation'.
Enable 'Windows Authentication'
If only a subset of your Web API methods requires users to be authenticated, leave 'Anonymous Authentication' enabled.
Open the Web.config file in the root of your Web API service and ensure that <authentication mode="Windows" />
I can see that you've already decorated your method with a [Authorize] attribute which should trigger an authentication challenge (HTTP 401) when the method is accessed. Now you should be able to access the identity of your end user through the User.Identity property of your ApiController class.
The key issue with double hop is delegation of user credential to second call. I want to elaborate a little bit about it. C1 = client browser , S1 = First Server , S2 = Second Server.
Suppose our complete system support window authentication. When user access S1 from browser , its default window credential pass to server S1, but when S1 make a call to S2 , by default it don't pass credential to S2.
Resolution :
We must enable window authentication/ impersonation on both machines.
WE need to enable delegation between server so that S1 can trust to S2 and will pass credential to S2.
You can find some useful details at below links :
http://blogs.msdn.com/b/farukcelik/archive/2008/01/02/how-to-set-up-a-kerberos-authentication-scenario-with-sql-server-linked-servers.aspx
https://sqlbadboy.wordpress.com/2013/10/11/the-kerberos-double-hop-problem/
If you are trying to access service which is hosted on windows authentication then do following.
var request = new RestRequest(Method.POST);
If you want to use applications default credentials which must have access on hosted service server
request.UseDefaultCredentials = true;
or user below to pass the credentials manually
request.Credentials = new NetworkCredential("Username", "Password", "Domain");

Categories

Resources