Skip to content

Commit 68d5809

Browse files
author
Georg Traar
committed
Test OAuth authentication against PostgreSQL 18
1 parent b19703e commit 68d5809

6 files changed

Lines changed: 203 additions & 0 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
name: OAuth integration
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- packages/pg/lib/client.js
7+
- packages/pg/lib/connection-parameters.js
8+
- packages/pg/lib/connection.js
9+
- packages/pg/lib/crypto/sasl.js
10+
- packages/pg-protocol/src/serializer.ts
11+
- packages/pg/test/integration/client/oauth-tests.js
12+
- packages/pg/test/oauth-postgres/**
13+
- .github/workflows/oauth-integration.yml
14+
push:
15+
branches: [master]
16+
paths:
17+
- packages/pg/lib/client.js
18+
- packages/pg/lib/connection-parameters.js
19+
- packages/pg/lib/connection.js
20+
- packages/pg/lib/crypto/sasl.js
21+
- packages/pg-protocol/src/serializer.ts
22+
- packages/pg/test/integration/client/oauth-tests.js
23+
- packages/pg/test/oauth-postgres/**
24+
- .github/workflows/oauth-integration.yml
25+
workflow_dispatch:
26+
27+
permissions:
28+
contents: read
29+
30+
jobs:
31+
test:
32+
runs-on: ubuntu-latest
33+
timeout-minutes: 10
34+
env:
35+
OAUTH_TEST_PGHOST: localhost
36+
OAUTH_TEST_PGPORT: 55433
37+
OAUTH_TEST_PGUSER: oauth_test
38+
OAUTH_TEST_PGDATABASE: oauth_test
39+
OAUTH_TEST_TOKEN: node-postgres-test-token
40+
steps:
41+
- uses: actions/checkout@v4
42+
with:
43+
persist-credentials: false
44+
- name: Setup node
45+
uses: actions/setup-node@v4
46+
with:
47+
node-version: 22
48+
cache: yarn
49+
- run: yarn install --frozen-lockfile
50+
- run: yarn build
51+
- uses: docker/setup-buildx-action@v3
52+
- name: Build OAuth PostgreSQL image
53+
uses: docker/build-push-action@v6
54+
with:
55+
context: packages/pg/test/oauth-postgres
56+
load: true
57+
tags: node-postgres-oauth
58+
cache-from: type=gha,scope=oauth-postgres
59+
cache-to: type=gha,mode=max,scope=oauth-postgres
60+
- name: Start OAuth PostgreSQL
61+
run: |
62+
docker run --detach --rm \
63+
--name node-postgres-oauth \
64+
--env POSTGRES_USER=postgres \
65+
--env POSTGRES_PASSWORD=postgres \
66+
--env POSTGRES_DB=oauth_test \
67+
--env PGDATA=/var/lib/postgresql/data \
68+
--publish 55433:5432 \
69+
node-postgres-oauth
70+
- name: Wait for PostgreSQL
71+
run: |
72+
for attempt in $(seq 1 30); do
73+
if docker exec node-postgres-oauth pg_isready; then
74+
exit 0
75+
fi
76+
sleep 1
77+
done
78+
exit 1
79+
- name: Test OAuth authentication
80+
run: node packages/pg/test/integration/client/oauth-tests.js
81+
- name: Show PostgreSQL logs
82+
if: failure()
83+
run: docker logs node-postgres-oauth || true
84+
- name: Stop PostgreSQL
85+
if: always()
86+
run: docker stop node-postgres-oauth || true
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
'use strict'
2+
const helper = require('./../test-helper')
3+
const assert = require('assert')
4+
const suite = new helper.Suite()
5+
const { pg } = helper
6+
7+
const token = process.env.OAUTH_TEST_TOKEN
8+
const config = {
9+
host: process.env.OAUTH_TEST_PGHOST,
10+
port: process.env.OAUTH_TEST_PGPORT,
11+
user: process.env.OAUTH_TEST_PGUSER,
12+
database: process.env.OAUTH_TEST_PGDATABASE,
13+
ssl: { rejectUnauthorized: false },
14+
connectionTimeoutMillis: 5000,
15+
}
16+
17+
if (helper.args.native) {
18+
suite.test('skipping OAuth tests (on native)', () => {})
19+
return
20+
}
21+
22+
if (!config.user || !token) {
23+
suite.test('skipping OAuth tests (missing env)', () => {})
24+
return
25+
}
26+
27+
suite.test('can connect using an OAuth bearer token', async () => {
28+
const client = new pg.Client({ ...config, oauthBearerToken: token })
29+
await client.connect()
30+
const result = await client.query('SELECT current_user')
31+
assert.equal(result.rows[0].current_user, config.user)
32+
await client.end()
33+
})
34+
35+
suite.test('can connect using an async OAuth bearer token callback', async () => {
36+
let callbackCalls = 0
37+
const client = new pg.Client({
38+
...config,
39+
oauthBearerToken: async (params) => {
40+
callbackCalls++
41+
assert.equal(params.user, config.user)
42+
assert.equal(params.database, config.database)
43+
return token
44+
},
45+
})
46+
47+
await client.connect()
48+
assert.equal(callbackCalls, 1)
49+
await client.end()
50+
})
51+
52+
suite.test('rejects an invalid OAuth bearer token', async () => {
53+
const client = new pg.Client({ ...config, oauthBearerToken: token + '-invalid' })
54+
await assert.rejects(() => client.connect(), /invalid_token/)
55+
})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
ARG POSTGRES_IMAGE=ghcr.io/railwayapp-templates/postgres-ssl:18@sha256:21c501ce65f9b7de4bc9e4b545a095c00894a10bbf299f37a86e6004a6f7b066
2+
3+
FROM ${POSTGRES_IMAGE} AS builder
4+
5+
RUN apt-get update \
6+
&& apt-get install -y --no-install-recommends build-essential libkrb5-dev postgresql-server-dev-18 \
7+
&& rm -rf /var/lib/apt/lists/*
8+
9+
COPY oauth_validator.c /usr/src/oauth-validator/oauth_validator.c
10+
COPY Makefile /usr/src/oauth-validator/Makefile
11+
12+
RUN make -C /usr/src/oauth-validator install
13+
14+
FROM ${POSTGRES_IMAGE}
15+
16+
COPY --from=builder /usr/lib/postgresql/18/lib/oauth_validator.so /usr/lib/postgresql/18/lib/oauth_validator.so
17+
COPY init-oauth.sh /docker-entrypoint-initdb.d/10-init-oauth.sh
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
MODULES = oauth_validator
2+
PG_CONFIG = pg_config
3+
PGXS := $(shell $(PG_CONFIG) --pgxs)
4+
include $(PGXS)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
cat >> "$PGDATA/postgresql.conf" <<'EOF'
5+
oauth_validator_libraries = 'oauth_validator'
6+
EOF
7+
8+
cat > "$PGDATA/pg_hba.conf" <<'EOF'
9+
local all all trust
10+
hostssl all all all oauth issuer="https://issuer.example" scope="postgres"
11+
EOF
12+
13+
psql --set ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<'SQL'
14+
CREATE ROLE oauth_test LOGIN;
15+
SQL
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "postgres.h"
2+
3+
#include "fmgr.h"
4+
#include "libpq/oauth.h"
5+
6+
PG_MODULE_MAGIC;
7+
8+
static bool
9+
validate_token(const ValidatorModuleState *state, const char *token,
10+
const char *role, ValidatorModuleResult *result)
11+
{
12+
result->authorized = strcmp(token, "node-postgres-test-token") == 0;
13+
result->authn_id = result->authorized ? pstrdup(role) : NULL;
14+
return true;
15+
}
16+
17+
static const OAuthValidatorCallbacks callbacks = {
18+
PG_OAUTH_VALIDATOR_MAGIC,
19+
.validate_cb = validate_token,
20+
};
21+
22+
const OAuthValidatorCallbacks *
23+
_PG_oauth_validator_module_init(void)
24+
{
25+
return &callbacks;
26+
}

0 commit comments

Comments
 (0)