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 all 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-auth/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"]
}
33 changes: 16 additions & 17 deletions examples/pglite/index.ts → examples/pglite-auth/cert.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
import { PGlite } from '@electric-sql/pglite';
import fs from 'node:fs';
import net from 'node:net';
import { PostgresConnection, hashMd5Password } from 'pg-gateway';
import { PGlite } from '@electric-sql/pglite';
import { type BackendError, PostgresConnection } from 'pg-gateway';

const db = new PGlite();

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;
tls: {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
},
auth: {
method: 'cert',
},

async onStartup() {
// Wait for PGlite to be ready before further processing
await db.waitReady;
Expand All @@ -33,10 +29,13 @@ const server = net.createServer((socket) => {

// Forward raw message to PGlite
jgoux marked this conversation as resolved.
Show resolved Hide resolved
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
54 changes: 54 additions & 0 deletions examples/pglite-auth/md5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import net from 'node:net';
import { PGlite } from '@electric-sql/pglite';
import {
type BackendError,
PostgresConnection,
createPreHashedPassword,
} from 'pg-gateway';

const db = new PGlite();

const server = net.createServer((socket) => {
const connection = new PostgresConnection(socket, {
serverVersion: '16.3 (PGlite 0.2.0)',
auth: {
method: 'md5',
getPreHashedPassword({ username }) {
return createPreHashedPassword(username, 'postgres');
},
},

async onStartup() {
// Wait for PGlite to be ready before further processing
await db.waitReady;
return false;
},
async onMessage(data, { isAuthenticated }) {
// Only forward messages to PGlite after authentication
if (!isAuthenticated) {
return false;
}

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

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

server.listen(5432, () => {
console.log('Server listening on port 5432');
});
20 changes: 20 additions & 0 deletions examples/pglite-auth/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "pglite-auth-example",
"type": "module",
"scripts": {
"format": "biome format --write .",
"lint": "biome lint --error-on-warnings .",
"type-check": "tsc --noEmit"
},
"dependencies": {
"pg-gateway": "*"
},
"devDependencies": {
"@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"
}
}
58 changes: 58 additions & 0 deletions examples/pglite-auth/password.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import net from 'node:net';
import { PGlite } from '@electric-sql/pglite';
import { type BackendError, PostgresConnection } from 'pg-gateway';

const db = new PGlite();

const server = net.createServer((socket) => {
const connection = new PostgresConnection(socket, {
serverVersion: '16.3 (PGlite 0.2.0)',
auth: {
method: 'password',
// this is the password stored in the server
getClearTextPassword(credentials) {
return 'postgres';
},
// uncomment to override the default password validation logic
// async validateCredentials(credentials) {
// const { clearTextPassword, password } = credentials;
// // we allow case insensitive password validation
// return password.toUpperCase() === clearTextPassword.toUpperCase();
// },
},

async onStartup() {
// Wait for PGlite to be ready before further processing
await db.waitReady;
return false;
},
async onMessage(data, { isAuthenticated }) {
// Only forward messages to PGlite after authentication
if (!isAuthenticated) {
return false;
}

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

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

server.listen(5432, () => {
console.log('Server listening on port 5432');
});
55 changes: 55 additions & 0 deletions examples/pglite-auth/scram-sha-256.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import net from 'node:net';
import { PGlite } from '@electric-sql/pglite';
import {
type BackendError,
PostgresConnection,
createScramSha256Data,
} from 'pg-gateway';

const db = new PGlite();

const server = net.createServer((socket) => {
const connection = new PostgresConnection(socket, {
serverVersion: '16.3 (PGlite 0.2.0)',
auth: {
method: 'scram-sha-256',
async getScramSha256Data(credentials) {
// Utility function to generate scram-sha-256 data (like salt) for the given user
// You would likely store this info in a database and retrieve it here
return createScramSha256Data('postgres');
jgoux marked this conversation as resolved.
Show resolved Hide resolved
},
},
async onStartup() {
// Wait for PGlite to be ready before further processing
await db.waitReady;
return false;
},
async onMessage(data, { isAuthenticated }) {
// Only forward messages to PGlite after authentication
if (!isAuthenticated) {
return false;
}

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

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

server.listen(5432, () => {
console.log('Server listening on port 5432');
});
47 changes: 47 additions & 0 deletions examples/pglite-auth/trust.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import net from 'node:net';
import { PGlite } from '@electric-sql/pglite';
import { type BackendError, PostgresConnection } from 'pg-gateway';

const db = new PGlite();

const server = net.createServer((socket) => {
const connection = new PostgresConnection(socket, {
serverVersion: '16.3 (PGlite 0.2.0)',
auth: {
method: 'trust',
},

async onStartup() {
// Wait for PGlite to be ready before further processing
await db.waitReady;
return false;
},
async onMessage(data, { isAuthenticated }) {
// Only forward messages to PGlite after authentication
if (!isAuthenticated) {
return false;
}

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

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

server.listen(5432, () => {
console.log('Server listening on port 5432');
});
4 changes: 4 additions & 0 deletions examples/pglite-auth/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": ["*.ts"]
}
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"]
}
Loading