diff --git a/.gitignore b/.gitignore index e766dea6..5c8a8b91 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*~ *.o *.o_p *.hi @@ -6,7 +7,12 @@ dist client_session_key.aes cabal-dev/ yesod/foobar/ -.virthualenv +.hsenv/ +.cabal-sandbox/ +cabal.sandbox.config /vendor/ -/.shelly/ -/tarballs/ +.shelly/ +tarballs/ +*.swp +dist +client_session_key.aes diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 91468aac..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "scripts"] - path = scripts - url = git://github.com/yesodweb/scripts.git diff --git a/.travis.yml b/.travis.yml index bac77556..a48e4200 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,12 +2,12 @@ language: haskell install: - cabal update - - cabal install mega-sdist hspec cabal-meta cabal-src - - git clone https://github.com/snoyberg/tagstream-conduit.git - - cd tagstream-conduit - - cabal-src-install --src-only - - cd .. - - cabal-meta install --force-reinstalls --enable-tests + - cabal install --force-reinstalls hspec cabal-meta cabal-src alex + - cabal-meta install --force-reinstalls script: - echo Done + - cabal-meta install --enable-tests + - mega-sdist --test + - cabal install hspec cabal-meta cabal-src + - cabal-meta install --force-reinstalls diff --git a/README b/README new file mode 100644 index 00000000..c2ba6ace --- /dev/null +++ b/README @@ -0,0 +1,15 @@ +Authentication methods for Haskell web applications. + +Note for Rpxnow: +By default on some (all?) installs wget does not come with root certificates +for SSL. If this is the case then Web.Authenticate.Rpxnow.authenticate will +fail as wget cannot establish a secure connection to rpxnow's servers. + +A simple *nix solution, if potentially insecure (man in the middle attacks as +you are downloading the certs) is to grab a copy of the certs extracted from +those that come with firefox, hosted by CURL at +http://curl.haxx.se/ca/cacert.pem , put them somewhere (for ex, +~/.wget/cacert.pem) and then edit your ~/.wgetrc to include: +ca_certificate=~/.wget/cacert.pem + +This should fix the problem. diff --git a/README.md b/README.md index 5098481a..7536fb5a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ An advanced web framework using the Haskell programming language. Featuring: * techniques for constant-space memory consumption * asynchronous IO * this is built in to the Haskell programming language (like Erlang) - * handles a greater concurrent load than any other web application server # Learn more: http://yesodweb.com/ @@ -27,18 +26,19 @@ Your application is a cabal package and you use `cabal` to install its dependenc Install conflicts are unfortunately common in Haskell development. If you are not using any sandbox tools, you may discover that some of the other haskell installs on your system are broken. -You can prevent this by using sandbox tools: `cabal-dev` or `hsenv`. +You can prevent this by using cabal sandbox. -Isolating an entire project with a virtual machine is also a great idea, you just need some tools to help that process. -[Vagrant](http://vagrantup.com) is a great tool for that and there is a [Haskell Platform installer](https://bitbucket.org/puffnfresh/vagrant-haskell-heroku) for it. +Isolating an entire project is also a great idea, you just need some tools to help that process. +On Linux you can use Docker. +On any OS you can use a virtual machine. [Vagrant](http://vagrantup.com) is a great tool for that and there is a [Haskell Platform installer](https://bitbucket.org/puffnfresh/vagrant-haskell-heroku) for it. -## Using cabal-dev +## Using cabal sandbox -cabal-dev creates a sandboxed environment for an individual cabal package. -Instead of using the `cabal` command, use the `cabal-dev` command which will use the sandbox. +To sandbox a project, type: -Use `yesod devel --dev` when developing your application. + cabal sandbox init +This ensures that future installs will be local to the sandboxed directory. ## Installing the latest development version from github for use with your application @@ -55,32 +55,18 @@ In your application folder, create a `sources.txt` file with the following conte https://github.com/yesodweb/wai `./` means build your app. The yesod repos will be cloned and placed in a `vendor` repo. -Now run: `cabal-meta install`. If you use `cabal-dev`, run `cabal-meta --dev install` +Now run: `cabal-meta install`. This should work almost all of the time. You can read more on [cabal-meta](https://github.com/yesodweb/cabal-meta) If you aren't building from an application, remove the `./` and create a new directory for your sources.txt first. -## hsenv (Linux only) +## hsenv (Linux and Mac OS X) -[hsenv](http://hackage.haskell.org/package/hsenv) prevents your custom build of Yesod from interfering with your currently installed cabal packages: +[hsenv](https://github.com/tmhedberg/hsenv) also provides a sandbox, but works at the shell level. +Generally we recommend using cabal sandbox, but hsenv has tools for allowing you to use different versions of GHC, which may be useful for you. -* hsenv creates an isolated environment like cabal-dev -* hsenv works at the shell level, so every shell must activate the hsenv -* cabal-dev by default isolates a single cabal package, but hsenv isolates multiple packages together. -* cabal-dev can isolate multiple packages together by using the -s sandbox argument - - -## cabal-src - -The cabal-src tool helps resolve dependency conflicts when installing local packages. -This capability is already built in if you are using cabal-dev or cabal-meta. Otherwise install cabal-src with: - - cabal install cabal-src - -Whenever you would use `cabal install` to install a local package, use `cabal-src-install` instead. -Our installer script now uses cabal-src-install when it is available. ## Cloning the repos @@ -100,7 +86,7 @@ done ## Building your changes to Yesod -Yesod is composed of 4 "mega-repos", each with multiple cabal packages. `./script/install` will run tests against each package and install each package. +The traditional Yesod stack requires 4 "mega-repos", each with multiple cabal packages. `./script/install` will run tests against each package and install each package. ### install package in all repos diff --git a/package-list.sh b/package-list.sh deleted file mode 100644 index 744f06ec..00000000 --- a/package-list.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -pkgs=( ./yesod-routes - ./yesod-core - ./yesod-json - ./crypto-conduit - ./authenticate/authenticate - ./yesod-static - ./yesod-persistent - ./yesod-newsfeed - ./yesod-form - ./yesod-auth - ./yesod-sitemap - ./yesod-default - ./yesod ) diff --git a/scripts b/scripts deleted file mode 160000 index 9902ff80..00000000 --- a/scripts +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9902ff808afbcb417c6ad125941343878e3afe11 diff --git a/sources.txt b/sources.txt index 415fc37e..b7eec6ed 100644 --- a/sources.txt +++ b/sources.txt @@ -9,3 +9,5 @@ ./yesod-test ./yesod-bin ./yesod +./yesod-eventsource +./yesod-websockets diff --git a/yesod-auth-oauth/Yesod/Auth/OAuth.hs b/yesod-auth-oauth/Yesod/Auth/OAuth.hs index fa7f1c76..4839a356 100644 --- a/yesod-auth-oauth/Yesod/Auth/OAuth.hs +++ b/yesod-auth-oauth/Yesod/Auth/OAuth.hs @@ -43,6 +43,7 @@ authOAuth oauth mkCreds = AuthPlugin name dispatch login url = PluginR name [] lookupTokenSecret = bsToText . fromMaybe "" . lookup "oauth_token_secret" . unCredential oauthSessionName = "__oauth_token_secret" + dispatch "GET" ["forward"] = do render <- lift getUrlRender tm <- getRouteToParent @@ -72,8 +73,9 @@ authOAuth oauth mkCreds = AuthPlugin name dispatch login master <- getYesod accTok <- getAccessToken oauth reqTok (authHttpManager master) creds <- liftIO $ mkCreds accTok - setCreds True creds + setCredsRedirect creds dispatch _ _ = notFound + login tm = do render <- getUrlRender let oaUrl = render $ tm $ oauthUrl name diff --git a/yesod-auth-oauth/yesod-auth-oauth.cabal b/yesod-auth-oauth/yesod-auth-oauth.cabal index 625e27d9..dff15167 100644 --- a/yesod-auth-oauth/yesod-auth-oauth.cabal +++ b/yesod-auth-oauth/yesod-auth-oauth.cabal @@ -1,9 +1,9 @@ name: yesod-auth-oauth -version: 1.2.0 +version: 1.3.0 license: BSD3 license-file: LICENSE author: Hiromi Ishii -maintainer: Hiromi Ishii +maintainer: Michael Litchard synopsis: OAuth Authentication for Yesod. category: Web, Yesod stability: Stable @@ -20,13 +20,13 @@ library cpp-options: -DGHC7 else build-depends: base >= 4 && < 4.3 - build-depends: authenticate-oauth >= 1.4 && < 1.5 + build-depends: authenticate-oauth >= 1.5 && < 1.6 , bytestring >= 0.9.1.4 , yesod-core >= 1.2 && < 1.3 - , yesod-auth >= 1.2 && < 1.3 - , text >= 0.7 && < 0.12 + , yesod-auth >= 1.3 && < 1.4 + , text >= 0.7 , yesod-form >= 1.3 && < 1.4 - , transformers >= 0.2.2 && < 0.4 + , transformers >= 0.2.2 && < 0.5 , lifted-base >= 0.2 && < 0.3 exposed-modules: Yesod.Auth.OAuth ghc-options: -Wall diff --git a/yesod-auth/Yesod/Auth.hs b/yesod-auth/Yesod/Auth.hs index a54f5dfd..d5f5d166 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 @@ -34,6 +35,11 @@ module Yesod.Auth , AuthException (..) -- * Helper , AuthHandler + -- * Internal + , credsKey + , provideJsonMessage + , messageJson401 + , asHtml ) where import Control.Monad (when) @@ -62,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 @@ -72,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 () } @@ -89,6 +96,10 @@ data Creds master = Creds class (Yesod master, PathPiece (AuthId master), RenderMessage master FormMessage) => YesodAuth master where type AuthId master + -- | specify the layout. Uses defaultLayout by default + authLayout :: WidgetT master IO () -> HandlerT master IO Html + authLayout = defaultLayout + -- | Default destination on successful login, if no other -- destination exists. loginDest :: master -> Route master @@ -104,10 +115,10 @@ 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 + lift $ authLayout $ do setTitleI Msg.LoginTitle master <- getYesod mapM_ (flip apLogin tp) (authPlugins master) @@ -163,6 +174,16 @@ class (Yesod master, PathPiece (AuthId master), RenderMessage master FormMessage => HandlerT master IO (Maybe (AuthId master)) maybeAuthId = defaultMaybeAuthId + -- | Called on login error for HTTP requests. By default, calls + -- @setMessage@ and redirects to @dest@. + onErrorHtml :: (MonadResourceBase m) => Route master -> Text -> HandlerT master m Html + onErrorHtml dest msg = do + setMessage $ toHtml msg + fmap asHtml $ redirect dest + +-- | Internal session key used to hold the authentication information. +-- +-- Since 1.2.3 credsKey :: Text credsKey = "_ID" @@ -212,7 +233,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 @@ -221,61 +242,74 @@ 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) -- | For HTML, set the message and redirect to the route. -- For JSON, send the message and a 401 status -loginErrorMessage :: MonadResourceBase m - => Route site +loginErrorMessage :: (YesodAuth master, MonadResourceBase m) + => Route master -> Text - -> HandlerT site m a -loginErrorMessage dest msg = - sendResponseStatus unauthorized401 =<< ( - selectRep $ do - provideRep $ do - setMessage $ toHtml msg - fmap asHtml $ redirect dest - provideJsonMessage msg - ) - where - asHtml :: Html -> Html - asHtml = id + -> 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" $ authLayout $ + 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 + +-- | same as defaultLayoutJson, but uses authLayout +authLayoutJson :: (YesodAuth site, ToJSON j) + => WidgetT site IO () -- ^ HTML + -> HandlerT site IO j -- ^ JSON + -> HandlerT site IO TypedContent +authLayoutJson w json = selectRep $ do + provideRep $ authLayout w + provideRep $ fmap toJSON json -- | Clears current user credentials for the session. -- @@ -293,7 +327,7 @@ clearCreds doRedirects = do getCheckR :: AuthHandler master TypedContent getCheckR = lift $ do creds <- maybeAuthId - defaultLayoutJson (do + authLayoutJson (do setTitle "Authentication Status" toWidget $ html' creds) (return $ jsonCreds creds) where @@ -316,7 +350,7 @@ setUltDestReferer' = lift $ do master <- getYesod when (redirectToReferer master) setUltDestReferer -getLoginR :: AuthHandler master RepHtml +getLoginR :: AuthHandler master Html getLoginR = setUltDestReferer' >> loginHandler getLogoutR :: AuthHandler master () @@ -325,7 +359,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 @@ -334,6 +368,11 @@ handlePluginR plugin pieces = do [] -> notFound ap:_ -> apDispatch ap method pieces +-- | Similar to 'maybeAuthId', but additionally look up the value associated +-- with the user\'s database identifier to get the value in the database. This +-- assumes that you are using a Persistent database. +-- +-- Since 1.1.0 maybeAuth :: ( YesodAuth master , PersistMonadBackend (b (HandlerT master IO)) ~ PersistEntityBackend val , b ~ YesodPersistBackend master @@ -383,6 +422,10 @@ type AuthEntity master = KeyEntity (AuthId master) requireAuthId :: YesodAuthPersist master => HandlerT master IO (AuthId master) requireAuthId = maybeAuthId >>= maybe redirectLogin return +-- | Similar to 'maybeAuth', but redirects to a login page if user is not +-- authenticated. +-- +-- Since 1.1.0 requireAuth :: YesodAuthPersist master => HandlerT master IO (Entity (AuthEntity master)) requireAuth = maybeAuth >>= maybe redirectLogin return @@ -403,3 +446,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 032de003..31c4d7fb 100644 --- a/yesod-auth/Yesod/Auth/BrowserId.hs +++ b/yesod-auth/Yesod/Auth/BrowserId.hs @@ -4,7 +4,7 @@ {-# LANGUAGE RecordWildCards #-} module Yesod.Auth.BrowserId ( authBrowserId - , createOnClick + , createOnClick, createOnClickOverride , def , BrowserIdSettings , bisAudience @@ -75,8 +75,9 @@ authBrowserId bis@BrowserIdSettings {..} = AuthPlugin case memail of Nothing -> do $logErrorS "yesod-auth" "BrowserID assertion failure" - loginErrorMessage LoginR "BrowserID login error." - Just email -> lift $ setCreds True Creds + tm <- getRouteToParent + lift $ loginErrorMessage (tm LoginR) "BrowserID login error." + Just email -> lift $ setCredsRedirect Creds { credsPlugin = pid , credsIdent = email , credsExtra = [] @@ -106,14 +107,16 @@ $newline never -- | Generates a function to handle on-click events, and returns that function -- name. -createOnClick :: BrowserIdSettings +createOnClickOverride :: BrowserIdSettings -> (Route Auth -> Route master) + -> Maybe (Route master) -> WidgetT master IO Text -createOnClick BrowserIdSettings {..} toMaster = do +createOnClickOverride BrowserIdSettings {..} toMaster mOnRegistration = do unless bisLazyLoad $ addScriptRemote browserIdJs onclick <- newIdent render <- getUrlRender - let login = toJSON $ getPath $ render (toMaster LoginR) + let login = toJSON $ getPath $ render loginRoute -- (toMaster LoginR) + loginRoute = maybe (toMaster LoginR) id mOnRegistration toWidget [julius| function #{rawJS onclick}() { if (navigator.id) { @@ -151,3 +154,10 @@ createOnClick BrowserIdSettings {..} toMaster = do getPath t = fromMaybe t $ do uri <- parseURI $ T.unpack t return $ T.pack $ uriPath uri + +-- | Generates a function to handle on-click events, and returns that function +-- name. +createOnClick :: BrowserIdSettings + -> (Route Auth -> Route master) + -> WidgetT master IO Text +createOnClick bidSettings toMaster = createOnClickOverride bidSettings toMaster Nothing 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 b57b02d8..95def734 100644 --- a/yesod-auth/Yesod/Auth/Email.hs +++ b/yesod-auth/Yesod/Auth/Email.hs @@ -2,6 +2,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE PatternGuards #-} +{-# LANGUAGE Rank2Types #-} module Yesod.Auth.Email ( -- * Plugin authEmail @@ -24,25 +25,32 @@ module Yesod.Auth.Email -- * Misc , loginLinkKey , setLoginLinkKey + -- * Default handlers + , defaultRegisterHandler + , defaultForgotPasswordHandler + , defaultSetPasswordHandler ) where import Network.Mail.Mime (randomString) import Yesod.Auth import System.Random -import Data.Digest.Pure.MD5 import qualified Data.Text as TS +import qualified Data.Text as T import qualified Data.Text.Lazy as TL +import qualified Data.Text.Encoding as TE import qualified Data.Text.Lazy.Encoding as TLE +import qualified Crypto.Hash.MD5 as H +import Data.ByteString.Base16 as B16 import Data.Text.Encoding (encodeUtf8, decodeUtf8With) import Data.Text.Encoding.Error (lenientDecode) import Data.Text (Text) import Yesod.Core -import qualified Crypto.PasswordStore as PS +import qualified Yesod.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 +86,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 @@ -164,6 +176,63 @@ class (YesodAuth site, PathPiece (AuthEmailId site)) => YesodAuthEmail site wher | TS.length x >= 3 = return $ Right () | otherwise = return $ Left "Password must be at least three characters" + -- | Response after sending a confirmation email. + -- + -- Since 1.2.2 + confirmationEmailSentResponse :: Text -> HandlerT site IO TypedContent + confirmationEmailSentResponse identifier = do + mr <- getMessageRender + messageJson401 (mr msg) $ authLayout $ do + setTitleI Msg.ConfirmationEmailSentTitle + [whamlet|

_{msg}|] + where + msg = Msg.ConfirmationEmailSent identifier + + -- | Additional normalization of email addresses, besides standard canonicalization. + -- + -- Default: Lower case the email address. + -- + -- Since 1.2.3 + normalizeEmailAddress :: site -> Text -> Text + normalizeEmailAddress _ = TS.toLower + + -- | Handler called to render the registration page. The + -- default works fine, but you may want to override it in + -- order to have a different DOM. + -- + -- Default: 'defaultRegisterHandler'. + -- + -- Since: 1.2.6. + registerHandler :: AuthHandler site Html + registerHandler = defaultRegisterHandler + + -- | Handler called to render the \"forgot password\" page. + -- The default works fine, but you may want to override it in + -- order to have a different DOM. + -- + -- Default: 'defaultForgotPasswordHandler'. + -- + -- Since: 1.2.6. + forgotPasswordHandler :: AuthHandler site Html + forgotPasswordHandler = defaultForgotPasswordHandler + + -- | Handler called to render the \"set password\" page. The + -- default works fine, but you may want to override it in + -- order to have a different DOM. + -- + -- Default: 'defaultSetPasswordHandler'. + -- + -- Since: 1.2.6. + setPasswordHandler :: + Bool + -- ^ Whether the old password is needed. If @True@, a + -- field for the old password should be presented. + -- Otherwise, just two fields for the new password are + -- needed. + -> AuthHandler site TypedContent + setPasswordHandler = defaultSetPasswordHandler + + authEmail :: YesodAuthEmail m => AuthPlugin m authEmail = AuthPlugin "email" dispatch $ \tm -> @@ -181,8 +250,11 @@ $newline never - - I don't have an account +