hoauth2-0.2.0: src/Network/OAuth2/OAuth2.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-
A simple OAuth2 Haskell binding.
This is sopposed to be independent with http client.
-}
module Network.OAuth2.OAuth2
( OAuth2 (..)
, AccessToken (..)
, OAuthException (..)
, authorizationUrl
, accessTokenUrl
) where
import Data.Aeson
import qualified Data.ByteString.Char8 as BS
import Data.Typeable (Typeable)
import Network.HTTP.Types (renderSimpleQuery)
import Control.Exception
import Control.Applicative ((<$>))
import Control.Monad (mzero)
-- | Query Parameter Representation
data OAuth2 = OAuth2 { oauthClientId :: BS.ByteString
, oauthClientSecret :: BS.ByteString
, oauthOAuthorizeEndpoint :: BS.ByteString
, oauthAccessTokenEndpoint :: BS.ByteString
, oauthCallback :: Maybe BS.ByteString
, oauthAccessToken :: Maybe BS.ByteString
} deriving (Show, Eq)
-- | Simple Exception representation.
data OAuthException = OAuthException String
deriving (Show, Eq, Typeable)
instance Exception OAuthException
-- | The gained Access Token. Use @Data.Aeson.decode@ to decode string to @AccessToken@.
data AccessToken = AccessToken { accessToken :: BS.ByteString } deriving (Show)
instance FromJSON AccessToken where
parseJSON (Object o) = AccessToken <$> o .: "access_token"
parseJSON _ = mzero
--------------------------------------------------
-- Parameter Util
-- | type synonym of query parameters
type QueryParams = [(BS.ByteString, BS.ByteString)]
-- | type synonym of post body content
type PostBody = [(BS.ByteString, BS.ByteString)]
-- | type synonym of a URI
type URI = BS.ByteString
-- | Append query parameters
appendQueryParam :: URI -> QueryParams -> URI
appendQueryParam uri q = uri `BS.append` renderSimpleQuery True q
-- | lift value in the Maybe and abonda Nothing
transform' :: [(a, Maybe b)] -> [(a, b)]
transform' = foldr step' []
where step' :: (a, Maybe b) -> [(a, b)] -> [(a, b)]
step' (a, Just b) xs = (a, b):xs
step' _ xs = xs
--------------------------------------------------
-- oauth request urls
-- | Prepare the authorization URL. Redirect to this URL asking for user interactive authentication.
authorizationUrl :: OAuth2 -> URI
authorizationUrl oa = (oauthOAuthorizeEndpoint oa) `appendQueryParam` queryStr
where queryStr = transform' [ ("client_id", Just $ oauthClientId oa)
, ("response_type", Just "code")
, ("redirect_uri", oauthCallback oa)]
-- | Prepare access token URL and the request body query.
accessTokenUrl :: OAuth2
-> BS.ByteString -- ^ access code gained via authorization URL
-> (URI, PostBody) -- ^ access token request URL plus the request body.
accessTokenUrl oa code = accessTokenUrl' oa code (Just "authorization_code")
accessTokenUrl' :: OAuth2
-> BS.ByteString -- ^ access code gained via authorization URL
-> Maybe BS.ByteString -- ^ Grant Type
-> (URI, PostBody) -- ^ access token request URL plus the request body.
accessTokenUrl' oa code gt = (uri, body)
where uri = oauthAccessTokenEndpoint oa
body = transform' [ ("client_id", Just $ oauthClientId oa)
, ("client_secret", Just $ oauthClientSecret oa)
, ("code", Just code)
, ("redirect_uri", oauthCallback oa)
, ("grant_type", gt) ]