diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
 # ChangeLog
 
+## [0.7.0.0]
+- Lookup by state with sessionStoreGet ([#57](https://github.com/krdlab/haskell-oidc-client/pull/57))
+
 ## [0.6.1.0]
 - Remove max version cap on bytestring and min >= 0.11 ([#54](https://github.com/krdlab/haskell-oidc-client/pull/54))
 
diff --git a/examples/scotty/Main.hs b/examples/scotty/Main.hs
--- a/examples/scotty/Main.hs
+++ b/examples/scotty/Main.hs
@@ -155,11 +155,11 @@
     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
+    getStateBy ssm sid _st     = liftIO $ do
         m <- M.lookup sid <$> readIORef ssm
         return $ case m of
-            Just (st, nonce) -> (Just st, Just nonce)
-            _                -> (Nothing, Nothing)
+            Just (_, nonce) -> Just nonce
+            _               -> Nothing
     deleteState ssm sid  = liftIO $ atomicModifyIORef' ssm $ \m -> (M.delete sid m, ())
 
     sessionStoreFromSession cprg ssm sid =
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.6.1.0
+version:            0.7.0.0
 synopsis:           OpenID Connect 1.0 library for RP
 homepage:           https://github.com/krdlab/haskell-oidc-client
 stability:          experimental
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
@@ -76,14 +76,11 @@
     -> 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 $ "Inconsistent state: " <> decodeUtf8With lenientDecode stateFromIdP
+    savedNonce <- sessionStoreGet store stateFromIdP
+    when (isNothing savedNonce) $ throwM UnknownState
+    result <- liftIO $ requestTokens oidc savedNonce code mgr
+    sessionStoreDelete store
+    return result
 
 -- | 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." #-}
diff --git a/src/Web/OIDC/Client/IdTokenFlow.hs b/src/Web/OIDC/Client/IdTokenFlow.hs
--- a/src/Web/OIDC/Client/IdTokenFlow.hs
+++ b/src/Web/OIDC/Client/IdTokenFlow.hs
@@ -61,19 +61,14 @@
     -> m B.ByteString
     -> m (IdTokenClaims a)
 getValidIdTokenClaims store oidc stateFromIdP getIdToken = do
-    (state, savedNonce) <- sessionStoreGet store
-    if state == Just stateFromIdP
-      then do
-          when (isNothing savedNonce) $ liftIO $ throwIO $ ValidationException "Nonce is not saved!"
-          jwt <- Jwt.Jwt <$> getIdToken
-          sessionStoreDelete store
-          idToken <- liftIO $ validateIdToken oidc jwt
-          when (fromMaybe True $ (/=) <$> savedNonce <*> nonce idToken)
-                $ liftIO
-                $ throwIO
-                $ ValidationException "Nonce does not match request."
-          pure idToken
-      else liftIO $ throwIO $ ValidationException $ "Inconsistent state: " <> decodeUtf8With lenientDecode stateFromIdP
+    msavedNonce <- sessionStoreGet store stateFromIdP
+    savedNonce <- maybe (liftIO $ throwIO UnknownState) pure msavedNonce
+    jwt <- Jwt.Jwt <$> getIdToken
+    sessionStoreDelete store
+    idToken <- liftIO $ validateIdToken oidc jwt
+    nonce' <- maybe (liftIO $ throwIO MissingNonceInResponse) pure (nonce idToken)
+    when (nonce' /= savedNonce) $ liftIO $ throwIO MismatchedNonces
+    pure idToken
 
 -- | 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." #-}
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
@@ -55,6 +55,9 @@
     | UnsecuredJwt ByteString
     | JwtException JwtError
     | ValidationException Text
+    | UnknownState
+    | MissingNonceInResponse
+    | MismatchedNonces
   deriving (Show, Typeable)
 
 instance Exception OpenIdException
@@ -64,8 +67,9 @@
 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 ()
+    , sessionStoreSave :: State -> Nonce -> m ()
+    , sessionStoreGet :: State -> m (Maybe Nonce)
+    -- ^ Returns 'Nothing' if 'State' is unknown
+    , sessionStoreDelete :: m ()
     -- ^ Should delete at least nonce
     }
