I am looking for a way to automatically authenticate for a web site.
I've got a WebView in my c# Windows Store App and I want to access a site that is password protected.
WebView.Source= new URI("http://UserId:Password#foo.com/");
This is not working as I get a Security exception:
A security problem occurred. (Exception from HRESULT: 0x800C000E);
The method below is also not working as I only get the html of a site, but no css or JavaScript:
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential("UserId", "Password");
HttpClient client = new HttpClient(handler);
string body = await client.GetStringAsync("http://foo.com");
webview.NavigateToString(body);
Is there any other way?
I have came across same problem, but luckily found an answer :)
Main problem in here is that Windows store applications contain 2 different HttpClient's
One of them is "classic" one we know from c# apps (used automatically) and the other one is "new" HttpClient - which is connected with WebView :)
Bellow are both types :
System.Net.Http.HttpClient ( classic one )
Windows.Web.Http.HttpClient ( new one )
So remember to declare the new One and do something like the code bellow
var filter = new HttpBaseProtocolFilter();
filter.ServerCredential = new Windows.Security.Credentials.PasswordCredential("http://website","login", "password");
Windows.Web.Http.HttpClient client2 = new Windows.Web.Http.HttpClient(filter);
var response = await client2.GetAsync(new Uri("http://website"));
WebView.Source = new Uri("http://website");
Now remember to change login and password to credentials you want to use, and a website is a site you want to authenticate to.
It is important to get the response from server - this will make user authenticated # server so next time you go with webView to that site you will be authenticated
It works fine with basic authentication and NTLM authentication
Hope it will help people searching for solution of this problem :)
Related
I've got the following scenario (all applications and services are using asp.net core 3.0 on Windows):
I have a client application that runs in the context of AppUser. It uses an HttpClient to send a request to WebServiceA that runs in the context of WebUser.
The request uses Windows authentication.
var credentialsCache = new CredentialCache { { uri, "Negotiate", CredentialCache.DefaultNetworkCredentials } };
var handler = new HttpClientHandler { Credentials = credentialsCache };
var httpClient = new HttpClient(handler);
In WebServiceA, the call is recieved correctly and it's authenticated:
HttpContext.User.Identity.IsAuthenticated is true and HttpContext.User.Identity.Name is AppUser.
To be able to process the request, WebServiceA must send another request to WebServiceB. This request must also be authenticated as AppUser.
Currently I'm using the following code to create the HttpClient that makes the request to WebServiceB:
var credentialsCache = new CredentialCache { { uri, "Negotiate", CredentialCache.DefaultNetworkCredentials } };
HttpClientHandler handler = new HttpClientHandler
{
Credentials = credentialsCache,
UseDefaultCredentials = true
};
HttpClient httpClient = new HttpClient(handler);
The problem is, the call is always authenticated as WebUser (who runs the service), NOT AppUser (who sent the request).
I thought the DefaultNetworkCredentials were always those of the authenticated user.
How can I send (from within a WebRequest) another WebRequest to another WebService that is authenticated with the same user who submitted the original request?
Is there a way to pass the authentication from one WebService to another?
Regarding Kerberos and AD:
WebServiceA and WebServiceB are running on different servers in the same domain. They are not hosted in IIS, but in a Windows Service.
I tried to set the SPN as described here:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.0&tabs=visual-studio
setspn -S HTTP/ServerA.domain domain\WebUser
Also tried:
setspn -a WebServiceA/WebUser domain\WebUser
But without success.
After three days of web research and trying around, my head is spinning ...
How do I get the UserCredentials into the second request?
Or is it because of (a possibly wrong?) AD configuration (SPN) that the WebService always silently takes the credentials of the user who started the service?
I am grateful for every hint!
Steffi
An answer accepted by the OP in the comment involves impersonation. According to this blog entry it's possible in .Net Core using the WindowsIdentity.RunImpersonated:
// The user to be impersonated
var userToImpersonate = (WindowsIdentity)HttpContext.User.Identity;
// Impersonating the current Windows user [HttpContext.User.Identity]...
var result = await WindowsIdentity.RunImpersonated(
userToImpersonate.AccessToken, async () =>
{
// This time WindowsIdentity.GetCurrent() will retrieve the impersonated
// user with its claims...
var impersonatedUser = WindowsIdentity.GetCurrent().Name;
// Your business logic code here...
});
I'm trying to use the Google Admin Settings API with a Service Account with no success from a C# Console application.
From what I've understood, I first have to get an OAuth token. I've tried 2 methods successfully for this: using Google.Apis.Auth.OAuth2.ServiceAccountCredentials or by creating manually the JWT assertion.
But when I call an Admin Settings API with the OAuth token (maximumNumberOfUsers for instance), I always get a 403 error with " You are not authorized to perform operations on the domain xxx" message.
I downloaded GAM as the author calls this API too so that I can compose the same HTTP requests. Like explained in GAM wiki, I followed all the steps to create a new Service Account and a new OAuth Client ID so that I can be sure it's not a scope issue. I also activated the debug mode like proposed by Jay Lee in this thread. Like explained in the thread comments, it still doesn't work with my OAuth token but the call to the API succeeds with GAM OAuth token.
So it seems it's related to the OAuth token itself. An issue I get while creating the OAuth token is that I can't specify the "sub" property (or User for ServiceAccountCredentials). If I add it, I get a 403 Forbidden response with "Requested client not authorized." as error_description while generating the token i.e. before calling the API. So maybe it is the issue but I don't see how to fix it as I use an Admin email.
Another possibility is that this API needs the OAuth Client credentials as GAM requires 2 different types of credentials, Service Account and OAuth Client. As I only can use Service Account credentials in my project, I'm afraid I will be stuck if it is the case...
I don't see other options and I'm stuck with both, so any help appreciated. Thanks!
My code:
public static string GetEnterpriseUsersCount()
{
string domain = MYDOMAIN;
string certPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
certPath = certPath.Substring(0, certPath.LastIndexOf("\\") + 1) + "GAMCreds.p12";
var certData = File.ReadAllBytes(certPath);
X509Certificate2 privateCertificate = new X509Certificate2(certData, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(SERVICE_ACCOUNT_EMAIL)
{
Scopes = new[] { "https://apps-apis.google.com/a/feeds/domain/" },
User = ADMIN_EMAIL
}.FromCertificate(privateCertificate));
Task<bool> oAuthRequest = credential.RequestAccessTokenAsync(new CancellationToken());
oAuthRequest.Wait();
string uri = string.Format("https://apps-apis.google.com/a/feeds/domain/2.0/{0}/general/maximumNumberOfUsers", domain);
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
if (request != null)
{
request.Method = "GET";
request.Headers.Add("Authorization", string.Format("Bearer {0}", credential.Token.AccessToken));
// Return the response
using (WebResponse response = request.GetResponse())
{
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
}
return null;
}
Edit: I focused on scopes like advised by Jay Lee below and it appears that the missing scope was 'https://www.googleapis.com/auth/admin.directory.domain'. However, nowhere is this written in Admin Settings API documentation page. At least, I didn't find it. 'https://apps-apis.google.com/a/feeds/domain/' is necessary too but I already added it to the list of allowed scopes. Thanks Jay!
Edit 2: I also updated the source code so that it can help in the future.
You need to grant your service account's client ID access to the scopes for admins settings API. Follow the Drive domain wide delegation instructions except sub in the correct correct scope. Then you can set sub= without an error.
I am porting my windows 8.1 app to windows 10 uwp app.
The problem I have encountered is that old code (it is still compiling though) that uses System.Net.HttpClient throws some stupid exception when I am trying to authenticate on the server using NTLM.
"Known Windows 10 SDK error - we are fixing it." - all help from Microsoft since months.
(described by someone else here: https://social.msdn.microsoft.com/Forums/en-US/9e137127-e0e5-4aec-a7a9-d66f5b84c70b/rtm-known-issue-systemnethttphttpclient-or-httpwebrequest-class-usage-in-a-uwp-app-throws-a?forum=Win10SDKToolsIssues)
The only workaround? - "Use Windows.Web.HttpClient instead"
So I am trying.
var request = new HttpRequestMessage();
request.RequestUri = MyURI;
request.Method = HttpMethod.Post;
request.Content = new HttpStringContent(MyContent);
request.Headers.Add("User-Agent", "MyApp");
request.Headers.Add("SOAPAction", "MySoapAction");
request.Content.Headers.ContentType = new HttpMediaTypeHeaderValue("text/xml; charset=utf-8");
var filter = new HttpBaseProtocolFilter();
filter.AllowAutoRedirect = true;
filter.AllowUI = true;
filter.ServerCredential = new PasswordCredential(address, username, password);
HttpClient client = new HttpClient(filter);
var response = await client.SendRequestAsync(httpMessage);
And it works! Displays ugly system popup over my app asking to enter credentials (it is already filled in - because I have set credentials in the code) - after hitting OK button response status is OK - all worked like a harm, the way it should.
So what is the problem? Problem is when I don't want to show this system popup to users.
If only I set:
filter.AllowUI = false;
all magic disappears. No popup, no authentication. Response gives 401 error. Unauthorized.
Why? What am I doing wrong? Is it possible to have NTLM authentication working fine on Windows 10 UWP apps without system popup? Another SDK issue?
I have a solution that is implemented similarly and it does work with NTLM. Do you have the "Enterprise Authentication" and "Internet (Client & Server)" capabilities checked and do you provide the domain with the username?
We use Windows authentication. I've created both WebAPI/MVC applications to play with.
When hosted on production server / IIS appplication requires credentials to perform HTTP GET to an external site. For some reasons we temporarily cannot use special AD account for that.
I've read about ASP.NET impersonation. As far as I understand it is possible to incorporate with caller's credentials? I've tried to use this code though I'm not sure whether it is an appropriate approach to use with WebAPI.
[HttpGet]
[Route("api/test")]
public HttpResponseMessage test() {
using(var ctx = WindowsIdentity.GetCurrent().Impersonate()) {
var proxyOptions = new WebProxy("[proxyUrl]");
proxyOptions.UseDefaultCredentials = true;
var client = new WebClient();
client.UseDefaultCredentials = true;
client.Proxy = proxyOptions;
return new HttpResponseMessage {
Content = new StringContent(
content: client.DownloadString("[externalApiUrl]"),
encoding: Encoding.Default,
mediaType: "text/plain"
)
};
}
}
Anyway this doesn't work.
Should I configure delegation instead of impersonation?
How to perform an external request without a separate AD account for that?
Have you tried explicitly setting the credentials using
var proxyConfig = new WebProxy(
proxyServerUrl,
BypassOnLocal: false,
BypassList: null,
Credentials: new NetworkCredential(username, password)
);
The username / password should be an authorized user on your proxy server. Since you are using Windows Authentication, you may need to prefix domain name in the username E.g. domain\username
This approach should work in all the 3 environments you have mentioned.
I'm calling a method on a web service from behind a proxy server using the following code:
myWebService.TestWebService webservice = new myWebService.TestWebService();
webservice.Url = "http://test.com/webservice?wsdl";
WebProxy proxy = new WebProxy("1.2.3.4", 8080);
proxy.Credentials = new NetworkCredential("username", "password");
webservice.Proxy = proxy;
string response = webservice.TestWebMethod();
This works fine when using HTTP, I get the response I'm expecting in the 'response' string.
However - if I change the URL to HTTPS then I get a (401) Unauthorized response.
If I put the URL into my browser it works fine using HTTP or HTTPS.
I've added code to handle the SSL certificate validation by creating a System.Net.ServicePointManager.ServerCertificateValidationCallback delegate but the code never gets this far. The request is rejected before it validates the certificate or so it seems.
Any help is really appreciated...
Do you need credentials to navigate to the SSL url?
If so you need the web service credentials set.
Have you tried adding a web reference in Visual Studio using the SSL url?
If you can't add web reference through Visual Studio then the code is not going to work either.
Can you make sure that the last thing that you set is the proxy (e.g. change the url before you set the proxy)?
There is a small chance that the proxy could be lost, this should be the very last thing to try
Here is an example using a client cert (which i'm sure you don't need) but might provide some insight & using credentials to a web service.
WebService.ManageOutboundDelivery oWS = new WebService.ManageOutboundDelivery();
if (My.Settings.HasClientCert == true) {
X509Certificate2 signedCert = new X509Certificate2(HttpContext.Current.Server.MapPath(My.Settings.ClientCertName), My.Settings.ClientCertPW);
oWS.ClientCertificates.Add(signedCert);
}
System.Net.CredentialCache oCred = new System.Net.CredentialCache();
Net.NetworkCredential netCred = new Net.NetworkCredential(My.Settings.WebServiceUID, My.Settings.WebServicePW);
oCred.Add(new Uri(oWS.Url), "Basic", netCred);
oWS.Credentials = oCred;
Have you also checked the SSL cert is valid - i'm guessing you would see this when you hit it through the browser but it could be causing a problem trying to hit it programmatically.