Repository: hasura/graphql-engine
File: server/src-lib/Hasura/Server/Auth/JWT.hs (Haskell)

Code before:
                            , "claims_format" J..= claimsFmt
                            , "audience" J..= aud
                            , "issuer" J..= iss
                            , item
                            ]

-- | Parse from a json string like:
-- | `{"type": "RS256", "key": "<PEM-encoded-public-key-or-X509-cert>"}`
-- | to JWTConfig
instance J.FromJSON JWTConfig where

  parseJSON = J.withObject "JWTConfig" $ \o -> do
    keyType <- o J..:? "type"
    mRawKey <- o J..:? "key"
    claimNs <- o J..:? "claims_namespace"
    aud     <- o J..:? "audience"
    iss     <- o J..:? "issuer"
    jwkUrl  <- o J..:? "jwk_url"
    isStrngfd <- o J..:? "claims_format"

    case (mRawKey, jwkUrl) of
      (Nothing, Nothing) -> fail "key and jwk_url both cannot be empty"
      (Just _, Just _)   -> fail "key, jwk_url both cannot be present"
      (Just rawKey, Nothing) -> do
        keyType' <- onNothing keyType $ fail "type of key not provided"
        key <- parseKey rawKey keyType'
        return $ JWTConfig keyType (Left key) claimNs aud isStrngfd iss
      (Nothing, Just url) ->
        return $ JWTConfig keyType (Right url) claimNs aud isStrngfd iss

    where
      parseKey rawKey keyType =
       case keyType of
          "HS256" -> runEither $ parseHmacKey rawKey 256
          "HS384" -> runEither $ parseHmacKey rawKey 384
          "HS512" -> runEither $ parseHmacKey rawKey 512
          "RS256" -> runEither $ parseRsaKey rawKey
          "RS384" -> runEither $ parseRsaKey rawKey
          "RS512" -> runEither $ parseRsaKey rawKey
          -- TODO: support ES256, ES384, ES512, PS256, PS384
          _       -> invalidJwk ("Key type: " <> T.unpack keyType <> " is not supported")

      runEither = either (invalidJwk . T.unpack) return
      invalidJwk msg = fail ("Invalid JWK: " <> msg)


Review comment (suggestion):
In the PR description, you write:

> The `type` key is only used to parse the type of `HS` algorithms so that we can throw a short key error. Nothing else. So it might be worth removing it alltogether.

So it sounds like we only do anything with it right here. In that case, it sounds like we might as well drop it from the `JWTConfig` type, and we can move the `keyType <- o J..: "type"` statement directly into this nested `do` block:

```haskell
(Just rawKey, Nothing) -> do
  keyType <- o J..: "type"
  key <- parseKey rawKey keyType
```

Then we don’t have to bother with `J.:?` only to assert its presence later in this branch.

Code after:
                     , "claims_format" J..= claimsFmt
                     , "audience" J..= aud
                     , "issuer" J..= iss
                     ]

-- | Parse from a json string like:
-- | `{"type": "RS256", "key": "<PEM-encoded-public-key-or-X509-cert>"}`
-- | to JWTConfig
instance J.FromJSON JWTConfig where

  parseJSON = J.withObject "JWTConfig" $ \o -> do
    mRawKey <- o J..:? "key"
    claimNs <- o J..:? "claims_namespace"
    aud     <- o J..:? "audience"
    iss     <- o J..:? "issuer"
    jwkUrl  <- o J..:? "jwk_url"
    isStrngfd <- o J..:? "claims_format"

    case (mRawKey, jwkUrl) of
      (Nothing, Nothing) -> fail "key and jwk_url both cannot be empty"
      (Just _, Just _)   -> fail "key, jwk_url both cannot be present"
      (Just rawKey, Nothing) -> do
        keyType <- o J..: "type"
        key <- parseKey rawKey keyType
        return $ JWTConfig (Left key) claimNs aud isStrngfd iss
      (Nothing, Just url) ->
        return $ JWTConfig (Right url) claimNs aud isStrngfd iss

    where
      parseKey rawKey keyType =
       case keyType of
          "HS256" -> runEither $ parseHmacKey rawKey 256
          "HS384" -> runEither $ parseHmacKey rawKey 384
          "HS512" -> runEither $ parseHmacKey rawKey 512
          "RS256" -> runEither $ parseRsaKey rawKey
          "RS384" -> runEither $ parseRsaKey rawKey
          "RS512" -> runEither $ parseRsaKey rawKey
          -- TODO: support ES256, ES384, ES512, PS256, PS384
          _       -> invalidJwk ("Key type: " <> T.unpack keyType <> " is not supported")

      runEither = either (invalidJwk . T.unpack) return
      invalidJwk msg = fail ("Invalid JWK: " <> msg)