这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 34 additions & 14 deletions server/src-lib/Hasura/Server/Auth/JWT.hs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ $(A.deriveJSON (A.aesonDrop 3 A.snakeCase) ''HasuraClaims)
-- | HGE's own representation of various JWKs
data JWTConfig
= JWTConfig
{ jcType :: !T.Text
, jcKey :: !JWK
{ jcType :: !T.Text
, jcKey :: !JWK
, jcClaimNs :: !(Maybe T.Text)
} deriving (Show, Eq)

allowedRolesClaim :: T.Text
Expand All @@ -64,6 +65,9 @@ allowedRolesClaim = "x-hasura-allowed-roles"
defaultRoleClaim :: T.Text
defaultRoleClaim = "x-hasura-default-role"

defaultClaimNs :: T.Text
defaultClaimNs = "https://hasura.io/jwt/claims"

-- | Process the request headers to verify the JWT and extract UserInfo from it
processJwt
:: ( MonadIO m
Expand All @@ -78,9 +82,16 @@ processJwt conf headers = do
-- verify the JWT
claims <- liftJWTError invalidJWTError $ verifyJwt (jcKey conf) jwt

let claimsNs = fromMaybe defaultClaimNs $ jcClaimNs conf

-- see if the hasura claims key exist in the claims map
let mHasuraClaims = Map.lookup claimsNs $ claims ^. unregisteredClaims
hasuraClaimsV <- maybe claimsNotFound return mHasuraClaims
-- the value of hasura claims key has to be an object
hasuraClaims <- validateIsObject hasuraClaimsV

-- filter only x-hasura claims
let claimsMap = Map.filterWithKey (\k _ -> T.isPrefixOf "x-hasura-" k) $
claims ^. unregisteredClaims
let claimsMap = Map.filterWithKey (\k _ -> T.isPrefixOf "x-hasura-" k) hasuraClaims

HasuraClaims allowedRoles defaultRole <- parseHasuraClaims claimsMap
let role = getCurrentRole defaultRole
Expand All @@ -107,6 +118,11 @@ processJwt conf headers = do
["Bearer", jwt] -> return jwt
_ -> malformedAuthzHeader

validateIsObject jVal =
case jVal of
A.Object x -> return x
_ -> throw400 JWTInvalidClaims "hasura claims should be an object"

-- see if there is a x-hasura-role header, or else pick the default role
getCurrentRole defaultRole =
let userRoleHeaderB = TE.encodeUtf8 userRoleHeader
Expand All @@ -131,6 +147,9 @@ processJwt conf headers = do
throw400 InvalidHeaders "Missing Authorization header in JWT authentication mode"
currRoleNotAllowed =
throw400 AccessDenied "Your current role is not in allowed roles"
claimsNotFound = do
let claimsNs = fromMaybe defaultClaimNs $ jcClaimNs conf
throw400 JWTInvalidClaims $ "claims key: '" <> claimsNs <> "' not found"


-- parse x-hasura-allowed-roles, x-hasura-default-role from JWT claims
Expand Down Expand Up @@ -191,26 +210,27 @@ instance A.FromJSON JWTConfig where
parseJSON = A.withObject "JWTConfig" $ \o -> do
keyType <- o A..: "type"
rawKey <- o A..: "key"
claimNs <- o A..:? "claims_namespace"
case keyType of
"HS256" -> parseHmacKey rawKey 256 keyType
"HS384" -> parseHmacKey rawKey 384 keyType
"HS512" -> parseHmacKey rawKey 512 keyType
"RS256" -> parseRsaKey rawKey keyType
"RS384" -> parseRsaKey rawKey keyType
"RS512" -> parseRsaKey rawKey keyType
"HS256" -> parseHmacKey rawKey 256 keyType claimNs
"HS384" -> parseHmacKey rawKey 384 keyType claimNs
"HS512" -> parseHmacKey rawKey 512 keyType claimNs
"RS256" -> parseRsaKey rawKey keyType claimNs
"RS384" -> parseRsaKey rawKey keyType claimNs
"RS512" -> parseRsaKey rawKey keyType claimNs
-- TODO: support ES256, ES384, ES512, PS256, PS384
_ -> invalidJwk ("Key type: " <> T.unpack keyType <> " is not supported")
where
parseHmacKey key size ktype = do
parseHmacKey key size ktype cns = do
let secret = BL.fromStrict $ TE.encodeUtf8 key
when (BL.length secret < size `div` 8) $
invalidJwk "Key size too small"
return $ JWTConfig ktype (fromOctets secret)
return $ JWTConfig ktype (fromOctets secret) cns

parseRsaKey key ktype = do
parseRsaKey key ktype cns = do
let res = fromRawPem (BL.fromStrict $ TE.encodeUtf8 key)
err e = "Could not decode PEM: " <> T.unpack e
either (invalidJwk . err) (return . JWTConfig ktype) res
either (invalidJwk . err) (\r -> return $ JWTConfig ktype r cns) res

invalidJwk msg = fail ("Invalid JWK: " <> msg)

Expand Down
4 changes: 2 additions & 2 deletions server/src-lib/Hasura/Server/Init.hs
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ parseJwtSecret =

jwtSecretHelp :: String
jwtSecretHelp = "The JSON containing type and the JWK used for verifying. e.g: "
<> "`{\"type\": \"HS256\", \"key\": \"<your-hmac-shared-secret>\"}`,"
<> "`{\"type\": \"RS256\", \"key\": \"<your-PEM-RSA-public-key>\"}`"
<> "`{\"type\": \"HS256\", \"key\": \"<your-hmac-shared-secret>\", \"claims_namespace\": \"<optional-custom-claims-key-name>\"}`,"
<> "`{\"type\": \"RS256\", \"key\": \"<your-PEM-RSA-public-key>\", \"claims_namespace\": \"<optional-custom-claims-key-name>\"}`"


parseCorsConfig :: Parser CorsConfigFlags
Expand Down