Skip to content

Commit 5a151a7

Browse files
committed
added email-auth bits
0 parents  commit 5a151a7

File tree

3 files changed

+220
-0
lines changed

3 files changed

+220
-0
lines changed

Diff for: .gitignore

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
dist*
2+
static/tmp/
3+
config/client_session_key.aes
4+
*.hi
5+
*.o
6+
*.sqlite3
7+
.hsenv*
8+
cabal-dev/
9+
yesod-devel/
10+
.cabal-sandbox
11+
cabal.sandbox.config
12+
client_session_key.aes
13+
SESCreds.hs
14+
*.db3

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Yesod email-auth example using amazon SES service to send emails

Diff for: email-auth.hs

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
{-# LANGUAGE DeriveDataTypeable #-}
2+
{-# LANGUAGE FlexibleContexts #-}
3+
{-# LANGUAGE GADTs #-}
4+
{-# LANGUAGE MultiParamTypeClasses #-}
5+
{-# LANGUAGE OverloadedStrings #-}
6+
{-# LANGUAGE QuasiQuotes #-}
7+
{-# LANGUAGE TemplateHaskell #-}
8+
{-# LANGUAGE TypeFamilies #-}
9+
import Control.Monad (join)
10+
import Control.Monad.Logger (runNoLoggingT)
11+
import Data.Maybe (isJust)
12+
import qualified Data.Text.Lazy.Encoding
13+
import Data.Typeable (Typeable)
14+
import Database.Persist.Sqlite
15+
import Database.Persist.TH
16+
import Network.Mail.Mime
17+
import Network.Mail.Mime.SES
18+
import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
19+
import Text.Hamlet (shamlet)
20+
import Text.Shakespeare.Text (stext)
21+
import Yesod
22+
import Yesod.Auth
23+
import Yesod.Auth.Email
24+
import SESCreds (access, secret)
25+
import Data.Text (Text, pack, unpack)
26+
import Data.Text.Encoding (encodeUtf8)
27+
import qualified Data.ByteString.Lazy.UTF8 as LU
28+
import Network.HTTP.Conduit (newManager, conduitManagerSettings)
29+
import Network.HTTP.Conduit (Manager)
30+
import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
31+
share [mkPersist sqlSettings { mpsGeneric = False }, mkMigrate "migrateAll"] [persistLowerCase|
32+
User
33+
email Text
34+
password Text Maybe -- Password may not be set yet
35+
verkey Text Maybe -- Used for resetting passwords
36+
verified Bool
37+
UniqueUser email
38+
deriving Typeable
39+
|]
40+
41+
data App = App
42+
{ httpManager :: Manager,
43+
connPool :: Connection
44+
}
45+
46+
mkYesod "App" [parseRoutes|
47+
/ HomeR GET
48+
/auth AuthR Auth getAuth
49+
|]
50+
51+
instance Yesod App where
52+
-- Emails will include links, so be sure to include an approot so that
53+
-- the links are valid!
54+
approot = ApprootStatic "http://localhost:3000"
55+
56+
instance RenderMessage App FormMessage where
57+
renderMessage _ _ = defaultFormMessage
58+
59+
-- Set up Persistent
60+
instance YesodPersist App where
61+
type YesodPersistBackend App = SqlPersistT
62+
runDB f = do
63+
h <- getYesod
64+
runSqlConn f $ connPool h
65+
66+
instance YesodAuth App where
67+
type AuthId App = UserId
68+
69+
loginDest _ = HomeR
70+
logoutDest _ = HomeR
71+
authPlugins _ = [authEmail]
72+
73+
-- Need to find the UserId for the given email address.
74+
getAuthId creds = runDB $ do
75+
x <- insertBy $ User (credsIdent creds) Nothing Nothing False
76+
return $ Just $
77+
case x of
78+
Left (Entity userid _) -> userid -- newly added user
79+
Right userid -> userid -- existing user
80+
81+
authHttpManager = error "Email doesn't need an HTTP manager"
82+
83+
-- Here's all of the email-specific code
84+
instance YesodAuthEmail App where
85+
type AuthEmailId App = UserId
86+
87+
afterPasswordRoute _ = HomeR
88+
89+
addUnverified email verkey =
90+
runDB $ insert $ User email Nothing (Just verkey) False
91+
92+
sendVerifyEmail email _ verurl = sendEmail email verurl
93+
where
94+
textPart = Part
95+
{ partType = "text/plain; charset=utf-8"
96+
, partEncoding = None
97+
, partFilename = Nothing
98+
, partContent = Data.Text.Lazy.Encoding.encodeUtf8
99+
[stext|
100+
Please confirm your email address by clicking on the link below.
101+
102+
#{verurl}
103+
104+
Thank you
105+
|]
106+
, partHeaders = []
107+
}
108+
htmlPart = Part
109+
{ partType = "text/html; charset=utf-8"
110+
, partEncoding = None
111+
, partFilename = Nothing
112+
, partContent = renderHtml
113+
[shamlet|
114+
<p>Please confirm your email address by clicking on the link below.
115+
<p>
116+
<a href=#{verurl}>#{verurl}
117+
<p>Thank you
118+
|]
119+
, partHeaders = []
120+
}
121+
getVerifyKey = runDB . fmap (join . fmap userVerkey) . get
122+
setVerifyKey uid key = runDB $ update uid [UserVerkey =. Just key]
123+
verifyAccount uid = runDB $ do
124+
mu <- get uid
125+
case mu of
126+
Nothing -> return Nothing
127+
Just u -> do
128+
update uid [UserVerified =. True]
129+
return $ Just uid
130+
getPassword = runDB . fmap (join . fmap userPassword) . get
131+
setPassword uid pass = runDB $ update uid [UserPassword =. Just pass]
132+
getEmailCreds email = runDB $ do
133+
mu <- getBy $ UniqueUser email
134+
case mu of
135+
Nothing -> return Nothing
136+
Just (Entity uid u) -> return $ Just EmailCreds
137+
{ emailCredsId = uid
138+
, emailCredsAuthId = Just uid
139+
, emailCredsStatus = isJust $ userPassword u
140+
, emailCredsVerkey = userVerkey u
141+
, emailCredsEmail = email
142+
}
143+
getEmail = runDB . fmap (fmap userEmail) . get
144+
145+
------------
146+
147+
148+
149+
sendEmail email url = do
150+
151+
let ses = SES
152+
{ sesFrom = "[email protected]"
153+
, sesTo = [encodeUtf8 email]
154+
, sesAccessKey = encodeUtf8 $ pack access
155+
, sesSecretKey = encodeUtf8 $ pack secret
156+
}
157+
h <- getYesod
158+
lift $ renderSendMailSES ( httpManager h) ses Mail
159+
{ mailHeaders =
160+
[ ("Subject", "Verify your email address")
161+
]
162+
, mailFrom = Address Nothing "[email protected]"
163+
, mailTo = [Address Nothing email]
164+
, mailCc = []
165+
, mailBcc = []
166+
, mailParts = return
167+
[ Part "text/plain" None Nothing [] $ LU.fromString $ unlines
168+
[ "Please go to the URL below to verify your email address."
169+
, ""
170+
, unpack url
171+
]
172+
, Part "text/html" None Nothing [] $ renderHtml [shamlet|\
173+
<img src="" alt="Haskellers">
174+
<p>Please go to the URL below to verify your email address.
175+
<p>
176+
<a href="#{url}">#{url}
177+
|]
178+
]
179+
}
180+
181+
182+
183+
----------------------
184+
185+
186+
getHomeR :: Handler Html
187+
getHomeR = do
188+
maid <- maybeAuthId
189+
defaultLayout
190+
[whamlet|
191+
<p>Your current auth ID: #{show maid}
192+
$maybe _ <- maid
193+
<p>
194+
<a href=@{AuthR LogoutR}>Logout
195+
$nothing
196+
<p>
197+
<a href=@{AuthR LoginR}>Go to the login page
198+
|]
199+
200+
main :: IO ()
201+
main = do
202+
manager <- newManager conduitManagerSettings
203+
withSqliteConn "email.db3" $ \conn -> do
204+
runNoLoggingT $ runSqlConn (runMigration migrateAll) conn
205+
warp 3000 $ App manager conn

0 commit comments

Comments
 (0)