tekul / jose-jwt Goto Github PK
View Code? Open in Web Editor NEWHaskell implementation of JOSE/JWT standards
License: BSD 3-Clause "New" or "Revised" License
Haskell implementation of JOSE/JWT standards
License: BSD 3-Clause "New" or "Revised" License
[4 of 8] Compiling Jose.Jwk ( Jose/Jwk.hs, dist/build/Jose/Jwk.o )
Jose/Jwk.hs:145:14:
No instance for (transformers-0.3.0.0:Control.Monad.Trans.Error.Error
Jose.Types.JwtError)
arising from a use of ‘B64.decode’
In the expression: B64.decode (TE.encodeUtf8 t)
In the expression:
case B64.decode (TE.encodeUtf8 t) of {
Left _ -> fail "could not base64 decode bytes"
Right b -> pure $ JwkBytes b }
In the second argument of ‘($)’, namely
‘\ t
-> case B64.decode (TE.encodeUtf8 t) of {
Left _ -> fail "could not base64 decode bytes"
Right b -> pure $ JwkBytes b }’
[5 of 8] Compiling Jose.Internal.Crypto ( Jose/Internal/Crypto.hs, dist/build/Jose/Internal/Crypto.o )
[6 of 8] Compiling Jose.Jws ( Jose/Jws.hs, dist/build/Jose/Jws.o )
Jose/Jws.hs:84:17:
No instance for (transformers-0.3.0.0:Control.Monad.Trans.Error.Error
JwtError)
arising from a use of ‘B64.decode’
In a stmt of a 'do' block: sigBytes <- B64.decode sig
In the expression:
do { unless (BC.count '.' jwt == 2) $ Left $ BadDots 2;
let (hdrPayload, sig) = spanEndDot jwt;
sigBytes <- B64.decode sig;
[h, payload] <- mapM B64.decode $ BC.split '.' hdrPayload;
.... }
In an equation for ‘decode’:
decode verify jwt
= do { unless (BC.count '.' jwt == 2) $ Left $ BadDots 2;
let (hdrPayload, sig) = ...;
sigBytes <- B64.decode sig;
.... }
where
spanEndDot bs = let ... in (BC.init toDot, end)
IntDate
should take an Int
instead of a POSIXTime
or make it clear in the documentation about rounding.
ghci> fmap IntDate (fmap utcTimeToPOSIXSeconds getCurrentTime)
IntDate 1695687669.311274325s
Hello there!
I am attempting to use this library to send symmetrically-encrypted information. I am able to run the JWE example fine, but this generates a new key every time.
I am having trouble figuring out how to use the library with a pregenerated key rather than a newly generated one.
And, related, how to export a key that was generated using generateSymmetricKey
.
I am guessing that this might have to do with the FromJSON
and ToJSON
typeclasses, but I am not entirely sure how to get their functions in scope. Also, I would just be happy to export the strings in some other easy-to-transport format (hexencoded, base64-encoded or similar) instead.
Thanks!
First, thanks very much for building this library. I'm not in a position to roll-my-own-crypto, so it's practical libraries like this one that keep me free of Scala's rough, wtf-is-this embrace.
Second, please create haddocks for Jose.Jwk. Lack of docs for Jose.Jwk definitely impeded my progress with the library.
Third, as a sanity check, is this proper usage of the lib? Further, do you have concerns with your library being used in production?
import qualified Data.Aeson.Encode as A
import Data.ByteString.Lazy (toStrict)
import Data.Either
import Data.Time.Clock
import Data.Time.Clock.POSIX
import Jose.Jwa
import Jose.Jwk
import Jose.Jws
import Jose.Jwt
-- Get a Jwk
privateJwk :: IO Jwk
privateJwk = do
(_, privKey) <- generateRsaKeyPair 256 (KeyId "mykey") Sig (Just (Signed RS256))
return privKey
-- Make some claims
makeJwtClaims :: IO JwtClaims
makeJwtClaims = do
currentUTC <- getCurrentTime
let laterDate = IntDate $ utcTimeToPOSIXSeconds $ addUTCTime (60 * 60 * 24 * 14) currentUTC
return $
JwtClaims (Just "issuer")
(Just "sub")
(Just ["aud1", "aud2"])
(Just laterDate)
Nothing
Nothing
(Just "jti")
-- Create a Payload
makePayload :: JwtClaims -> Payload
makePayload claims = Claims $ toStrict $ A.encode claims
-- Test Encode and Decode a Jwt
encodeDecodePrint :: IO ()
encodeDecodePrint = do
jwk <- privateJwk
claims <- makeJwtClaims
let encAlg = JwsEncoding RS256
payload = makePayload claims
eitherJwt <- encode [jwk] encAlg payload
case eitherJwt of
Right jwt -> do
eitherContent <- decode [jwk] (Just encAlg) (unJwt jwt)
either (\_ -> print "Decode Failure")
(\(Jws (_, bs)) -> print bs)
eitherContent
_ -> print "Encode failure"
Finally, if possible, please post details on how to send donations to support your work on this. I'd gladly contribute.
Cheers,
Jason
Citing from http://hydra.cryp.to/build/876114/nixlog/1/raw:
Jose/Types.hs:88:24:
Ambiguous occurrence ‘defaultTimeLocale’
It could refer to either ‘Data.Time.Format.defaultTimeLocale’,
imported from ‘Data.Time.Format’ at Jose/Types.hs:35:1-23
(and originally defined in ‘time-1.5.0.1:Data.Time.Format.Locale’)
or ‘System.Locale.defaultTimeLocale’,
imported from ‘System.Locale’ at Jose/Types.hs:41:23-39
I was using the library at work and since decodeClaims
is using decodeStrict'
from the aeson
library, no meaningful error message is returned if parsing fails. I think it would be better to use eitherDecodeStrict'
in order to have more information if parsing fails. Is there a good reason why eitherDecodeStrict'
was not used in the first place? If not and you think it would be beneficial to use eitherDecodeStrict'
, then I would be happy to have a go at making those changes and modifying BadClaims
to have an additional parameter which would carry the error message returned from eitherDecodeStrict'
.
Configuring GHCi with the following packages:
GHCi, version 8.0.2: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /tmp/ghci24350/ghci-script
Prelude> import Jose.Jws
Prelude Jose.Jws> import Jose.Jwa
Prelude Jose.Jws Jose.Jwa> hmacEncode HS384 "somehmackey" "my JSON message"
<interactive>:3:18: error:
• Couldn't match expected type ‘Data.ByteString.Internal.ByteString’
with actual type ‘[Char]’
• In the second argument of ‘hmacEncode’, namely ‘"somehmackey"’
In the expression: hmacEncode HS384 "somehmackey" "my JSON message" In an equation for ‘it’: it = hmacEncode HS384 "somehmackey" "my JSON message"
<interactive>:3:32: error:
• Couldn't match expected type ‘Data.ByteString.Internal.ByteString’
with actual type ‘[Char]’
• In the third argument of ‘hmacEncode’, namely ‘"my JSON message"’
In the expression: hmacEncode HS384 "somehmackey" "my JSON message"
In an equation for ‘it’:
it = hmacEncode HS384 "somehmackey" "my JSON message"
I get a type error for the two "strings" I pass to hmacEncode.
Am I doing something wrong, or is the documentation old?
The test suite will be disabled in stackage because of this, please send a pull request to re-enable it when this gets fixed. Thanks!
https://github.com/bergmark/blog/blob/master/2016/package-faq.md#test-files-cant-be-found
> /tmp/stackage-build8/jose-jwt-0.7.2$ dist/build/tests/tests
tests: tests/jwks.json: openBinaryFile: does not exist (No such file or directory)
I'm probably not getting the API properly, but the following fails with:
Left (KeyError "cipher initialization failed")
new-template-exe: src/Lib.hs:28:7-30: Irrefutable pattern failed for pattern Right (Jwt jwt)
Maybe an example could be added to the docs for this?
{-# LANGUAGE OverloadedStrings,TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
module Lib
( someFunc
) where
import Crypto.Random
import qualified Data.Aeson as A
import Data.Aeson.QQ
import qualified Data.ByteString.Char8 as B
import Jose.Jwa
import Jose.Jwe
import Jose.Jwk
import Jose.Jwt
someFunc :: IO ()
someFunc = do
let keyj = [aesonQQ|
{"kty": "oct",
"k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow",
"kid":"HMAC key used in JWS A.1 example"
}
|]
g <- drgNew
let A.Success key = A.fromJSON keyj :: A.Result Jwk
let (result, g') = withDRG g (jwkEncode A128KW A128GCM key (Claims "secret claims"))
putStrLn $ show result
let Right (Jwt jwt) = result
B.putStrLn jwt
return ()
Is this library protected against the invalid curve attack?
http://blogs.adobe.com/security/2017/03/critical-vulnerability-uncovered-in-json-encryption.html
The attacks the library is secure against should be prominently displayed in the README.md as there has been a few in the last years.
I might have missed something. But I don't see how I can get claims not already predefined in JwtClaims (typically email
claim from OIDC from Google for example).
A quick fix would be to simply change the type of decodeClaims
or to add another function like this:
decodeClaims :: ByteString
-> Either JwtError (JwtHeader, JwtClaims)
decodeClaims = decodeJwt
decodeJwt :: (FromJSON a) => ByteString
-> Either JwtError (JwtHeader, a)
decodeJwt jwt = do
let components = BC.split '.' jwt
when (length components /= 3) $ Left $ BadDots 2
hdr <- B64.decode (head components) >>= parseHeader
claims <- B64.decode ((head . tail) components) >>= parseClaims
return (hdr, claims)
where
parseClaims bs = maybe (Left BadClaims) Right $ decodeStrict' bs
So I will be able to get any claim easily.
Another solution would be to change the type of JwtClaims to contains a Map for all non predefined claims.
If you believe this is reasonable I'll make a quick PR.
jose-jwt > Building library for jose-jwt-0.9.2..
jose-jwt > [1 of 9] Compiling Jose.Jwa
jose-jwt > [2 of 9] Compiling Jose.Types
jose-jwt >
jose-jwt > /tmp/stack-0da44745a837d1f4/jose-jwt-0.9.2/Jose/Types.hs:129:50: error:
jose-jwt > • Couldn't match type: Data.Aeson.KeyMap.KeyMap Value
jose-jwt > with: H.HashMap k0 Value
jose-jwt > Expected: H.HashMap k0 Value
jose-jwt > Actual: Object
jose-jwt > • In the second argument of ‘H.lookup’, namely ‘o’
jose-jwt > In the expression: H.lookup "aud" o
jose-jwt > In the expression:
jose-jwt > case H.lookup "aud" o of
jose-jwt > Just (a@(String _))
jose-jwt > -> genericParseJSON claimsOptions
jose-jwt > $ Object $ H.insert "aud" (Array $ singleton a) o
jose-jwt > _ -> genericParseJSON claimsOptions v
jose-jwt > |
jose-jwt > 129 | parseJSON v@(Object o) = case H.lookup "aud" o of
jose-jwt > | ^
jose-jwt >
jose-jwt > /tmp/stack-0da44745a837d1f4/jose-jwt-0.9.2/Jose/Types.hs:130:74: error:
jose-jwt > • Couldn't match type: H.HashMap k1 Value
jose-jwt > with: Data.Aeson.KeyMap.KeyMap Value
jose-jwt > Expected: Object
jose-jwt > Actual: H.HashMap k1 Value
jose-jwt > • In the second argument of ‘($)’, namely
jose-jwt > ‘H.insert "aud" (Array $ singleton a) o’
jose-jwt > In the second argument of ‘($)’, namely
jose-jwt > ‘Object $ H.insert "aud" (Array $ singleton a) o’
jose-jwt > In the expression:
jose-jwt > genericParseJSON claimsOptions
jose-jwt > $ Object $ H.insert "aud" (Array $ singleton a) o
jose-jwt > |
jose-jwt > 130 | Just (a@(String _)) -> genericParseJSON claimsOptions $ Object $ H.insert "aud" (Array $ singleton a) o
jose-jwt > | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
jose-jwt >
jose-jwt > /tmp/stack-0da44745a837d1f4/jose-jwt-0.9.2/Jose/Types.hs:130:111: error:
jose-jwt > • Couldn't match type: Data.Aeson.KeyMap.KeyMap Value
jose-jwt > with: H.HashMap k1 Value
jose-jwt > Expected: H.HashMap k1 Value
jose-jwt > Actual: Object
jose-jwt > • In the third argument of ‘H.insert’, namely ‘o’
jose-jwt > In the second argument of ‘($)’, namely
jose-jwt > ‘H.insert "aud" (Array $ singleton a) o’
jose-jwt > In the second argument of ‘($)’, namely
jose-jwt > ‘Object $ H.insert "aud" (Array $ singleton a) o’
jose-jwt > |
jose-jwt > 130 | Just (a@(String _)) -> genericParseJSON claimsOptions $ Object $ H.insert "aud" (Array $ singleton a) o
jose-jwt > | ^
jose-jwt >
jose-jwt > /tmp/stack-0da44745a837d1f4/jose-jwt-0.9.2/Jose/Types.hs:177:50: error:
jose-jwt > • Couldn't match type: Data.Aeson.KeyMap.KeyMap Value
jose-jwt > with: H.HashMap k2 Value
jose-jwt > Expected: H.HashMap k2 Value
jose-jwt > Actual: Object
jose-jwt > • In the second argument of ‘H.lookup’, namely ‘o’
jose-jwt > In the expression: H.lookup "alg" o
jose-jwt > In the expression:
jose-jwt > case H.lookup "alg" o of
jose-jwt > Just (String "none") -> pure UnsecuredH
jose-jwt > _ -> case H.lookup "enc" o of
jose-jwt > Nothing -> JwsH <$> parseJSON v
jose-jwt > _ -> JweH <$> parseJSON v
jose-jwt > |
jose-jwt > 177 | parseJSON v@(Object o) = case H.lookup "alg" o of
jose-jwt > | ^
jose-jwt >
jose-jwt > /tmp/stack-0da44745a837d1f4/jose-jwt-0.9.2/Jose/Types.hs:179:53: error:
jose-jwt > • Couldn't match type: Data.Aeson.KeyMap.KeyMap Value
jose-jwt > with: H.HashMap k3 v0
jose-jwt > Expected: H.HashMap k3 v0
jose-jwt > Actual: Object
jose-jwt > • In the second argument of ‘H.lookup’, namely ‘o’
jose-jwt > In the expression: H.lookup "enc" o
jose-jwt > In the expression:
jose-jwt > case H.lookup "enc" o of
jose-jwt > Nothing -> JwsH <$> parseJSON v
jose-jwt > _ -> JweH <$> parseJSON v
jose-jwt > |
jose-jwt > 179 | _ -> case H.lookup "enc" o of
jose-jwt > | ^
There is a critical vulnerability in most jwt implementations
https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/
The decode function looks vulnerable as well, because the set of decode keys is decided based on untrusted header information.
Apparently 'cryptonite' is not actively maintained, and 'crypton' is the new standard.
A number of key networking libraries have switched to 'crypton' already : https://packdeps.haskellers.com/reverse/crypton
It's API-compatible, so this should be just an import renaming chore.
Hi, I was wondering why MonadRandom
is required for decoding. Is it possible to remove that constraint (or perhaps document why it's necessary)?
The link to hackage.org is still http
and should be updated.
Since IntDate
is a newtype of POSIXTime
and POSIXTime
has the Num
class, I think you should derive Num
for IntDate
as well.
My use case is I want to be able to compare times like this (iat
is within 30 seconds of now):
currentTime <- getPOSIXTime
if abs(iat - (IntDate currentTime)) < 30
...
but since there's no Num
for IntDate
, I have to do this:
currentTime <- getPOSIXTime
let ahead30s = IntDate (currentTime + 30)
behind30s = IntDate (currentTime - 30)
if behind30s < iat && iat < ahead30s
...
Hi there, I'm trying to use this library with Auth0 & their RS256 signed tokens. As part of that I'm downloading a JWK set they provide me and trying to decode it:
getJwks :: IO ()
getJwks = do
manager <- getGlobalManager
jwksUrl <- parseRequest "https://iand675.eu.auth0.com/.well-known/jwks.json"
r <- httpLbs jwksUrl manager
let body = responseBody r
print body
let result = eitherDecode body
print (result :: Either String JWKSet)
However, there seems to be an issue with decoding the key provided in the set:
Left "Error in $.keys[0].x5t: incorrect number of bytes"
I am not terribly familiar with any aspect of JWTs and the associated specs, so I am a bit at a loss as to what is the matter here. Hoping you might have an idea of how to solve this.
Thanks!
The benchmarks for this package has been disabled in stackage because of this issue. When a fix is published to hackage could you please send a PR to re-enable the benchmarks to stackage (it's in the expected-benchmark-failures section) or ping me?
Thanks!
> /tmp/stackage-build8/jose-jwt-0.7$ runghc -clear-package-db -global-package-db -package-db=/var/stackage/work/builds/nightly/pkgdb Setup configure --enable-benchmarks --package-db=clear --package-db=global --package-db=/var/stackage/work/builds/nightly/pkgdb --libdir=/var/stackage/work/builds/nightly/lib --bindir=/var/stackage/work/builds/nightly/bin --datadir=/var/stackage/work/builds/nightly/share --libexecdir=/var/stackage/work/builds/nightly/libexec --sysconfdir=/var/stackage/work/builds/nightly/etc --docdir=/var/stackage/work/builds/nightly/doc/jose-jwt-0.7 --htmldir=/var/stackage/work/builds/nightly/doc/jose-jwt-0.7 --haddockdir=/var/stackage/work/builds/nightly/doc/jose-jwt-0.7 --flags=-doctest
Configuring jose-jwt-0.7...
> /tmp/stackage-build8/jose-jwt-0.7$ runghc -clear-package-db -global-package-db -package-db=/var/stackage/work/builds/nightly/pkgdb Setup build
Building jose-jwt-0.7...
Preprocessing library jose-jwt-0.7...
[1 of 8] Compiling Jose.Jwa ( Jose/Jwa.hs, dist/build/Jose/Jwa.o )
[2 of 8] Compiling Jose.Types ( Jose/Types.hs, dist/build/Jose/Types.o )
Jose/Types.hs:25:1: warning: [-Wunused-imports]
The import of ‘Control.Applicative’ is redundant
except perhaps to import instances from ‘Control.Applicative’
To import instances alone, use: import Control.Applicative()
[3 of 8] Compiling Jose.Internal.Base64 ( Jose/Internal/Base64.hs, dist/build/Jose/Internal/Base64.o )
Jose/Internal/Base64.hs:8:1: warning: [-Wdeprecations]
Module ‘Control.Monad.Error’ is deprecated:
Use Control.Monad.Except instead
[4 of 8] Compiling Jose.Jwk ( Jose/Jwk.hs, dist/build/Jose/Jwk.o )
[5 of 8] Compiling Jose.Internal.Crypto ( Jose/Internal/Crypto.hs, dist/build/Jose/Internal/Crypto.o )
[6 of 8] Compiling Jose.Jwe ( Jose/Jwe.hs, dist/build/Jose/Jwe.o )
[7 of 8] Compiling Jose.Jws ( Jose/Jws.hs, dist/build/Jose/Jws.o )
Jose/Jws.hs:27:1: warning: [-Wunused-imports]
The import of ‘Control.Applicative’ is redundant
except perhaps to import instances from ‘Control.Applicative’
To import instances alone, use: import Control.Applicative()
[8 of 8] Compiling Jose.Jwt ( Jose/Jwt.hs, dist/build/Jose/Jwt.o )
Preprocessing benchmark 'bench-jwt' for jose-jwt-0.7...
[1 of 2] Compiling Keys ( benchmarks/Keys.hs, dist/build/bench-jwt/bench-jwt-tmp/Keys.o )
benchmarks/Keys.hs:3:1: error:
Failed to load interface for ‘Crypto.PubKey.RSA’
It is a member of the hidden package ‘cryptonite-0.19’.
Perhaps you need to add ‘cryptonite’ to the build-depends in your .cabal file.
It is a member of the hidden package ‘crypto-pubkey-0.2.8’.
Perhaps you need to add ‘crypto-pubkey’ to the build-depends in your .cabal file.
Use -v to see a list of the files searched for.
Since the JWT is now parsed in advance, before decoding, none of the existing error codes really make sense for this stage (or can't be applied, since attoparsec returns an Either String a
) result.
A complete build log that shows the errors is available at http://hydra.cryp.to/build/578072/nixlog/2/raw.
When I try to decode an invalid JWE, for example =.
, rsaDecode
gives Exception: Invalid Base64
. I think it should return Left (Base64Error "error message")
instead.
Hi, would it be possible to get a new version published please?
errors-2.0
will switch to use ExceptT
instead of EitherT
. If that will break your package then add an upper bound of errors < 2.0
to your dependencies.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.