From ada25bdd38a09c0620ed9e9eddb8964cc6291dc0 Mon Sep 17 00:00:00 2001 From: Daniele Briggi <=> Date: Mon, 31 Mar 2025 15:52:24 +0200 Subject: [PATCH 1/2] feat(token): add to config --- package-lock.json | 4 ++-- package.json | 2 +- src/drivers/types.ts | 2 ++ src/drivers/utilities.ts | 22 ++++++++++++++-------- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index b1e7e3d..ba67b6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sqlitecloud/drivers", - "version": "1.0.438", + "version": "1.0.455", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@sqlitecloud/drivers", - "version": "1.0.438", + "version": "1.0.455", "license": "MIT", "dependencies": { "buffer": "^6.0.3", diff --git a/package.json b/package.json index df49d59..2f1f20f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sqlitecloud/drivers", - "version": "1.0.438", + "version": "1.0.455", "description": "SQLiteCloud drivers for Typescript/Javascript in edge, web and node clients", "main": "./lib/index.js", "types": "./lib/index.d.ts", diff --git a/src/drivers/types.ts b/src/drivers/types.ts index 6a1c7a3..e074bcd 100644 --- a/src/drivers/types.ts +++ b/src/drivers/types.ts @@ -26,6 +26,8 @@ export interface SQLiteCloudConfig { password_hashed?: boolean /** API key can be provided instead of username and password */ apikey?: string + /** Access Token provided in place of API Key or username/password */ + token?: string /** Host name is required unless connectionstring is provided, eg: xxx.sqlitecloud.io */ host?: string diff --git a/src/drivers/utilities.ts b/src/drivers/utilities.ts index b7075ab..a20d881 100644 --- a/src/drivers/utilities.ts +++ b/src/drivers/utilities.ts @@ -2,12 +2,10 @@ // utilities.ts - utility methods to manipulate SQL statements // -import { SQLiteCloudConfig, SQLiteCloudError, SQLiteCloudDataTypes, DEFAULT_PORT, DEFAULT_TIMEOUT } from './types' -import { SQLiteCloudArrayType } from './types' +import { DEFAULT_PORT, DEFAULT_TIMEOUT, SQLiteCloudArrayType, SQLiteCloudConfig, SQLiteCloudDataTypes, SQLiteCloudError } from './types' // explicitly importing these libraries to allow cross-platform support by replacing them import { URL } from 'whatwg-url' -import { Buffer } from 'buffer' // // determining running environment, thanks to browser-or-node @@ -44,6 +42,7 @@ export function getInitializationCommands(config: SQLiteCloudConfig): string { // then we bring back linearizability unless specified otherwise let commands = 'SET CLIENT KEY NONLINEARIZABLE TO 1; ' + // TODO: include authentication via token // first user authentication, then all other commands if (config.apikey) { commands += `AUTH APIKEY ${config.apikey}; ` @@ -177,16 +176,19 @@ export function validateConfiguration(config: SQLiteCloudConfig): SQLiteCloudCon config.non_linearizable = parseBoolean(config.non_linearizable) config.insecure = parseBoolean(config.insecure) - const hasCredentials = (config.username && config.password) || config.apikey + const hasCredentials = (config.username && config.password) || config.apikey || config.token if (!config.host || !hasCredentials) { console.error('SQLiteCloudConnection.validateConfiguration - missing arguments', config) - throw new SQLiteCloudError('The user, password and host arguments or the ?apikey= must be specified.', { errorCode: 'ERR_MISSING_ARGS' }) + throw new SQLiteCloudError('The user, password and host arguments, the ?apikey= or the ?token= must be specified.', { errorCode: 'ERR_MISSING_ARGS' }) } if (!config.connectionstring) { // build connection string from configuration, values are already validated + config.connectionstring = `sqlitecloud://${config.host}:${config.port}/${config.database || ''}` if (config.apikey) { - config.connectionstring = `sqlitecloud://${config.host}:${config.port}/${config.database || ''}?apikey=${config.apikey}` + config.connectionstring += `?apikey=${config.apikey}` + } else if (config.token) { + config.connectionstring += `?token=${config.token}` } else { config.connectionstring = `sqlitecloud://${encodeURIComponent(config.username || '')}:${encodeURIComponent(config.password || '')}@${config.host}:${ config.port @@ -215,7 +217,7 @@ export function parseconnectionstring(connectionstring: string): SQLiteCloudConf // all lowecase options const options: { [key: string]: string } = {} url.searchParams.forEach((value, key) => { - options[key.toLowerCase().replace(/-/g, '_')] = value + options[key.toLowerCase().replace(/-/g, '_')] = value.trim() }) const config: SQLiteCloudConfig = { @@ -243,6 +245,10 @@ export function parseconnectionstring(connectionstring: string): SQLiteCloudConf // either you use an apikey or username and password if (config.apikey) { + if (config.token) { + console.error('SQLiteCloudConnection.parseconnectionstring - apikey and token cannot be both specified') + throw new SQLiteCloudError('apikey and token cannot be both specified') + } if (config.username || config.password) { console.warn('SQLiteCloudConnection.parseconnectionstring - apikey and username/password are both specified, using apikey') } @@ -257,7 +263,7 @@ export function parseconnectionstring(connectionstring: string): SQLiteCloudConf return config } catch (error) { - throw new SQLiteCloudError(`Invalid connection string: ${connectionstring}`) + throw new SQLiteCloudError(`Invalid connection string: ${connectionstring} - error: ${error}`) } } From 8b5cb8e15041bdda8249ef48c6fe33215c27098c Mon Sep 17 00:00:00 2001 From: Daniele Briggi <=> Date: Tue, 6 May 2025 14:38:55 +0200 Subject: [PATCH 2/2] feat(token): support auth with access token --- package-lock.json | 4 ++-- package.json | 2 +- src/drivers/utilities.ts | 27 ++++++++++++++------------- test/connection-tls.test.ts | 2 +- test/utilities.test.ts | 16 +++++++++++++++- 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index ba67b6f..a64e4a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sqlitecloud/drivers", - "version": "1.0.455", + "version": "1.0.491", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@sqlitecloud/drivers", - "version": "1.0.455", + "version": "1.0.491", "license": "MIT", "dependencies": { "buffer": "^6.0.3", diff --git a/package.json b/package.json index 2f1f20f..53c5642 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sqlitecloud/drivers", - "version": "1.0.455", + "version": "1.0.491", "description": "SQLiteCloud drivers for Typescript/Javascript in edge, web and node clients", "main": "./lib/index.js", "types": "./lib/index.d.ts", diff --git a/src/drivers/utilities.ts b/src/drivers/utilities.ts index a20d881..7ceee2f 100644 --- a/src/drivers/utilities.ts +++ b/src/drivers/utilities.ts @@ -40,46 +40,47 @@ export function anonimizeError(error: Error): Error { export function getInitializationCommands(config: SQLiteCloudConfig): string { // we check the credentials using non linearizable so we're quicker // then we bring back linearizability unless specified otherwise - let commands = 'SET CLIENT KEY NONLINEARIZABLE TO 1; ' + let commands = 'SET CLIENT KEY NONLINEARIZABLE TO 1;' - // TODO: include authentication via token // first user authentication, then all other commands if (config.apikey) { - commands += `AUTH APIKEY ${config.apikey}; ` + commands += `AUTH APIKEY ${config.apikey};` + } else if (config.token) { + commands += `AUTH TOKEN ${config.token};` } else { - commands += `AUTH USER ${config.username || ''} ${config.password_hashed ? 'HASH' : 'PASSWORD'} ${config.password || ''}; ` + commands += `AUTH USER ${config.username || ''} ${config.password_hashed ? 'HASH' : 'PASSWORD'} ${config.password || ''};` } if (config.compression) { - commands += 'SET CLIENT KEY COMPRESSION TO 1; ' + commands += 'SET CLIENT KEY COMPRESSION TO 1;' } if (config.zerotext) { - commands += 'SET CLIENT KEY ZEROTEXT TO 1; ' + commands += 'SET CLIENT KEY ZEROTEXT TO 1;' } if (config.noblob) { - commands += 'SET CLIENT KEY NOBLOB TO 1; ' + commands += 'SET CLIENT KEY NOBLOB TO 1;' } if (config.maxdata) { - commands += `SET CLIENT KEY MAXDATA TO ${config.maxdata}; ` + commands += `SET CLIENT KEY MAXDATA TO ${config.maxdata};` } if (config.maxrows) { - commands += `SET CLIENT KEY MAXROWS TO ${config.maxrows}; ` + commands += `SET CLIENT KEY MAXROWS TO ${config.maxrows};` } if (config.maxrowset) { - commands += `SET CLIENT KEY MAXROWSET TO ${config.maxrowset}; ` + commands += `SET CLIENT KEY MAXROWSET TO ${config.maxrowset};` } // we ALWAYS set non linearizable to 1 when we start so we can be quicker on login // but then we need to put it back to its default value if "linearizable" unless set if (!config.non_linearizable) { - commands += 'SET CLIENT KEY NONLINEARIZABLE TO 0; ' + commands += 'SET CLIENT KEY NONLINEARIZABLE TO 0;' } if (config.database) { if (config.create && !config.memory) { - commands += `CREATE DATABASE ${config.database} IF NOT EXISTS; ` + commands += `CREATE DATABASE ${config.database} IF NOT EXISTS;` } - commands += `USE DATABASE ${config.database}; ` + commands += `USE DATABASE ${config.database};` } return commands diff --git a/test/connection-tls.test.ts b/test/connection-tls.test.ts index 8425004..2856808 100644 --- a/test/connection-tls.test.ts +++ b/test/connection-tls.test.ts @@ -174,7 +174,7 @@ it( expect(error).toBeDefined() expect(error).toBeInstanceOf(SQLiteCloudError) const sqliteCloudError = error as SQLiteCloudError - expect(sqliteCloudError.message).toBe('The user, password and host arguments or the ?apikey= must be specified.') + expect(sqliteCloudError.message).toBe('The user, password and host arguments, the ?apikey= or the ?token= must be specified.') expect(sqliteCloudError.errorCode).toBe('ERR_MISSING_ARGS') expect(sqliteCloudError.externalErrorCode).toBeUndefined() expect(sqliteCloudError.offsetCode).toBeUndefined() diff --git a/test/utilities.test.ts b/test/utilities.test.ts index 423b3ca..2cf91cc 100644 --- a/test/utilities.test.ts +++ b/test/utilities.test.ts @@ -3,7 +3,7 @@ // import { SQLiteCloudError } from '../src/index' -import { parseconnectionstring, sanitizeSQLiteIdentifier } from '../src/drivers/utilities' +import { getInitializationCommands, parseconnectionstring, sanitizeSQLiteIdentifier } from '../src/drivers/utilities' import { getTestingDatabaseName } from './shared' import { expect, describe, it } from '@jest/globals' @@ -190,3 +190,17 @@ describe('sanitizeSQLiteIdentifier()', () => { expect(sanitized).toBe('"chinook.sql; DROP TABLE \"\"albums\"\""') }) }) + +describe('getInitializationCommands()', () => { + it('should return commands with auth token command', () => { + const config = { + token: 'mytoken', + database: 'mydb', + } + + const result = getInitializationCommands(config) + + expect(result).toContain('AUTH TOKEN mytoken;') + expect(result).not.toContain('AUTH APIKEY') + }) +}) \ No newline at end of file