packages feed

sproxy-0.9.7: src/Authenticate/Token.hs

{-# LANGUAGE OverloadedStrings #-}

module Authenticate.Token (
  AuthToken(..)
, AuthUser(..)
, mkAuthToken
, tokenDigest
) where

import Data.Digest.Pure.SHA (hmacSha1, showDigest)
import Data.List.Split (splitOn)
import System.Posix.Time (epochTime)
import System.Posix.Types (EpochTime)
import qualified Data.ByteString.Lazy.Char8 as BL8

import Authenticate.Types (AuthConfig(authConfigAuthTokenKey, authConfigShelfLife))

data AuthUser = AuthUser {
  authUserEmail :: String
, authUserGivenName :: String
, authUserFamilyName :: String
} deriving (Eq)

data AuthToken = AuthToken {
  authDigest :: String
, authExpiry :: EpochTime
, authUser   :: AuthUser
}

-- Here is the format of the actual cookie we send to the client.
instance Show AuthToken where
  show a = user ++ ":" ++ show (authExpiry a) ++ ":" ++ authDigest a
    where
      u = authUser a
      user = authUserEmail u ++
          ":" ++ authUserGivenName u ++
          ":" ++ authUserFamilyName u

instance Read AuthToken where
  readsPrec _ s = case splitOn ":" s of
    [email, fn, ln, xpr, dgst]
      -> [(AuthToken
          { authUser = AuthUser email fn ln
          , authExpiry = read xpr
          , authDigest = dgst
          }, "")]
    _ -> []


-- | This generates the HMAC digest of the auth token using SHA1.
tokenDigest :: String -> AuthToken -> String
tokenDigest key a = showDigest $ hmacSha1 (BL8.pack key) (BL8.pack token)
  where token = show (authUserEmail . authUser $ a) ++ show (authExpiry a)

-- | Create an AuthToken with the default expiration time, automatically
-- calculating the digest.
mkAuthToken :: AuthConfig -> AuthUser -> IO AuthToken
mkAuthToken acfg auser = do
  now <- epochTime
  let
      expires = now + authConfigShelfLife acfg
      token = AuthToken {
          authUser = auser
        , authExpiry = expires
        , authDigest = ""
        }
      digest = tokenDigest (authConfigAuthTokenKey acfg) token
  return $ token {authDigest = digest}