windowslive-0.1.1: src/Network/WindowsLive/Login.hs
-- |Windows Live Web Authentication. See
-- <http://msdn.microsoft.com/en-us/library/bb676633.aspx>.
module Network.WindowsLive.Login
( -- * Generate URLs for starting authentication and signing out
getLoginUrl
, getLogoutUrl
, baseUrl
, secureUrl
-- * Proces authentication responses
, processToken
, User(..)
)
where
import Data.Char ( isDigit )
import Control.Monad ( liftM, ap, unless )
import Control.Monad.Error ( MonadError )
import Data.Time.Clock.POSIX ( POSIXTime )
import Network.WindowsLive.Token
import Network.URI ( URI, parseURI, parseRelativeReference )
import Data.Monoid ( mconcat )
import qualified Data.URLEncoded as URLEnc
import Data.URLEncoded ( (%=), (%=?) )
baseUrl :: URI
Just baseUrl = parseURI "http://login.live.com/"
secureUrl :: URI
Just secureUrl = parseURI "https://login.live.com/"
appIdQ :: App -> URLEnc.URLEncoded
appIdQ = ("appid" %=) . appId
-- |Generate a /relative/ authentication start URL
getLoginUrl :: App
-> Maybe String -- ^The application context
-> Maybe String -- ^The locale in which to display the
-- authentication UI
-> URI
getLoginUrl app ctx mkt =
let Just u = parseRelativeReference "wlogin.srf"
loginQuery = mconcat [ appIdQ app
, "alg" %= "wsignin1.0"
, "appctx" %=? ctx
, "mkt" %=? mkt
]
in URLEnc.addToURI loginQuery u
-- |Generate a /relative/ sign out URL
getLogoutUrl :: App -> Maybe String -- ^The locale in which to display
-- the sign out process
-> URI
getLogoutUrl app mkt =
let Just u = parseRelativeReference "logout.srf"
logoutQuery = mconcat [ appIdQ app, "mkt" %=? mkt ]
in URLEnc.addToURI logoutQuery u
data User = User { userID :: String
, userTimestamp :: POSIXTime
} deriving Show
-- |Parse and validate a token from an authentication response. Throws
-- an error on failure.
processToken :: MonadError e m => App -> String -> m User
processToken app encryptedToken = do
tok <- decodeToken app encryptedToken
validateToken app tok
q <- URLEnc.importString tok
let qval k = URLEnc.lookup1 k q
qAppID <- qval "appid"
unless (appId app == qAppID) $
fail $ "Expected AppID " ++ show (appId app)
++ " but got " ++ show qAppID
User `liftM` qval "uid" `ap` (parseTimestamp =<< qval "ts")
parseTimestamp :: MonadError e m => String -> m POSIXTime
parseTimestamp ts = do
unless (all isDigit ts && not (null ts)) $
fail $ "Bad timestamp value: " ++ show ts
return $ fromInteger $ read ts