From 8a7a3b29a9413d634b8f8a71119cc54a6132df41 Mon Sep 17 00:00:00 2001 From: William Carroll Date: Sat, 8 Aug 2020 14:47:39 +0100 Subject: Add tests for "exp" field of the JWT Assert that the exp field of the JWT is "fresh". --- .../learnpianochords/src/server/Fixtures.hs | 24 +++++++++++++++------- .../learnpianochords/src/server/GoogleSignIn.hs | 24 ++++++++++++++++------ .../sandbox/learnpianochords/src/server/Spec.hs | 22 ++++++++++++++++++++ .../learnpianochords/src/server/TestUtils.hs | 4 ++++ 4 files changed, 61 insertions(+), 13 deletions(-) (limited to 'website/sandbox') diff --git a/website/sandbox/learnpianochords/src/server/Fixtures.hs b/website/sandbox/learnpianochords/src/server/Fixtures.hs index ea7e0301ec4f..169e29632bd3 100644 --- a/website/sandbox/learnpianochords/src/server/Fixtures.hs +++ b/website/sandbox/learnpianochords/src/server/Fixtures.hs @@ -9,6 +9,8 @@ import Utils import qualified Data.Map as Map import qualified GoogleSignIn import qualified TestUtils +import qualified Data.Time.Clock.POSIX as POSIX +import qualified System.IO.Unsafe as Unsafe -------------------------------------------------------------------------------- -- | These are the JWT fields that I'd like to overwrite in the `googleJWT` @@ -17,15 +19,23 @@ data JWTFields = JWTFields { overwriteSigner :: Signer , overwriteAuds :: [StringOrURI] , overwriteIss :: StringOrURI + , overwriteExp :: NumericDate } defaultJWTFields :: JWTFields -defaultJWTFields = JWTFields - { overwriteSigner = hmacSecret "secret" - , overwriteAuds = ["771151720060-buofllhed98fgt0j22locma05e7rpngl.apps.googleusercontent.com"] - |> fmap TestUtils.unsafeStringOrURI - , overwriteIss = TestUtils.unsafeStringOrURI "accounts.google.com" - } +defaultJWTFields = do + let tenDaysFromToday = POSIX.getPOSIXTime + |> Unsafe.unsafePerformIO + |> (\x -> x * 60 * 60 * 25 * 10) + |> numericDate + |> TestUtils.unsafeJust + JWTFields + { overwriteSigner = hmacSecret "secret" + , overwriteAuds = ["771151720060-buofllhed98fgt0j22locma05e7rpngl.apps.googleusercontent.com"] + |> fmap TestUtils.unsafeStringOrURI + , overwriteIss = TestUtils.unsafeStringOrURI "accounts.google.com" + , overwriteExp = tenDaysFromToday + } googleJWT :: JWTFields -> GoogleSignIn.EncodedJWT googleJWT JWTFields{..} = @@ -49,7 +59,7 @@ googleJWT JWTFields{..} = , sub = stringOrURI "114079822315085727057" , aud = overwriteAuds |> Right |> Just -- TODO: Replace date creation with a human-readable date constructor. - , Web.JWT.exp = numericDate 1596756453 + , Web.JWT.exp = Just overwriteExp , nbf = Nothing -- TODO: Replace date creation with a human-readable date constructor. , iat = numericDate 1596752853 diff --git a/website/sandbox/learnpianochords/src/server/GoogleSignIn.hs b/website/sandbox/learnpianochords/src/server/GoogleSignIn.hs index f138f2b615c8..e83ec2cfdb45 100644 --- a/website/sandbox/learnpianochords/src/server/GoogleSignIn.hs +++ b/website/sandbox/learnpianochords/src/server/GoogleSignIn.hs @@ -9,6 +9,8 @@ import Utils import qualified Network.HTTP.Simple as HTTP import qualified Data.Text as Text +import qualified Web.JWT as JWT +import qualified Data.Time.Clock.POSIX as POSIX -------------------------------------------------------------------------------- newtype EncodedJWT = EncodedJWT Text @@ -21,7 +23,9 @@ data ValidationResult | NoMatchingClientIDs [StringOrURI] | WrongIssuer StringOrURI | StringOrURIParseFailure Text - | MissingIssuer + | TimeConversionFailure + | MissingRequiredClaim Text + | StaleExpiry NumericDate deriving (Eq, Show) -- | Returns True when the supplied `jwt` meets the following criteria: @@ -73,10 +77,18 @@ jwtIsValid skipHTTP (EncodedJWT encodedJWT) = do if not $ clientID `elem` audValues then pure $ NoMatchingClientIDs audValues else - case jwt |> claims |> iss of - Nothing -> pure MissingIssuer - Just jwtIssuer -> + case (jwt |> claims |> iss, jwt |> claims |> JWT.exp) of + (Nothing, _) -> pure $ MissingRequiredClaim "iss" + (_, Nothing) -> pure $ MissingRequiredClaim "exp" + (Just jwtIssuer, Just jwtExpiry) -> if not $ jwtIssuer `elem` parsedIssuers then pure $ WrongIssuer jwtIssuer - else - pure Valid + else do + mCurrentTime <- POSIX.getPOSIXTime |> fmap numericDate + case mCurrentTime of + Nothing -> pure TimeConversionFailure + Just currentTime -> + if not $ currentTime <= jwtExpiry then + pure $ StaleExpiry jwtExpiry + else + pure Valid diff --git a/website/sandbox/learnpianochords/src/server/Spec.hs b/website/sandbox/learnpianochords/src/server/Spec.hs index 96f10a9c4332..097ae3d5158d 100644 --- a/website/sandbox/learnpianochords/src/server/Spec.hs +++ b/website/sandbox/learnpianochords/src/server/Spec.hs @@ -4,11 +4,13 @@ module Spec where -------------------------------------------------------------------------------- import Test.Hspec import Utils +import Web.JWT (numericDate) import GoogleSignIn (ValidationResult(..)) import qualified GoogleSignIn import qualified Fixtures as F import qualified TestUtils +import qualified Data.Time.Clock.POSIX as POSIX -------------------------------------------------------------------------------- main :: IO () @@ -44,3 +46,23 @@ main = hspec $ do encodedJWT = F.defaultJWTFields { F.overwriteIss = erroneousIssuer } |> F.googleJWT jwtIsValid' encodedJWT `shouldReturn` Valid + + it "fails validation when the exp field has expired" $ do + let mErroneousExp = numericDate 0 + case mErroneousExp of + Nothing -> True `shouldBe` False + Just erroneousExp -> do + let encodedJWT = F.defaultJWTFields { F.overwriteExp = erroneousExp } + |> F.googleJWT + jwtIsValid' encodedJWT `shouldReturn` StaleExpiry erroneousExp + + it "passes validation when the exp field is current" $ do + mFreshExp <- POSIX.getPOSIXTime + |> fmap (\x -> x * 60 * 60 * 24 * 10) -- 10 days later + |> fmap numericDate + case mFreshExp of + Nothing -> True `shouldBe` False + Just freshExp -> do + let encodedJWT = F.defaultJWTFields { F.overwriteExp = freshExp } + |> F.googleJWT + jwtIsValid' encodedJWT `shouldReturn` Valid diff --git a/website/sandbox/learnpianochords/src/server/TestUtils.hs b/website/sandbox/learnpianochords/src/server/TestUtils.hs index c586f7f219ba..e62950369e65 100644 --- a/website/sandbox/learnpianochords/src/server/TestUtils.hs +++ b/website/sandbox/learnpianochords/src/server/TestUtils.hs @@ -10,3 +10,7 @@ unsafeStringOrURI x = case stringOrURI (cs x) of Nothing -> error $ "Failed to convert to StringOrURI: " ++ x Just x -> x + +unsafeJust :: Maybe a -> a +unsafeJust Nothing = error "Attempted to force a Nothing to be a something" +unsafeJust (Just x) = x -- cgit 1.4.1