module Network.Pusher.Internal.Auth
( AuthString
, AuthSignature
, authenticatePresence
, authenticatePresenceWithEncoder
, authenticatePrivate
, makeQS
) where
import qualified Data.Aeson as A
import Data.Char (toLower)
import Data.Monoid ((<>))
import Data.Text.Encoding (encodeUtf8)
import GHC.Exts (sortWith)
#if MIN_VERSION_aeson(1,0,0)
import qualified Data.Aeson.Text as A
#else
import qualified Data.Aeson.Encode as A
#endif
import qualified Crypto.Hash as Hash
import qualified Crypto.MAC.HMAC as HMAC
import qualified Data.ByteArray as BA
import qualified Data.ByteString as B
import qualified Data.ByteString.Base16 as B16
import qualified Data.ByteString.Char8 as BC
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Builder as TL
import Network.Pusher.Data
(AppKey, AppSecret, Channel, Credentials(..), SocketID,
renderChannel)
import Network.Pusher.Internal.HTTP (RequestQueryString)
import Network.Pusher.Internal.Util (show')
makeQS ::
AppKey
-> AppSecret
-> T.Text
-> T.Text
-> RequestQueryString
-> B.ByteString
-> Int
-> RequestQueryString
makeQS appKey appSecret method fullPath params body ts
=
let allParams =
alphabeticalOrder . lowercaseKeys . (params ++) $
[ ("auth_key", appKey)
, ("auth_timestamp", show' ts)
, ("auth_version", "1.0")
, ( "body_md5"
, B16.encode $ BA.convert (Hash.hash body :: Hash.Digest Hash.MD5))
]
authSig =
authSignature appSecret $
B.intercalate
"\n"
[ encodeUtf8 . T.toUpper $ method
, encodeUtf8 fullPath
, formQueryString allParams
]
in ("auth_signature", authSig) : allParams
where
alphabeticalOrder = sortWith fst
lowercaseKeys = map (\(k, v) -> (BC.map toLower k, v))
formQueryString :: RequestQueryString -> B.ByteString
formQueryString = B.intercalate "&" . map (\(a, b) -> a <> "=" <> b)
type AuthString = B.ByteString
type AuthSignature = B.ByteString
authSignature :: AppSecret -> AuthString -> AuthSignature
authSignature appSecret authString =
B16.encode $
BA.convert (HMAC.hmac appSecret authString :: HMAC.HMAC Hash.SHA256)
authenticatePrivate :: Credentials -> SocketID -> Channel -> AuthSignature
authenticatePrivate cred socketID channel =
let sig =
authSignature
(credentialsAppSecret cred)
(encodeUtf8 $ socketID <> ":" <> renderChannel channel)
in credentialsAppKey cred <> ":" <> sig
authenticatePresence ::
A.ToJSON a => Credentials -> SocketID -> Channel -> a -> AuthSignature
authenticatePresence =
authenticatePresenceWithEncoder
(TL.toStrict . TL.toLazyText . A.encodeToTextBuilder . A.toJSON)
authenticatePresenceWithEncoder ::
(a -> T.Text)
-> Credentials
-> SocketID
-> Channel
-> a
-> AuthSignature
authenticatePresenceWithEncoder userEncoder cred socketID channel userData =
let authString =
encodeUtf8 $
socketID <> ":" <> renderChannel channel <> ":" <> userEncoder userData
sig = authSignature (credentialsAppSecret cred) authString
in credentialsAppKey cred <> ":" <> sig