Skip to content

Commit

Permalink
Check nbf and exp claims
Browse files Browse the repository at this point in the history
This commit adds support for the reserved nbf (not before) and exp
(expires) claims. In addition to a valid signature, the current
time to be within the range expressed in the nbf and exp claims.
Both nbf and exp are optional: If omitted or assigned an invalid
value, the lower or upper time boundary does not apply, respectively.
  • Loading branch information
burrscurr committed Oct 27, 2021
1 parent f00296f commit 91d40d9
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
EXTENSION = pgjwt
DATA = pgjwt--0.1.1.sql pgjwt--0.1.0--0.1.1.sql
DATA = pgjwt--0.1.1.sql pgjwt--0.1.0--0.1.1.sql pgjwt--0.1.1--0.2.0.sql

# postgres build stuff
PG_CONFIG = pg_config
Expand Down
29 changes: 29 additions & 0 deletions pgjwt--0.1.1--0.2.0.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
CREATE OR REPLACE FUNCTION try_cast_double(inp text)
RETURNS double precision AS $$
BEGIN
BEGIN
RETURN inp::double precision;
EXCEPTION
WHEN OTHERS THEN RETURN NULL;
END;
END;
$$ language plpgsql IMMUTABLE;


CREATE OR REPLACE FUNCTION verify(token text, secret text, algorithm text DEFAULT 'HS256')
RETURNS table(header json, payload json, valid boolean) LANGUAGE sql AS $$
SELECT
jwt.header AS header,
jwt.payload AS payload,
jwt.signature_ok AND tstzrange(
to_timestamp(try_cast_double(jwt.payload->>'nbf')),
to_timestamp(try_cast_double(jwt.payload->>'exp'))
) @> CURRENT_TIMESTAMP AS valid
FROM (
SELECT
convert_from(@[email protected]_decode(r[1]), 'utf8')::json AS header,
convert_from(@[email protected]_decode(r[2]), 'utf8')::json AS payload,
r[3] = @[email protected]_sign(r[1] || '.' || r[2], secret, algorithm) AS signature_ok
FROM regexp_split_to_array(token, '\.') r
) jwt
$$ IMMUTABLE;
2 changes: 1 addition & 1 deletion pgjwt.control
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# pgjwt extension
comment = 'JSON Web Token API for Postgresql'
default_version = '0.1.1'
default_version = '0.2.0'
relocatable = false
requires = pgcrypto
superuser = false
98 changes: 97 additions & 1 deletion test.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ CREATE EXTENSION pgtap;
CREATE EXTENSION pgjwt;

BEGIN;
SELECT plan(14);
SELECT plan(23);

SELECT
is(sign('{"sub":"1234567890","name":"John Doe","admin":true}', 'secret'),
Expand Down Expand Up @@ -131,5 +131,101 @@ SELECT
);


SELECT
results_eq(
$$SELECT header::text, payload::text, valid FROM verify(
E'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYmYiOiJuby1kb3VibGUifQ.6TDHvMKq3Z67KaexRMuhoQ20sYSj9jConcUCO3g2bGyHXACq-FPkJIRAsy1xX90fWKieIAW_tz-4bbFLwwOTPg',
'secret', 'HS512')$$,
$$VALUES ('{"alg":"HS512","typ":"JWT"}', '{"nbf":"no-double"}', true)$$,
'verify() should ignore a nbf claim with a invalid value'
);


SELECT
results_eq(
$$SELECT header::text, payload::text, valid FROM verify(
E'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOiI3NXoifQ.nfXYiSNNdYNsLrQp5Zry9p0xDCh_CkMSY1dOdqDCLc1YrDxrItwEmZIlTi3BBO8_9OCameSKMyGysYGDCNoaRg',
'secret', 'HS512')$$,
$$VALUES ('{"alg":"HS512","typ":"JWT"}', '{"exp":"75z"}', true)$$,
'verify() should ignore a exp claim with a invalid value'
);


SELECT
results_eq(
$$SELECT valid
FROM verify(sign(json_build_object('nbf', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) + 1), 'secret', 'HS512'), 'secret', 'HS512')$$,
$$VALUES (false)$$,
'verify() should not verify a jwt checked before its nbf claim'
);


SELECT
results_eq(
$$SELECT valid
FROM verify(sign(json_build_object('nbf', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) - 1), 'secret', 'HS512'), 'secret', 'HS512')$$,
$$VALUES (true)$$,
'verify() should verify a jwt checked after its nbf claim'
);


SELECT
results_eq(
$$SELECT valid
FROM verify(sign(json_build_object('exp', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) - 1), 'secret', 'HS512'), 'secret', 'HS512')$$,
$$VALUES (false)$$,
'verify() should not verify a jwt checked after its exp claim'
);


SELECT
results_eq(
$$SELECT valid
FROM verify(sign(json_build_object('exp', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) + 1), 'secret', 'HS512'), 'secret', 'HS512')$$,
$$VALUES (true)$$,
'verify() should verify a jwt checked before its exp claim'
);



SELECT
results_eq(
$$SELECT valid
FROM verify(sign(json_build_object('exp', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) + 1), 'secret', 'HS512'), 'secret', 'HS512')$$,
$$VALUES (true)$$,
'verify() should verify a jwt checked before its exp claim'
);

SELECT
results_eq(
$$SELECT valid
FROM verify(sign(
json_build_object(
'nbf', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) - 3,
'exp', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) - 1
),
'secret', 'HS512'), 'secret', 'HS512')$$,
$$VALUES (false)$$,
'verify() should not verify a jwt checked outside of its claimed nbf-exp range'
);


SELECT
results_eq(
$$SELECT valid
FROM verify(
sign(
json_build_object(
'nbf', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) - 3,
'exp', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) + 3
),
'secret', 'HS512'
),
'secret', 'HS512')$$,
$$VALUES (true)$$,
'verify() should verify a jwt checked within its claimed nbf-exp range'
);


SELECT * FROM finish();
ROLLBACK;

0 comments on commit 91d40d9

Please sign in to comment.