Giter VIP home page Giter VIP logo

jose-jwt's Introduction

jose-jwt

A Haskell implementation of the JSON Object Signing and Encryption (JOSE) specifications and the related JWT specification, as used, for example, in OpenID Connect.

Background

The JWT specification was split into JWS and JWE during its development so does not contain much. A JWT is either a JWS or a JWE depending on whether it is signed or encrypted. It is encoded as a sequence of base64 strings separated by '.' characters [1].

Technically, the content of a JWT should be JSON (unless it's a nested JWT), but this library doesn't care - it only requires a bytestring. The application should verify that the content is valid. Exactly what that means will depend on what you are using JWTs for.

Examples

You can either use the high-level encode and decode functions in the Jwt module or specific functions in the Jws and Jwe modules.

The following examples can be entered directly into ghci. Use

> :set -XOverloadedStrings

to begin with.

JWS signing example with a symmetric HMAC algorithm

HMAC is a good choice when both signer and verifier have a copy of the key.

> import Jose.Jws (hmacEncode, hmacDecode)
> import Jose.Jwa (JwsAlg(HS256))
>
> hmacEncode HS256 "aRANDOMlygeneratedkey" "my JSON message"
Right (Jwt {unJwt = "eyJhbGciOiJIUzI1NiJ9.bXkgSlNPTiBtZXNzYWdl.lTJx7ECLwYF3P7WbrrUpcp_2SdLiFXaDwK-PXcipt5Q"})
> hmacDecode "aRANDOMlygeneratedkey" "eyJhbGciOiJIUzI1NiJ9.bXkgSlNPTiBtZXNzYWdl.lTJx7ECLwYF3P7WbrrUpcp_2SdLiFXaDwK-PXcipt5Q"
Right (JwsHeader {jwsAlg = HS256, jwsTyp = Nothing, jwsCty = Nothing, jwsKid = Nothing},"my JSON message")

Trying to decode with a different key would return a Left BadSignature [2].

JWS signing using Ed25519 private key

Some situations require the use of public key cryptography for signing. For example, only a trusted party is allowed to create a signed token, but it must be verified by others.

Elliptic-curve EdDSA signing and verification are supported as defined in RFC 8037, as well as the older RSA JWS algorithms.

> import Jose.Jwt
> import Jose.Jwk
> import Jose.Jwa (JwsAlg(EdDSA))
> import Data.ByteString (ByteString)
> import Data.Aeson (decodeStrict)
>
> jsonJwk = "{\"kty\":\"OKP\", \"crv\":\"Ed25519\", \"d\":\"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A\", \"x\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\"}" :: ByteString
> Just jwk = decodeStrict jsonJwk :: Maybe Jwk
> Jose.Jwt.encode [jwk] (JwsEncoding EdDSA) (Claims "public claims")
Right (Jwt {unJwt = "eyJhbGciOiJFZERTQSJ9.cHVibGljIGNsYWltcw.xYekeeGSQVpnQbl16lOCqFcmYsUj3goSTrZ4UBQqogjHLrvFUaVJ_StBqly-Tb-0xvayjUMM4INYBTwFMt_xAQ"})

To verify the JWT you would use the Jose.Jwt.decode function with the corresponding public key.

More examples can be found in the package documentation.

Build Status

Build Status

[1] This is now referred to as "compact serialization". The additional "JSON serialization" is not supported in this library.

[2] Note that a real key for HMAC256 should be a much longer, random string of bytes. See, for example, this stackexchange answer.

jose-jwt's People

Contributors

9999years avatar alexadewit avatar alexanderkjeldaas avatar avieth avatar carterd888 avatar lf- avatar phyrex1an avatar proofofkeags avatar psibi avatar tekul avatar teofilc avatar yogsototh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

jose-jwt's Issues

Precision loss in IntDate serialisation

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

Trouble exporting and importing keys

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!

Give `IntDate` a `Num` instance

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
  ...

Add upper bound to `errors` dependency

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.

Add ParseError for invalid JWT structures

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.

Ability to decode non predefined fields

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.Jwk Haddocks and Questions

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

jose-jwt-0.6 does not compile

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

`decodeClaims` uses `decodeStrict'`

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'.

build fails with aeson-2.0

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                       >     |                                                     ^

Document AES GCM symmetric JWE?

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 ()

benchmark build failure

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.

Unable to decode JWK set

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!

Example in README.md does not work

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?

Compile errors on nixos.

[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)

MonadRandom for decoding?

Hi, I was wondering why MonadRandom is required for decoding. Is it possible to remove that constraint (or perhaps document why it's necessary)?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.