smtp-mail-0.5.0.0: Network/Mail/SMTP/Auth.hs
module Network.Mail.SMTP.Auth (
UserName,
Password,
Token,
AuthType(..),
encodeLogin,
encodeLoginOAuth,
auth,
) where
import Crypto.MAC.HMAC (hmac, HMAC)
import Crypto.Hash.Algorithms (MD5)
import Data.ByteArray (copyAndFreeze)
import qualified Data.ByteString.Base16 as B16 (encode)
import qualified Data.ByteString.Base64 as B64 (encode)
import Data.ByteString (ByteString)
import Data.List
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8 (unwords)
type UserName = String
type Password = String
type Token = String
data AuthType
= PLAIN
| LOGIN
| LOGIN_OAUTH
| CRAM_MD5
deriving Eq
instance Show AuthType where
showsPrec d at = showParen (d>app_prec) $ showString $ showMain at
where app_prec = 10
showMain PLAIN = "PLAIN"
showMain LOGIN = "LOGIN"
showMain LOGIN_OAUTH = "XOAUTH2"
showMain CRAM_MD5 = "CRAM-MD5"
toAscii :: String -> ByteString
toAscii = B.pack . map (toEnum.fromEnum)
b64Encode :: String -> ByteString
b64Encode = B64.encode . toAscii
hmacMD5 :: ByteString -> ByteString -> ByteString
hmacMD5 text key =
let mac = hmac key text :: HMAC MD5
in copyAndFreeze mac (const $ return ())
encodePlain :: UserName -> Password -> ByteString
encodePlain user pass = b64Encode $ intercalate "\0" [user, user, pass]
encodeLogin :: UserName -> Password -> (ByteString, ByteString)
encodeLogin user pass = (b64Encode user, b64Encode pass)
-- | Encode the xoauth 2 message based on:
-- https://docs.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#sasl-xoauth2
encodeLoginOAuth :: UserName -> Token -> ByteString
encodeLoginOAuth user oauthToken =
b64Encode ("user=" <> user <> "\x01" <> "auth=Bearer " <> oauthToken <> "\x01\x01")
cramMD5 :: String -> UserName -> Password -> ByteString
cramMD5 challenge user pass =
B64.encode $ B8.unwords [user', B16.encode (hmacMD5 challenge' pass')]
where
challenge' = toAscii challenge
user' = toAscii user
pass' = toAscii pass
auth :: AuthType -> String -> UserName -> Password -> ByteString
auth PLAIN _ u p = encodePlain u p
auth LOGIN _ u p = let (u', p') = encodeLogin u p in B8.unwords [u', p']
auth LOGIN_OAUTH _ u t = encodeLoginOAuth u t
auth CRAM_MD5 c u p = cramMD5 c u p