diff --git a/yesod-auth/Yesod/Auth.hs b/yesod-auth/Yesod/Auth.hs
index 503443b5..70b9648c 100644
--- a/yesod-auth/Yesod/Auth.hs
+++ b/yesod-auth/Yesod/Auth.hs
@@ -22,6 +22,7 @@ module Yesod.Auth
-- * Plugin interface
, Creds (..)
, setCreds
+ , setCredsRedirect
, clearCreds
, loginErrorMessage
, loginErrorMessageI
@@ -36,6 +37,9 @@ module Yesod.Auth
, AuthHandler
-- * Internal
, credsKey
+ , provideJsonMessage
+ , messageJson401
+ , asHtml
) where
import Control.Monad (when)
@@ -64,6 +68,7 @@ import Control.Exception (Exception)
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 @@ type Piece = Text
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 ()
}
@@ -106,7 +111,7 @@ class (Yesod master, PathPiece (AuthId master), RenderMessage master FormMessage
authPlugins :: master -> [AuthPlugin master]
-- | What to show on the login page.
- loginHandler :: AuthHandler master RepHtml
+ loginHandler :: AuthHandler master Html
loginHandler = do
tp <- getRouteToParent
lift $ defaultLayout $ do
@@ -171,9 +176,6 @@ class (Yesod master, PathPiece (AuthId master), RenderMessage master FormMessage
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.
--
@@ -227,7 +229,7 @@ cachedAuth aid = runMaybeT $ do
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
@@ -236,7 +238,7 @@ loginErrorMessageI dest msg = do
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)
@@ -246,47 +248,55 @@ loginErrorMessageMasterI dest msg = do
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]
+setCredsRedirect :: YesodAuth master
+ => Creds master -- ^ new credentials
+ -> HandlerT master IO TypedContent
+setCredsRedirect creds = do
+ y <- getYesod
+ maid <- getAuthId creds
+ case maid of
+ Nothing ->
+ case authRoute y of
+ Nothing -> do
+ messageJson401 "Invalid Login" $ defaultLayout $
+ toWidget [shamlet|
Invalid login|]
+ Just ar -> loginErrorMessageMasterI ar Msg.InvalidLogin
+ Just aid -> do
+ setSession credsKey $ toPathPiece aid
+ 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 = do
- y <- getYesod
- maid <- getAuthId creds
- case maid of
- Nothing -> when doRedirects $ do
- case authRoute y of
- Nothing -> do
- sendResponseStatus unauthorized401 =<< (
- selectRep $ do
- provideRep $ defaultLayout $ toWidget [shamlet|
Invalid login|]
- provideJsonMessage "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
+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
-- | Clears current user credentials for the session.
--
@@ -327,7 +337,7 @@ setUltDestReferer' = lift $ do
master <- getYesod
when (redirectToReferer master) setUltDestReferer
-getLoginR :: AuthHandler master RepHtml
+getLoginR :: AuthHandler master Html
getLoginR = setUltDestReferer' >> loginHandler
getLogoutR :: AuthHandler master ()
@@ -336,7 +346,7 @@ getLogoutR = setUltDestReferer' >> redirectToPost LogoutR
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
@@ -423,3 +433,6 @@ instance Exception AuthException
instance YesodAuth master => YesodSubDispatch Auth (HandlerT master IO) where
yesodSubDispatch = $(mkYesodSubDispatch resourcesAuth)
+
+asHtml :: Html -> Html
+asHtml = id
diff --git a/yesod-auth/Yesod/Auth/BrowserId.hs b/yesod-auth/Yesod/Auth/BrowserId.hs
index ee7617b9..3e2875c7 100644
--- a/yesod-auth/Yesod/Auth/BrowserId.hs
+++ b/yesod-auth/Yesod/Auth/BrowserId.hs
@@ -77,7 +77,7 @@ authBrowserId bis@BrowserIdSettings {..} = AuthPlugin
$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/Yesod/Auth/Dummy.hs b/yesod-auth/Yesod/Auth/Dummy.hs
index 9670f709..323f0d10 100644
--- a/yesod-auth/Yesod/Auth/Dummy.hs
+++ b/yesod-auth/Yesod/Auth/Dummy.hs
@@ -18,7 +18,7 @@ authDummy =
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/Yesod/Auth/Email.hs b/yesod-auth/Yesod/Auth/Email.hs
index 9cdb3061..13296cc8 100644
--- a/yesod-auth/Yesod/Auth/Email.hs
+++ b/yesod-auth/Yesod/Auth/Email.hs
@@ -41,8 +41,8 @@ import qualified Crypto.PasswordStore as PS
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)
@@ -78,7 +78,11 @@ data EmailCreds site = EmailCreds
, 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
@@ -167,10 +171,14 @@ class (YesodAuth site, PathPiece (AuthEmailId site)) => YesodAuthEmail site wher
-- | Response after sending a confirmation email.
--
-- Since 1.2.2
- confirmationEmailSentResponse :: Text -> HandlerT site IO Html
- confirmationEmailSentResponse identifier = defaultLayout $ do
- setTitleI Msg.ConfirmationEmailSentTitle
- [whamlet|
_{Msg.ConfirmationEmailSent identifier}|]
+ confirmationEmailSentResponse :: Text -> HandlerT site IO TypedContent
+ confirmationEmailSentResponse identifier = do
+ mr <- getMessageRender
+ messageJson401 (mr msg) $ defaultLayout $ do
+ setTitleI Msg.ConfirmationEmailSentTitle
+ [whamlet|
_{msg}|]
+ where
+ msg = Msg.ConfirmationEmailSent identifier
-- | Additional normalization of email addresses, besides standard canonicalization.
--
@@ -183,6 +191,7 @@ class (YesodAuth site, PathPiece (AuthEmailId site)) => YesodAuthEmail site wher
normalizeEmailAddress :: site -> Text -> Text
normalizeEmailAddress _ = TS.toLower
+
authEmail :: YesodAuthEmail m => AuthPlugin m
authEmail =
AuthPlugin "email" dispatch $ \tm ->
@@ -235,40 +244,46 @@ getRegisterR = do
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
@@ -286,35 +301,43 @@ getForgotPasswordR = do