-- 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:
--
--
-- - Create a Handle from the API credentials and any API
-- options with createHandle. This requires a surrounding IO block
-- because token refreshing is done as an atomic update in the STM
-- monad.
-- - Build up the program that interacts with the external API in the
-- WebApiM monad.
-- - Run the program using the handle with evalWebApiIO.
--
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:
--
--
-- - Authentication credentials (e.g. username/password)
-- - Authentication token (used to authenticate every API call)
-- - Options (e.g. GCP authentication scopes)
--
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