diff --git a/changelog b/changelog
--- a/changelog
+++ b/changelog
@@ -1,3 +1,9 @@
+2014-06-02 0.3.0
+================
+
+	* Add verify function (thanks to Robert Massaioli) to allow verifying an
+	already decoded JWT token
+
 2014-03-10 0.2.1
 =================
 
diff --git a/jwt.cabal b/jwt.cabal
--- a/jwt.cabal
+++ b/jwt.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                jwt
-version:             0.2.1
+version:             0.3.0
 synopsis:            JSON Web Token (JWT) decoding and encoding
 license:             MIT
 license-file:        LICENSE
@@ -62,7 +62,6 @@
   build-depends:       base < 5 && >= 4.4
                      , tasty >= 0.7
                      , tasty-th >= 0.1
-                     , tasty-hspec >= 0.1
                      , tasty-hunit >= 0.4
                      , tasty-quickcheck >= 0.3
                      , HUnit
diff --git a/src/Web/JWT.hs b/src/Web/JWT.hs
--- a/src/Web/JWT.hs
+++ b/src/Web/JWT.hs
@@ -28,7 +28,9 @@
     (
     -- * Encoding & Decoding JWTs
     -- ** Decoding
+    -- $docDecoding
       decode
+    , verify
     , decodeAndVerifySignature
     -- ** Encoding
     , encodeSigned
@@ -104,24 +106,24 @@
 
 -- | The JSON Web Token
 data JWT r where
-   Unverified :: JWTHeader -> JWTClaimsSet -> JWT UnverifiedJWT
+   Unverified :: JWTHeader -> JWTClaimsSet -> Signature -> T.Text -> JWT UnverifiedJWT
    Verified   :: JWTHeader -> JWTClaimsSet -> Signature -> JWT VerifiedJWT
 
 deriving instance Show (JWT r)
 
 -- | Extract the claims set from a JSON Web Token
 claims :: JWT r -> JWTClaimsSet
-claims (Unverified _ c) = c
+claims (Unverified _ c _ _) = c
 claims (Verified _ c _) = c
 
 -- | Extract the header from a JSON Web Token
 header :: JWT r -> JWTHeader
-header (Unverified h _) = h
+header (Unverified h _ _ _) = h
 header (Verified h _ _) = h
 
 -- | Extract the signature from a verified JSON Web Token
 signature :: JWT r -> Maybe Signature
-signature (Unverified _ _) = Nothing
+signature Unverified{}     = Nothing
 signature (Verified _ _ s) = Just s
 
 -- | A JSON numeric value representing the number of seconds from
@@ -271,14 +273,40 @@
 -- Just (JWTClaimsSet {iss = Nothing, sub = Nothing, aud = Nothing, exp = Nothing, nbf = Nothing, iat = Nothing, jti = Nothing, unregisteredClaims = fromList [("some",String "payload")]})
 decode :: JSON -> Maybe (JWT UnverifiedJWT)
 decode input = do
-    (h,c) <- extractElems $ T.splitOn "." input
+    (h,c,s) <- extractElems $ T.splitOn "." input
     let header' = parseJWT h
         claims' = parseJWT c
-    Unverified <$> header' <*> claims'
+    Unverified <$> header' <*> claims' <*> (pure . Signature $ s) <*> (pure . dotted $ [h,c])
     where
-        extractElems (h:c:_) = Just (h,c)
+        extractElems (h:c:s:_) = Just (h,c,s)
         extractElems _       = Nothing
 
+-- | Using a known secret and a decoded claims set verify that the signature is correct
+-- and return a verified JWT token as a result.
+--
+-- This will return a VerifiedJWT if and only if the signature can be verified using the
+-- given secret.
+--
+-- The separation between decode and verify is very useful if you are communicating with
+-- multiple different services with different secrets and it allows you to lookup the
+-- correct secret for the unverified JWT before trying to verify it. If this is not an
+-- isuse for you (there will only ever be one secret) then you should just use
+-- 'decodeAndVerifySignature'.
+--
+-- >>> :{
+--  let
+--      input = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzb21lIjoicGF5bG9hZCJ9.Joh1R2dYzkRvDkqv3sygm5YyK8Gi4ShZqbhK2gxcs2U" :: T.Text
+--      mUnverifiedJwt = decode input
+--      mVerifiedJwt = verify (secret "secret") =<< mUnverifiedJwt
+--  in signature =<< mVerifiedJwt
+-- :}
+-- Just (Signature "Joh1R2dYzkRvDkqv3sygm5YyK8Gi4ShZqbhK2gxcs2U")
+verify :: Secret -> JWT UnverifiedJWT -> Maybe (JWT VerifiedJWT)
+verify secret' (Unverified header' claims' unverifiedSignature originalClaim) = do
+   algo <- alg header'
+   let calculatedSignature = Signature $ calculateDigest algo secret' originalClaim
+   guard (unverifiedSignature == calculatedSignature)
+   pure $ Verified header' claims' calculatedSignature
 
 -- | Decode a claims set and verify that the signature matches by using the supplied secret.
 -- The algorithm is based on the supplied header value.
@@ -290,22 +318,11 @@
 --  let
 --      input = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzb21lIjoicGF5bG9hZCJ9.Joh1R2dYzkRvDkqv3sygm5YyK8Gi4ShZqbhK2gxcs2U" :: T.Text
 --      mJwt = decodeAndVerifySignature (secret "secret") input
---  in join $ fmap signature mJwt
+--  in signature =<< mJwt
 -- :}
 -- Just (Signature "Joh1R2dYzkRvDkqv3sygm5YyK8Gi4ShZqbhK2gxcs2U")
-decodeAndVerifySignature :: Secret -> T.Text -> Maybe (JWT VerifiedJWT)
-decodeAndVerifySignature secret' input = do
-        (h,c,s) <- extractElems $ T.splitOn "." input
-        header' <- parseJWT h
-        claims' <- parseJWT c
-        algo  <- fmap alg header'
-        let sign = if Just s == calculateMessageDigest h c algo then pure $ Signature s else mzero
-        Verified <$> header' <*> claims' <*> sign
-    where
-      calculateMessageDigest header' claims' (Just algo') = Just $ calculateDigest algo' secret' (dotted [header', claims'])
-      calculateMessageDigest _ _ Nothing = Nothing
-      extractElems (h:c:s:_) = Just (h,c,s)
-      extractElems _         = Nothing
+decodeAndVerifySignature :: Secret -> JSON -> Maybe (JWT VerifiedJWT)
+decodeAndVerifySignature secret' input = verify secret' =<< decode input
 
 -- | Try to extract the value for the issue claim field 'iss' from the web token in JSON form
 tokenIssuer :: JSON -> Maybe StringOrURI
@@ -424,4 +441,24 @@
     parseJSON (String s) = return $ S s
     parseJSON _          = mzero
 
-
+-- $docDecoding
+-- There are three use cases supported by the set of decoding/verification
+-- functions:
+--
+-- (1) Plaintext JWTs (<http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-16#section-6>).
+--      This is supported by the decode function 'decode'.
+--      As a client you don't care about signing or encrypting so you only get back a 'JWT' 'UnverifiedJWT'.
+--      I.e. the type makes it clear that no signature verification was attempted.
+--
+-- (2) Signed JWTs you want to verify using a known secret.
+--      This is what 'decodeAndVerifySignature' supports, given a secret
+--      and JSON it will return a 'JWT' 'VerifiedJWT' if the signature can be
+--      verified.
+--
+-- (3) Signed JWTs that need to be verified using a secret that depends on
+--      information contained in the JWT. E.g. the secret depends on
+--      some claim, therefore the JWT needs to be decoded first and after
+--      retrieving the appropriate secret value, verified in a subsequent step.
+--      This is supported by using the `verify` function which given
+--      a 'JWT' 'UnverifiedJWT' and a secret will return a 'JWT' 'VerifiedJWT' iff the
+--      signature can be verified.
diff --git a/tests/src/Web/JWTTests.hs b/tests/src/Web/JWTTests.hs
--- a/tests/src/Web/JWTTests.hs
+++ b/tests/src/Web/JWTTests.hs
@@ -51,6 +51,12 @@
     Just HS256 @=? alg (header unverified)
     Just "payload" @=? Map.lookup "some" (unregisteredClaims $ claims unverified)
 
+case_verify = do
+    -- Generated with ruby-jwt
+    let input = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzb21lIjoicGF5bG9hZCJ9.Joh1R2dYzkRvDkqv3sygm5YyK8Gi4ShZqbhK2gxcs2U"
+        mVerified = verify (secret "secret") =<< decode input
+    True @=? isJust mVerified
+
 case_decodeAndVerifyJWT = do
     -- Generated with ruby-jwt
     let input = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzb21lIjoicGF5bG9hZCJ9.Joh1R2dYzkRvDkqv3sygm5YyK8Gi4ShZqbhK2gxcs2U"
