{-# LANGUAGE OverloadedStrings #-}
{-|
    Module: Web.OIDC.Client.Internal
    Maintainer: krdlab@gmail.com
    Stability: experimental
-}
module Web.OIDC.Client.Internal where

import           Control.Applicative   ((<|>))
import           Control.Monad         (mzero)
import           Control.Monad.Catch   (MonadCatch, MonadThrow, throwM)
import           Data.Aeson            (FromJSON, Value (..), parseJSON, (.:),
                                        (.:?))
import           Data.Aeson.Types      (Parser)
import           Data.Text             (Text, unpack)
import           Data.Text.Read        (decimal)
import           Jose.Jwt              (Jwt)
import           Network.HTTP.Client   (HttpException, Request, parseRequest)
import           Prelude               hiding (exp)
import           Web.OIDC.Client.Types (OpenIdException (InternalHttpException))

data TokensResponse = TokensResponse
    { TokensResponse -> Text
accessToken  :: !Text
    , TokensResponse -> Text
tokenType    :: !Text
    , TokensResponse -> Jwt
idToken      :: !Jwt
    , TokensResponse -> Maybe Integer
expiresIn    :: !(Maybe Integer)
    , TokensResponse -> Maybe Text
refreshToken :: !(Maybe Text)
    }
  deriving (Int -> TokensResponse -> ShowS
[TokensResponse] -> ShowS
TokensResponse -> String
(Int -> TokensResponse -> ShowS)
-> (TokensResponse -> String)
-> ([TokensResponse] -> ShowS)
-> Show TokensResponse
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> TokensResponse -> ShowS
showsPrec :: Int -> TokensResponse -> ShowS
$cshow :: TokensResponse -> String
show :: TokensResponse -> String
$cshowList :: [TokensResponse] -> ShowS
showList :: [TokensResponse] -> ShowS
Show, TokensResponse -> TokensResponse -> Bool
(TokensResponse -> TokensResponse -> Bool)
-> (TokensResponse -> TokensResponse -> Bool) -> Eq TokensResponse
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: TokensResponse -> TokensResponse -> Bool
== :: TokensResponse -> TokensResponse -> Bool
$c/= :: TokensResponse -> TokensResponse -> Bool
/= :: TokensResponse -> TokensResponse -> Bool
Eq)

instance FromJSON TokensResponse where
    parseJSON :: Value -> Parser TokensResponse
parseJSON (Object Object
o) = Text
-> Text -> Jwt -> Maybe Integer -> Maybe Text -> TokensResponse
TokensResponse
        (Text
 -> Text -> Jwt -> Maybe Integer -> Maybe Text -> TokensResponse)
-> Parser Text
-> Parser
     (Text -> Jwt -> Maybe Integer -> Maybe Text -> TokensResponse)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>  Object
o Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.:  Key
"access_token"
        Parser
  (Text -> Jwt -> Maybe Integer -> Maybe Text -> TokensResponse)
-> Parser Text
-> Parser (Jwt -> Maybe Integer -> Maybe Text -> TokensResponse)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*>  Object
o Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.:  Key
"token_type"
        Parser (Jwt -> Maybe Integer -> Maybe Text -> TokensResponse)
-> Parser Jwt
-> Parser (Maybe Integer -> Maybe Text -> TokensResponse)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*>  Object
o Object -> Key -> Parser Jwt
forall a. FromJSON a => Object -> Key -> Parser a
.:  Key
"id_token"
        Parser (Maybe Integer -> Maybe Text -> TokensResponse)
-> Parser (Maybe Integer) -> Parser (Maybe Text -> TokensResponse)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> ((Object
o Object -> Key -> Parser (Maybe Integer)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"expires_in") Parser (Maybe Integer)
-> Parser (Maybe Integer) -> Parser (Maybe Integer)
forall a. Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Maybe Text -> Parser (Maybe Integer)
textToInt (Maybe Text -> Parser (Maybe Integer))
-> Parser (Maybe Text) -> Parser (Maybe Integer)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< (Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"expires_in")))
        Parser (Maybe Text -> TokensResponse)
-> Parser (Maybe Text) -> Parser TokensResponse
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*>  Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"refresh_token"
    parseJSON Value
_          = Parser TokensResponse
forall a. Parser a
forall (m :: * -> *) a. MonadPlus m => m a
mzero

textToInt :: Maybe Text -> Parser (Maybe Integer)
textToInt :: Maybe Text -> Parser (Maybe Integer)
textToInt (Just Text
t) =
    case Reader Integer
forall a. Integral a => Reader a
decimal Text
t of
        Right (Integer
i, Text
_) -> Maybe Integer -> Parser (Maybe Integer)
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe Integer -> Parser (Maybe Integer))
-> Maybe Integer -> Parser (Maybe Integer)
forall a b. (a -> b) -> a -> b
$ Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
i
        Left  String
_      -> String -> Parser (Maybe Integer)
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"expires_in: expected a decimal text, encountered a non decimal text"
textToInt Maybe Text
_        = Maybe Integer -> Parser (Maybe Integer)
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe Integer
forall a. Maybe a
Nothing

rethrow :: (MonadCatch m) => HttpException -> m a
rethrow :: forall (m :: * -> *) a. MonadCatch m => HttpException -> m a
rethrow = OpenIdException -> m a
forall e a. (HasCallStack, Exception e) => e -> m a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM (OpenIdException -> m a)
-> (HttpException -> OpenIdException) -> HttpException -> m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HttpException -> OpenIdException
InternalHttpException

parseUrl :: MonadThrow m => Text -> m Request
parseUrl :: forall (m :: * -> *). MonadThrow m => Text -> m Request
parseUrl = String -> m Request
forall (m :: * -> *). MonadThrow m => String -> m Request
Network.HTTP.Client.parseRequest (String -> m Request) -> (Text -> String) -> Text -> m Request
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
unpack