Today I found myself in need to make a call with the default network credentials. The best I could do was:
using (var fc = new FlurlClient())
{
((HttpClientHandler)fc.HttpMessageHandler).UseDefaultCredentials = true;
using (var stream = await url.WithClient(fc).GetStreamAsync())
{
etc...
}
}
wouldn't be nice to have a WithDefaultCredentials() extension method?
In your particular case, yes it would be nice. :) I'm a little picky about what extension methods get baked into the core - generally things that are extremely common, or not trivial to implement (like cookies). I don't know how common UseDefaultCredentials is, personally I've never had a need for it. For things like this I try to make the library as easily extensible as possible.
2 options: Write your own extension method for your own use (you've already demonstrated how easy this would be), or create a custom HttpClientFactory and configure it globally. I see my docs are a little out of date (sorry), but IHttpClientFactory also has a CreateMessageHandler method that you can override similarly to CreateClient - that would be the ideal place to set UseDefaultCredentials if you want it set to true everywhere in your app.
I understand your interest in keeping the library clean. I made the suggestion because, after using the library for several other cases with Basic and Bearer Authentication I was "expecting" the DefaultNetworkCredentials just to be there. Nothing difficult to fix as you said (I did override the CreateMessaeHandler).
Thanks to you and to @kroniak for a wonderful tool!
We use Windows authentication heavily, and it'd be nice to have Windows authentication built directly into the Flurl library without having to add another extension method or create a custom NuGet package that we then have to consume everywhere. I feel like Windows authentication is a normal and common use case and should be easily accessible from Flurl.
Any chance of getting this baked into Flurl?
@mason-mcglothlin Sorry, I've evaluated this and I don't think NTLM is a good fit for the library. As I understand it, it didn't even work in .NET Core until 2.1.
In combination with IIS as a reverse proxy (the recommended use case) you should be able to use WindowsAuthentication (NTLM/Kerberos) since ASP.NET Core 1.0 I guess.
I know organizations which have dozens of intranet sites all secured by internal Windows domain mappings.
@tmenier Can you explain why you don't think that supporting it would be good fit? Flurl is a great library, but it's extremely annoying to have to extend it to support such a commonly used scenario. .NET has its roots in Windows ecosystems, and therefore Windows Authentication is a common mechanism for securing web applications. Flurl should support authenticating to them out of the box, without the need to hack around it.
using Flurl;
using Flurl.Http;
using System.Net.Http;
using System.Threading.Tasks;
namespace MyCompany
{
public static class FlurlExtensions
{
public static async Task<HttpResponseMessage> PostJsonAsyncWithDefaultCredentials(this Url url, object data)
{
using (var fc = new FlurlClient())
{
((HttpClientHandler)fc.HttpMessageHandler).UseDefaultCredentials = true;
return await url
.WithClient(fc)
.PostJsonAsync(data);
}
}
}
}
I need to put this extension method in every application that needs to consume it. And I'd need to come up with similarly fluent extension methods to support the other functionalities such as GetJsonAsync, GetJsonListAsync etc. That's a big pain. Flurl should be making things easier, not harder.
RestSharp has this built in:
RestClient client = new RestClient(_baseURL) { Authenticator = new NtlmAuthenticator() };
What I'd really like to see for Flurl is something like:
await url.WithDefaultCredentials().GetJsonAsync();
// or
await url.WithCurrentWindowsAuthentication().GetJsonAsync();
Haven't really tested this, but I think it'd be as easy as:
public static IFlurlRequest WithWindowsAuth(this string url)
=> new FlurlRequest(url).WithWindowsAuth();
public static IFlurlRequest WithWindowsAuth(this Url url)
=> new FlurlRequest(url).WithWindowsAuth();
public static IFlurlRequest WithWindowsAuth(this IFlurlRequest request)
{
((HttpClientHandler)request.Client.HttpMessageHandler).UseDefaultCredentials = true;
return request;
}
Please consider this again. If you don't think it'd be a good fit for including with Flurl, then I'll be disappointed and respectfully disagree - and I'd personally consider creating this as an extension package for Flurl. But I've never maintained an open source library and you've done a great job with Flurl, I think it'd be in better hands with you.
@mason-mcglothlin I think it's even easier than that. As a user I probably don't want to have to specify WithWindowsAuth() on every call - chances are you want to do it on every call to the same host. Also, tinkering with request.Client.HttpMessageHandler is not thread safe. So, create and register a custom factory:
public class WindowsAuthClientFactory : DefaultHttpClientFactory
{
public override HttpMessageHandler CreateMessageHandler() {
return new HttpClientHandler { UseDefaultCredentials = true };
}
}
FlurlHttp.ConfigureClient("https://www.somedomain.com", cli =>
cli.Settings.HttpClientFactory = new WindowsAuthClientFactory());
So no, you shouldn't have to create tons of extension methods or "hack around" anything. You can even use this factory app-wide via FlurlHttp.Configure rather than FlurlHttp.ConfigureClient if you're hitting many domains that all require it.
@tmenier Thanks for that, I integrated it with my application which cleaned up the code a bit. I think this would be good to document. It doesn't look like flurl.io has a way for me to submit a documentation update PR. Am I wrong about that? Or would you mind adding this to the documentation yourself?
@mason-mcglothlin Docs are in a different repo. You could submit a PR there if you want, but I don't really have a great spot for this. Someday I'd like to add "common use cases" section or something along those lines. You'd be surprised by how many common scenarios can be solved by configuring the message handler like this, many asked about a lot more frequently than Windows auth (proxies, certs, retries, disabling redirects, etc).
Honestly, this would make a great SO question and would get better visibility than the docs. You could even answer it yourself if you want - that's encouraged.
@tmenier Good point, I have created an example on Stack Overflow.
using NtlmAuthenticator
public class MyCustomHttpClientFactory : DefaultHttpClientFactory
{
public override HttpMessageHandler CreateMessageHandler()
{
return new HttpClientHandler { Credentials = new NetworkCredential("user", "password") };
}
}
var mycall = await _options.url.WithClient(new FlurlClient("url").Configure(settings => {
settings.HttpClientFactory = new MyCustomHttpClientFactory();
settings.HttpClientFactory.CreateMessageHandler();
})).WithHeaders(....
@reinerra We've already covered how to handle Windows authentication in this issue and the associated Stack Overflow question. If you have a different problem, then perhaps you should create a new issue where you can clearly explain your problem, rather than piggybacking off this one.
Most helpful comment
We use Windows authentication heavily, and it'd be nice to have Windows authentication built directly into the Flurl library without having to add another extension method or create a custom NuGet package that we then have to consume everywhere. I feel like Windows authentication is a normal and common use case and should be easily accessible from Flurl.
Any chance of getting this baked into Flurl?