{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Network.OAuth.OAuth2.HttpClient (
fetchAccessToken,
fetchAccessToken2,
fetchRefreshToken,
refreshAccessToken,
doJSONPostRequest,
doFlexiblePostRequest,
doSimplePostRequest,
authGetJSON,
authGetBS,
authGetBS2,
authPostJSON,
authPostBS,
authPostBS2,
authPostBS3,
authRequest,
handleResponse,
parseResponseJSON,
parseResponseFlexible,
updateRequestHeaders,
setMethod
) where
import Data.Aeson
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import qualified Data.HashMap.Strict as HM (fromList)
import Data.Maybe
import qualified Data.Text.Encoding as T
import Network.HTTP.Conduit
import qualified Network.HTTP.Types as HT
import Network.HTTP.Types.URI (parseQuery)
import Network.OAuth.OAuth2.Internal
import qualified Network.OAuth.OAuth2.TokenRequest as TR
import URI.ByteString
fetchAccessToken :: Manager
-> OAuth2
-> ExchangeToken
-> IO (OAuth2Result TR.Errors OAuth2Token)
fetchAccessToken manager oa code = doFlexiblePostRequest manager oa uri body
where (uri, body) = accessTokenUrl oa code
fetchAccessToken2 :: Manager
-> OAuth2
-> ExchangeToken
-> IO (OAuth2Result TR.Errors OAuth2Token)
fetchAccessToken2 mgr oa code = do
let (url, body1) = accessTokenUrl oa code
let extraBody = [ ("client_id", T.encodeUtf8 $ oauthClientId oa)
, ("client_secret", T.encodeUtf8 $ oauthClientSecret oa)
]
doFlexiblePostRequest mgr oa url (extraBody ++ body1)
refreshAccessToken :: Manager
-> OAuth2
-> RefreshToken
-> IO (OAuth2Result TR.Errors OAuth2Token)
refreshAccessToken manager oa token = doFlexiblePostRequest manager oa uri body
where (uri, body) = refreshAccessTokenUrl oa token
{-# DEPRECATED fetchRefreshToken "Use refreshAccessToken since this method will be removed in future release" #-}
fetchRefreshToken :: Manager
-> OAuth2
-> RefreshToken
-> IO (OAuth2Result TR.Errors OAuth2Token)
fetchRefreshToken = refreshAccessToken
doJSONPostRequest :: FromJSON err => FromJSON a
=> Manager
-> OAuth2
-> URI
-> PostBody
-> IO (OAuth2Result err a)
doJSONPostRequest manager oa uri body = fmap parseResponseJSON (doSimplePostRequest manager oa uri body)
{-# DEPRECATED doFlexiblePostRequest "Use doJSONPostRequest since this function would be removed in future release." #-}
doFlexiblePostRequest :: FromJSON err => FromJSON a
=> Manager
-> OAuth2
-> URI
-> PostBody
-> IO (OAuth2Result err a)
doFlexiblePostRequest manager oa uri body = fmap parseResponseFlexible (doSimplePostRequest manager oa uri body)
doSimplePostRequest :: FromJSON err => Manager
-> OAuth2
-> URI
-> PostBody
-> IO (OAuth2Result err BSL.ByteString)
doSimplePostRequest manager oa url body = fmap handleResponse go
where go = do
req <- uriToRequest url
let addBasicAuth = applyBasicAuth (T.encodeUtf8 $ oauthClientId oa) (T.encodeUtf8 $ oauthClientSecret oa)
req' = (addBasicAuth . updateRequestHeaders Nothing) req
httpLbs (urlEncodedBody body req') manager
authGetJSON :: FromJSON err => FromJSON a
=> Manager
-> AccessToken
-> URI
-> IO (OAuth2Result err a)
authGetJSON manager t uri = parseResponseJSON <$> authGetBS manager t uri
authGetBS :: FromJSON err => Manager
-> AccessToken
-> URI
-> IO (OAuth2Result err BSL.ByteString)
authGetBS manager token url = do
req <- uriToRequest url
authRequest req upReq manager
where upReq = updateRequestHeaders (Just token) . setMethod HT.GET
authGetBS2 :: FromJSON err => Manager
-> AccessToken
-> URI
-> IO (OAuth2Result err BSL.ByteString)
authGetBS2 manager token url = do
req <- uriToRequest (url `appendAccessToken` token)
authRequest req upReq manager
where upReq = updateRequestHeaders Nothing . setMethod HT.GET
authPostJSON :: FromJSON err => FromJSON a
=> Manager
-> AccessToken
-> URI
-> PostBody
-> IO (OAuth2Result err a)
authPostJSON manager t uri pb = parseResponseJSON <$> authPostBS manager t uri pb
authPostBS :: FromJSON err => Manager
-> AccessToken
-> URI
-> PostBody
-> IO (OAuth2Result err BSL.ByteString)
authPostBS manager token url pb = do
req <- uriToRequest url
authRequest req upReq manager
where upBody = urlEncodedBody (pb ++ accessTokenToParam token)
upHeaders = updateRequestHeaders (Just token) . setMethod HT.POST
upReq = upHeaders . upBody
authPostBS2 :: FromJSON err => Manager
-> AccessToken
-> URI
-> PostBody
-> IO (OAuth2Result err BSL.ByteString)
authPostBS2 manager token url pb = do
req <- uriToRequest url
authRequest req upReq manager
where upBody = urlEncodedBody (pb ++ accessTokenToParam token)
upHeaders = updateRequestHeaders Nothing . setMethod HT.POST
upReq = upHeaders . upBody
authPostBS3 :: FromJSON err => Manager
-> AccessToken
-> URI
-> IO (OAuth2Result err BSL.ByteString)
authPostBS3 manager token url = do
req <- uriToRequest url
authRequest req upReq manager
where upBody req = req { requestBody = "null" }
upHeaders = updateRequestHeaders (Just token) . setMethod HT.POST
upReq = upHeaders . upBody
authRequest :: FromJSON err => Request
-> (Request -> Request)
-> Manager
-> IO (OAuth2Result err BSL.ByteString)
authRequest req upReq manager = fmap handleResponse (httpLbs (upReq req) manager)
handleResponse :: FromJSON err => Response BSL.ByteString -> OAuth2Result err BSL.ByteString
handleResponse rsp =
if HT.statusIsSuccessful (responseStatus rsp)
then Right $ responseBody rsp
else Left $ parseOAuth2Error (responseBody rsp)
parseResponseJSON :: FromJSON err => FromJSON a
=> OAuth2Result err BSL.ByteString
-> OAuth2Result err a
parseResponseJSON (Left b) = Left b
parseResponseJSON (Right b) = case eitherDecode b of
Left e -> Left $ mkDecodeOAuth2Error b e
Right x -> Right x
parseResponseString :: FromJSON err => FromJSON a
=> OAuth2Result err BSL.ByteString
-> OAuth2Result err a
parseResponseString (Left b) = Left b
parseResponseString (Right b) = case parseQuery $ BSL.toStrict b of
[] -> Left errorMessage
a -> case fromJSON $ queryToValue a of
Error _ -> Left errorMessage
Success x -> Right x
where
queryToValue = Object . HM.fromList . map paramToPair
paramToPair (k, mv) = (T.decodeUtf8 k, maybe Null (String . T.decodeUtf8) mv)
errorMessage = parseOAuth2Error b
parseResponseFlexible :: FromJSON err => FromJSON a
=> OAuth2Result err BSL.ByteString
-> OAuth2Result err a
parseResponseFlexible r = case parseResponseJSON r of
Left _ -> parseResponseString r
x -> x
updateRequestHeaders :: Maybe AccessToken -> Request -> Request
updateRequestHeaders t req =
let extras = [ (HT.hUserAgent, "hoauth2")
, (HT.hAccept, "application/json") ]
bearer = [(HT.hAuthorization, "Bearer " `BS.append` T.encodeUtf8 (fromJust (fmap atoken t))) | isJust t]
headers = bearer ++ extras ++ requestHeaders req
in
req { requestHeaders = headers }
setMethod :: HT.StdMethod -> Request -> Request
setMethod m req = req { method = HT.renderStdMethod m }