Skip to content

Commit 9f64448

Browse files
committed
feat(driver): support unauthenticated connections
1 parent 614e739 commit 9f64448

5 files changed

Lines changed: 84 additions & 11 deletions

File tree

src/drivers/connection-tls.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ export class SQLiteCloudTlsConnection extends SQLiteCloudConnection {
9393
if (this.config.verbose) {
9494
console.debug(`SQLiteCloudTlsConnection - connected to ${this.config.host}, authorized: ${this.socket?.authorized}`)
9595
}
96+
if (!initializationCommands) {
97+
if (this.config.verbose) {
98+
console.debug(`SQLiteCloudTlsConnection - initialized connection`)
99+
}
100+
callback?.call(this, null)
101+
return
102+
}
96103
this.transportCommands(initializationCommands, error => {
97104
if (this.config.verbose) {
98105
console.debug(`SQLiteCloudTlsConnection - initialized connection`)

src/drivers/connection-ws.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,11 @@ export class SQLiteCloudWebsocketConnection extends SQLiteCloudConnection {
9999
// (→ crvheg7dhk.g4.gateway.sqlite.cloud). For local development, pass a `gatewayurl`
100100
// containing `localhost` — the driver routes TCP to it and injects the tenant Host header
101101
// (with the gateway marker label) so the gateway still tenant-routes correctly.
102-
const authToken = this.config.apikey || this.config.token
103-
const ioOpts: Record<string, unknown> = { auth: { token: authToken }, parser: createSocketIOParser(websocketMaxAttachments) }
102+
const authToken = this.config.unauthenticated ? undefined : this.config.apikey || this.config.token
103+
const ioOpts: Record<string, unknown> = { parser: createSocketIOParser(websocketMaxAttachments) }
104+
if (authToken) {
105+
ioOpts.auth = { token: authToken }
106+
}
104107
let gatewayUrl: string
105108
if (this.config.gatewayurl?.includes('localhost')) {
106109
const raw = this.config.gatewayurl

src/drivers/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ export interface SQLiteCloudConfig {
5858
apikey?: string
5959
/** Access Token provided in place of API Key or username/password */
6060
token?: string
61+
/** Connect without sending AUTH. Only commands explicitly allowed pre-auth can run. */
62+
unauthenticated?: boolean
6163

6264
/** Host name is required unless connectionstring is provided, eg: xxx.sqlitecloud.io */
6365
host?: string

src/drivers/utilities.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,14 @@ export function getInitializationCommands(config: SQLiteCloudConfig): string {
6161
let commands = 'SET CLIENT KEY NONLINEARIZABLE TO 1;'
6262

6363
// first user authentication, then all other commands
64-
if (config.apikey) {
65-
commands += `AUTH APIKEY ${config.apikey};`
66-
} else if (config.token) {
67-
commands += `AUTH TOKEN ${config.token};`
68-
} else {
69-
commands += `AUTH USER ${config.username || ''} ${config.password_hashed ? 'HASH' : 'PASSWORD'} ${config.password || ''};`
64+
if (!config.unauthenticated) {
65+
if (config.apikey) {
66+
commands += `AUTH APIKEY ${config.apikey};`
67+
} else if (config.token) {
68+
commands += `AUTH TOKEN ${config.token};`
69+
} else {
70+
commands += `AUTH USER ${config.username || ''} ${config.password_hashed ? 'HASH' : 'PASSWORD'} ${config.password || ''};`
71+
}
7072
}
7173

7274
if (config.compression) {
@@ -94,7 +96,7 @@ export function getInitializationCommands(config: SQLiteCloudConfig): string {
9496
commands += 'SET CLIENT KEY NONLINEARIZABLE TO 0;'
9597
}
9698

97-
if (config.database) {
99+
if (!config.unauthenticated && config.database) {
98100
if (config.create && !config.memory) {
99101
commands += `CREATE DATABASE ${config.database} IF NOT EXISTS;`
100102
}
@@ -198,14 +200,15 @@ export function validateConfiguration(config: SQLiteCloudConfig): SQLiteCloudCon
198200
config.create = parseBoolean(config.create)
199201
config.non_linearizable = parseBoolean(config.non_linearizable)
200202
config.insecure = parseBoolean(config.insecure)
203+
config.unauthenticated = parseBoolean(config.unauthenticated)
201204

202205
const hasCredentials = (config.username && config.password) || config.apikey || config.token
203-
if (!config.host || !hasCredentials) {
206+
if (!config.host || (!config.unauthenticated && !hasCredentials)) {
204207
console.error('SQLiteCloudConnection.validateConfiguration - missing arguments', config)
205208
throw new SQLiteCloudError('The user, password and host arguments, the ?apikey= or the ?token= must be specified.', { errorCode: 'ERR_MISSING_ARGS' })
206209
}
207210

208-
if (!config.connectionstring) {
211+
if (!config.connectionstring && !config.unauthenticated) {
209212
// build connection string from configuration, values are already validated
210213
config.connectionstring = `sqlitecloud://${config.host}:${config.port}/${config.database || ''}`
211214
if (config.apikey) {
@@ -258,6 +261,7 @@ export function parseconnectionstring(connectionstring: string): SQLiteCloudConf
258261
memory: options.memory ? parseBoolean(options.memory) : undefined,
259262
compression: options.compression ? parseBoolean(options.compression) : undefined,
260263
non_linearizable: options.non_linearizable ? parseBoolean(options.non_linearizable) : undefined,
264+
unauthenticated: options.unauthenticated ? parseBoolean(options.unauthenticated) : undefined,
261265
noblob: options.noblob ? parseBoolean(options.noblob) : undefined,
262266
maxdata: options.maxdata ? parseInt(options.maxdata) : undefined,
263267
maxrows: options.maxrows ? parseInt(options.maxrows) : undefined,

test/utilities.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,26 @@ describe('validateConfiguration()', () => {
263263
expect(config.websocketBlobFormat).toBe('base64-blobs-v1')
264264
expect(config.websocketMaxAttachments).toBe(100000)
265265
})
266+
267+
it('should allow unauthenticated configuration without credentials', () => {
268+
const config = validateConfiguration({
269+
host: 'host',
270+
unauthenticated: true
271+
})
272+
273+
expect(config.host).toBe('host')
274+
expect(config.unauthenticated).toBe(true)
275+
expect(config.connectionstring).toBeUndefined()
276+
})
277+
278+
it('should parse unauthenticated from connection string params', () => {
279+
const config = validateConfiguration({
280+
connectionstring: 'sqlitecloud://host:1234?unauthenticated=1'
281+
})
282+
283+
expect(config.host).toBe('host')
284+
expect(config.unauthenticated).toBe(true)
285+
})
266286
})
267287

268288
describe('safe integer marker utilities', () => {
@@ -326,4 +346,41 @@ describe('getInitializationCommands()', () => {
326346
expect(result).toContain('AUTH TOKEN mytoken;')
327347
expect(result).not.toContain('AUTH APIKEY')
328348
})
349+
350+
it('should keep existing authenticated initialization behavior', () => {
351+
const config = {
352+
username: 'admin',
353+
password: 'secret',
354+
database: 'mydb',
355+
compression: true,
356+
non_linearizable: true
357+
}
358+
359+
const result = getInitializationCommands(config)
360+
361+
expect(result).toBe('SET CLIENT KEY NONLINEARIZABLE TO 1;AUTH USER admin PASSWORD secret;SET CLIENT KEY COMPRESSION TO 1;USE DATABASE mydb;')
362+
})
363+
364+
it('should omit auth and database commands for unauthenticated initialization', () => {
365+
const config = {
366+
host: 'host',
367+
database: 'mydb',
368+
unauthenticated: true,
369+
compression: true,
370+
non_linearizable: true,
371+
noblob: true,
372+
maxdata: 128,
373+
maxrows: 256,
374+
maxrowset: 512
375+
}
376+
377+
const result = getInitializationCommands(config)
378+
379+
expect(result).toBe(
380+
'SET CLIENT KEY NONLINEARIZABLE TO 1;SET CLIENT KEY COMPRESSION TO 1;SET CLIENT KEY NOBLOB TO 1;SET CLIENT KEY MAXDATA TO 128;SET CLIENT KEY MAXROWS TO 256;SET CLIENT KEY MAXROWSET TO 512;'
381+
)
382+
expect(result).not.toContain('AUTH ')
383+
expect(result).not.toContain('USE DATABASE')
384+
expect(result).not.toContain('CREATE DATABASE')
385+
})
329386
})

0 commit comments

Comments
 (0)