/// <summary> /// Executes the authentication failure action, sending a WWW-Authenticate header with the configured realm out through the context and /// setting the HTTP status code to 401 (Unauthorized). /// </summary> /// <remarks> ebrown, 1/3/2011. </remarks> /// <exception cref="ArgumentNullException"> Thrown when one or more required arguments are null. </exception> /// <param name="context"> The incoming context. </param> /// <param name="inspectorResults"> The set of failed inspector results. </param> /// <returns> Null for an IPrincipal and ShouldTerminateRequest to true since a WWW-Authenticate header is sent. </returns> public override FailureHandlerAction OnAuthenticationFailure(HttpContextBase context, Dictionary <IAuthenticator, AuthenticationResult> inspectorResults) { if (null == context) { throw new ArgumentNullException("context"); } //http://en.wikipedia.org/wiki/Digest_access_authentication //not sure that 'auth-int' is supported across the board, so stick with just 'auth' context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; string stale = "FALSE"; //attempt to extract credentials from header -- assuming they might be stale DigestHeader digestHeader; HttpRequestBase request = context.Request; if (HttpDigestAuthHeaderParser.TryExtractDigestHeader(request.HttpMethod ?? request.RequestType, request.RetrieveHeader(HttpHeaderFields.Authorization), out digestHeader) && NonceManager.Validate(digestHeader.Nonce, request.UserHostAddress, privateHashEncoder) && NonceManager.IsStale(digestHeader.Nonce, Configuration.NonceValidDuration)) { stale = "TRUE"; } context.Response.AddHeader("WWW-Authenticate", String.Format(CultureInfo.InvariantCulture, "Digest realm=\"{0}\", nonce=\"{1}\", opaque=\"{2}\", stale={3}, algorithm=MD5, qop=\"{4}\"", Configuration.Realm, NonceManager.Generate(request.UserHostAddress, privateHashEncoder), Opaque.Current(), stale, DigestQualityOfProtectionType.Authentication.ToEnumValueString())); return(new FailureHandlerAction() { User = null, ShouldTerminateRequest = true }); }
/// <summary> Authenticates a HttpContextBase given a specified MembershipProvider. </summary> /// <remarks> ebrown, 1/3/2011. </remarks> /// <exception cref="ArgumentNullException"> Thrown when one or more required arguments are null. </exception> /// <exception cref="Exception"> Thrown when an unexpected exception occurs. </exception> /// <param name="context"> The context. </param> /// <returns> A success or failure if the MembershipProvider validated the credentials found in the header. </returns> public override AuthenticationResult Authenticate(HttpContextBase context) { try { if (null == context) { throw new ArgumentNullException("context"); } HttpRequestBase request = context.Request; string authHeader = request.Headers["Authorization"]; Log.InfoFormat(CultureInfo.InvariantCulture, "Authorization header [{0}] received", authHeader.IfMissing("**Missing**")); ////attempt to extract credentials from header DigestHeader digestHeader; if (!HttpDigestAuthHeaderParser.TryExtractDigestHeader(request.HttpMethod ?? request.RequestType, authHeader, out digestHeader)) { //don't log an event since there was nothing to succeed / fail return(new AuthenticationResult(false, null, "No digest credentials found in HTTP header")); } MembershipProvider membershipProvider = null; MembershipUser membershipUser = null; string userPassword = null; //try our specially configured function first, and fail out to membership if not available if (null != Configuration.PasswordRetriever) { userPassword = Configuration.PasswordRetriever.GetPassword(digestHeader.UserName); } else { membershipProvider = MembershipProviderLocator.GetProvider(Configuration.ProviderName); if (null == membershipProvider) { throw new ArgumentException("MembershipProvider specified in configuration cannot be found, but is required to lookup user password for comparison"); } membershipUser = membershipProvider.GetUser(digestHeader.UserName, true); userPassword = membershipUser.GetPassword(); } //three things validate this digest request -- that the nonce matches the given address, that its not stale //and that the credentials match the given realm / opaque / password if (NonceManager.Validate(digestHeader.Nonce, request.UserHostAddress, privateHashEncoder) && !NonceManager.IsStale(digestHeader.Nonce, Configuration.NonceValidDuration) && digestHeader.MatchesCredentials(Configuration.Realm, Opaque.Current(), userPassword)) { if (null != membershipProvider) { new AuthenticationSuccessEvent(this, digestHeader.UserName).Raise(); } IPrincipal principal = GetPrincipal(context, membershipUser, new NetworkCredential(digestHeader.UserName, userPassword)); IIdentity identity = null != principal ? principal.Identity : null; if (null != identity && identity.IsAuthenticated) { new AuthenticationSuccessEvent(this, identity.Name).Raise(); return(new AuthenticationResult(true, principal, string.Empty)); } } new AuthenticationFailureEvent(this, digestHeader.UserName).Raise(); return(new AuthenticationResult(false, null, string.Empty)); } catch (Exception) { new AuthenticationFailureEvent(this, string.Empty).Raise(); throw; } }