diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,41 @@
+# ChangeLog
+
+## [Unreleased]
+
+Nothing yet.
+
+## [0.4.0.0]
+
+### Added
+- Added a validation of 'nonce' parameter. See [#24](https://github.com/krdlab/haskell-oidc-client/pull/24).
+- Made optional claims available. See [#24](https://github.com/krdlab/haskell-oidc-client/pull/24).
+- The lifecycles of 'nonce' and 'state' can also be managed by `SessionStore`. See [#24](https://github.com/krdlab/haskell-oidc-client/pull/24).
+
+### Changed
+- Made `TokenResponse` parsing strict. See [#23](https://github.com/krdlab/haskell-oidc-client/pull/23).
+- A signing algorithm is now obtained from OpenID Provider Metadata. See [#24](https://github.com/krdlab/haskell-oidc-client/pull/24).
+- 'profile' scope added to 'examples/scotty', and name / email / picture shown. See [#25](https://github.com/krdlab/haskell-oidc-client/pull/25).
+
+## [0.3.0.1]
+### Changed
+- 'expires_in' can now parsed both String and Decimal number. See [#15](https://github.com/krdlab/haskell-oidc-client/pull/15).
+### Fixed
+- Improved error messages. See [#15](https://github.com/krdlab/haskell-oidc-client/pull/15).
+
+## [0.3.0.0]
+### Changed
+- Changed `Configuration` fileds. See [#11](https://github.com/krdlab/haskell-oidc-client/pull/11).
+### Fixed
+- Fixed Hackage tarball. See [#13](https://github.com/krdlab/haskell-oidc-client/pull/13).
+
+## [0.2.0.0]
+### Changed
+- Refactored modules, exports, types, and functions.
+
+## [0.1.0.1]
+### Changed
+- Adjusted dependency version.
+
+## [0.1.0.0]
+
+First public release.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,35 @@
+# OpenID Connect 1.0 library for Relying Party
+
+[![Circle CI](https://circleci.com/gh/krdlab/haskell-oidc-client.svg?style=svg)](https://circleci.com/gh/krdlab/haskell-oidc-client)
+
+This package supports implementing of an OpenID Connect 1.0 Relying Party. It's written in Haskell.
+
+This package uses [jose-jwt](http://github.com/tekul/jose-jwt) package for decoding a received tokens.
+
+## Usage
+
+```sh
+$ cabal update
+$ cabal install oidc-client
+```
+
+The documentation is available in [Hackage](https://hackage.haskell.org/package/oidc-client).
+
+## Run example
+
+`examples/scotty` is a runnable code. If you try to run it, execute commands as follows:
+
+```sh
+$ stack build --flag oidc-client:build-examples
+```
+
+and then
+
+```sh
+$ export OPENID_CLIENT_BASE_URL="http://localhost:3000"
+$ export OPENID_CLIENT_ID="Your client ID"
+$ export OPENID_CLIENT_SECRET="Your client secret"
+$ stack exec scotty-example
+```
+
+You can access to <http://localhost:3000/login>.
diff --git a/examples/scotty/Main.hs b/examples/scotty/Main.hs
--- a/examples/scotty/Main.hs
+++ b/examples/scotty/Main.hs
@@ -1,102 +1,176 @@
+{-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards   #-}
+{-# LANGUAGE DeriveGeneric   #-}
 
 module Main where
 
-import Control.Monad.IO.Class (liftIO)
-import Crypto.Random.AESCtr (makeSystem)
-import Crypto.Random.API (CPRG, cprgGenBytes)
-import Data.ByteString (ByteString)
-import Data.ByteString.Base64.URL (encode)
-import qualified Data.ByteString.Char8 as B
-import Data.IORef (IORef, newIORef, atomicModifyIORef', readIORef)
-import Data.Map (Map)
-import qualified Data.Map as M
-import Data.Text (Text)
-import Data.Text.Encoding (decodeUtf8)
-import Data.Text.Lazy (pack)
-import Data.Tuple (swap)
-import Network.HTTP.Client (newManager, Manager)
-import Network.HTTP.Client.TLS (tlsManagerSettings)
-import Network.HTTP.Types (badRequest400, unauthorized401)
-import Network.Wai.Middleware.RequestLogger (logStdoutDev)
-import System.Environment (getEnv)
-import Text.Blaze.Html.Renderer.Text (renderHtml)
-import Text.Blaze.Html5 ((!))
-import qualified Text.Blaze.Html5 as H
-import qualified Text.Blaze.Html5.Attributes as A
-import qualified Web.OIDC.Client as O
-import Web.Scotty (scotty, middleware, get, param, post, redirect, html, status, text, rescue)
-import Web.Scotty.Cookie (setSimpleCookie, getCookie)
+import           Control.Monad.IO.Class               (liftIO)
+import           Control.Monad.Reader                 (ReaderT, ask, lift,
+                                                       runReaderT)
+import           Crypto.Random.AESCtr                 (AESRNG, makeSystem)
+import           Crypto.Random.API                    (cprgGenBytes)
+import           Data.Aeson                           (FromJSON)
+import           Data.ByteString                      (ByteString)
+import           Data.ByteString.Base64.URL           (encode)
+import qualified Data.ByteString.Char8                as B
+import           Data.IORef                           (IORef,
+                                                       atomicModifyIORef',
+                                                       newIORef, readIORef)
+import           Data.List                            as L
+import           Data.Map                             (Map)
+import qualified Data.Map                             as M
+import           Data.Maybe                           (Maybe (..), fromMaybe)
+import           Data.Monoid                          ((<>))
+import           Data.Text                            as T
+import           Data.Text.Encoding                   (decodeUtf8)
+import           Data.Text.Lazy                       as TL
+import           Data.Tuple                           (swap)
+import           GHC.Generics (Generic)
+import           Network.HTTP.Client                  (Manager, newManager)
+import           Network.HTTP.Client.TLS              (tlsManagerSettings)
+import           Network.HTTP.Types                   (badRequest400,
+                                                       unauthorized401)
+import           Network.Wai.Middleware.RequestLogger (logStdoutDev)
+import           System.Environment                   (getEnv)
+import           Text.Blaze.Html                      (Html)
+import           Text.Blaze.Html.Renderer.Text        (renderHtml)
+import           Text.Blaze.Html5                     ((!))
+import qualified Text.Blaze.Html5                     as H
+import qualified Text.Blaze.Html5.Attributes          as A
+import qualified Web.OIDC.Client                      as O
+import           Web.Scotty.Cookie                    (getCookie,
+                                                       setSimpleCookie)
+import           Web.Scotty.Trans                     (ScottyT, get, html,
+                                                       middleware, param, post,
+                                                       redirect, rescue,
+                                                       scottyT, status, text)
 
-type SessionStateMap = Map Text O.State
+type SessionStateMap = Map T.Text (O.State, O.Nonce)
 
-redirectUri :: ByteString
-redirectUri  = "http://localhost:3000/callback"
+data AuthServerEnv = AuthServerEnv
+    { oidc :: O.OIDC
+    , cprg :: IORef AESRNG
+    , ssm  :: IORef SessionStateMap
+    , mgr  :: Manager
+    }
 
+type AuthServer a = ScottyT TL.Text (ReaderT AuthServerEnv IO) a
+
+data ProfileClaims = ProfileClaims
+    { name    :: T.Text
+    , email   :: T.Text
+    , picture :: T.Text
+    } deriving (Show, Generic)
+
+instance FromJSON ProfileClaims
+
 main :: IO ()
 main = do
+    baseUrl      <- B.pack <$> getEnv "OPENID_CLIENT_BASE_URL"
     clientId     <- B.pack <$> getEnv "OPENID_CLIENT_ID"
     clientSecret <- B.pack <$> getEnv "OPENID_CLIENT_SECRET"
 
+    let port = getPort baseUrl
+        redirectUri = baseUrl <> "/login/cb"
+
     cprg <- makeSystem >>= newIORef
     ssm  <- newIORef M.empty
     mgr  <- newManager tlsManagerSettings
-    prov <- O.discover O.google mgr
+    prov <- O.discover "https://accounts.google.com" mgr
     let oidc = O.setCredentials clientId clientSecret redirectUri $ O.newOIDC prov
 
-    run oidc cprg ssm mgr
+    run port oidc cprg ssm mgr
 
-run :: CPRG g => O.OIDC -> IORef g -> IORef SessionStateMap -> Manager -> IO ()
-run oidc cprg ssm mgr = scotty 3000 $ do
+getPort :: ByteString -> Int
+getPort bs = fromMaybe 3000 port
+  where
+    port = case B.split ':' bs of
+        []  -> Nothing
+        [_] -> Nothing
+        xs  -> let p = (!! 0) . L.reverse $ xs
+                    in fst <$> B.readInt p
+
+run :: Int -> O.OIDC -> IORef AESRNG -> IORef SessionStateMap -> Manager -> IO ()
+run port oidc cprg ssm mgr = scottyT port runReader run'
+  where
+    runReader a = runReaderT a (AuthServerEnv oidc cprg ssm mgr)
+
+run' :: AuthServer ()
+run' = do
     middleware logStdoutDev
 
     get "/login" $
-        blaze $ do
-            H.h1 "Login"
-            H.form ! A.method "post" ! A.action "/login" $
-                H.button ! A.type_ "submit" $ "login"
+        blaze htmlLogin
 
     post "/login" $ do
-        state <- genState
-        loc <- liftIO $ O.getAuthenticationRequestUrl oidc [O.email] (Just state) []
-        sid <- genSessionId
-        saveState sid state
-        setSimpleCookie "test-session" sid
-        redirect $ pack . show $ loc
+        AuthServerEnv{..} <- lift ask
 
-    get "/callback" $ do
+        sid <- genSessionId cprg
+        let store = sessionStoreFromSession cprg ssm sid
+        loc <- liftIO $ O.prepareAuthenticationRequestUrl store oidc [O.email, O.profile] []
+        setSimpleCookie cookieName sid
+        redirect . TL.pack . show $ loc
+
+    get "/login/cb" $ do
         err <- param' "error"
         case err of
-            Just err' -> status401 err'
-            Nothing   -> do
-                code  :: O.Code  <- param "code"
-                state :: O.State <- param "state"
-                cookie <- getCookie "test-session"
-                case cookie of
-                    Just sid -> do
-                        sst <- getStateBy sid
-                        if state == sst
-                            then do
-                                tokens <- liftIO $ O.requestTokens oidc code mgr
-                                blaze $ do
-                                    H.h1 "Result"
-                                    H.pre . H.toHtml . show . O.claims . O.idToken $ tokens
-                            else status400 "state not match"
-                    Nothing  -> status400 "cookie not found"
+            Just e  -> status401 e
+            Nothing -> getCookie cookieName >>= doCallback
 
   where
+    cookieName = "test-session"
+
+    htmlLogin = do
+        H.h1 "Login"
+        H.form ! A.method "post" ! A.action "/login" $
+            H.button ! A.type_ "submit" $ "login"
+
+    doCallback cookie =
+        case cookie of
+            Just sid -> do
+                AuthServerEnv{..} <- lift ask
+                let store = sessionStoreFromSession cprg ssm sid
+                state <- param "state"
+                code  <- param "code"
+                tokens <- liftIO $ O.getValidTokens store oidc mgr state code
+                blaze $ htmlResult tokens
+            Nothing  -> status400 "cookie not found"
+
+    htmlResult :: O.Tokens ProfileClaims -> Html
+    htmlResult tokens = do
+        H.h1 "Result"
+        H.pre . H.toHtml . show $ tokens
+        let profile = O.otherClaims $ O.idToken tokens
+        H.div $ do
+          H.p $ do
+            H.toHtml ("Name: " :: T.Text)
+            H.toHtml (name profile)
+          H.p $ do
+            H.toHtml ("Email: " :: T.Text)
+            H.toHtml (email profile)
+          H.p $ H.img ! (A.src $ H.textValue $ picture profile)
+
+    gen cprg                   = encode <$> atomicModifyIORef' cprg (swap . cprgGenBytes 64)
+    genSessionId cprg          = liftIO $ decodeUtf8 <$> gen cprg
+    genBytes cprg              = liftIO $ gen cprg
+    saveState ssm sid st nonce = liftIO $ atomicModifyIORef' ssm $ \m -> (M.insert sid (st, nonce) m, ())
+    getStateBy ssm sid         = liftIO $ do
+        m <- M.lookup sid <$> readIORef ssm
+        return $ case m of
+            Just (st, nonce) -> (Just st, Just nonce)
+            _                -> (Nothing, Nothing)
+    deleteState ssm sid  = liftIO $ atomicModifyIORef' ssm $ \m -> (M.delete sid m, ())
+
+    sessionStoreFromSession cprg ssm sid =
+        O.SessionStore
+            { sessionStoreGenerate = genBytes cprg
+            , sessionStoreSave     = saveState ssm sid
+            , sessionStoreGet      = getStateBy ssm sid
+            , sessionStoreDelete   = deleteState ssm sid
+            }
+
     blaze = html . renderHtml
     param' n = (Just <$> param n) `rescue` (\_ -> return Nothing)
-    status400 m = status badRequest400 >> text m
+    status400 m = status badRequest400   >> text m
     status401 m = status unauthorized401 >> text m
-
-    gen              = encode <$> atomicModifyIORef' cprg (swap . cprgGenBytes 64)
-    genSessionId     = liftIO $ decodeUtf8 <$> gen
-    genState         = liftIO gen
-    saveState sid st = liftIO $ atomicModifyIORef' ssm $ \m -> (M.insert sid st m, ())
-    getStateBy sid   = liftIO $ do
-        m <- readIORef ssm
-        case M.lookup sid m of
-            Just st -> return st
-            Nothing -> return ""
diff --git a/oidc-client.cabal b/oidc-client.cabal
--- a/oidc-client.cabal
+++ b/oidc-client.cabal
@@ -1,5 +1,5 @@
 name:               oidc-client
-version:            0.3.0.1
+version:            0.4.0.0
 synopsis:           OpenID Connect 1.0 library for RP
 homepage:           https://github.com/krdlab/haskell-oidc-client
 stability:          experimental
@@ -15,6 +15,8 @@
     This package supports implementing of an OpenID Connect 1.0 Relying Party.
     .
     Examples: <https://github.com/krdlab/haskell-oidc-client/tree/master/examples>
+extra-source-files: CHANGELOG.md
+                  , README.md
 
 source-repository head
     type: git
@@ -46,12 +48,14 @@
     , bytestring        >=0.10 && <0.11
     , text              >=1.2 && <1.3
     , aeson             >=0.10
+    , scientific
     , attoparsec        >=0.12
     , exceptions
     , http-client
     , tls               >=1.3.2
     , http-client-tls
     , jose-jwt          >=0.7
+    , cryptonite
     , time
   if flag(network-uri)
     build-depends: network-uri >=2.6, network >=2.6
@@ -80,6 +84,7 @@
     , Web.OIDC.Client.Types
   build-depends:
       base
+    , aeson
     , hspec
     , oidc-client
     , bytestring
@@ -88,7 +93,9 @@
     , http-client
     , http-client-tls
     , aeson
+    , scientific
     , jose-jwt
+    , cryptonite
     , exceptions
     , time
     , network-uri
@@ -102,10 +109,12 @@
     build-depends:
         base                >=4.7 && <5
       , oidc-client
+      , aeson
       , bytestring
       , text
       , containers
       , transformers
+      , mtl
       , wai-extra
       , scotty
       , scotty-cookie
diff --git a/src/Web/OIDC/Client.hs b/src/Web/OIDC/Client.hs
--- a/src/Web/OIDC/Client.hs
+++ b/src/Web/OIDC/Client.hs
@@ -22,12 +22,12 @@
     , module Jose.Jwt
     ) where
 
-import Web.OIDC.Client.CodeFlow
-import Web.OIDC.Client.Settings (OIDC, newOIDC, setCredentials)
-import Web.OIDC.Client.Discovery
-import Web.OIDC.Client.Tokens
-import Web.OIDC.Client.Types
+import           Web.OIDC.Client.CodeFlow
+import           Web.OIDC.Client.Discovery
+import           Web.OIDC.Client.Settings  (OIDC, newOIDC, setCredentials)
+import           Web.OIDC.Client.Tokens
+import           Web.OIDC.Client.Types
 
-import Jose.Jwt
+import           Jose.Jwt
 
 {-# ANN module "HLint: ignore Use import/export shortcut" #-}
diff --git a/src/Web/OIDC/Client/CodeFlow.hs b/src/Web/OIDC/Client/CodeFlow.hs
--- a/src/Web/OIDC/Client/CodeFlow.hs
+++ b/src/Web/OIDC/Client/CodeFlow.hs
@@ -7,6 +7,8 @@
 module Web.OIDC.Client.CodeFlow
     (
       getAuthenticationRequestUrl
+    , getValidTokens
+    , prepareAuthenticationRequestUrl
     , requestTokens
 
     -- * For testing
@@ -14,28 +16,78 @@
     , getCurrentIntDate
     ) where
 
-import Control.Monad (unless)
-import Control.Monad.Catch (MonadThrow, throwM, MonadCatch, catch)
-import Data.Aeson (eitherDecode)
-import qualified Data.ByteString.Char8 as B
-import Data.List (nub)
-import Data.Monoid ((<>))
-import Data.Text (Text, pack, unpack)
-import Data.Text.Encoding (decodeUtf8)
-import Data.Time.Clock.POSIX (getPOSIXTime)
-import Jose.Jwt (Jwt)
-import qualified Jose.Jwt as Jwt
-import Network.HTTP.Client (getUri, setQueryString, urlEncodedBody, Request(..), Manager, httpLbs, responseBody)
-import Network.URI (URI)
+import           Control.Monad                      (unless, when)
+import           Control.Monad.Catch                (MonadCatch, MonadThrow,
+                                                     catch, throwM)
+import           Control.Monad.IO.Class             (MonadIO, liftIO)
+import           Data.Aeson                         (FromJSON, eitherDecode)
+import qualified Data.ByteString.Char8              as B
+import qualified Data.ByteString.Lazy.Char8         as BL
+import           Data.List                          (nub)
+import           Data.Maybe                         (isNothing, listToMaybe)
+import           Data.Monoid                        ((<>))
+import           Data.Text                          (Text, pack, unpack)
+import           Data.Text.Encoding                 (decodeUtf8)
+import           Data.Time.Clock.POSIX              (getPOSIXTime)
+import           Jose.Jwt                           (Jwt, JwtContent (Jwe, Jws, Unsecured))
+import qualified Jose.Jwt                           as Jwt
+import           Network.HTTP.Client                (Manager, Request (..),
+                                                     getUri, httpLbs,
+                                                     responseBody,
+                                                     setQueryString,
+                                                     urlEncodedBody)
+import           Network.URI                        (URI)
 
-import Web.OIDC.Client.Settings (OIDC(..))
+import           Prelude                            hiding (exp)
+
 import qualified Web.OIDC.Client.Discovery.Provider as P
-import qualified Web.OIDC.Client.Internal as I
-import Web.OIDC.Client.Internal (parseUrl)
-import Web.OIDC.Client.Tokens (Tokens(..), IdToken(..))
-import Web.OIDC.Client.Types (Scope, openId, Code, State, Parameters, OpenIdException(..))
+import           Web.OIDC.Client.Internal           (parseUrl)
+import qualified Web.OIDC.Client.Internal           as I
+import           Web.OIDC.Client.Settings           (OIDC (..))
+import           Web.OIDC.Client.Tokens             (IdTokenClaims (..),
+                                                     Tokens (..))
+import           Web.OIDC.Client.Types              (Code, Nonce,
+                                                     OpenIdException (..),
+                                                     Parameters, Scope,
+                                                     SessionStore (..), State,
+                                                     openId)
 
+-- | Make URL for Authorization Request after generating state and nonce from 'SessionStore'.
+prepareAuthenticationRequestUrl
+    :: (MonadThrow m, MonadCatch m)
+    => SessionStore m
+    -> OIDC
+    -> Scope            -- ^ used to specify what are privileges requested for tokens. (use `ScopeValue`)
+    -> Parameters       -- ^ Optional parameters
+    -> m URI
+prepareAuthenticationRequestUrl store oidc scope params = do
+    state <- sessionStoreGenerate store
+    nonce' <- sessionStoreGenerate store
+    sessionStoreSave store state nonce'
+    getAuthenticationRequestUrl oidc scope (Just state) $ params ++ [("nonce", Just nonce')]
+
+-- | Get and validate access token and with code and state stored in the 'SessionStore'.
+--   Then deletes session info by 'sessionStoreDelete'.
+getValidTokens
+    :: (MonadThrow m, MonadCatch m, MonadIO m, FromJSON a)
+    => SessionStore m
+    -> OIDC
+    -> Manager
+    -> State
+    -> Code
+    -> m (Tokens a)
+getValidTokens store oidc mgr stateFromIdP code = do
+    (state, savedNonce) <- sessionStoreGet store
+    if state == Just stateFromIdP
+      then do
+          when (isNothing savedNonce) $ throwM $ ValidationException "Nonce is not saved!"
+          result <- liftIO $ requestTokens oidc savedNonce code mgr
+          sessionStoreDelete store
+          return result
+      else throwM $ ValidationException $ "Incosistent state: " <> decodeUtf8 stateFromIdP
+
 -- | Make URL for Authorization Request.
+{-# WARNING getAuthenticationRequestUrl "This function doesn't manage state and nonce. Use prepareAuthenticationRequestUrl only unless your IdP doesn't support state and/or nonce." #-}
 getAuthenticationRequestUrl
     :: (MonadThrow m, MonadCatch m)
     => OIDC
@@ -68,12 +120,13 @@
 -- Returned `Tokens` value is a valid.
 --
 -- If a HTTP error has occurred or a tokens validation has failed, this function throws `OpenIdException`.
-requestTokens :: OIDC -> Code -> Manager -> IO Tokens
-requestTokens oidc code manager = do
+{-# WARNING requestTokens "This function doesn't manage state and nonce. Use getValidTokens only unless your IdP doesn't support state and/or nonce." #-}
+requestTokens :: FromJSON a => OIDC -> Maybe Nonce -> Code -> Manager -> IO (Tokens a)
+requestTokens oidc savedNonce code manager = do
     json <- getTokensJson `catch` I.rethrow
     case eitherDecode json of
-        Right ts -> validate oidc ts
-        Left err -> error $ "failed to decode tokens json: " ++ err     -- TODO: Exception
+        Right ts -> validate oidc savedNonce ts
+        Left err -> throwM . JsonException $ pack err
   where
     getTokensJson = do
         req <- parseUrl endpoint
@@ -92,59 +145,62 @@
         , ("redirect_uri",  redirect)
         ]
 
-validate :: OIDC -> I.TokensResponse -> IO Tokens
-validate oidc tres = do
+validate :: FromJSON a => OIDC -> Maybe Nonce -> I.TokensResponse -> IO (Tokens a)
+validate oidc savedNonce tres = do
     let jwt' = I.idToken tres
-    validateIdToken oidc jwt'
-    claims' <- getClaims jwt'
+    claims' <- validateIdToken oidc jwt'
     now <- getCurrentIntDate
     validateClaims
         (P.issuer . P.configuration . oidcProvider $ oidc)
         (decodeUtf8 . oidcClientId $ oidc)
         now
+        savedNonce
         claims'
     return Tokens {
           accessToken  = I.accessToken tres
         , tokenType    = I.tokenType tres
-        , idToken      = IdToken { claims = I.toIdTokenClaims claims', jwt = jwt' }
+        , idToken      = claims'
         , expiresIn    = I.expiresIn tres
         , refreshToken = I.refreshToken tres
         }
 
-validateIdToken :: OIDC -> Jwt -> IO ()
+validateIdToken :: FromJSON a => OIDC -> Jwt -> IO (IdTokenClaims a)
 validateIdToken oidc jwt' = do
     let jwks = P.jwkSet . oidcProvider $ oidc
         token = Jwt.unJwt jwt'
-    decoded <- Jwt.decode jwks Nothing token
+        alg = fmap (Jwt.JwsEncoding . P.getJwsAlg)
+                . listToMaybe
+                . P.idTokenSigningAlgValuesSupported
+                . P.configuration
+                $ oidcProvider oidc
+    decoded <- Jwt.decode jwks alg token
     case decoded of
-        Right _  -> return ()
-        Left err -> throwM $ JwtExceptoin err
+        Right (Unsecured payload)      -> throwM $ UnsecuredJwt payload
+        Right (Jws (_header, payload)) -> parsePayload payload
+        Right (Jwe (_header, payload)) -> parsePayload payload
+        Left err                       -> throwM $ JwtExceptoin err
 
-getClaims :: MonadThrow m => Jwt -> m Jwt.JwtClaims
-getClaims jwt' = case Jwt.decodeClaims (Jwt.unJwt jwt') of
-                Right (_, c) -> return c
-                Left  cause  -> throwM $ JwtExceptoin cause
+  where
+    parsePayload payload =
+        case eitherDecode $ BL.fromStrict payload of
+            Right x  -> return x
+            Left err -> throwM . JsonException $ pack err
 
-validateClaims :: Text -> Text -> Jwt.IntDate -> Jwt.JwtClaims -> IO ()
-validateClaims issuer' clientId' now claims' = do
-    iss' <- getIss claims'
+validateClaims :: Text -> Text -> Jwt.IntDate -> Maybe Nonce -> IdTokenClaims a -> IO ()
+validateClaims issuer' clientId' now savedNonce claims' = do
+    let iss' = iss claims'
     unless (iss' == issuer')
         $ throwM $ ValidationException $ "issuer from token \"" <> iss' <> "\" is different than expected issuer \"" <> issuer' <> "\""
 
-    aud' <- getAud claims'
+    let aud' = aud claims'
     unless (clientId' `elem` aud')
         $ throwM $ ValidationException $ "our client \"" <> clientId' <> "\" isn't contained in the token's audience " <> (pack . show) aud'
 
-    exp' <- getExp claims'
-    unless (now < exp')
+    unless (now < exp claims')
         $ throwM $ ValidationException "received token has expired"
-  where
-    getIss c = get Jwt.jwtIss c "'iss' claim was not found"
-    getAud c = get Jwt.jwtAud c "'aud' claim was not found"
-    getExp c = get Jwt.jwtExp c "'exp' claim was not found"
-    get f v msg = case f v of
-        Just v' -> return v'
-        Nothing -> throwM $ ValidationException msg
+
+    unless (nonce claims' == savedNonce)
+        $ throwM $ ValidationException "Inconsistent nonce"
 
 getCurrentIntDate :: IO Jwt.IntDate
 getCurrentIntDate = Jwt.IntDate <$> getPOSIXTime
diff --git a/src/Web/OIDC/Client/Discovery.hs b/src/Web/OIDC/Client/Discovery.hs
--- a/src/Web/OIDC/Client/Discovery.hs
+++ b/src/Web/OIDC/Client/Discovery.hs
@@ -16,17 +16,19 @@
     , Configuration(..)
     ) where
 
-import Control.Monad.Catch (throwM, catch)
-import Data.Aeson (decode)
-import Data.Text (append)
-import Data.Maybe (fromMaybe)
-import qualified Jose.Jwk as Jwk
-import Network.HTTP.Client (Manager, httpLbs, responseBody)
+import           Control.Monad.Catch                (catch, throwM)
+import           Data.Aeson                         (decode)
+import           Data.Text                          (append)
+import qualified Jose.Jwk                           as Jwk
+import           Network.HTTP.Client                (Manager, httpLbs,
+                                                     responseBody)
 
-import Web.OIDC.Client.Discovery.Issuers (google)
-import Web.OIDC.Client.Discovery.Provider (Provider(..), Configuration(..))
-import Web.OIDC.Client.Internal (rethrow, parseUrl)
-import Web.OIDC.Client.Types (IssuerLocation, OpenIdException(..))
+import           Web.OIDC.Client.Discovery.Issuers  (google)
+import           Web.OIDC.Client.Discovery.Provider (Configuration (..),
+                                                     Provider (..))
+import           Web.OIDC.Client.Internal           (parseUrl, rethrow)
+import           Web.OIDC.Client.Types              (IssuerLocation,
+                                                     OpenIdException (..))
 
 -- | This function obtains OpenID Provider configuration and JWK set.
 discover
@@ -47,7 +49,7 @@
         req <- parseUrl url
         res <- httpLbs req manager
         return $ responseBody res
-    jwks j = fromMaybe single (Jwk.keys <$> decode j)
+    jwks j = maybe single Jwk.keys (decode j)
       where
         single = case decode j of
                      Just k  -> return k
diff --git a/src/Web/OIDC/Client/Discovery/Issuers.hs b/src/Web/OIDC/Client/Discovery/Issuers.hs
--- a/src/Web/OIDC/Client/Discovery/Issuers.hs
+++ b/src/Web/OIDC/Client/Discovery/Issuers.hs
@@ -10,7 +10,7 @@
     -- TODO: other services
     ) where
 
-import Web.OIDC.Client.Types (IssuerLocation)
+import           Web.OIDC.Client.Types (IssuerLocation)
 
 google :: IssuerLocation
 google = "https://accounts.google.com"
diff --git a/src/Web/OIDC/Client/Discovery/Provider.hs b/src/Web/OIDC/Client/Discovery/Provider.hs
--- a/src/Web/OIDC/Client/Discovery/Provider.hs
+++ b/src/Web/OIDC/Client/Discovery/Provider.hs
@@ -1,5 +1,6 @@
+{-# LANGUAGE LambdaCase        #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TemplateHaskell   #-}
 {-|
     Module: Web.OIDC.Client.Discovery.Provider
     Maintainer: krdlab@gmail.com
@@ -9,18 +10,39 @@
     (
       Provider(..)
     , Configuration(..)
+    , JwsAlgJson(..)
     ) where
 
-import Data.Aeson.TH (deriveFromJSON, Options(..), defaultOptions)
-import Data.Aeson.Types (camelTo2)
-import Data.Text (Text)
-import Jose.Jwk (Jwk)
+import           Data.Aeson            (FromJSON, parseJSON, withText)
+import           Data.Aeson.TH         (Options (..), defaultOptions,
+                                        deriveFromJSON)
+import           Data.Aeson.Types      (camelTo2)
+import           Data.Text             (Text, unpack)
+import           Jose.Jwa              (JwsAlg (..))
+import           Jose.Jwk              (Jwk)
 
-import Web.OIDC.Client.Types (ScopeValue, IssuerLocation)
+import           Web.OIDC.Client.Types (IssuerLocation, ScopeValue)
 
 -- | An OpenID Provider information
 data Provider = Provider { configuration :: Configuration, jwkSet :: [Jwk] }
 
+newtype JwsAlgJson = JwsAlgJson { getJwsAlg :: JwsAlg } deriving (Show, Eq)
+
+instance FromJSON JwsAlgJson where
+    parseJSON = withText "JwsAlgJson" $ \case
+        "HS256" -> pure $ JwsAlgJson HS256
+        "HS384" -> pure $ JwsAlgJson HS384
+        "HS512" -> pure $ JwsAlgJson HS512
+        "RS256" -> pure $ JwsAlgJson RS256
+        "RS384" -> pure $ JwsAlgJson RS384
+        "RS512" -> pure $ JwsAlgJson RS512
+        "ES256" -> pure $ JwsAlgJson ES256
+        "ES384" -> pure $ JwsAlgJson ES384
+        "ES512" -> pure $ JwsAlgJson ES512
+        "none"  -> pure $ JwsAlgJson None
+        other   -> fail $ "Non-supported alg: " <> show (unpack other)
+
+
 -- | An OpenID Provider Configuration
 data Configuration = Configuration
     { issuer                            :: IssuerLocation
@@ -31,7 +53,7 @@
     , jwksUri                           :: Text
     , responseTypesSupported            :: [Text]
     , subjectTypesSupported             :: [Text]
-    , idTokenSigningAlgValuesSupported  :: [Text]
+    , idTokenSigningAlgValuesSupported  :: [JwsAlgJson]
     , scopesSupported                   :: Maybe [ScopeValue]
     , tokenEndpointAuthMethodsSupported :: Maybe [Text]
     , claimsSupported                   :: Maybe [Text]
diff --git a/src/Web/OIDC/Client/Internal.hs b/src/Web/OIDC/Client/Internal.hs
--- a/src/Web/OIDC/Client/Internal.hs
+++ b/src/Web/OIDC/Client/Internal.hs
@@ -6,25 +6,25 @@
 -}
 module Web.OIDC.Client.Internal where
 
-import Control.Applicative ((<|>))
-import Control.Monad (mzero)
-import Control.Monad.Catch (MonadThrow, throwM, MonadCatch)
-import Data.Aeson (FromJSON, parseJSON, Value(..), (.:), (.:?))
-import Data.Maybe (fromJust)
-import Data.Text (Text, unpack)
-import Data.Text.Read (decimal)
-import Jose.Jwt (Jwt, JwtClaims(..))
-import Network.HTTP.Client (HttpException, parseRequest, Request)
-import Prelude hiding (exp)
-import Web.OIDC.Client.Tokens (IdTokenClaims(..))
-import Web.OIDC.Client.Types (OpenIdException(InternalHttpException))
+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
-    { accessToken   :: !Text
-    , tokenType     :: !Text
-    , idToken       :: !Jwt
-    , expiresIn     :: !(Maybe Integer)
-    , refreshToken  :: !(Maybe Text)
+    { accessToken  :: !Text
+    , tokenType    :: !Text
+    , idToken      :: !Jwt
+    , expiresIn    :: !(Maybe Integer)
+    , refreshToken :: !(Maybe Text)
     }
   deriving (Show, Eq)
 
@@ -33,26 +33,19 @@
         <$>  o .:  "access_token"
         <*>  o .:  "token_type"
         <*>  o .:  "id_token"
-        <*> (o .:? "expires_in" <|> (>>= textToInt) <$> (o .:? "expires_in"))
+        <*> ((o .:? "expires_in") <|> (textToInt =<< (o .:? "expires_in")))
         <*>  o .:? "refresh_token"
     parseJSON _          = mzero
 
-textToInt :: Text -> Maybe Integer
-textToInt t = case decimal t of
-    Right (i, _) -> Just i
-    Left  _      -> Nothing
+textToInt :: Maybe Text -> Parser (Maybe Integer)
+textToInt (Just t) =
+    case decimal t of
+        Right (i, _) -> pure $ Just i
+        Left  _      -> fail "expires_in: expected a decimal text, encountered a non decimal text"
+textToInt _        = pure Nothing
 
 rethrow :: (MonadCatch m) => HttpException -> m a
 rethrow = throwM . InternalHttpException
-
-toIdTokenClaims :: JwtClaims -> IdTokenClaims
-toIdTokenClaims c = IdTokenClaims   -- FIXME: fromJust
-    { iss = fromJust (jwtIss c)
-    , sub = fromJust (jwtSub c)
-    , aud = fromJust (jwtAud c)
-    , exp = fromJust (jwtExp c)
-    , iat = fromJust (jwtIat c)
-    }
 
 parseUrl :: MonadThrow m => Text -> m Request
 parseUrl = Network.HTTP.Client.parseRequest . unpack
diff --git a/src/Web/OIDC/Client/Settings.hs b/src/Web/OIDC/Client/Settings.hs
--- a/src/Web/OIDC/Client/Settings.hs
+++ b/src/Web/OIDC/Client/Settings.hs
@@ -12,10 +12,10 @@
     , setCredentials
     ) where
 
-import Data.ByteString (ByteString)
-import Data.Text (Text)
+import           Data.ByteString                    (ByteString)
+import           Data.Text                          (Text)
 
-import Web.OIDC.Client.Discovery.Provider (Provider)
+import           Web.OIDC.Client.Discovery.Provider (Provider)
 import qualified Web.OIDC.Client.Discovery.Provider as P
 
 -- | This data type represents information needed in the OpenID flow.
diff --git a/src/Web/OIDC/Client/Tokens.hs b/src/Web/OIDC/Client/Tokens.hs
--- a/src/Web/OIDC/Client/Tokens.hs
+++ b/src/Web/OIDC/Client/Tokens.hs
@@ -1,4 +1,6 @@
+{-# LANGUAGE DeriveGeneric     #-}
 {-# LANGUAGE OverloadedStrings #-}
+
 {-|
     Module: Web.OIDC.Client.Tokens
     Maintainer: krdlab@gmail.com
@@ -7,35 +9,49 @@
 module Web.OIDC.Client.Tokens
     (
       Tokens(..)
-    , IdToken(..)
     , IdTokenClaims(..)
     ) where
 
-import Data.Text (Text)
-import Jose.Jwt (Jwt, IntDate)
-import Prelude hiding (exp)
+import           Control.Applicative ((<|>))
+import           Data.Aeson         (FromJSON (parseJSON), Value (Object),
+                                     withObject, (.:), (.:?))
+import           Data.ByteString    (ByteString)
+import           Data.Text          (Text)
+import           Data.Text.Encoding (encodeUtf8)
+import           GHC.Generics       (Generic)
+import           Jose.Jwt           (IntDate)
+import           Prelude            hiding (exp)
 
-data Tokens = Tokens
-    { accessToken   :: Text
-    , tokenType     :: Text
-    , idToken       :: IdToken
-    , expiresIn     :: Maybe Integer
-    , refreshToken  :: Maybe Text
+data Tokens a = Tokens
+    { accessToken  :: Text
+    , tokenType    :: Text
+    , idToken      :: IdTokenClaims a
+    , expiresIn    :: Maybe Integer
+    , refreshToken :: Maybe Text
     }
   deriving (Show, Eq)
 
-data IdToken = IdToken
-    { claims    :: IdTokenClaims
-    , jwt       :: Jwt
+-- | Claims required for an <https://openid.net/specs/openid-connect-core-1_0.html#IDToken ID Token>,
+--   plus recommended claims (nonce) and other custom claims.
+data IdTokenClaims a = IdTokenClaims
+    { iss         :: !Text
+    , sub         :: !Text
+    , aud         :: ![Text]
+    , exp         :: !IntDate
+    , iat         :: !IntDate
+    , nonce       :: !(Maybe ByteString)
+    , otherClaims :: !a
     }
-  deriving (Show, Eq)
+  deriving (Show, Eq, Generic)
 
-data IdTokenClaims = IdTokenClaims
-    { iss :: Text
-    , sub :: Text
-    , aud :: [Text]
-    , exp :: IntDate
-    , iat :: IntDate
-    -- TODO: optional
-    }
-  deriving (Show, Eq)
+
+instance FromJSON a => FromJSON (IdTokenClaims a) where
+    parseJSON = withObject "IdTokenClaims" $ \o ->
+        IdTokenClaims
+            <$> o .: "iss"
+            <*> o .: "sub"
+            <*> (o .: "aud" <|> ((:[]) <$> (o .: "aud")))
+            <*> o .: "exp"
+            <*> o .: "iat"
+            <*> (fmap encodeUtf8 <$> o .:? "nonce")
+            <*> parseJSON (Object o)
diff --git a/src/Web/OIDC/Client/Types.hs b/src/Web/OIDC/Client/Types.hs
--- a/src/Web/OIDC/Client/Types.hs
+++ b/src/Web/OIDC/Client/Types.hs
@@ -1,5 +1,5 @@
-{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings  #-}
 {-|
     Module: Web.OIDC.Client.Types
     Maintainer: krdlab@gmail.com
@@ -11,18 +11,20 @@
     , openId, profile, email, address, phone, offlineAccess
     , Scope
     , State
+    , Nonce
     , Parameters
     , Code
     , IssuerLocation
     , OpenIdException(..)
+    , SessionStore (..)
     ) where
 
-import Control.Exception (Exception)
-import Data.ByteString (ByteString)
-import Data.Text (Text)
-import Data.Typeable (Typeable)
-import Jose.Jwt (JwtError)
-import Network.HTTP.Client (HttpException)
+import           Control.Exception   (Exception)
+import           Data.ByteString     (ByteString)
+import           Data.Text           (Text)
+import           Data.Typeable       (Typeable)
+import           Jose.Jwt            (JwtError)
+import           Network.HTTP.Client (HttpException)
 
 type IssuerLocation = Text
 
@@ -40,6 +42,8 @@
 
 type State = ByteString
 
+type Nonce = ByteString
+
 type Parameters = [(ByteString, Maybe ByteString)]
 
 type Code = ByteString
@@ -47,8 +51,21 @@
 data OpenIdException =
       DiscoveryException Text
     | InternalHttpException HttpException
+    | JsonException Text
+    | UnsecuredJwt ByteString
     | JwtExceptoin JwtError
     | ValidationException Text
   deriving (Show, Typeable)
 
 instance Exception OpenIdException
+
+-- | Manages state and nonce.
+--   (Maybe 'OIDC' should have them)
+data SessionStore m = SessionStore
+    { sessionStoreGenerate :: m ByteString
+    -- ^ Generate state and nonce at random
+    , sessionStoreSave :: State -> Nonce -> m ()
+    , sessionStoreGet :: m (Maybe State, Maybe Nonce)
+    , sessionStoreDelete :: m ()
+    -- ^ Should delete at least nonce
+    }
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -1,6 +1,6 @@
-import Test.Hspec (hspec)
-import qualified Spec.Client as Client
+import qualified Spec.Client          as Client
 import qualified Spec.Client.Internal as Internal
+import           Test.Hspec           (hspec)
 
 main :: IO ()
 main = hspec $ do
diff --git a/test/Spec/Client.hs b/test/Spec/Client.hs
--- a/test/Spec/Client.hs
+++ b/test/Spec/Client.hs
@@ -1,20 +1,25 @@
+{-# OPTIONS_GHC -Wno-warnings-deprecations #-}
 {-# LANGUAGE OverloadedStrings #-}
 module Spec.Client where
 
-import Data.ByteString (ByteString)
-import Data.Text (unpack)
-import Data.Text.Encoding (decodeUtf8)
-import Network.HTTP.Client (newManager)
-import Network.HTTP.Client.TLS (tlsManagerSettings)
-import Network.HTTP.Types (urlEncode)
-import Test.Hspec (Spec, describe, it, shouldContain, shouldNotContain, shouldThrow)
-import Web.OIDC.Client
+import           Data.Aeson              (Value (Null))
+import           Data.ByteString         (ByteString)
+import           Data.Text               (unpack)
+import           Data.Text.Encoding      (decodeUtf8)
+import           Network.HTTP.Client     (newManager)
+import           Network.HTTP.Client.TLS (tlsManagerSettings)
+import           Network.HTTP.Types      (urlEncode)
+import           Test.Hspec              (Spec, describe, it, shouldContain,
+                                          shouldNotContain, shouldThrow)
+import           Web.OIDC.Client
 
-clientId, clientSecret, redirectUri, nonce :: ByteString
+import           Prelude                 hiding (exp)
+
+clientId, clientSecret, redirectUri, nonce' :: ByteString
 clientId = "dummy client id"
 clientSecret = "dummy client secret"
 redirectUri = "http://localhost"
-nonce = "dummy nonce"
+nonce' = "dummy nonce"
 
 tests :: Spec
 tests = do
@@ -36,66 +41,62 @@
             provider <- discover google manager
             let oidc = setCredentials clientId clientSecret redirectUri $ newOIDC provider
                 state = "dummy state"
-            url <- getAuthenticationRequestUrl oidc [email] (Just state) [("nonce", Just nonce)]
+            url <- getAuthenticationRequestUrl oidc [email] (Just state) [("nonce", Just nonce')]
             show url `shouldContain` (toES "scope" ++ "=" ++ toES "openid email")
             show url `shouldContain` (toES "state" ++ "=" ++ toES state)
-            show url `shouldContain` (toES "nonce" ++ "=" ++ toES nonce)
+            show url `shouldContain` (toES "nonce" ++ "=" ++ toES nonce')
 
     describe "CodeFlow.validateClaims" $ do
+        let issuer' = "http://localhost"
+            clientId' = decodeUtf8 clientId
+            createValidClaims now =
+                IdTokenClaims
+                    { iss = issuer'
+                    , sub = "sub"
+                    , aud = [clientId']
+                    , exp = add 10 now
+                    , iat = now
+                    , nonce = Just nonce'
+                    , otherClaims = Null
+                    }
         it "should succeed at a validation of correct claims" $ do
-            let issuer' = "http://localhost"
-                clientId' = decodeUtf8 clientId
             now <- getCurrentIntDate
-            let claims' = defClaims { jwtIss = Just issuer'
-                                    , jwtAud = Just [clientId']
-                                    , jwtExp = Just (add 10 now)
-                                    }
-            validateClaims issuer' clientId' now claims'
-            validateClaims issuer' clientId' now (claims' { jwtAud = Just ["other id", clientId'] })
+            let claims' = createValidClaims now
+            validateClaims issuer' clientId' now (Just nonce') claims'
+            validateClaims issuer' clientId' now (Just nonce') claims' { aud = ["other id", clientId'] }
 
         it "should throw ValidationException if 'iss' field is invalid" $ do
-            let issuer' = "http://localhost"
-                clientId' = decodeUtf8 clientId
             now <- getCurrentIntDate
-            let claims' = defClaims { jwtIss = Just "http://localhost/hoge"
-                                    , jwtAud = Just [clientId']
-                                    , jwtExp = Just (add 10 now)
-                                    }
-            validateClaims issuer' clientId' now claims'
+            let claims' = createValidClaims now
+            validateClaims issuer' clientId' now (Just nonce') claims' { iss = "http://localhost/hoge" }
                 `shouldThrow` isValidationException
 
         it "should throw ValidationException if 'aud' field does not contain Client ID" $ do
-            let issuer' = "http://localhost"
-                clientId' = decodeUtf8 clientId
             now <- getCurrentIntDate
-            let claims' = defClaims { jwtIss = Just issuer'
-                                    , jwtAud = Just ["other id"]
-                                    , jwtExp = Just (add 10 now)
-                                    }
-            validateClaims issuer' clientId' now claims'
+            let claims' = createValidClaims now
+            validateClaims issuer' clientId' now (Just nonce') claims' { aud = ["other id"] }
                 `shouldThrow` isValidationException
 
         it "should throw ValidationException if 'exp' field expired" $ do
-            let issuer' = "http://localhost"
-                clientId' = decodeUtf8 clientId
             now <- getCurrentIntDate
-            let claims' = defClaims { jwtIss = Just issuer'
-                                    , jwtAud = Just [clientId']
-                                    , jwtExp = Just (add (-1) now)
-                                    }
-            validateClaims issuer' clientId' now claims'
+            let claims' = createValidClaims now
+            validateClaims issuer' clientId' now (Just nonce') claims' { exp = add (-1) now }
                 `shouldThrow` isValidationException
 
+        it "should throw ValidationException if 'nonce' is not given" $ do
+            now <- getCurrentIntDate
+            let claims' = createValidClaims now
+            validateClaims issuer' clientId' now (Just nonce') claims' { nonce = Nothing }
+                `shouldThrow` isValidationException
+
+        it "should throw ValidationException if 'nonce' is invalid" $ do
+            now <- getCurrentIntDate
+            let claims' = createValidClaims now
+            validateClaims issuer' clientId' now (Just nonce') claims' { nonce = Just "other nonce" }
+                `shouldThrow` isValidationException
+
   where
     toES = unpack . decodeUtf8 . urlEncode True
-    defClaims = JwtClaims { jwtIss = Nothing
-                          , jwtSub = Nothing
-                          , jwtAud = Nothing
-                          , jwtExp = Nothing
-                          , jwtNbf = Nothing
-                          , jwtIat = Nothing
-                          , jwtJti = Nothing
-                          }
     add sec (IntDate t) = IntDate $ t + sec
     isValidationException e = case e of
         (ValidationException _) -> True
diff --git a/test/Spec/Client/Internal.hs b/test/Spec/Client/Internal.hs
--- a/test/Spec/Client/Internal.hs
+++ b/test/Spec/Client/Internal.hs
@@ -1,9 +1,11 @@
 {-# LANGUAGE OverloadedStrings #-}
 module Spec.Client.Internal where
 
-import Data.Aeson (decode)
-import Jose.Jwt (Jwt(..))
-import Test.Hspec (Spec, describe, it, shouldBe)
+import           Data.Aeson               (eitherDecode)
+import           Data.Either              (isLeft)
+import           Jose.Jwt                 (Jwt (..))
+import           Test.Hspec               (Spec, describe, it, shouldBe,
+                                           shouldSatisfy)
 import qualified Web.OIDC.Client.Internal as I
 
 tests :: Spec
@@ -11,22 +13,26 @@
     describe "Internal: Decode TokensResponse JSON data" $ do
         it "should be successful decoding a JSON data which has full fields" $ do
             let json = "{\"access_token\":\"access token\",\"token_type\":\"token type\",\"id_token\":\"dummy jwt\",\"expires_in\":123,\"refresh_token\":\"refresh token\"}"
-            decode json `shouldBe` Just (I.TokensResponse "access token" "token type" (Jwt "dummy jwt") (Just 123) (Just "refresh token"))
+            eitherDecode json `shouldBe` Right (I.TokensResponse "access token" "token type" (Jwt "dummy jwt") (Just 123) (Just "refresh token"))
 
         it "should be successful decoding a JSON data without 'refresh_token' field" $ do
             let json = "{\"access_token\":\"access token\",\"token_type\":\"token type\",\"id_token\":\"dummy jwt\",\"expires_in\":123}"
-            decode json `shouldBe` Just (I.TokensResponse "access token" "token type" (Jwt "dummy jwt") (Just 123) Nothing)
+            eitherDecode json `shouldBe` Right (I.TokensResponse "access token" "token type" (Jwt "dummy jwt") (Just 123) Nothing)
 
         it "should be successful decoding a JSON data without 'expires_in' and 'refresh_token' fields" $ do
             let json = "{\"access_token\":\"access token\",\"token_type\":\"token type\",\"id_token\":\"dummy jwt\"}"
-            decode json `shouldBe` Just (I.TokensResponse "access token" "token type" (Jwt "dummy jwt") Nothing Nothing)
+            eitherDecode json `shouldBe` Right (I.TokensResponse "access token" "token type" (Jwt "dummy jwt") Nothing Nothing)
 
         it "should be failure decoding a JSON data from the lack of a required field" $ do
             let json = "{\"access_token\":\"access token\",\"token_type\":\"token type\"}"
-            decode json `shouldBe` (Nothing :: Maybe I.TokensResponse)
+            (eitherDecode json :: Either String I.TokensResponse) `shouldSatisfy` isLeft
 
         -- NOTE: The 'expires_in' field is an integer, is not included a fractional part. But, I received the report https://github.com/krdlab/haskell-oidc-client/pull/15.
         -- see also: https://tools.ietf.org/html/rfc6749#appendix-A.14
-        it "should be successful decoding a JSON data with 'expires_in' field which has a fractional part" $ do
-            let json = "{\"access_token\":\"access token\",\"token_type\":\"token type\",\"id_token\":\"dummy jwt\",\"expires_in\":123.45}"
-            decode json `shouldBe` Just (I.TokensResponse "access token" "token type" (Jwt "dummy jwt") (Just 123) Nothing)
+        it "should successfully decode a JSON data with 'expires_in' field whose value is a decimal text" $ do
+            let json = "{\"access_token\":\"access token\",\"token_type\":\"token type\",\"id_token\":\"dummy jwt\",\"expires_in\":\"123.45\"}"
+            eitherDecode json `shouldBe` Right (I.TokensResponse "access token" "token type" (Jwt "dummy jwt") (Just 123) Nothing)
+
+        it "should failed to decode a JSON data with 'expires_in' field value is a non-decimal text" $ do
+            let json = "{\"access_token\":\"access token\",\"token_type\":\"token type\",\"id_token\":\"dummy jwt\",\"expires_in\":\"non-numeric\"}"
+            (eitherDecode json :: Either String I.TokensResponse) `shouldSatisfy` isLeft
