Simple PostgreSQL functions for generating and verifying JWT tokens directly inside your database.
This repository is meant to be easy to reuse: if you want JWT token generation and verification in your own PostgreSQL setup, you can copy the SQL from pgjwt.sql and run it in your database.
The SQL script creates functions that let you:
- generate signed JWT tokens
- verify JWT signatures and expiration
- issue both access and refresh tokens
- issue a new access token from a valid refresh token
Included functions:
base64_url_encoder(input bytea)base64_url_decoder(input text)sign(data text)generate_token(header jsonb, payload jsonb)issue_tokens(sub uuid, tenant uuid, role text, permissions text[])issue_access_token(refresh_token text)verify_token(token text)
- PostgreSQL
pgcryptoextension enabled- a JWT secret stored in PostgreSQL as
app.jwt_secret
pgjwt.sql already creates the pgcrypto extension if it is not installed:
CREATE EXTENSION IF NOT EXISTS pgcrypto;This project reads the secret from:
current_setting('app.jwt_secret')That means you should set app.jwt_secret before using the JWT functions.
You can do that in one of these ways.
Useful for local testing:
SET app.jwt_secret = 'replace-with-a-strong-random-secret';To generate a strong secret safely, use one of these methods:
python -c "import secrets; print(secrets.token_urlsafe(32))"openssl rand -base64 32Other safe options:
python -c "import secrets; print(secrets.token_hex(32))"for a hex-encoded secret- PostgreSQL:
SELECT encode(gen_random_bytes(32), 'base64');after enablingpgcrypto pwgen -s 32 1ifpwgenis installed on your system
Use a fresh random value for each environment, and keep the secret at least 32 bytes long before encoding when possible.
Useful when you want the setting to persist for all future connections to one database:
ALTER DATABASE your_database_name
SET app.jwt_secret = 'replace-with-a-strong-random-secret';Reconnect after running the command so the new setting is available in your session.
Useful if a specific database role should always use the same JWT secret:
ALTER ROLE your_role_name
SET app.jwt_secret = 'replace-with-a-strong-random-secret';Reconnect after running the command so the new setting is available in your session.
- Set
app.jwt_secretusing one of the methods above. - Open your PostgreSQL client.
- Run the contents of
pgjwt.sql.
Example with psql:
psql -d your_database_name -f pgjwt.sqlSELECT issue_tokens(
'11111111-1111-1111-1111-111111111111'::uuid,
'22222222-2222-2222-2222-222222222222'::uuid,
'authenticated',
ARRAY['read:profile', 'write:profile']
);Returns JSON like:
{
"access_token": "...",
"refresh_token": "...",
"token_type": "Bearer"
}SELECT verify_token('your.jwt.token');If the token is valid, the decoded payload is returned as jsonb.
SELECT issue_access_token('your.refresh.token');- access token expiry: 30 minutes
- refresh token expiry: 7 days
- signing algorithm:
HS256 - token type claims used by this script:
ACCESSandREFRESH
verify_tokenchecks token format, signature, and expiration.issue_access_tokenonly accepts tokens with"type": "REFRESH".- If the secret is missing, token signing and verification will fail.
- Keep your JWT secret private and use a strong random value in production.
If you only need the functionality, you can simply:
- copy
pgjwt.sql - set
app.jwt_secret - run the script in your database
That is enough to start issuing and verifying JWTs from PostgreSQL.