-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Extensible interface to Web APIs -- -- goggles helps with exchanging data with APIs that require -- authentication. In particular, it handles the details of expiring -- session tokens, so the user does not have to implement this logic in -- her program. @package goggles @version 0.3.2 module Network.Goggles.Auth -- | send a POST request over HTTPS to a given URI that will return a -- OAuth2Token requestOAuth2Token :: (MonadHttp m, MonadThrow m) => Url scheme -> [(Text, Text)] -> Option scheme -> m OAuth2Token data OAuth2Token OAuth2Token :: Int -> Text -> Maybe Text -> OAuth2Token [oaTokenExpirySeconds] :: OAuth2Token -> Int [oaTokenString] :: OAuth2Token -> Text [oaTokenType] :: OAuth2Token -> Maybe Text data OAuth2TokenUTC OAuth2TokenUTC :: UTCTime -> Text -> Maybe Text -> OAuth2TokenUTC [oauTokenExpiry] :: OAuth2TokenUTC -> UTCTime [oauTokenString] :: OAuth2TokenUTC -> Text [oauTokenType] :: OAuth2TokenUTC -> Maybe Text mkOAuth2TokenUTC :: (MonadIO m, Integral t) => t -> OAuth2Token -> m OAuth2TokenUTC -- |

Introduction

-- -- This library aims to abstract away (part of) the bookkeeping related -- to exchanging data with web APIs. -- -- In particular, goggles can take care of automatically -- refreshing a token that has a finite lifetime such that the program -- never uses an invalid token. The token is furthermore cached such that -- network usage is reduced to a minimum. -- --

Preliminaries

-- --
--   import Network.Goggles
--   
-- -- Required language extensions: OverloadedStrings, -- TypeFamilies, FlexibleInstances . -- --

Usage

-- -- To begin with, the user provides a type for the remote service she -- wishes to interface to, along with a couple typeclass instances. -- --
--   data Remote
--   
--   newtype C = C { apiKey :: Text } deriving Eq   -- API authentication credentials
--   
-- -- Notice we don't actually need any data constructor associated with the -- Remote type. In the simplest case it can be a "phantom type", -- only used to label typeclass instances. -- -- This library design allows to be general as possible (many instances -- are polymorphic in this label, so the user doesn't need to write -- them), and specific where needed (as we will see with the exception -- handling mechanism further below. -- -- There are so far two main use cases for goggles, -- corresponding to the complexity of the remote API authentication -- mechanism. -- --

1. Simple authentication

-- -- If calling the remote API only requires a key of some sort (i.e. does -- not involve a session token), the Remote type should only be -- extended with a HasCredentials interface: -- --
--   instance HasCredentials Remote where
--     type Credentials Remote = C
--   
-- --

2. Token-based authentication

-- -- If the API requires a token as well (this is the case with -- OAuth2-based implementations), the user must extend the -- HasToken typeclass as well, by providing two associated types -- and a method implementation : -- --
--   instance HasToken Remote where
--     type Options Remote = ...                  -- any parameters that should be passed to the API call
--     type TokenContent Remote = ...             -- the raw token string type returned by the API, often a ByteString             
--     tokenFetch = ...                           -- function that creates and retrieves the token from the remote API
--   
-- -- Once this is in place, a valid token can always be retrieved with -- accessToken. This checks the validity of the locally cached -- token and performs a tokenFetch only when this is about to -- expire. -- --

Exception handling

-- -- Internally, goggles uses req for HTTP connections, -- so the user must always provide a MonadHttp instance for her -- Remote type : -- --
--   instance MonadHttp (WebApiM Remote) where
--     handleHttpExcepion = ...
--   
-- -- The actual implementation of handleHttpException depends on -- the actual API semantics, but the user can just use throwM -- here (imported from the Catch module of the exceptions -- package). -- --

Putting it all together

-- -- The usual workflow is as follows: -- -- module Network.Goggles -- | GET a lazy bytestring from an API endpoint getLbs :: (HasCredentials c, MonadHttp (WebApiM c)) => Url scheme -> Option scheme -> WebApiM c LbsResponse -- | POST a request to an API endpoint and receive a lazy bytestring -- in return postLbs :: (HasCredentials c, MonadHttp (WebApiM c)) => Url scheme -> Option scheme -> ByteString -> WebApiM c LbsResponse -- | PUT a request to an API endpoint and receive a lazy bytestring -- in return putLbs :: (HasCredentials c, MonadHttp (WebApiM c)) => Url scheme -> Option scheme -> ByteString -> WebApiM c LbsResponse -- | Create an authenticated GET call getLbsWithToken :: (HasCredentials c, HasToken c, MonadHttp (WebApiM c)) => (TokenContent c -> Url scheme -> Option scheme -> (Url scheme, Option scheme)) -> Url scheme -> Option scheme -> WebApiM c LbsResponse -- | Create an authenticated POST call postLbsWithToken :: (HasCredentials c, HasToken c, MonadHttp (WebApiM c)) => (TokenContent c -> Url scheme -> Option scheme -> ByteString -> (Url scheme, Option scheme, ByteString)) -> Url scheme -> Option scheme -> ByteString -> WebApiM c LbsResponse -- | Create an authenticated PUT call putLbsWithToken :: (HasCredentials c, HasToken c, MonadHttp (WebApiM c)) => (TokenContent c -> Url scheme -> Option scheme -> ByteString -> (Url scheme, Option scheme, ByteString)) -> Url scheme -> Option scheme -> ByteString -> WebApiM c LbsResponse -- | Create a Handle with an initially empty token createHandle :: HasCredentials c => Credentials c -> Options c -> IO (Handle c) -- | Evaluate a WebApiM action, given a Handle. -- -- NB : Assumes all exceptions are handled by throwM evalWebApiIO :: Handle c -> WebApiM c a -> IO a -- | Lift an `IO a` action into the WebApiM monad, and catch -- synchronous exceptions, while rethrowing the asynchronous ones to IO liftWebApiIO :: IO a -> WebApiM c a -- | The main type of the library. It can easily be re-used in libraries -- that interface with more than one cloud API provider because its type -- parameter c lets us declare distinct behaviours for each. newtype WebApiM c a WebApiM :: ReaderT (Handle c) IO a -> WebApiM c a [runWebApiM] :: WebApiM c a -> ReaderT (Handle c) IO a class HasCredentials c where { type family Credentials c; } class HasToken c where { type family TokenContent c; type family Options c; } tokenFetch :: HasToken c => WebApiM c (Token c) -- | An authentication Token with an expiry date data Token c Token :: TokenContent c -> UTCTime -> Token c [tToken] :: Token c -> TokenContent c [tTime] :: Token c -> UTCTime -- | Extract the token content (needed to authenticate subsequent -- requests). The token will be valid for at least 60 seconds accessToken :: (HasToken c) => WebApiM c (TokenContent c) refreshToken :: HasToken c => WebApiM c (Token c) -- | A Handle contains all information necessary to communicating -- with a cloud API provider: -- -- data Handle c Handle :: Credentials c -> TVar (Maybe (Token c)) -> Options c -> Handle c [credentials] :: Handle c -> Credentials c [token] :: Handle c -> TVar (Maybe (Token c)) [options] :: Handle c -> Options c -- | Parse a chunk of text into an RSA private key. For Google Cloud -- Platform , this is the private key associated with the user's "service -- account" (for server-to-server API use) -- --
--   https://console.cloud.google.com/apis/credentials
--   
-- -- Note: do not supply the RSA header and footer or any newlines -- (they will be inserted by this function). parseRSAPrivateKey :: MonadThrow m => Text -> m PrivateKey data OAuth2Token OAuth2Token :: Int -> Text -> Maybe Text -> OAuth2Token [oaTokenExpirySeconds] :: OAuth2Token -> Int [oaTokenString] :: OAuth2Token -> Text [oaTokenType] :: OAuth2Token -> Maybe Text -- | Authentication key exceptions data KeyException NoSecretFound :: !String -> KeyException NoParsePK :: !String -> KeyException NoRSAKey :: !String -> KeyException -- | Errors associated with JWT-encoded token request data JWTError BadExpirationTime :: !String -> JWTError CryptoSignError :: !String -> JWTError -- | Token exchange exceptions data TokenExchangeException -- | Something went wrong with the request, token not found NotFound :: !String -> TokenExchangeException APICredentialsNotFound :: !String -> TokenExchangeException -- | Cloud API exception data CloudException UnknownError :: !String -> CloudException IOError :: !String -> CloudException TimeoutError :: !String -> CloudException JsonDecodeError :: !String -> CloudException XMLDecodeError :: !String -> CloudException urlEncode :: String -> String