using System; using System.Text; using Best.HTTP.Request.Authentication; using Best.HTTP.Shared; namespace Best.HTTP.Request.Authenticators { /// /// An implementation for HTTP Basic or Digest authentication. /// public class CredentialAuthenticator : IAuthenticator { /// /// Gets or sets the associated with this authenticator. /// public Credentials Credentials { get; set; } /// /// Initializes a new instance of the CrendetialAuthenticator class with the specified . /// /// The to use for authentication. /// Thrown if is null. public CredentialAuthenticator(Credentials credentials) { if (credentials == null) throw new ArgumentNullException(nameof(credentials)); this.Credentials = credentials; } /// /// Sets up the required headers for the HTTP request based on the provided credentials. /// /// The HTTP request for which headers should be added. public void SetupRequest(HTTPRequest request) { HTTPManager.Logger.Information(nameof(CredentialAuthenticator), $"SetupRequest({request}, {Credentials?.Type})", request.Context); if (Credentials == null) return; switch (Credentials.Type) { case AuthenticationTypes.Basic: // With Basic authentication we don't want to wait for a challenge, we will send the hash with the first request request.SetHeader("Authorization", string.Concat("Basic ", Convert.ToBase64String(Encoding.UTF8.GetBytes(Credentials.UserName + ":" + Credentials.Password)))); break; case AuthenticationTypes.Unknown: case AuthenticationTypes.Digest: var digest = DigestStore.Get(request.CurrentUri); if (digest != null) { string authentication = digest.GenerateResponseHeader(Credentials, false, request.MethodType, request.CurrentUri); if (!string.IsNullOrEmpty(authentication)) request.SetHeader("Authorization", authentication); } break; } } /// /// Handles the server response with a 401 (Unauthorized) status code and a WWW-Authenticate header. /// The authenticator might determine the authentication method to use and initiate authentication if needed. /// /// The HTTP request that received the 401 response. /// The HTTP response containing the 401 (Unauthorized) status. /// true if the challenge is handled by the authenticator and the request can be resent with authentication; otherwise, false. public bool HandleChallange(HTTPRequest req, HTTPResponse resp) { var www_authenticate = resp.GetHeaderValues("www-authenticate"); HTTPManager.Logger.Information(nameof(CredentialAuthenticator), $"HandleChallange({req}, {resp}, \"{www_authenticate}\")", req.Context); string authHeader = DigestStore.FindBest(www_authenticate); if (!string.IsNullOrEmpty(authHeader)) { var digest = DigestStore.GetOrCreate(req.CurrentUri); digest.ParseChallange(authHeader); if (this.Credentials != null && digest.IsUriProtected(req.CurrentUri) && (!req.HasHeader("Authorization") || digest.Stale)) return true; } return false; } } }