Skip to content

Commit 91d40d9

Browse files
committed
Check nbf and exp claims
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.
1 parent f00296f commit 91d40d9

File tree

4 files changed

+128
-3
lines changed

4 files changed

+128
-3
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
EXTENSION = pgjwt
2-
DATA = pgjwt--0.1.1.sql pgjwt--0.1.0--0.1.1.sql
2+
DATA = pgjwt--0.1.1.sql pgjwt--0.1.0--0.1.1.sql pgjwt--0.1.1--0.2.0.sql
33

44
# postgres build stuff
55
PG_CONFIG = pg_config

pgjwt--0.1.1--0.2.0.sql

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
CREATE OR REPLACE FUNCTION try_cast_double(inp text)
2+
RETURNS double precision AS $$
3+
BEGIN
4+
BEGIN
5+
RETURN inp::double precision;
6+
EXCEPTION
7+
WHEN OTHERS THEN RETURN NULL;
8+
END;
9+
END;
10+
$$ language plpgsql IMMUTABLE;
11+
12+
13+
CREATE OR REPLACE FUNCTION verify(token text, secret text, algorithm text DEFAULT 'HS256')
14+
RETURNS table(header json, payload json, valid boolean) LANGUAGE sql AS $$
15+
SELECT
16+
jwt.header AS header,
17+
jwt.payload AS payload,
18+
jwt.signature_ok AND tstzrange(
19+
to_timestamp(try_cast_double(jwt.payload->>'nbf')),
20+
to_timestamp(try_cast_double(jwt.payload->>'exp'))
21+
) @> CURRENT_TIMESTAMP AS valid
22+
FROM (
23+
SELECT
24+
convert_from(@[email protected]_decode(r[1]), 'utf8')::json AS header,
25+
convert_from(@[email protected]_decode(r[2]), 'utf8')::json AS payload,
26+
r[3] = @[email protected]_sign(r[1] || '.' || r[2], secret, algorithm) AS signature_ok
27+
FROM regexp_split_to_array(token, '\.') r
28+
) jwt
29+
$$ IMMUTABLE;

pgjwt.control

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# pgjwt extension
22
comment = 'JSON Web Token API for Postgresql'
3-
default_version = '0.1.1'
3+
default_version = '0.2.0'
44
relocatable = false
55
requires = pgcrypto
66
superuser = false

test.sql

+97-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ CREATE EXTENSION pgtap;
1414
CREATE EXTENSION pgjwt;
1515

1616
BEGIN;
17-
SELECT plan(14);
17+
SELECT plan(23);
1818

1919
SELECT
2020
is(sign('{"sub":"1234567890","name":"John Doe","admin":true}', 'secret'),
@@ -131,5 +131,101 @@ SELECT
131131
);
132132

133133

134+
SELECT
135+
results_eq(
136+
$$SELECT header::text, payload::text, valid FROM verify(
137+
E'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYmYiOiJuby1kb3VibGUifQ.6TDHvMKq3Z67KaexRMuhoQ20sYSj9jConcUCO3g2bGyHXACq-FPkJIRAsy1xX90fWKieIAW_tz-4bbFLwwOTPg',
138+
'secret', 'HS512')$$,
139+
$$VALUES ('{"alg":"HS512","typ":"JWT"}', '{"nbf":"no-double"}', true)$$,
140+
'verify() should ignore a nbf claim with a invalid value'
141+
);
142+
143+
144+
SELECT
145+
results_eq(
146+
$$SELECT header::text, payload::text, valid FROM verify(
147+
E'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOiI3NXoifQ.nfXYiSNNdYNsLrQp5Zry9p0xDCh_CkMSY1dOdqDCLc1YrDxrItwEmZIlTi3BBO8_9OCameSKMyGysYGDCNoaRg',
148+
'secret', 'HS512')$$,
149+
$$VALUES ('{"alg":"HS512","typ":"JWT"}', '{"exp":"75z"}', true)$$,
150+
'verify() should ignore a exp claim with a invalid value'
151+
);
152+
153+
154+
SELECT
155+
results_eq(
156+
$$SELECT valid
157+
FROM verify(sign(json_build_object('nbf', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) + 1), 'secret', 'HS512'), 'secret', 'HS512')$$,
158+
$$VALUES (false)$$,
159+
'verify() should not verify a jwt checked before its nbf claim'
160+
);
161+
162+
163+
SELECT
164+
results_eq(
165+
$$SELECT valid
166+
FROM verify(sign(json_build_object('nbf', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) - 1), 'secret', 'HS512'), 'secret', 'HS512')$$,
167+
$$VALUES (true)$$,
168+
'verify() should verify a jwt checked after its nbf claim'
169+
);
170+
171+
172+
SELECT
173+
results_eq(
174+
$$SELECT valid
175+
FROM verify(sign(json_build_object('exp', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) - 1), 'secret', 'HS512'), 'secret', 'HS512')$$,
176+
$$VALUES (false)$$,
177+
'verify() should not verify a jwt checked after its exp claim'
178+
);
179+
180+
181+
SELECT
182+
results_eq(
183+
$$SELECT valid
184+
FROM verify(sign(json_build_object('exp', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) + 1), 'secret', 'HS512'), 'secret', 'HS512')$$,
185+
$$VALUES (true)$$,
186+
'verify() should verify a jwt checked before its exp claim'
187+
);
188+
189+
190+
191+
SELECT
192+
results_eq(
193+
$$SELECT valid
194+
FROM verify(sign(json_build_object('exp', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) + 1), 'secret', 'HS512'), 'secret', 'HS512')$$,
195+
$$VALUES (true)$$,
196+
'verify() should verify a jwt checked before its exp claim'
197+
);
198+
199+
SELECT
200+
results_eq(
201+
$$SELECT valid
202+
FROM verify(sign(
203+
json_build_object(
204+
'nbf', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) - 3,
205+
'exp', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) - 1
206+
),
207+
'secret', 'HS512'), 'secret', 'HS512')$$,
208+
$$VALUES (false)$$,
209+
'verify() should not verify a jwt checked outside of its claimed nbf-exp range'
210+
);
211+
212+
213+
SELECT
214+
results_eq(
215+
$$SELECT valid
216+
FROM verify(
217+
sign(
218+
json_build_object(
219+
'nbf', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) - 3,
220+
'exp', EXTRACT (EPOCH FROM CURRENT_TIMESTAMP) + 3
221+
),
222+
'secret', 'HS512'
223+
),
224+
'secret', 'HS512')$$,
225+
$$VALUES (true)$$,
226+
'verify() should verify a jwt checked within its claimed nbf-exp range'
227+
);
228+
229+
134230
SELECT * FROM finish();
135231
ROLLBACK;

0 commit comments

Comments
 (0)