Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(auth): SASL SCRAM-SHA-256 support #1

Merged
merged 43 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
d9bc422
init
jgoux Aug 6, 2024
e5e5418
simplify
jgoux Aug 6, 2024
81d13bd
fix salt
jgoux Aug 6, 2024
8a6a596
fix sendAuthentictionSASL
jgoux Aug 6, 2024
540639a
getting closer
jgoux Aug 6, 2024
bcfc264
read clientFinalMessage correctly
jgoux Aug 6, 2024
06b3796
it works!
jgoux Aug 6, 2024
ebaf371
refactor
jgoux Aug 6, 2024
d289bf0
use biome
jgoux Aug 6, 2024
1e3d59e
lock
jgoux Aug 6, 2024
159939b
call validateCredentials for the certificate auth mode
jgoux Aug 6, 2024
ddbac77
delete extra types
jgoux Aug 6, 2024
0a7cb3c
use supabase styles
jgoux Aug 7, 2024
60df052
align sasl workflow with pg server
jgoux Aug 7, 2024
3d0425e
use biome and tsconfig everywhere
jgoux Aug 7, 2024
16a2c26
fix types
jgoux Aug 7, 2024
61a6486
refactor incoming
jgoux Aug 7, 2024
1bc57ca
big refactor
jgoux Aug 7, 2024
fdf0e34
remove commented code
jgoux Aug 7, 2024
46bc054
rename types
jgoux Aug 7, 2024
bdc951a
remove util
jgoux Aug 7, 2024
30ef55d
rename type
jgoux Aug 7, 2024
5a01051
isolate buffer logic
jgoux Aug 7, 2024
4dfdcab
isolate TLS
jgoux Aug 7, 2024
1a641df
address comments
jgoux Aug 8, 2024
322451e
export BackendError
jgoux Aug 8, 2024
25764a5
isolate all the auth flow
jgoux Aug 8, 2024
f488d5d
add all the auth examples
jgoux Aug 8, 2024
2e66022
put regular pg port back
jgoux Aug 8, 2024
64da415
remove logs
jgoux Aug 8, 2024
b8c8b37
harmonize how the password is provided
jgoux Aug 8, 2024
ff64552
memoize for scram
jgoux Aug 8, 2024
3f428aa
fix cert flow
jgoux Aug 8, 2024
2ec114b
pass ConnectionState in all callbacks
jgoux Aug 8, 2024
9a301a3
Update examples/pglite-auth/package.json
jgoux Aug 8, 2024
0fe04a0
Update examples/pglite-auth/cert.ts
jgoux Aug 8, 2024
39a77ee
apply comment
jgoux Aug 8, 2024
716c1d9
apply password naming change
jgoux Aug 8, 2024
8a2554d
Update examples/pglite-auth/scram-sha-256.ts
jgoux Aug 8, 2024
be9649d
Update examples/pglite-auth/scram-sha-256.ts
jgoux Aug 8, 2024
e1d19ff
Update examples/pglite-auth/trust.ts
jgoux Aug 8, 2024
7a0bb68
remove the possibility to pass a salt
jgoux Aug 8, 2024
82a1310
pause/resume socket
jgoux Aug 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist/
dbs/
*.pem
*.srl
tsconfig.tsbuildinfo
22 changes: 22 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
},
"javascript": {
"formatter": {
"quoteStyle": "single"
}
}
}
4 changes: 4 additions & 0 deletions examples/pglite-multiple/biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"extends": ["../../biome.json"]
}
31 changes: 17 additions & 14 deletions examples/pglite-multiple/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { PGlite, PGliteInterface } from '@electric-sql/pglite';
import { mkdir, readFile } from 'node:fs/promises';
import net from 'node:net';
import { PGlite, type PGliteInterface } from '@electric-sql/pglite';
import {
type BackendError,
PostgresConnection,
TlsOptionsCallback,
type TlsOptionsCallback,
hashMd5Password,
} from 'pg-gateway';

Expand All @@ -29,26 +30,25 @@ const server = net.createServer((socket) => {

const connection = new PostgresConnection(socket, {
serverVersion: '16.3 (PGlite 0.2.0)',
authMode: 'md5Password',
tls,
async validateCredentials(credentials) {
if (credentials.authMode === 'md5Password') {
auth: {
method: 'md5',
async validateCredentials(credentials) {
const { hash, salt } = credentials;
const expectedHash = await hashMd5Password(
'postgres',
'postgres',
salt
salt,
);
return hash === expectedHash;
}
return false;
},
},
tls,
async onTlsUpgrade({ tlsInfo }) {
if (!tlsInfo) {
connection.sendError({
severity: 'FATAL',
code: '08000',
message: `ssl connection required`,
message: 'ssl connection required',
});
connection.socket.end();
return;
Expand All @@ -58,7 +58,7 @@ const server = net.createServer((socket) => {
connection.sendError({
severity: 'FATAL',
code: '08000',
message: `ssl sni extension required`,
message: 'ssl sni extension required',
});
connection.socket.end();
return;
Expand All @@ -81,10 +81,13 @@ const server = net.createServer((socket) => {

// Forward raw message to PGlite
try {
const [[_, responseData]] = await db.execProtocol(data);
connection.sendData(responseData);
const [result] = await db.execProtocol(data);
if (result) {
const [_, responseData] = result;
connection.sendData(responseData);
}
} catch (err) {
connection.sendError(err);
connection.sendError(err as BackendError);
connection.sendReadyForQuery();
}
return true;
Expand Down
12 changes: 9 additions & 3 deletions examples/pglite-multiple/package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
{
"name": "pglite-multiple-example",
"type": "module",
"scripts": {
"dev": "tsx index.ts"
"dev": "tsx index.ts",
"format": "biome format --write .",
"lint": "biome lint --error-on-warnings .",
"type-check": "tsc --noEmit"
},
"dependencies": {
"pg-gateway": "*"
},
"devDependencies": {
"@electric-sql/pglite": "npm:@gregnr/pglite@0.2.0-dev.8",
"@biomejs/biome": "1.8.3",
"@electric-sql/pglite": "0.2.0-alpha.7",
"@total-typescript/tsconfig": "^1.0.4",
"@types/node": "^20.14.11",
"tsx": "^4.16.2",
"typescript": "^5.5.3"
}
}
}
4 changes: 4 additions & 0 deletions examples/pglite-multiple/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@total-typescript/tsconfig/tsc/no-dom/app",
"include": ["index.ts"]
}
4 changes: 4 additions & 0 deletions examples/pglite/biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"extends": ["../../biome.json"]
}
45 changes: 26 additions & 19 deletions examples/pglite/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { PGlite } from '@electric-sql/pglite';
import net from 'node:net';
import { PostgresConnection, hashMd5Password } from 'pg-gateway';
import { PGlite } from '@electric-sql/pglite';
import {
type BackendError,
PostgresConnection,
type ScramSha256Data,
createScramSha256Data,
} from 'pg-gateway';

const db = new PGlite();

let data: ScramSha256Data | undefined;

const server = net.createServer((socket) => {
const connection = new PostgresConnection(socket, {
serverVersion: '16.3 (PGlite 0.2.0)',
authMode: 'md5Password',
async validateCredentials(credentials) {
if (credentials.authMode === 'md5Password') {
const { hash, salt } = credentials;
const expectedHash = await hashMd5Password(
'postgres',
'postgres',
salt
);
return hash === expectedHash;
}
return false;
auth: {
method: 'scram-sha-256',
async getScramSha256Data({ username }) {
if (!data) {
// helper function to create the metadata for SASL auth
data = createScramSha256Data('postgres');
}
return data;
},
},
async onStartup() {
// Wait for PGlite to be ready before further processing
Expand All @@ -33,21 +37,24 @@ const server = net.createServer((socket) => {

// Forward raw message to PGlite
try {
const [[_, responseData]] = await db.execProtocol(data);
connection.sendData(responseData);
const [result] = await db.execProtocol(data);
if (result) {
const [_, responseData] = result;
connection.sendData(responseData);
}
} catch (err) {
connection.sendError(err);
connection.sendError(err as BackendError);
connection.sendReadyForQuery();
}
return true;
},
});

socket.on('end', () => {
socket.on('close', () => {
console.log('Client disconnected');
});
});

server.listen(5432, () => {
server.listen(2345, () => {
console.log('Server listening on port 5432');
});
12 changes: 9 additions & 3 deletions examples/pglite/package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
{
"name": "pglite-example",
"type": "module",
"scripts": {
"dev": "tsx index.ts"
"dev": "tsx index.ts",
"format": "biome format --write .",
"lint": "biome lint --error-on-warnings .",
"type-check": "tsc --noEmit"
},
"dependencies": {
"pg-gateway": "*"
},
"devDependencies": {
"@electric-sql/pglite": "npm:@gregnr/pglite@0.2.0-dev.8",
"@biomejs/biome": "1.8.3",
"@electric-sql/pglite": "0.2.0-alpha.7",
"@total-typescript/tsconfig": "^1.0.4",
"@types/node": "^20.14.11",
"tsx": "^4.16.2",
"typescript": "^5.5.3"
}
}
}
4 changes: 4 additions & 0 deletions examples/pglite/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@total-typescript/tsconfig/tsc/no-dom/app",
"include": ["index.ts"]
}
4 changes: 4 additions & 0 deletions examples/reverse-proxy-sni/biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"extends": ["../../biome.json"]
}
18 changes: 14 additions & 4 deletions examples/reverse-proxy-sni/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { readFile } from 'node:fs/promises';
import net, { connect, Socket } from 'node:net';
import { PostgresConnection, TlsOptionsCallback } from '../../src';
import net, { connect } from 'node:net';
import { PostgresConnection, type TlsOptionsCallback } from 'pg-gateway';

const tls: TlsOptionsCallback = async ({ sniServerName }) => {
// Optionally serve different certs based on `sniServerName`
Expand Down Expand Up @@ -31,7 +31,7 @@ const server = net.createServer((socket) => {
connection.sendError({
severity: 'FATAL',
code: '08000',
message: `ssl connection required`,
message: 'ssl connection required',
});
connection.socket.end();
return;
Expand All @@ -41,7 +41,7 @@ const server = net.createServer((socket) => {
connection.sendError({
severity: 'FATAL',
code: '08000',
message: `ssl sni extension required`,
message: 'ssl sni extension required',
});
connection.socket.end();
return;
Expand All @@ -51,6 +51,16 @@ const server = net.createServer((socket) => {
// ie. 12345.db.example.com -> 12345
const [serverId] = tlsInfo.sniServerName.split('.');

if (!serverId) {
connection.sendError({
severity: 'FATAL',
code: '08000',
message: 'server id required',
});
connection.socket.end();
return;
}

// Lookup the server host/port based on ID
const serverInfo = await getServerById(serverId);

Expand Down
10 changes: 8 additions & 2 deletions examples/reverse-proxy-sni/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
{
"name": "reverse-proxy-sni-example",
"type": "module",
"scripts": {
"dev": "tsx index.ts"
"dev": "tsx index.ts",
"format": "biome format --write .",
"lint": "biome lint --error-on-warnings .",
"type-check": "tsc --noEmit"
},
"dependencies": {
"pg-gateway": "*"
},
"devDependencies": {
"@biomejs/biome": "1.8.3",
"@total-typescript/tsconfig": "^1.0.4",
"@types/node": "^20.14.11",
"tsx": "^4.16.2",
"typescript": "^5.5.3"
}
}
}
4 changes: 4 additions & 0 deletions examples/reverse-proxy-sni/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@total-typescript/tsconfig/tsc/no-dom/app",
"include": ["index.ts"]
}
Loading