This document outlines the recommended improvements and missing features for the WebDAV Client library to enhance its functionality, performance, and usability.
Priority: High Target: v3.0.0
Status update (2.7.0): Bearer token / OAuth 2.0 authentication has shipped via two new
Clientconstructor overloads (static token + async refreshable provider) backed by the publicWebDAVClient.Authentication.BearerTokenAuthenticationHandler. The remaining items below —TokenCredential/ApiKeyCredentialintegration,AuthenticationOptions, and the fluentClientBuilder— are still planned for v3.0.0.
The current implementation only supports basic authentication and Windows authentication. We need to add support for modern authentication methods using established .NET patterns:
Instead of custom authentication enums, leverage standard .NET authentication abstractions:
// Enhanced Client constructor overloads for modern authentication
// Existing constructor - maintain backward compatibility
public Client(ICredentials credential = null, TimeSpan? uploadTimeout = null, IWebProxy proxy = null)
{
// Keep existing basic/Windows authentication support
}
// Token-based authentication (OAuth2, Azure AD, JWT)
public Client(TokenCredential tokenCredential, TimeSpan? uploadTimeout = null, IWebProxy proxy = null)
{
// For Azure AD, OAuth2, JWT tokens using System.ClientModel.Primitives.TokenCredential
// Integrates seamlessly with Azure Identity libraries
}
// API Key authentication
public Client(ApiKeyCredential apiKeyCredential, string headerName = "X-API-Key", TimeSpan? uploadTimeout = null, IWebProxy proxy = null)
{
// For API key authentication using System.ClientModel.Primitives.ApiKeyCredential
}
// Custom authentication handler for maximum flexibility
public Client(Func<HttpRequestMessage, CancellationToken, ValueTask> authenticationHandler, TimeSpan? uploadTimeout = null, IWebProxy proxy = null)
{
// For digest authentication, custom schemes, or complex auth flows
// Handler is called before each request to add authentication
}
// Fluent builder pattern integration
public Client(AuthenticationOptions authOptions, TimeSpan? uploadTimeout = null, IWebProxy proxy = null)
{
// Used with fluent builder for complex authentication scenarios
}// Authentication options for complex scenarios
public class AuthenticationOptions
{
public TokenCredential? TokenCredential { get; set; }
public ApiKeyCredential? ApiKeyCredential { get; set; }
public string? ApiKeyHeaderName { get; set; } = "X-API-Key";
public ICredentials? Credentials { get; set; }
public Func<HttpRequestMessage, CancellationToken, ValueTask>? CustomHandler { get; set; }
public Dictionary<string, string>? StaticHeaders { get; set; }
}
// Bearer token helper for simple scenarios
public static class BearerToken
{
public static TokenCredential FromString(string token) => new StaticTokenCredential(token);
public static TokenCredential FromProvider(Func<CancellationToken, ValueTask<AccessToken>> provider)
=> new DelegatingTokenCredential(provider);
}-
System.ClientModel.Primitives.TokenCredential- OAuth2 tokens
- Azure AD authentication
- JWT tokens
- Custom token providers
-
System.ClientModel.Primitives.ApiKeyCredential- API key in headers
- API key in query parameters
- Custom API key placement
-
System.Net.ICredentials(existing)- Basic authentication
- Windows authentication
- Network credentials
-
Custom Authentication Handlers
- Digest authentication
- Multi-factor authentication
- Complex custom schemes
- Dynamic token refresh
// Azure AD integration
var tokenCredential = new DefaultAzureCredential();
var client = new Client(tokenCredential);
// API Key authentication
var apiKey = new ApiKeyCredential("your-api-key");
var client = new Client(apiKey, "Authorization"); // Custom header name
// JWT Bearer token
var bearerToken = BearerToken.FromString("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...");
var client = new Client(bearerToken);
// Custom digest authentication
var digestHandler = async (request, cancellationToken) =>
{
// Implement digest authentication logic
var authHeader = await CreateDigestAuthHeader(request, cancellationToken);
request.Headers.Authorization = new AuthenticationHeaderValue("Digest", authHeader);
};
var client = new Client(digestHandler);
// Fluent builder pattern
var client = ClientBuilder.Create()
.WithServer("https://dav.example.com")
.WithTokenCredential(new DefaultAzureCredential())
.WithRetryPolicy(3, TimeSpan.FromSeconds(2))
.Build();- Standard .NET Patterns: Uses established authentication abstractions
- Azure Integration: Seamless integration with Azure Identity libraries
- OAuth2/JWT Support: Built-in support for modern token-based authentication
- Extensibility: Custom handlers allow any authentication scheme
- Backward Compatibility: Existing
ICredentialsconstructor remains unchanged - Dependency Injection Friendly: Works well with DI containers
- Testability: Easy to mock and test authentication flows
<!-- Required for modern authentication support -->
<PackageReference Include="System.ClientModel.Primitives" Version="1.0.0" />
<!-- Optional - for Azure AD integration -->
<PackageReference Include="Azure.Identity" Version="1.10.4" />Priority: High Target: v3.0.0
Status update (2.7.0): Shipped.
IClientnow exposesLockFile/LockFolder/UnlockFile/UnlockFolder/RefreshLockreturning a strongly-typedLockInfo, and theIflock-token header is sent on PUT / DELETE / MOVE / COPY when callers pass the new optionallockToken(and source / destination variants) parameters.
Critical WebDAV operations that are currently missing:
// Lock operations
Task<string> LockFile(string filePath, int timeoutSeconds = 600, CancellationToken cancellationToken = default);
Task<string> LockFolder(string folderPath, int timeoutSeconds = 600, CancellationToken cancellationToken = default);
Task UnlockFile(string filePath, string lockToken, CancellationToken cancellationToken = default);
Task UnlockFolder(string folderPath, string lockToken, CancellationToken cancellationToken = default);
Task<bool> RefreshLock(string path, string lockToken, int timeoutSeconds = 600, CancellationToken cancellationToken = default);
Task<LockInfo> GetLockInfo(string path, CancellationToken cancellationToken = default);public class LockInfo
{
public string Token { get; set; }
public string Owner { get; set; }
public DateTime ExpirationDate { get; set; }
public string LockType { get; set; }
public string LockScope { get; set; }
}Priority: Medium Target: v3.1.0
Status update (2.7.0): Shipped.
IClient.SetProperty(path, name, namespace, value)andIClient.RemoveProperty(path, name, namespace)are available; targeted PROPFIND via<prop>/<propname/>also shipped (see the newList/GetFolder/GetFileoverloads takingIEnumerable<PropertyName>and the*PropertyNamesmethods).
The client can read properties but cannot set or manage custom properties:
Task<bool> SetProperty(string path, string propertyName, string propertyValue, string nameSpace = "DAV:", CancellationToken cancellationToken = default);
Task<Dictionary<string, string>> GetCustomProperties(string path, string[] propertyNames = null, CancellationToken cancellationToken = default);
Task<bool> RemoveProperty(string path, string propertyName, string nameSpace = "DAV:", CancellationToken cancellationToken = default);
Task<bool> SetMultipleProperties(string path, Dictionary<string, object> properties, string nameSpace = "DAV:", CancellationToken cancellationToken = default);Priority: Low Target: v3.2.0
Add support for WebDAV search capabilities:
Task<IEnumerable<Item>> Search(string basePath, string query, SearchScope scope = SearchScope.Subtree, CancellationToken cancellationToken = default);
Task<IEnumerable<Item>> SearchByProperty(string basePath, string propertyName, string propertyValue, CancellationToken cancellationToken = default);Priority: High Target: v2.3.0
Add built-in retry logic for transient failures:
public int MaxRetryAttempts { get; set; } = 3;
public TimeSpan RetryDelay { get; set; } = TimeSpan.FromSeconds(1);
public bool EnableExponentialBackoff { get; set; } = true;
public Func<Exception, bool> RetryPredicate { get; set; } = DefaultRetryPredicate;- Configurable retry attempts
- Exponential backoff strategy
- Custom retry predicates
- Circuit breaker pattern for persistent failures
Priority: Medium Target: v2.4.0
Add progress callbacks for long-running operations:
Task<bool> Upload(string remoteFilePath, Stream content, string name, IProgress<ProgressInfo> progress = null, CancellationToken cancellationToken = default);
Task<Stream> Download(string remoteFilePath, IProgress<ProgressInfo> progress = null, CancellationToken cancellationToken = default);public class ProgressInfo
{
public long BytesTransferred { get; set; }
public long TotalBytes { get; set; }
public double PercentageComplete => TotalBytes > 0 ? (double)BytesTransferred / TotalBytes * 100 : 0;
public TimeSpan ElapsedTime { get; set; }
public TimeSpan EstimatedTimeRemaining { get; set; }
public long TransferRate { get; set; } // bytes per second
}Priority: Medium Target: v2.3.0
Optimize HTTP connection management:
- Improve HttpClient configuration for connection reuse
- Add connection pool size configuration
- Implement proper keep-alive settings
- Add connection health monitoring
Priority: Medium Target: v2.4.0
Reduce memory allocations and improve garbage collection:
- Stream-based XML parsing to reduce memory footprint
- Object pooling for frequently created objects
- Lazy loading of properties
- Implement
IAsyncDisposablewhere appropriate
Priority: High Target: v2.3.0
Add comprehensive logging infrastructure:
public Client(ICredentials credential = null, TimeSpan? uploadTimeout = null,
IWebProxy proxy = null, ILogger<Client> logger = null)- Request/Response details
- Authentication events
- Error conditions
- Performance metrics
- Connection events
Priority: High Target: v2.3.0
Improve exception context and debugging information:
public class WebDAVException : Exception
{
public string OperationType { get; set; }
public string RequestUri { get; set; }
public string RequestMethod { get; set; }
public Dictionary<string, string> RequestHeaders { get; set; }
public string ResponseContent { get; set; }
public TimeSpan RequestDuration { get; set; }
public string ServerVersion { get; set; }
}Priority: Medium Target: v3.0.0
Status update (2.7.0):
GetServerCapabilitiesis partially covered by the newIClient.GetServerOptions(path, ct)returning a strongly-typedServerOptions(DAV compliance classes + allowed methods). The fullCheckHealth/GetConnectionInfosurface below remains planned.
Add server connectivity and health monitoring:
Task<HealthCheckResult> CheckHealth(CancellationToken cancellationToken = default);
Task<ServerCapabilities> GetServerCapabilities(CancellationToken cancellationToken = default);
Task<ConnectionInfo> GetConnectionInfo(CancellationToken cancellationToken = default);Priority: Medium Target: v3.0.0
Create a more intuitive configuration experience that integrates with modern authentication patterns:
public static class ClientBuilder
{
public static ClientConfiguration Create() => new ClientConfiguration();
}
public class ClientConfiguration
{
public ClientConfiguration WithServer(string server);
public ClientConfiguration WithBasePath(string basePath);
public ClientConfiguration WithPort(int port);
public ClientConfiguration WithTimeout(TimeSpan timeout);
public ClientConfiguration WithUploadTimeout(TimeSpan uploadTimeout);
public ClientConfiguration WithProxy(IWebProxy proxy);
// Modern authentication methods
public ClientConfiguration WithCredentials(ICredentials credentials);
public ClientConfiguration WithTokenCredential(TokenCredential tokenCredential);
public ClientConfiguration WithApiKey(ApiKeyCredential apiKeyCredential, string headerName = "X-API-Key");
public ClientConfiguration WithBearerToken(string token);
public ClientConfiguration WithCustomAuthentication(Func<HttpRequestMessage, CancellationToken, ValueTask> handler);
// Configuration options
public ClientConfiguration WithRetryPolicy(int maxAttempts, TimeSpan delay, bool exponentialBackoff = true);
public ClientConfiguration WithCustomHeaders(Dictionary<string, string> headers);
public ClientConfiguration WithUserAgent(string userAgent, string? version = null);
public ClientConfiguration WithLogging(ILogger logger);
public ClientConfiguration WithCertificateValidation(RemoteCertificateValidationCallback callback);
// Build the client
public Client Build();
}
// Extension methods for common scenarios
public static class ClientConfigurationExtensions
{
public static ClientConfiguration WithBasicAuth(this ClientConfiguration config, string username, string password)
=> config.WithCredentials(new NetworkCredential(username, password));
public static ClientConfiguration WithAzureAD(this ClientConfiguration config)
=> config.WithTokenCredential(new DefaultAzureCredential());
public static ClientConfiguration WithJwtToken(this ClientConfiguration config, string jwtToken)
=> config.WithBearerToken(jwtToken);
}// Basic authentication
var client = ClientBuilder.Create()
.WithServer("https://dav.example.com")
.WithBasePath("/dav/")
.WithBasicAuth("username", "password")
.WithRetryPolicy(3, TimeSpan.FromSeconds(2))
.Build();
// Azure AD authentication
var client = ClientBuilder.Create()
.WithServer("https://sharepoint.example.com")
.WithAzureAD()
.WithLogging(logger)
.Build();
// API Key authentication
var client = ClientBuilder.Create()
.WithServer("https://api.example.com")
.WithApiKey(new ApiKeyCredential("your-key"), "X-API-Key")
.WithTimeout(TimeSpan.FromMinutes(5))
.Build();
// JWT Bearer token
var client = ClientBuilder.Create()
.WithServer("https://dav.example.com")
.WithJwtToken("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...")
.WithCustomHeaders(new Dictionary<string, string> { ["X-Client-Version"] = "3.0" })
.Build();
// Custom digest authentication
var client = ClientBuilder.Create()
.WithServer("https://dav.example.com")
.WithCustomAuthentication(async (request, ct) =>
{
var digestAuth = await CreateDigestAuth(request, ct);
request.Headers.Authorization = new AuthenticationHeaderValue("Digest", digestAuth);
})
.Build();
// Complex configuration
var client = ClientBuilder.Create()
.WithServer("https://enterprise-dav.example.com")
.WithBasePath("/webdav/")
.WithPort(8443)
.WithTokenCredential(tokenCredential)
.WithRetryPolicy(maxAttempts: 5, delay: TimeSpan.FromSeconds(1), exponentialBackoff: true)
.WithTimeout(TimeSpan.FromMinutes(10))
.WithUploadTimeout(TimeSpan.FromHours(1))
.WithUserAgent("MyApp", "2.1.0")
.WithLogging(logger)
.WithCertificateValidation((sender, cert, chain, errors) => true) // Accept all certs
.Build();For scenarios requiring async initialization:
public static class AsyncClientBuilder
{
public static async Task<Client> CreateAsync(Func<ClientConfiguration, Task<ClientConfiguration>> configure)
{
var config = ClientBuilder.Create();
config = await configure(config);
return config.Build();
}
}
// Usage with async token acquisition
var client = await AsyncClientBuilder.CreateAsync(async config =>
config.WithServer("https://dav.example.com")
.WithTokenCredential(await GetTokenCredentialAsync())
.WithRetryPolicy(3, TimeSpan.FromSeconds(2))
);- Retry mechanism with exponential backoff
- Structured logging support (Microsoft.Extensions.Logging)
- Enhanced exception information with better context
- Improved documentation and examples
- Progress reporting for large operations
- Memory usage optimization
- Connection pooling and keep-alive optimization
- IAsyncDisposable support
- Advanced authentication support with modern .NET patterns
- TokenCredential integration (Azure AD, OAuth2, JWT)
- ApiKeyCredential support
- Custom authentication handlers
- WebDAV Lock/Unlock operations (LOCK/UNLOCK methods)
- Fluent configuration API with authentication integration
- Nullable reference types support (.NET 8/9)
- Breaking changes cleanup and API improvements
- WebDAV properties management (PROPPATCH method)
- Batch operations support for improved performance
- Health checking and diagnostics capabilities
- Performance monitoring and metrics collection
- WebDAV search support (DASL extension)
- Async enumerable support for large listings
- Source generator for strongly-typed WebDAV properties
- Advanced connection management features
The authentication system will be modernized but maintain backward compatibility:
// Basic authentication - continues to work
var client = new Client(new NetworkCredential("user", "pass"));
// Windows authentication - continues to work
var client = new Client();// Modern token-based authentication
var client = new Client(new DefaultAzureCredential());
// API key authentication
var client = new Client(new ApiKeyCredential("key"), "X-API-Key");
// Fluent configuration
var client = ClientBuilder.Create()
.WithServer("https://dav.example.com")
.WithTokenCredential(tokenCredential)
.Build();- No Breaking Changes: Existing constructors remain functional
- Modern Authentication: Support for OAuth2, Azure AD, JWT
- Better Testing: Easier to mock authentication in unit tests
- Cloud Ready: Seamless integration with cloud identity providers
- Extensible: Custom authentication handlers for any scenario