diff --git a/src/PostgREST/Middleware.hs b/src/PostgREST/Middleware.hs index e85a56dec..c70a40dfc 100644 --- a/src/PostgREST/Middleware.hs +++ b/src/PostgREST/Middleware.hs @@ -47,7 +47,7 @@ runWithClaims conf eClaims app req = (toS $ "{\"message\":\""<>message<>"\"}") exposeSecretToSQL :: Maybe Text -> H.Transaction () -exposeSecretToSQL mS = do +exposeSecretToSQL mS = for_ mS $ \s -> H.sql $ "set local postgrest.jwt_secret = " <> toS (pgFmtLit s) <> ";" diff --git a/test/Feature/NoJwtSpec.hs b/test/Feature/NoJwtSpec.hs index fed0a8aa0..148123f5f 100644 --- a/test/Feature/NoJwtSpec.hs +++ b/test/Feature/NoJwtSpec.hs @@ -18,6 +18,6 @@ spec = describe "server started without JWT secret" $ do request methodGet "/authors_only" [auth] "" `shouldRespondWith` 500 - it "behaves normally when user does not attempt auth" $ do + it "behaves normally when user does not attempt auth" $ request methodGet "/items" [] "" `shouldRespondWith` 200 diff --git a/test/fixtures/jwt.sql b/test/fixtures/jwt.sql new file mode 100644 index 000000000..6074f6d2c --- /dev/null +++ b/test/fixtures/jwt.sql @@ -0,0 +1,64 @@ +-- From michelp/pgjwt commit c02bbd3 +BEGIN; +DROP SCHEMA IF EXISTS jwt CASCADE; +CREATE SCHEMA jwt; + + +CREATE OR REPLACE FUNCTION jwt.url_encode(data bytea) RETURNS text LANGUAGE sql AS $$ + SELECT translate(encode(data, 'base64'), E'+/=\n', '-_'); +$$; + + +CREATE OR REPLACE FUNCTION jwt.url_decode(data text) RETURNS bytea LANGUAGE sql AS $$ +WITH t AS (SELECT translate(data, '-_', '+/')), + rem AS (SELECT length((SELECT * FROM t)) % 4) -- compute padding size + SELECT decode( + (SELECT * FROM t) || + CASE WHEN (SELECT * FROM rem) > 0 + THEN repeat('=', (4 - (SELECT * FROM rem))) + ELSE '' END, + 'base64'); +$$; + + +CREATE OR REPLACE FUNCTION jwt.algorithm_sign(signables text, secret text, algorithm text) +RETURNS text LANGUAGE sql AS $$ +WITH + alg AS ( + SELECT CASE + WHEN algorithm = 'HS256' THEN 'sha256' + WHEN algorithm = 'HS384' THEN 'sha384' + WHEN algorithm = 'HS512' THEN 'sha512' + ELSE '' END) -- hmac throws error +SELECT jwt.url_encode(hmac(signables, secret, (select * FROM alg))); +$$; + + +CREATE OR REPLACE FUNCTION jwt.sign(payload json, secret text, algorithm text DEFAULT 'HS256') +RETURNS text LANGUAGE sql AS $$ +WITH + header AS ( + SELECT jwt.url_encode(convert_to('{"alg":"' || algorithm || '","typ":"JWT"}', 'utf8')) + ), + payload AS ( + SELECT jwt.url_encode(convert_to(payload::text, 'utf8')) + ), + signables AS ( + SELECT (SELECT * FROM header) || '.' || (SELECT * FROM payload) + ) +SELECT + (SELECT * FROM signables) + || '.' || + jwt.algorithm_sign((SELECT * FROM signables), secret, algorithm); +$$; + + +CREATE OR REPLACE FUNCTION jwt.verify(token text, secret text, algorithm text DEFAULT 'HS256') +RETURNS table(header json, payload json, valid boolean) LANGUAGE sql AS $$ + SELECT + convert_from(jwt.url_decode(r[1]), 'utf8')::json AS header, + convert_from(jwt.url_decode(r[2]), 'utf8')::json AS payload, + r[3] = jwt.algorithm_sign(r[1] || '.' || r[2], secret, algorithm) AS valid + FROM regexp_split_to_array(token, '\.') r; +$$; +COMMIT;