mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 17:31:56 +03:00
parent
fd53eb9c1e
commit
f726bb549d
@ -54,8 +54,9 @@ $(A.deriveJSON (A.aesonDrop 3 A.snakeCase) ''HasuraClaims)
|
|||||||
-- | HGE's own representation of various JWKs
|
-- | HGE's own representation of various JWKs
|
||||||
data JWTConfig
|
data JWTConfig
|
||||||
= JWTConfig
|
= JWTConfig
|
||||||
{ jcType :: !T.Text
|
{ jcType :: !T.Text
|
||||||
, jcKey :: !JWK
|
, jcKey :: !JWK
|
||||||
|
, jcClaimNs :: !(Maybe T.Text)
|
||||||
} deriving (Show, Eq)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
allowedRolesClaim :: T.Text
|
allowedRolesClaim :: T.Text
|
||||||
@ -64,6 +65,9 @@ allowedRolesClaim = "x-hasura-allowed-roles"
|
|||||||
defaultRoleClaim :: T.Text
|
defaultRoleClaim :: T.Text
|
||||||
defaultRoleClaim = "x-hasura-default-role"
|
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
|
-- | Process the request headers to verify the JWT and extract UserInfo from it
|
||||||
processJwt
|
processJwt
|
||||||
:: ( MonadIO m
|
:: ( MonadIO m
|
||||||
@ -78,9 +82,16 @@ processJwt conf headers = do
|
|||||||
-- verify the JWT
|
-- verify the JWT
|
||||||
claims <- liftJWTError invalidJWTError $ verifyJwt (jcKey conf) 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
|
-- filter only x-hasura claims
|
||||||
let claimsMap = Map.filterWithKey (\k _ -> T.isPrefixOf "x-hasura-" k) $
|
let claimsMap = Map.filterWithKey (\k _ -> T.isPrefixOf "x-hasura-" k) hasuraClaims
|
||||||
claims ^. unregisteredClaims
|
|
||||||
|
|
||||||
HasuraClaims allowedRoles defaultRole <- parseHasuraClaims claimsMap
|
HasuraClaims allowedRoles defaultRole <- parseHasuraClaims claimsMap
|
||||||
let role = getCurrentRole defaultRole
|
let role = getCurrentRole defaultRole
|
||||||
@ -107,6 +118,11 @@ processJwt conf headers = do
|
|||||||
["Bearer", jwt] -> return jwt
|
["Bearer", jwt] -> return jwt
|
||||||
_ -> malformedAuthzHeader
|
_ -> 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
|
-- see if there is a x-hasura-role header, or else pick the default role
|
||||||
getCurrentRole defaultRole =
|
getCurrentRole defaultRole =
|
||||||
let userRoleHeaderB = TE.encodeUtf8 userRoleHeader
|
let userRoleHeaderB = TE.encodeUtf8 userRoleHeader
|
||||||
@ -131,6 +147,9 @@ processJwt conf headers = do
|
|||||||
throw400 InvalidHeaders "Missing Authorization header in JWT authentication mode"
|
throw400 InvalidHeaders "Missing Authorization header in JWT authentication mode"
|
||||||
currRoleNotAllowed =
|
currRoleNotAllowed =
|
||||||
throw400 AccessDenied "Your current role is not in allowed roles"
|
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
|
-- parse x-hasura-allowed-roles, x-hasura-default-role from JWT claims
|
||||||
@ -191,26 +210,27 @@ instance A.FromJSON JWTConfig where
|
|||||||
parseJSON = A.withObject "JWTConfig" $ \o -> do
|
parseJSON = A.withObject "JWTConfig" $ \o -> do
|
||||||
keyType <- o A..: "type"
|
keyType <- o A..: "type"
|
||||||
rawKey <- o A..: "key"
|
rawKey <- o A..: "key"
|
||||||
|
claimNs <- o A..:? "claims_namespace"
|
||||||
case keyType of
|
case keyType of
|
||||||
"HS256" -> parseHmacKey rawKey 256 keyType
|
"HS256" -> parseHmacKey rawKey 256 keyType claimNs
|
||||||
"HS384" -> parseHmacKey rawKey 384 keyType
|
"HS384" -> parseHmacKey rawKey 384 keyType claimNs
|
||||||
"HS512" -> parseHmacKey rawKey 512 keyType
|
"HS512" -> parseHmacKey rawKey 512 keyType claimNs
|
||||||
"RS256" -> parseRsaKey rawKey keyType
|
"RS256" -> parseRsaKey rawKey keyType claimNs
|
||||||
"RS384" -> parseRsaKey rawKey keyType
|
"RS384" -> parseRsaKey rawKey keyType claimNs
|
||||||
"RS512" -> parseRsaKey rawKey keyType
|
"RS512" -> parseRsaKey rawKey keyType claimNs
|
||||||
-- TODO: support ES256, ES384, ES512, PS256, PS384
|
-- TODO: support ES256, ES384, ES512, PS256, PS384
|
||||||
_ -> invalidJwk ("Key type: " <> T.unpack keyType <> " is not supported")
|
_ -> invalidJwk ("Key type: " <> T.unpack keyType <> " is not supported")
|
||||||
where
|
where
|
||||||
parseHmacKey key size ktype = do
|
parseHmacKey key size ktype cns = do
|
||||||
let secret = BL.fromStrict $ TE.encodeUtf8 key
|
let secret = BL.fromStrict $ TE.encodeUtf8 key
|
||||||
when (BL.length secret < size `div` 8) $
|
when (BL.length secret < size `div` 8) $
|
||||||
invalidJwk "Key size too small"
|
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)
|
let res = fromRawPem (BL.fromStrict $ TE.encodeUtf8 key)
|
||||||
err e = "Could not decode PEM: " <> T.unpack e
|
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)
|
invalidJwk msg = fail ("Invalid JWK: " <> msg)
|
||||||
|
|
||||||
|
@ -181,8 +181,8 @@ parseJwtSecret =
|
|||||||
|
|
||||||
jwtSecretHelp :: String
|
jwtSecretHelp :: String
|
||||||
jwtSecretHelp = "The JSON containing type and the JWK used for verifying. e.g: "
|
jwtSecretHelp = "The JSON containing type and the JWK used for verifying. e.g: "
|
||||||
<> "`{\"type\": \"HS256\", \"key\": \"<your-hmac-shared-secret>\"}`,"
|
<> "`{\"type\": \"HS256\", \"key\": \"<your-hmac-shared-secret>\", \"claims_namespace\": \"<optional-custom-claims-key-name>\"}`,"
|
||||||
<> "`{\"type\": \"RS256\", \"key\": \"<your-PEM-RSA-public-key>\"}`"
|
<> "`{\"type\": \"RS256\", \"key\": \"<your-PEM-RSA-public-key>\", \"claims_namespace\": \"<optional-custom-claims-key-name>\"}`"
|
||||||
|
|
||||||
|
|
||||||
parseCorsConfig :: Parser CorsConfigFlags
|
parseCorsConfig :: Parser CorsConfigFlags
|
||||||
|
Loading…
Reference in New Issue
Block a user