diff --git a/Yesod/Auth.hs b/Yesod/Auth.hs
--- a/Yesod/Auth.hs
+++ b/Yesod/Auth.hs
@@ -22,6 +22,7 @@
       -- * Plugin interface
     , Creds (..)
     , setCreds
+    , setCredsRedirect
     , clearCreds
     , loginErrorMessage
     , loginErrorMessageI
@@ -36,6 +37,9 @@
     , AuthHandler
       -- * Internal
     , credsKey
+    , provideJsonMessage
+    , messageJson401
+    , asHtml
     ) where
 
 import Control.Monad                 (when)
@@ -64,6 +68,7 @@
 import Network.HTTP.Types          (unauthorized401)
 import Control.Monad.Trans.Resource (MonadResourceBase)
 import qualified Control.Monad.Trans.Writer    as Writer
+import Control.Monad (void)
 
 type AuthRoute = Route Auth
 
@@ -74,7 +79,7 @@
 
 data AuthPlugin master = AuthPlugin
     { apName :: Text
-    , apDispatch :: Method -> [Piece] -> AuthHandler master ()
+    , apDispatch :: Method -> [Piece] -> AuthHandler master TypedContent
     , apLogin :: (Route Auth -> Route master) -> WidgetT master IO ()
     }
 
@@ -175,9 +180,6 @@
     onErrorHtml dest msg = do
         setMessage $ toHtml msg
         fmap asHtml $ redirect dest
-        where
-          asHtml :: Html -> Html
-          asHtml = id
 
 -- | Internal session key used to hold the authentication information.
 --
@@ -231,7 +233,7 @@
 loginErrorMessageI :: (MonadResourceBase m, YesodAuth master)
                    => Route child
                    -> AuthMessage
-                   -> HandlerT child (HandlerT master m) a
+                   -> HandlerT child (HandlerT master m) TypedContent
 loginErrorMessageI dest msg = do
   toParent <- getRouteToParent
   lift $ loginErrorMessageMasterI (toParent dest) msg
@@ -240,7 +242,7 @@
 loginErrorMessageMasterI :: (YesodAuth master, MonadResourceBase m, RenderMessage master AuthMessage)
          => Route master
          -> AuthMessage
-         -> HandlerT master m a
+         -> HandlerT master m TypedContent
 loginErrorMessageMasterI dest msg = do
   mr <- getMessageRender
   loginErrorMessage dest (mr msg)
@@ -250,48 +252,56 @@
 loginErrorMessage :: (YesodAuth master, MonadResourceBase m)
          => Route master
          -> Text
-         -> HandlerT master m a
-loginErrorMessage dest msg =
-  sendResponseStatus unauthorized401 =<< (
-    selectRep $ do
-      provideRep $ do
-          onErrorHtml dest msg
-      provideJsonMessage msg
-  )
+         -> HandlerT master m TypedContent
+loginErrorMessage dest msg = messageJson401 msg (onErrorHtml dest msg)
 
+messageJson401 :: MonadResourceBase m => Text -> HandlerT master m Html -> HandlerT master m TypedContent
+messageJson401 msg html = selectRep $ do
+    provideRep html
+    provideRep $ do
+        let obj = object ["message" .= msg]
+        void $ sendResponseStatus unauthorized401 obj
+        return obj
+
 provideJsonMessage :: Monad m => Text -> Writer.Writer (Endo [ProvidedRep m]) ()
 provideJsonMessage msg = provideRep $ return $ object ["message" .= msg]
 
 
--- | Sets user credentials for the session after checking them with authentication backends.
-setCreds :: YesodAuth master
-         => Bool         -- ^ if HTTP redirects should be done
-         -> Creds master -- ^ new credentials
-         -> HandlerT master IO ()
-setCreds doRedirects creds = do
+setCredsRedirect :: YesodAuth master
+         => Creds master -- ^ new credentials
+         -> HandlerT master IO TypedContent
+setCredsRedirect creds = do
     y    <- getYesod
     maid <- getAuthId creds
     case maid of
-        Nothing -> when doRedirects $ do
+        Nothing ->
             case authRoute y of
                 Nothing -> do
-                    sendResponseStatus unauthorized401 =<< (
-                      selectRep $ do
-                        provideRep $ authLayout $ toWidget [shamlet|<h1>Invalid login|]
-                        provideJsonMessage "Invalid Login"
-                      )
+                    messageJson401 "Invalid Login" $ authLayout $
+                        toWidget [shamlet|<h1>Invalid login|]
                 Just ar -> loginErrorMessageMasterI ar Msg.InvalidLogin
         Just aid -> do
             setSession credsKey $ toPathPiece aid
-            when doRedirects $ do
-              onLogin
-              res <- selectRep $ do
-                  provideRepType typeHtml $ do
-                      _ <- redirectUltDest $ loginDest y
-                      return ()
-                  provideJsonMessage "Login Successful"
-              sendResponse res
+            onLogin
+            res <- selectRep $ do
+                provideRepType typeHtml $
+                    fmap asHtml $ redirectUltDest $ loginDest y
+                provideJsonMessage "Login Successful"
+            sendResponse res
 
+-- | Sets user credentials for the session after checking them with authentication backends.
+setCreds :: YesodAuth master
+         => Bool         -- ^ if HTTP redirects should be done
+         -> Creds master -- ^ new credentials
+         -> HandlerT master IO ()
+setCreds doRedirects creds =
+    if doRedirects
+      then void $ setCredsRedirect creds
+      else do maid <- getAuthId creds
+              case maid of
+                  Nothing -> return ()
+                  Just aid -> setSession credsKey $ toPathPiece aid
+
 -- | same as defaultLayoutJson, but uses authLayout
 authLayoutJson :: (YesodAuth site, ToJSON j)
                   => WidgetT site IO ()  -- ^ HTML
@@ -349,7 +359,7 @@
 postLogoutR :: AuthHandler master ()
 postLogoutR = lift $ clearCreds True
 
-handlePluginR :: Text -> [Text] -> AuthHandler master ()
+handlePluginR :: Text -> [Text] -> AuthHandler master TypedContent
 handlePluginR plugin pieces = do
     master <- lift getYesod
     env <- waiRequest
@@ -436,3 +446,6 @@
 
 instance YesodAuth master => YesodSubDispatch Auth (HandlerT master IO) where
     yesodSubDispatch = $(mkYesodSubDispatch resourcesAuth)
+
+asHtml :: Html -> Html
+asHtml = id
diff --git a/Yesod/Auth/BrowserId.hs b/Yesod/Auth/BrowserId.hs
--- a/Yesod/Auth/BrowserId.hs
+++ b/Yesod/Auth/BrowserId.hs
@@ -77,7 +77,7 @@
                       $logErrorS "yesod-auth" "BrowserID assertion failure"
                       tm <- getRouteToParent
                       lift $ loginErrorMessage (tm LoginR) "BrowserID login error."
-                    Just email -> lift $ setCreds True Creds
+                    Just email -> lift $ setCredsRedirect Creds
                         { credsPlugin = pid
                         , credsIdent = email
                         , credsExtra = []
diff --git a/Yesod/Auth/Dummy.hs b/Yesod/Auth/Dummy.hs
--- a/Yesod/Auth/Dummy.hs
+++ b/Yesod/Auth/Dummy.hs
@@ -18,7 +18,7 @@
   where
     dispatch "POST" [] = do
         ident <- lift $ runInputPost $ ireq textField "ident"
-        lift $ setCreds True $ Creds "dummy" ident []
+        lift $ setCredsRedirect $ Creds "dummy" ident []
     dispatch _ _ = notFound
     url = PluginR "dummy" []
     login authToMaster =
diff --git a/Yesod/Auth/Email.hs b/Yesod/Auth/Email.hs
--- a/Yesod/Auth/Email.hs
+++ b/Yesod/Auth/Email.hs
@@ -46,8 +46,8 @@
 import qualified Text.Email.Validate
 import qualified Yesod.Auth.Message as Msg
 import Control.Applicative ((<$>), (<*>))
+import Control.Monad (void)
 import Yesod.Form
-import Control.Monad (when)
 import Data.Time (getCurrentTime, addUTCTime)
 import Safe (readMay)
 
@@ -83,7 +83,11 @@
     , emailCredsEmail :: Email
     }
 
-class (YesodAuth site, PathPiece (AuthEmailId site)) => YesodAuthEmail site where
+class ( YesodAuth site
+      , PathPiece (AuthEmailId site)
+      , (RenderMessage site Msg.AuthMessage)
+      )
+  => YesodAuthEmail site where
     type AuthEmailId site
 
     -- | Add a new email address to the database, but indicate that the address
@@ -172,10 +176,14 @@
     -- | Response after sending a confirmation email.
     --
     -- Since 1.2.2
-    confirmationEmailSentResponse :: Text -> HandlerT site IO Html
-    confirmationEmailSentResponse identifier = authLayout $ do
-        setTitleI Msg.ConfirmationEmailSentTitle
-        [whamlet|<p>_{Msg.ConfirmationEmailSent identifier}|]
+    confirmationEmailSentResponse :: Text -> HandlerT site IO TypedContent
+    confirmationEmailSentResponse identifier = do
+        mr <- getMessageRender
+        messageJson401 (mr msg) $ authLayout $ do
+              setTitleI Msg.ConfirmationEmailSentTitle
+              [whamlet|<p>_{msg}|]
+      where
+        msg = Msg.ConfirmationEmailSent identifier
 
     -- | Additional normalization of email addresses, besides standard canonicalization.
     --
@@ -218,7 +226,7 @@
          -- field for the old password should be presented.
          -- Otherwise, just two fields for the new password are
          -- needed.
-      -> AuthHandler site Html
+      -> AuthHandler site TypedContent
     setPasswordHandler = defaultSetPasswordHandler
 
 
@@ -280,40 +288,46 @@
 registerHelper :: YesodAuthEmail master
                => Bool -- ^ allow usernames?
                -> Route Auth
-               -> HandlerT Auth (HandlerT master IO) Html
+               -> HandlerT Auth (HandlerT master IO) TypedContent
 registerHelper allowUsername dest = do
     y <- lift getYesod
     midentifier <- lookupPostParam "email"
-    identifier <-
-        case midentifier of
-            Nothing -> loginErrorMessageI dest Msg.NoIdentifierProvided
+    let eidentifier = case midentifier of
+            Nothing -> Left Msg.NoIdentifierProvided
             Just x
                 | Just x' <- Text.Email.Validate.canonicalizeEmail (encodeUtf8 x) ->
-                    return $ normalizeEmailAddress y $ decodeUtf8With lenientDecode x'
-                | allowUsername -> return $ TS.strip x
-                | otherwise -> loginErrorMessageI dest Msg.InvalidEmailAddress
+                    Right $ normalizeEmailAddress y $ decodeUtf8With lenientDecode x'
+                | allowUsername -> Right $ TS.strip x
+                | otherwise -> Left Msg.InvalidEmailAddress
 
-    mecreds <- lift $ getEmailCreds identifier
-    (lid, verKey, email) <-
-        case mecreds of
-            Just (EmailCreds lid _ _ (Just key) email) -> return (lid, key, email)
-            Just (EmailCreds lid _ _ Nothing email) -> do
-                key <- liftIO $ randomKey y
-                lift $ setVerifyKey lid key
-                return (lid, key, email)
-            Nothing
-                | allowUsername ->
-                    loginErrorMessageI dest (Msg.IdentifierNotFound identifier)
-                | otherwise -> do
-                    key <- liftIO $ randomKey y
-                    lid <- lift $ addUnverified identifier key
-                    return (lid, key, identifier)
-    render <- getUrlRender
-    let verUrl = render $ verify (toPathPiece lid) verKey
-    lift $ sendVerifyEmail email verKey verUrl
-    lift $ confirmationEmailSentResponse identifier
+    case eidentifier of
+        Left route -> loginErrorMessageI dest route
+        Right identifier -> do
 
-postRegisterR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) Html
+            mecreds <- lift $ getEmailCreds identifier
+            registerCreds <-
+                case mecreds of
+                    Just (EmailCreds lid _ _ (Just key) email) -> return $ Just (lid, key, email)
+                    Just (EmailCreds lid _ _ Nothing email) -> do
+                        key <- liftIO $ randomKey y
+                        lift $ setVerifyKey lid key
+                        return $ Just (lid, key, email)
+                    Nothing
+                        | allowUsername -> return Nothing
+                        | otherwise -> do
+                            key <- liftIO $ randomKey y
+                            lid <- lift $ addUnverified identifier key
+                            return $ Just (lid, key, identifier)
+
+            case registerCreds of
+                Nothing -> loginErrorMessageI dest (Msg.IdentifierNotFound identifier)
+                Just (lid, verKey, email) -> do
+                    render <- getUrlRender
+                    let verUrl = render $ verify (toPathPiece lid) verKey
+                    lift $ sendVerifyEmail email verKey verUrl
+                    lift $ confirmationEmailSentResponse identifier
+
+postRegisterR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) TypedContent
 postRegisterR = registerHelper False registerR
 
 getForgotPasswordR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) Html
@@ -337,35 +351,43 @@
                 <button .btn>_{Msg.SendPasswordResetEmail}
         |]
 
-postForgotPasswordR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) Html
+postForgotPasswordR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) TypedContent
 postForgotPasswordR = registerHelper True forgotPasswordR
 
 getVerifyR :: YesodAuthEmail site
            => AuthEmailId site
            -> Text
-           -> HandlerT Auth (HandlerT site IO) Html
+           -> HandlerT Auth (HandlerT site IO) TypedContent
 getVerifyR lid key = do
     realKey <- lift $ getVerifyKey lid
     memail <- lift $ getEmail lid
+    mr <- lift getMessageRender
     case (realKey == Just key, memail) of
         (True, Just email) -> do
             muid <- lift $ verifyAccount lid
             case muid of
-                Nothing -> return ()
+                Nothing -> invalidKey mr
                 Just uid -> do
                     lift $ setCreds False $ Creds "email-verify" email [("verifiedEmail", email)] -- FIXME uid?
-                    lift $ setMessageI Msg.AddressVerified
                     lift $ setLoginLinkKey uid
-                    redirect setpassR
-        _ -> return ()
-    lift $ authLayout $ do
-        setTitleI Msg.InvalidKey
+                    let msgAv = Msg.AddressVerified
+                    selectRep $ do
+                      provideRep $ do
+                        lift $ setMessageI msgAv
+                        fmap asHtml $ redirect setpassR
+                      provideJsonMessage $ mr msgAv
+        _ -> invalidKey mr
+  where
+    msgIk = Msg.InvalidKey
+    invalidKey mr = messageJson401 (mr msgIk) $ lift $ authLayout $ do
+        setTitleI msgIk
         [whamlet|
 $newline never
-<p>_{Msg.InvalidKey}
+<p>_{msgIk}
 |]
 
-postLoginR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) ()
+
+postLoginR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) TypedContent
 postLoginR = do
     (identifier, pass) <- lift $ runInputPost $ (,)
         <$> ireq textField "email"
@@ -388,37 +410,40 @@
     let isEmail = Text.Email.Validate.isValid $ encodeUtf8 identifier
     case maid of
         Just email ->
-            lift $ setCreds True $ Creds
+            lift $ setCredsRedirect $ Creds
                 (if isEmail then "email" else "username")
                 email
                 [("verifiedEmail", email)]
-        Nothing -> do
+        Nothing ->
             loginErrorMessageI LoginR $
                 if isEmail
                     then Msg.InvalidEmailPass
                     else Msg.InvalidUsernamePass
 
-getPasswordR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) Html
+getPasswordR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) TypedContent
 getPasswordR = do
     maid <- lift maybeAuthId
     case maid of
-        Just _ -> return ()
         Nothing -> loginErrorMessageI LoginR Msg.BadSetPass
-    needOld <- maybe (return True) (lift . needOldPassword) maid
-    setPasswordHandler needOld
+        Just _ -> do
+            needOld <- maybe (return True) (lift . needOldPassword) maid
+            setPasswordHandler needOld
 
 -- | Default implementation of 'setPasswordHandler'.
 --
 -- Since: 1.2.6
-defaultSetPasswordHandler :: YesodAuthEmail master => Bool -> AuthHandler master Html
+defaultSetPasswordHandler :: YesodAuthEmail master => Bool -> AuthHandler master TypedContent
 defaultSetPasswordHandler needOld = do
     tp <- getRouteToParent
     pass0 <- newIdent
     pass1 <- newIdent
     pass2 <- newIdent
-    lift $ authLayout $ do
-        setTitleI Msg.SetPassTitle
-        [whamlet|
+    mr <- lift getMessageRender
+    selectRep $ do
+      provideJsonMessage $ mr Msg.SetPass
+      provideRep $ lift $ authLayout $ do
+          setTitleI Msg.SetPassTitle
+          [whamlet|
 $newline never
 <h3>_{Msg.SetPass}
 <form method="post" action="@{tp setpassR}">
@@ -444,45 +469,52 @@
                 <input type="submit" value=_{Msg.SetPassTitle}>
 |]
 
-postPasswordR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) ()
+postPasswordR :: YesodAuthEmail master => HandlerT Auth (HandlerT master IO) TypedContent
 postPasswordR = do
     maid <- lift maybeAuthId
-    aid <- case maid of
-            Nothing -> loginErrorMessageI LoginR Msg.BadSetPass
-            Just aid -> return aid
-
-    tm <- getRouteToParent
+    case maid of
+        Nothing -> loginErrorMessageI LoginR Msg.BadSetPass
+        Just aid -> do
+            tm <- getRouteToParent
 
-    needOld <- lift $ needOldPassword aid
-    when needOld $ do
-        current <- lift $ runInputPost $ ireq textField "current"
-        mrealpass <- lift $ getPassword aid
-        case mrealpass of
-            Nothing ->
-                lift $ loginErrorMessage (tm setpassR) "You do not currently have a password set on your account"
-            Just realpass
-                | isValidPass current realpass -> return ()
-                | otherwise ->
-                    lift $ loginErrorMessage (tm setpassR) "Invalid current password, please try again"
+            needOld <- lift $ needOldPassword aid
+            if not needOld then confirmPassword aid tm else do
+                current <- lift $ runInputPost $ ireq textField "current"
+                mrealpass <- lift $ getPassword aid
+                case mrealpass of
+                    Nothing ->
+                        lift $ loginErrorMessage (tm setpassR) "You do not currently have a password set on your account"
+                    Just realpass
+                        | isValidPass current realpass -> confirmPassword aid tm
+                        | otherwise ->
+                            lift $ loginErrorMessage (tm setpassR) "Invalid current password, please try again"
 
-    (new, confirm) <- lift $ runInputPost $ (,)
-        <$> ireq textField "new"
-        <*> ireq textField "confirm"
-    when (new /= confirm) $
-        loginErrorMessageI setpassR Msg.PassMismatch
+  where
+    msgOk = Msg.PassUpdated
+    confirmPassword aid tm = do
+        (new, confirm) <- lift $ runInputPost $ (,)
+            <$> ireq textField "new"
+            <*> ireq textField "confirm"
 
-    isSecure <- lift $ checkPasswordSecurity aid new
-    case isSecure of
-        Left e -> lift $ loginErrorMessage (tm setpassR) e
-        Right () -> return ()
+        if new /= confirm
+          then loginErrorMessageI setpassR Msg.PassMismatch
+          else do
+              isSecure <- lift $ checkPasswordSecurity aid new
+              case isSecure of
+                  Left e -> lift $ loginErrorMessage (tm setpassR) e
+                  Right () -> do
+                      salted <- liftIO $ saltPass new
+                      y <- lift $ do
+                          setPassword aid salted
+                          deleteSession loginLinkKey
+                          setMessageI msgOk
+                          getYesod
 
-    salted <- liftIO $ saltPass new
-    lift $ do
-        y <- getYesod
-        setPassword aid salted
-        setMessageI Msg.PassUpdated
-        deleteSession loginLinkKey
-        redirect $ afterPasswordRoute y
+                      mr <- lift getMessageRender
+                      selectRep $ do
+                        provideRep $
+                          fmap asHtml $ lift $ redirect $ afterPasswordRoute y
+                        provideJsonMessage (mr msgOk)
 
 saltLength :: Int
 saltLength = 5
diff --git a/Yesod/Auth/GoogleEmail.hs b/Yesod/Auth/GoogleEmail.hs
--- a/Yesod/Auth/GoogleEmail.hs
+++ b/Yesod/Auth/GoogleEmail.hs
@@ -69,19 +69,19 @@
         completeHelper posts
     dispatch _ _ = notFound
 
-completeHelper :: YesodAuth master => [(Text, Text)] -> AuthHandler master ()
+completeHelper :: YesodAuth master => [(Text, Text)] -> AuthHandler master TypedContent
 completeHelper gets' = do
       master <- lift getYesod
       eres <- lift $ try $ OpenId.authenticateClaimed gets' (authHttpManager master)
       tm <- getRouteToParent
       either (onFailure tm) (onSuccess tm) eres
     where
-      onFailure tm err = do
+      onFailure tm err =
         lift $ loginErrorMessage (tm LoginR) $ T.pack $ show (err :: SomeException)
       onSuccess tm oir = do
               let OpenId.Identifier ident = OpenId.oirOpLocal oir
               memail <- lookupGetParam "openid.ext1.value.email"
               case (memail, "https://www.google.com/accounts/o8/id" `T.isPrefixOf` ident) of
-                  (Just email, True) -> lift $ setCreds True $ Creds pid email []
+                  (Just email, True) -> lift $ setCredsRedirect $ Creds pid email []
                   (_, False)   -> lift $ loginErrorMessage (tm LoginR) "Only Google login is supported"
                   (Nothing, _) -> lift $ loginErrorMessage (tm LoginR) "No email address provided"
diff --git a/Yesod/Auth/HashDB.hs b/Yesod/Auth/HashDB.hs
deleted file mode 100644
--- a/Yesod/Auth/HashDB.hs
+++ /dev/null
@@ -1,278 +0,0 @@
-{-# LANGUAGE DeriveDataTypeable         #-}
-{-# LANGUAGE QuasiQuotes                #-}
-{-# LANGUAGE ConstraintKinds            #-}
-{-# LANGUAGE FlexibleContexts           #-}
-{-# LANGUAGE TypeFamilies               #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE TemplateHaskell            #-}
-{-# LANGUAGE OverloadedStrings          #-}
-{-# LANGUAGE GADTs                      #-}
-{-# LANGUAGE CPP                        #-}
--------------------------------------------------------------------------------
--- |
--- Module      :  Yesod.Auth.HashDB
--- Copyright   :  (c) Patrick Brisbin 2010 
--- License     :  as-is
---
--- Maintainer  :  pbrisbin@gmail.com
--- Stability   :  Stable
--- Portability :  Portable
---
--- /WARNING/: This module was /not/ designed with security in mind, and is not
--- suitable for production sites. In the near future, it will likely be either
--- deprecated or rewritten to have a more secure implementation. For more
--- information, see: <https://github.com/yesodweb/yesod/issues/668>.
---
--- A yesod-auth AuthPlugin designed to look users up in Persist where
--- their user id's and a salted SHA1 hash of their password is stored.
---
--- Example usage:
---
--- > -- import the function
--- > import Auth.HashDB
--- >
--- > -- make sure you have an auth route
--- > mkYesodData "MyApp" [$parseRoutes|
--- > / RootR GET
--- > /auth AuthR Auth getAuth
--- > |]
--- >
--- >
--- > -- make your app an instance of YesodAuth using this plugin
--- > instance YesodAuth MyApp where
--- >    type AuthId MyApp = UserId
--- >
--- >    loginDest _  = RootR
--- >    logoutDest _ = RootR
--- >    getAuthId    = getAuthIdHashDB AuthR (Just . UniqueUser)
--- >    authPlugins  = [authHashDB (Just . UniqueUser)]
--- >
--- >
--- > -- include the migration function in site startup
--- > withServer :: (Application -> IO a) -> IO a
--- > withServer f = withConnectionPool $ \p -> do
--- >     runSqlPool (runMigration migrateUsers) p
--- >     let h = DevSite p
---
--- Note that function which converts username to unique identifier must be same.
---
--- Your app must be an instance of YesodPersist. and the username,
--- salt and hashed-passwords should be added to the database.
---
--- > echo -n 'MySaltMyPassword' | sha1sum
---
--- can be used to get the hash from the commandline.
---
--------------------------------------------------------------------------------
-module Yesod.Auth.HashDB
-    ( HashDBUser(..)
-    , Unique (..)
-    , setPassword
-      -- * Authentification
-    , validateUser
-    , authHashDB
-    , getAuthIdHashDB
-      -- * Predefined data type
-    , User
-    , UserGeneric (..)
-    , UserId
-    , EntityField (..)
-    , migrateUsers
-    ) where
-
-import Yesod.Persist
-import Yesod.Form
-import Yesod.Auth
-import Yesod.Core
-import Text.Hamlet (hamlet)
-
-import Control.Applicative         ((<$>), (<*>))
-import Control.Monad               (replicateM,liftM)
-import Data.Typeable
-
-import qualified Data.ByteString.Lazy.Char8 as BS (pack)
-import Data.Digest.Pure.SHA        (sha1, showDigest)
-import Data.Text                   (Text, pack, unpack, append)
-import Data.Maybe                  (fromMaybe)
-import System.Random               (randomRIO)
--- | Interface for data type which holds user info. It's just a
---   collection of getters and setters
-class HashDBUser user where
-  -- | Retrieve password hash from user data
-  userPasswordHash :: user -> Maybe Text
-  -- | Retrieve salt for password
-  userPasswordSalt :: user -> Maybe Text
-
-  -- | Deprecated for the better named setSaltAndPasswordHash 
-  setUserHashAndSalt :: Text    -- ^ Salt
-                     -> Text    -- ^ Password hash
-                     -> user -> user
-  setUserHashAndSalt = setSaltAndPasswordHash
-
-  -- | a callback for setPassword
-  setSaltAndPasswordHash :: Text    -- ^ Salt
-                     -> Text    -- ^ Password hash
-                     -> user -> user
-  setSaltAndPasswordHash = setUserHashAndSalt
-
--- | Generate random salt. Length of 8 is chosen arbitrarily
-randomSalt :: MonadIO m => m Text
-randomSalt = pack `liftM` liftIO (replicateM 8 (randomRIO ('0','z')))
-
--- | Calculate salted hash using SHA1.
-saltedHash :: Text              -- ^ Salt
-           -> Text              -- ^ Password
-           -> Text
-saltedHash salt = 
-  pack . showDigest . sha1 . BS.pack . unpack . append salt
-
--- | Set password for user. This function should be used for setting
---   passwords. It generates random salt and calculates proper hashes.
-setPassword :: (MonadIO m, HashDBUser user) => Text -> user -> m user
-setPassword pwd u = do salt <- randomSalt
-                       return $ setSaltAndPasswordHash salt (saltedHash salt pwd) u
-
-
-----------------------------------------------------------------
--- Authentification
-----------------------------------------------------------------
-
--- | Given a user ID and password in plaintext, validate them against
---   the database values.
-validateUser :: ( YesodPersist yesod
-                , b ~ YesodPersistBackend yesod
-                , PersistMonadBackend (b (HandlerT yesod IO)) ~ PersistEntityBackend user
-                , PersistUnique (b (HandlerT yesod IO))
-                , PersistEntity user
-                , HashDBUser    user
-                ) => 
-                Unique user     -- ^ User unique identifier
-             -> Text            -- ^ Password in plaint-text
-             -> HandlerT yesod IO Bool
-validateUser userID passwd = do
-  -- Checks that hash and password match
-  let validate u = do hash <- userPasswordHash u
-                      salt <- userPasswordSalt u
-                      return $ hash == saltedHash salt passwd
-  -- Get user data
-  user <- runDB $ getBy userID
-  return $ fromMaybe False $ validate . entityVal =<< user
-
-
-login :: AuthRoute
-login = PluginR "hashdb" ["login"]
-
-
--- | Handle the login form. First parameter is function which maps
---   username (whatever it might be) to unique user ID.
-postLoginR :: ( YesodAuth y, YesodPersist y
-              , HashDBUser user, PersistEntity user
-              , b ~ YesodPersistBackend y
-              , PersistMonadBackend (b (HandlerT y IO)) ~ PersistEntityBackend user
-              , PersistUnique (b (HandlerT y IO))
-              )
-           => (Text -> Maybe (Unique user))
-           -> HandlerT Auth (HandlerT y IO) ()
-postLoginR uniq = do
-    (mu,mp) <- lift $ runInputPost $ (,)
-        <$> iopt textField "username"
-        <*> iopt textField "password"
-
-    isValid <- lift $ fromMaybe (return False) 
-                 (validateUser <$> (uniq =<< mu) <*> mp)
-    if isValid 
-       then lift $ setCreds True $ Creds "hashdb" (fromMaybe "" mu) []
-       else do
-           tm <- getRouteToParent
-           lift $ loginErrorMessage (tm LoginR) "Invalid username/password"
-
-
--- | A drop in for the getAuthId method of your YesodAuth instance which
---   can be used if authHashDB is the only plugin in use.
-getAuthIdHashDB :: ( YesodAuth master, YesodPersist master
-                   , HashDBUser user, PersistEntity user
-                   , Key user ~ AuthId master
-                   , b ~ YesodPersistBackend master
-                   , PersistMonadBackend (b (HandlerT master IO)) ~ PersistEntityBackend user
-                   , PersistUnique (b (HandlerT master IO))
-                   )
-                => (AuthRoute -> Route master)   -- ^ your site's Auth Route
-                -> (Text -> Maybe (Unique user)) -- ^ gets user ID
-                -> Creds master                  -- ^ the creds argument
-                -> HandlerT master IO (Maybe (AuthId master))
-getAuthIdHashDB authR uniq creds = do
-    muid <- maybeAuthId
-    case muid of
-        -- user already authenticated
-        Just uid -> return $ Just uid
-        Nothing       -> do
-            x <- case uniq (credsIdent creds) of
-                   Nothing -> return Nothing
-                   Just u  -> runDB (getBy u)
-            case x of
-                -- user exists
-                Just (Entity uid _) -> return $ Just uid
-                Nothing       -> loginErrorMessage (authR LoginR) "User not found"
-
--- | Prompt for username and password, validate that against a database
---   which holds the username and a hash of the password
-authHashDB :: ( YesodAuth m, YesodPersist m
-              , HashDBUser user
-              , PersistEntity user
-              , b ~ YesodPersistBackend m
-              , PersistMonadBackend (b (HandlerT m IO)) ~ PersistEntityBackend user
-              , PersistUnique (b (HandlerT m IO)))
-           => (Text -> Maybe (Unique user)) -> AuthPlugin m
-authHashDB uniq = AuthPlugin "hashdb" dispatch $ \tm -> toWidget [hamlet|
-$newline never
-    <div id="header">
-        <h1>Login
-
-    <div id="login">
-        <form method="post" action="@{tm login}">
-            <table>
-                <tr>
-                    <th>Username:
-                    <td>
-                        <input id="x" name="username" autofocus="" required>
-                <tr>
-                    <th>Password:
-                    <td>
-                        <input type="password" name="password" required>
-                <tr>
-                    <td>&nbsp;
-                    <td>
-                        <input type="submit" value="Login">
-
-            <script>
-                if (!("autofocus" in document.createElement("input"))) {
-                    document.getElementById("x").focus();
-                }
-
-|]
-    where
-        dispatch "POST" ["login"] = postLoginR uniq >>= sendResponse
-        dispatch _ _              = notFound
-
-
-----------------------------------------------------------------
--- Predefined datatype
-----------------------------------------------------------------
-
--- | Generate data base instances for a valid user
-share [mkPersist sqlSettings, mkMigrate "migrateUsers"]
-         [persistUpperCase|
-User
-    username Text Eq
-    password Text
-    salt     Text
-    UniqueUser username
-    deriving Typeable
-|]
-
-instance HashDBUser (UserGeneric backend) where
-  userPasswordHash = Just . userPassword
-  userPasswordSalt = Just . userSalt
-  setSaltAndPasswordHash s h u = u { userSalt     = s
-                               , userPassword = h
-                               }
diff --git a/Yesod/Auth/OpenId.hs b/Yesod/Auth/OpenId.hs
--- a/Yesod/Auth/OpenId.hs
+++ b/Yesod/Auth/OpenId.hs
@@ -85,7 +85,7 @@
         completeHelper idType posts
     dispatch _ _ = notFound
 
-completeHelper :: IdentifierType -> [(Text, Text)] -> AuthHandler master ()
+completeHelper :: IdentifierType -> [(Text, Text)] -> AuthHandler master TypedContent
 completeHelper idType gets' = do
     master <- lift getYesod
     eres <- try $ OpenId.authenticateClaimed gets' (authHttpManager master)
@@ -108,7 +108,7 @@
                         case idType of
                             OPLocal -> OpenId.oirOpLocal oir
                             Claimed -> fromMaybe (OpenId.oirOpLocal oir) $ OpenId.oirClaimed oir
-            lift $ setCreds True $ Creds "openid" i gets''
+            lift $ setCredsRedirect $ Creds "openid" i gets''
 
 -- | The main identifier provided by the OpenID authentication plugin is the
 -- \"OP-local identifier\". There is also sometimes a \"claimed\" identifier
diff --git a/Yesod/Auth/Rpxnow.hs b/Yesod/Auth/Rpxnow.hs
--- a/Yesod/Auth/Rpxnow.hs
+++ b/Yesod/Auth/Rpxnow.hs
@@ -48,7 +48,7 @@
                 $ maybe id (\x -> (:) ("displayName", x))
                     (fmap pack $ getDisplayName $ map (unpack *** unpack) extra)
                   []
-        lift $ setCreds True creds
+        lift $ setCredsRedirect creds
     dispatch _ _ = notFound
 
 -- | Get some form of a display name.
diff --git a/yesod-auth.cabal b/yesod-auth.cabal
--- a/yesod-auth.cabal
+++ b/yesod-auth.cabal
@@ -1,5 +1,5 @@
 name:            yesod-auth
-version:         1.2.7.1
+version:         1.3.0.0
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman, Patrick Brisbin
@@ -10,9 +10,12 @@
 cabal-version:   >= 1.6.0
 build-type:      Simple
 homepage:        http://www.yesodweb.com/
-description:     This package provides a pluggable mechanism for allowing users to authenticate with your site. It comes with a number of common plugins, such as OpenID, BrowserID (a.k.a., Mozilla Persona), and email. Other packages are available from Hackage as well. If you've written such an add-on, please notify me so that it can be added to this description.
-                 .
-                 * <http://hackage.haskell.org/package/yesod-auth-account>: An account authentication plugin for Yesod
+description:
+    This package provides a pluggable mechanism for allowing users to authenticate with your site. It comes with a number of common plugins, such as OpenID, BrowserID (a.k.a., Mozilla Persona), and email. Other packages are available from Hackage as well. If you've written such an add-on, please notify me so that it can be added to this description.
+   .
+   * <http://hackage.haskell.org/package/yesod-auth-account>: An account authentication plugin for Yesod
+   .
+   * <https://github.com/ollieh/yesod-auth-bcrypt/>: A replacement for the previously provided HashDB module, which has been removed.
 extra-source-files: persona_sign_in_blue.png
 
 library
@@ -27,10 +30,9 @@
                    , text                    >= 0.7
                    , mime-mail               >= 0.3
                    , yesod-persistent        >= 1.2
-                   , hamlet                  >= 1.1
-                   , shakespeare
-                   , shakespeare-css         >= 1.0
-                   , shakespeare-js          >= 1.0.2
+                   , hamlet                  >= 1.1       && < 1.2
+                   , shakespeare-css         >= 1.0       && < 1.1
+                   , shakespeare-js          >= 1.0.2     && < 1.3
                    , containers
                    , unordered-containers
                    , yesod-form              >= 1.3       && < 1.4
@@ -59,7 +61,6 @@
                      Yesod.Auth.Email
                      Yesod.Auth.OpenId
                      Yesod.Auth.Rpxnow
-                     Yesod.Auth.HashDB
                      Yesod.Auth.Message
                      Yesod.Auth.GoogleEmail
     other-modules:   Yesod.Auth.Routes
