@abapify/adt-auth 0.4.43 → 0.4.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/env-BQ8Uy_8m.d.mts.map +1 -1
- package/dist/index.mjs +5 -2
- package/dist/index.mjs.map +1 -1
- package/dist/utils/env.mjs +1 -0
- package/dist/utils/env.mjs.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-BQ8Uy_8m.d.mts","names":[],"sources":["../src/storage/file-storage.ts","../src/auth-manager.ts","../src/types/service-key.ts","../src/utils/env.ts"],"mappings":";cAuBa;mBACM;mBACA;mBACA;EAEL,YAAA;;;;EAcZ,KAAK,SAAS;;;;EASd,KAAK,cAAc;;;;EAoBnB,OAAO;;;;EAUP;;;;EAaA,OAAO;;;;EAWP,cAAc;;;;EASd;;;;EAQA;UAUQ;
|
|
1
|
+
{"version":3,"file":"env-BQ8Uy_8m.d.mts","names":[],"sources":["../src/storage/file-storage.ts","../src/auth-manager.ts","../src/types/service-key.ts","../src/utils/env.ts"],"mappings":";cAuBa;mBACM;mBACA;mBACA;EAEL,YAAA;;;;EAcZ,KAAK,SAAS;;;;EASd,KAAK,cAAc;;;;EAoBnB,OAAO;;;;EAUP;;;;EAaA,OAAO;;;;EAWP,cAAc;;;;EASd;;;;EAQA;UAUQ;UAUA;UAMA;UAWA;;;;;UC1IO;;EAEf;;EAEA,SAAS;;cAGE;mBACM;EAEL,YAAA,UAAU;;;;;;;;;EAgBhB,MAAM,aAAa,aAAa,cAAc,QAAQ;;;;UAgCpD;;;;EA0CR,YAAY,SAAS;;;;EAOrB,WAAW,eAAe;;;;EAW1B,cAAc;;;;EAOd;;;;EAWA,cAAc;;;;EAOd;;;;EAOA;;;;EAWA,UAAU,SAAS;;;;;;EAab,mBACJ,SAAS,aACT;IAAY,OAAO;MAClB,QAAQ;;;;EAyFX,gBAAgB,SAAS;;;;EAWzB,aACE,SAAS;IACN;IAAkB;;;UC1SR;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;EACA;;UAGe;EACf;EACA;EACA;EACA;;UAGe;EACf,KAAK;EACL;EACA;EACA;EACA,WAAW;EACX,UAAU,eAAe;EACzB,SAAS;EACT;;UAYe;EACf;EACA;EACA;;EAEA,eAAe,gBAAgB;;EAE/B;;EAEA;;EAEA;GACC;;;;;;;;;;iBCrDa,eAAe,mBAAmB;;;;;;;;;;;iBAmClC,4BAA4B"}
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import authPlugin from "./plugins/basic.mjs";
|
|
|
2
2
|
import { t as authPlugin$1 } from "./service-key-C11sqpQ1.mjs";
|
|
3
3
|
import { readServiceKey, resolveServiceKeyFromEnv } from "./utils/env.mjs";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
|
-
import { dirname, join } from "node:path";
|
|
5
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
|
|
8
8
|
// src/storage/file-storage.ts
|
|
@@ -86,7 +86,10 @@ var FileStorage = class {
|
|
|
86
86
|
this.saveConfig(config);
|
|
87
87
|
}
|
|
88
88
|
getSessionPath(sid) {
|
|
89
|
-
|
|
89
|
+
const storageDir = resolve(this.storageDir);
|
|
90
|
+
const filePath = resolve(storageDir, `${sid}.json`);
|
|
91
|
+
if (!filePath.startsWith(storageDir + sep)) throw new Error(`Unsafe session id: ${sid}`);
|
|
92
|
+
return filePath;
|
|
90
93
|
}
|
|
91
94
|
ensureStorageDir() {
|
|
92
95
|
if (!existsSync(this.storageDir)) mkdirSync(this.storageDir, {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/storage/file-storage.ts","../src/auth-manager.ts"],"sourcesContent":["/**\n * File-based session storage\n *\n * Stores auth sessions in ~/.adt/sessions/\n * Stores config in ~/.adt/config.json\n */\n\nimport {\n readFileSync,\n writeFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n unlinkSync,\n} from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { AuthSession } from '../types';\n\ninterface Config {\n defaultSid?: string;\n}\n\nexport class FileStorage {\n private readonly baseDir: string;\n private readonly storageDir: string;\n private readonly configPath: string;\n\n constructor(baseDir?: string) {\n this.baseDir = baseDir || join(homedir(), '.adt');\n this.storageDir = join(this.baseDir, 'sessions');\n this.configPath = join(this.baseDir, 'config.json');\n this.ensureStorageDir();\n }\n\n // ===========================================================================\n // Session Storage\n // ===========================================================================\n\n /**\n * Save session to file\n */\n save(session: AuthSession): void {\n const filePath = this.getSessionPath(session.sid);\n const data = JSON.stringify(session, null, 2);\n writeFileSync(filePath, data, { mode: 0o600 }); // Owner read/write only\n }\n\n /**\n * Load session from file\n */\n load(sid: string): AuthSession | null {\n const filePath = this.getSessionPath(sid);\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const data = readFileSync(filePath, 'utf8');\n return JSON.parse(data) as AuthSession;\n } catch (error) {\n throw new Error(\n `Failed to load session ${sid}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n /**\n * Delete session file\n */\n delete(sid: string): void {\n const filePath = this.getSessionPath(sid);\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n }\n }\n\n /**\n * List all available SIDs\n */\n list(): string[] {\n if (!existsSync(this.storageDir)) {\n return [];\n }\n\n return readdirSync(this.storageDir)\n .filter((file) => file.endsWith('.json'))\n .map((file) => file.replace('.json', ''));\n }\n\n /**\n * Check if session exists\n */\n exists(sid: string): boolean {\n return existsSync(this.getSessionPath(sid));\n }\n\n // ===========================================================================\n // Default SID Management\n // ===========================================================================\n\n /**\n * Set default SID\n */\n setDefaultSid(sid: string): void {\n const config = this.loadConfig();\n config.defaultSid = sid;\n this.saveConfig(config);\n }\n\n /**\n * Get default SID\n */\n getDefaultSid(): string | null {\n const config = this.loadConfig();\n return config.defaultSid ?? null;\n }\n\n /**\n * Clear default SID\n */\n clearDefaultSid(): void {\n const config = this.loadConfig();\n delete config.defaultSid;\n this.saveConfig(config);\n }\n\n // ===========================================================================\n // Private Helpers\n // ===========================================================================\n\n private getSessionPath(sid: string): string {\n return join(this.storageDir, `${sid}.json`);\n }\n\n private ensureStorageDir(): void {\n if (!existsSync(this.storageDir)) {\n mkdirSync(this.storageDir, { recursive: true, mode: 0o700 }); // Owner only\n }\n }\n\n private loadConfig(): Config {\n if (!existsSync(this.configPath)) {\n return {};\n }\n try {\n return JSON.parse(readFileSync(this.configPath, 'utf8'));\n } catch {\n return {};\n }\n }\n\n private saveConfig(config: Config): void {\n mkdirSync(dirname(this.configPath), { recursive: true });\n writeFileSync(this.configPath, JSON.stringify(config, null, 2), {\n mode: 0o600,\n });\n }\n}\n","/**\n * Authentication Manager\n *\n * Single source of truth for authentication across all consumers:\n * - CLI\n * - MCP Server\n * - Other tools\n */\n\nimport type {\n AuthSession,\n CookieCredentials,\n AuthPlugin,\n AuthPluginOptions,\n AuthPluginResult,\n} from './types';\nimport { FileStorage } from './storage/file-storage';\n\n/**\n * Destination configuration (from adt.config.ts)\n */\nexport interface Destination {\n /** Plugin package to use (e.g., '@abapify/adt-puppeteer', '@abapify/adt-auth/basic') */\n type: string;\n /** Plugin-specific options */\n options: AuthPluginOptions;\n}\n\nexport class AuthManager {\n private readonly storage: FileStorage;\n\n constructor(storage?: FileStorage) {\n this.storage = storage || new FileStorage();\n }\n\n // ===========================================================================\n // Login - Main entry point\n // ===========================================================================\n\n /**\n * Login using a destination configuration\n *\n * Dynamically loads the plugin specified in destination.type and authenticates.\n *\n * @param sid - System ID (e.g., 'TRL')\n * @param destination - Destination config with plugin type and options\n */\n async login(sid: string, destination: Destination): Promise<AuthSession> {\n // Dynamic import of the auth plugin\n const pluginModule = (await import(destination.type)) as {\n default?: AuthPlugin;\n };\n\n if (!pluginModule.default?.authenticate) {\n throw new Error(\n `Plugin ${destination.type} does not have a default export with authenticate method`,\n );\n }\n\n // Authenticate using the plugin\n const result = await pluginModule.default.authenticate(destination.options);\n\n // Build session based on result type\n const session = this.buildSession(sid, destination, result);\n\n // Save session\n this.saveSession(session);\n\n // Set as default if first system\n if (this.listSids().length === 1) {\n this.setDefaultSid(sid);\n }\n\n return session;\n }\n\n /**\n * Build session from plugin result\n */\n private buildSession(\n sid: string,\n destination: Destination,\n result: AuthPluginResult,\n ): AuthSession {\n if (result.method === 'cookie') {\n return {\n sid: sid.toUpperCase(),\n host: destination.options.url,\n client: destination.options.client,\n auth: {\n method: 'cookie',\n plugin: destination.type,\n pluginOptions: destination.options, // Store full plugin options for refresh fallback\n credentials: {\n cookies: result.credentials.cookies,\n expiresAt: result.credentials.expiresAt.toISOString(),\n },\n },\n };\n } else {\n return {\n sid: sid.toUpperCase(),\n host: destination.options.url,\n client: destination.options.client,\n auth: {\n method: 'basic',\n plugin: destination.type,\n pluginOptions: destination.options, // Store full plugin options for consistency\n credentials: result.credentials,\n },\n };\n }\n }\n\n // ===========================================================================\n // Session Management\n // ===========================================================================\n\n /**\n * Save a session (after successful authentication)\n */\n saveSession(session: AuthSession): void {\n this.storage.save(session);\n }\n\n /**\n * Get session by SID\n */\n getSession(sid?: string): AuthSession | null {\n const targetSid = sid || this.getDefaultSid();\n if (!targetSid) {\n return null;\n }\n return this.storage.load(targetSid);\n }\n\n /**\n * Delete session (logout)\n */\n deleteSession(sid: string): void {\n this.storage.delete(sid);\n }\n\n /**\n * List all available SIDs\n */\n listSids(): string[] {\n return this.storage.list();\n }\n\n // ===========================================================================\n // Default SID Management\n // ===========================================================================\n\n /**\n * Set default SID (persisted to config)\n */\n setDefaultSid(sid: string): void {\n this.storage.setDefaultSid(sid);\n }\n\n /**\n * Get default SID\n */\n getDefaultSid(): string | null {\n return this.storage.getDefaultSid() || (this.storage.list()[0] ?? null);\n }\n\n /**\n * Clear default SID\n */\n clearDefaultSid(): void {\n this.storage.clearDefaultSid();\n }\n\n // ===========================================================================\n // Credential Helpers\n // ===========================================================================\n\n /**\n * Check if session credentials are expired\n */\n isExpired(session: AuthSession): boolean {\n if (session.auth.method !== 'cookie') {\n return false; // Basic auth doesn't expire\n }\n const creds = session.auth.credentials as CookieCredentials;\n return new Date(creds.expiresAt) < new Date();\n }\n\n /**\n * Refresh credentials using the auth plugin\n *\n * @returns Updated session with new credentials, or null if refresh not supported/failed\n */\n async refreshCredentials(\n session: AuthSession,\n options?: { log?: (message: string) => void },\n ): Promise<AuthSession | null> {\n const log = options?.log ?? console.log;\n\n if (!session.auth.plugin) {\n return null; // No plugin = can't refresh\n }\n\n // Dynamic import of the auth plugin (expects default export)\n const pluginModule = (await import(session.auth.plugin)) as {\n default?: AuthPlugin;\n };\n\n if (!pluginModule.default) {\n throw new Error(\n `Plugin ${session.auth.plugin} does not have a default export`,\n );\n }\n\n const plugin = pluginModule.default;\n\n // Try plugin's refresh method first (preferred for session-based auth)\n if (plugin.refresh) {\n const sessionWithLog: AuthSession = {\n ...session,\n auth: {\n ...session.auth,\n pluginOptions: {\n url: session.host,\n client: session.client,\n ...session.auth.pluginOptions,\n log,\n },\n },\n };\n const result = await plugin.refresh(sessionWithLog);\n\n if (result) {\n // Refresh succeeded - build and save updated session\n // Preserve original plugin options (including userDataDir)\n const destination: Destination = {\n type: session.auth.plugin,\n options: session.auth.pluginOptions || {\n url: session.host,\n client: session.client,\n },\n };\n\n const updatedSession = this.buildSession(\n session.sid,\n destination,\n result,\n );\n this.saveSession(updatedSession);\n\n return updatedSession;\n }\n\n // Refresh failed - fall through to interactive authenticate\n log(\n '⚠️ Silent refresh failed - falling back to interactive authentication...',\n );\n }\n\n // Fallback: Call authenticate (full re-auth with browser interaction)\n // Use stored plugin options to preserve settings like userDataDir\n const baseOptions: AuthPluginOptions = session.auth.pluginOptions || {\n url: session.host,\n client: session.client,\n };\n const runtimeOptions = { ...baseOptions, log };\n\n const result = await plugin.authenticate(runtimeOptions);\n\n // Build destination for buildSession\n const destination: Destination = {\n type: session.auth.plugin,\n options: baseOptions,\n };\n\n // Build and save updated session\n const updatedSession = this.buildSession(session.sid, destination, result);\n this.saveSession(updatedSession);\n\n return updatedSession;\n }\n\n /**\n * Get cookie header for HTTP client (convenience method)\n */\n getCookieHeader(session: AuthSession): string | null {\n if (session.auth.method !== 'cookie') {\n return null;\n }\n const creds = session.auth.credentials as CookieCredentials;\n return creds.cookies;\n }\n\n /**\n * Get basic auth credentials (convenience method)\n */\n getBasicAuth(\n session: AuthSession,\n ): { username: string; password: string } | null {\n if (session.auth.method !== 'basic') {\n return null;\n }\n return session.auth.credentials as { username: string; password: string };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/storage/file-storage.ts","../src/auth-manager.ts"],"sourcesContent":["/**\n * File-based session storage\n *\n * Stores auth sessions in ~/.adt/sessions/\n * Stores config in ~/.adt/config.json\n */\n\nimport {\n readFileSync,\n writeFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n unlinkSync,\n} from 'node:fs';\nimport { join, dirname, resolve, sep } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { AuthSession } from '../types';\n\ninterface Config {\n defaultSid?: string;\n}\n\nexport class FileStorage {\n private readonly baseDir: string;\n private readonly storageDir: string;\n private readonly configPath: string;\n\n constructor(baseDir?: string) {\n this.baseDir = baseDir || join(homedir(), '.adt');\n this.storageDir = join(this.baseDir, 'sessions');\n this.configPath = join(this.baseDir, 'config.json');\n this.ensureStorageDir();\n }\n\n // ===========================================================================\n // Session Storage\n // ===========================================================================\n\n /**\n * Save session to file\n */\n save(session: AuthSession): void {\n const filePath = this.getSessionPath(session.sid);\n const data = JSON.stringify(session, null, 2);\n writeFileSync(filePath, data, { mode: 0o600 }); // Owner read/write only\n }\n\n /**\n * Load session from file\n */\n load(sid: string): AuthSession | null {\n const filePath = this.getSessionPath(sid);\n if (!existsSync(filePath)) {\n return null;\n }\n\n try {\n const data = readFileSync(filePath, 'utf8');\n return JSON.parse(data) as AuthSession;\n } catch (error) {\n throw new Error(\n `Failed to load session ${sid}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n }\n\n /**\n * Delete session file\n */\n delete(sid: string): void {\n const filePath = this.getSessionPath(sid);\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n }\n }\n\n /**\n * List all available SIDs\n */\n list(): string[] {\n if (!existsSync(this.storageDir)) {\n return [];\n }\n\n return readdirSync(this.storageDir)\n .filter((file) => file.endsWith('.json'))\n .map((file) => file.replace('.json', ''));\n }\n\n /**\n * Check if session exists\n */\n exists(sid: string): boolean {\n return existsSync(this.getSessionPath(sid));\n }\n\n // ===========================================================================\n // Default SID Management\n // ===========================================================================\n\n /**\n * Set default SID\n */\n setDefaultSid(sid: string): void {\n const config = this.loadConfig();\n config.defaultSid = sid;\n this.saveConfig(config);\n }\n\n /**\n * Get default SID\n */\n getDefaultSid(): string | null {\n const config = this.loadConfig();\n return config.defaultSid ?? null;\n }\n\n /**\n * Clear default SID\n */\n clearDefaultSid(): void {\n const config = this.loadConfig();\n delete config.defaultSid;\n this.saveConfig(config);\n }\n\n // ===========================================================================\n // Private Helpers\n // ===========================================================================\n\n private getSessionPath(sid: string): string {\n const storageDir = resolve(this.storageDir);\n const filePath = resolve(storageDir, `${sid}.json`);\n // Guard against path traversal from attacker-controlled session ids.\n if (!filePath.startsWith(storageDir + sep)) {\n throw new Error(`Unsafe session id: ${sid}`);\n }\n return filePath;\n }\n\n private ensureStorageDir(): void {\n if (!existsSync(this.storageDir)) {\n mkdirSync(this.storageDir, { recursive: true, mode: 0o700 }); // Owner only\n }\n }\n\n private loadConfig(): Config {\n if (!existsSync(this.configPath)) {\n return {};\n }\n try {\n return JSON.parse(readFileSync(this.configPath, 'utf8'));\n } catch {\n return {};\n }\n }\n\n private saveConfig(config: Config): void {\n mkdirSync(dirname(this.configPath), { recursive: true });\n writeFileSync(this.configPath, JSON.stringify(config, null, 2), {\n mode: 0o600,\n });\n }\n}\n","/**\n * Authentication Manager\n *\n * Single source of truth for authentication across all consumers:\n * - CLI\n * - MCP Server\n * - Other tools\n */\n\nimport type {\n AuthSession,\n CookieCredentials,\n AuthPlugin,\n AuthPluginOptions,\n AuthPluginResult,\n} from './types';\nimport { FileStorage } from './storage/file-storage';\n\n/**\n * Destination configuration (from adt.config.ts)\n */\nexport interface Destination {\n /** Plugin package to use (e.g., '@abapify/adt-puppeteer', '@abapify/adt-auth/basic') */\n type: string;\n /** Plugin-specific options */\n options: AuthPluginOptions;\n}\n\nexport class AuthManager {\n private readonly storage: FileStorage;\n\n constructor(storage?: FileStorage) {\n this.storage = storage || new FileStorage();\n }\n\n // ===========================================================================\n // Login - Main entry point\n // ===========================================================================\n\n /**\n * Login using a destination configuration\n *\n * Dynamically loads the plugin specified in destination.type and authenticates.\n *\n * @param sid - System ID (e.g., 'TRL')\n * @param destination - Destination config with plugin type and options\n */\n async login(sid: string, destination: Destination): Promise<AuthSession> {\n // Dynamic import of the auth plugin\n const pluginModule = (await import(destination.type)) as {\n default?: AuthPlugin;\n };\n\n if (!pluginModule.default?.authenticate) {\n throw new Error(\n `Plugin ${destination.type} does not have a default export with authenticate method`,\n );\n }\n\n // Authenticate using the plugin\n const result = await pluginModule.default.authenticate(destination.options);\n\n // Build session based on result type\n const session = this.buildSession(sid, destination, result);\n\n // Save session\n this.saveSession(session);\n\n // Set as default if first system\n if (this.listSids().length === 1) {\n this.setDefaultSid(sid);\n }\n\n return session;\n }\n\n /**\n * Build session from plugin result\n */\n private buildSession(\n sid: string,\n destination: Destination,\n result: AuthPluginResult,\n ): AuthSession {\n if (result.method === 'cookie') {\n return {\n sid: sid.toUpperCase(),\n host: destination.options.url,\n client: destination.options.client,\n auth: {\n method: 'cookie',\n plugin: destination.type,\n pluginOptions: destination.options, // Store full plugin options for refresh fallback\n credentials: {\n cookies: result.credentials.cookies,\n expiresAt: result.credentials.expiresAt.toISOString(),\n },\n },\n };\n } else {\n return {\n sid: sid.toUpperCase(),\n host: destination.options.url,\n client: destination.options.client,\n auth: {\n method: 'basic',\n plugin: destination.type,\n pluginOptions: destination.options, // Store full plugin options for consistency\n credentials: result.credentials,\n },\n };\n }\n }\n\n // ===========================================================================\n // Session Management\n // ===========================================================================\n\n /**\n * Save a session (after successful authentication)\n */\n saveSession(session: AuthSession): void {\n this.storage.save(session);\n }\n\n /**\n * Get session by SID\n */\n getSession(sid?: string): AuthSession | null {\n const targetSid = sid || this.getDefaultSid();\n if (!targetSid) {\n return null;\n }\n return this.storage.load(targetSid);\n }\n\n /**\n * Delete session (logout)\n */\n deleteSession(sid: string): void {\n this.storage.delete(sid);\n }\n\n /**\n * List all available SIDs\n */\n listSids(): string[] {\n return this.storage.list();\n }\n\n // ===========================================================================\n // Default SID Management\n // ===========================================================================\n\n /**\n * Set default SID (persisted to config)\n */\n setDefaultSid(sid: string): void {\n this.storage.setDefaultSid(sid);\n }\n\n /**\n * Get default SID\n */\n getDefaultSid(): string | null {\n return this.storage.getDefaultSid() || (this.storage.list()[0] ?? null);\n }\n\n /**\n * Clear default SID\n */\n clearDefaultSid(): void {\n this.storage.clearDefaultSid();\n }\n\n // ===========================================================================\n // Credential Helpers\n // ===========================================================================\n\n /**\n * Check if session credentials are expired\n */\n isExpired(session: AuthSession): boolean {\n if (session.auth.method !== 'cookie') {\n return false; // Basic auth doesn't expire\n }\n const creds = session.auth.credentials as CookieCredentials;\n return new Date(creds.expiresAt) < new Date();\n }\n\n /**\n * Refresh credentials using the auth plugin\n *\n * @returns Updated session with new credentials, or null if refresh not supported/failed\n */\n async refreshCredentials(\n session: AuthSession,\n options?: { log?: (message: string) => void },\n ): Promise<AuthSession | null> {\n const log = options?.log ?? console.log;\n\n if (!session.auth.plugin) {\n return null; // No plugin = can't refresh\n }\n\n // Dynamic import of the auth plugin (expects default export)\n const pluginModule = (await import(session.auth.plugin)) as {\n default?: AuthPlugin;\n };\n\n if (!pluginModule.default) {\n throw new Error(\n `Plugin ${session.auth.plugin} does not have a default export`,\n );\n }\n\n const plugin = pluginModule.default;\n\n // Try plugin's refresh method first (preferred for session-based auth)\n if (plugin.refresh) {\n const sessionWithLog: AuthSession = {\n ...session,\n auth: {\n ...session.auth,\n pluginOptions: {\n url: session.host,\n client: session.client,\n ...session.auth.pluginOptions,\n log,\n },\n },\n };\n const result = await plugin.refresh(sessionWithLog);\n\n if (result) {\n // Refresh succeeded - build and save updated session\n // Preserve original plugin options (including userDataDir)\n const destination: Destination = {\n type: session.auth.plugin,\n options: session.auth.pluginOptions || {\n url: session.host,\n client: session.client,\n },\n };\n\n const updatedSession = this.buildSession(\n session.sid,\n destination,\n result,\n );\n this.saveSession(updatedSession);\n\n return updatedSession;\n }\n\n // Refresh failed - fall through to interactive authenticate\n log(\n '⚠️ Silent refresh failed - falling back to interactive authentication...',\n );\n }\n\n // Fallback: Call authenticate (full re-auth with browser interaction)\n // Use stored plugin options to preserve settings like userDataDir\n const baseOptions: AuthPluginOptions = session.auth.pluginOptions || {\n url: session.host,\n client: session.client,\n };\n const runtimeOptions = { ...baseOptions, log };\n\n const result = await plugin.authenticate(runtimeOptions);\n\n // Build destination for buildSession\n const destination: Destination = {\n type: session.auth.plugin,\n options: baseOptions,\n };\n\n // Build and save updated session\n const updatedSession = this.buildSession(session.sid, destination, result);\n this.saveSession(updatedSession);\n\n return updatedSession;\n }\n\n /**\n * Get cookie header for HTTP client (convenience method)\n */\n getCookieHeader(session: AuthSession): string | null {\n if (session.auth.method !== 'cookie') {\n return null;\n }\n const creds = session.auth.credentials as CookieCredentials;\n return creds.cookies;\n }\n\n /**\n * Get basic auth credentials (convenience method)\n */\n getBasicAuth(\n session: AuthSession,\n ): { username: string; password: string } | null {\n if (session.auth.method !== 'basic') {\n return null;\n }\n return session.auth.credentials as { username: string; password: string };\n }\n}\n"],"mappings":"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"}
|
package/dist/utils/env.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
13
13
|
function readServiceKey(filePath) {
|
|
14
14
|
const trimmed = filePath.trim();
|
|
15
15
|
if (trimmed.startsWith("{")) throw new Error("Expected a path to a JSON file, but received a raw JSON string.\nWrite the service key JSON to a file and pass the file path instead.\nExample: /path/to/service-key.json");
|
|
16
|
+
if (trimmed.includes("..")) throw new Error("Service key path must not contain path traversal segments");
|
|
16
17
|
if (!existsSync(trimmed)) throw new Error(`Service key file not found: ${trimmed}`);
|
|
17
18
|
const raw = readFileSync(trimmed, "utf-8");
|
|
18
19
|
return ServiceKeyParser.parse(raw);
|
package/dist/utils/env.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.mjs","names":[],"sources":["../../src/utils/env.ts"],"sourcesContent":["import { readFileSync, existsSync } from 'node:fs';\nimport type { Destination } from '../auth-manager';\nimport { ServiceKeyParser, type BTPServiceKey } from '../types/service-key';\n\n/**\n * Read a BTP service key from a file path.\n *\n * @param filePath - Path to a JSON file containing the BTP service key\n * @returns Parsed and validated `BTPServiceKey`\n * @throws Error if the input looks like raw JSON (a file path is required)\n * @throws Error if the file does not exist\n */\nexport function readServiceKey(filePath: string): BTPServiceKey {\n const trimmed = filePath.trim();\n\n if (trimmed.startsWith('{')) {\n throw new Error(\n 'Expected a path to a JSON file, but received a raw JSON string.\\n' +\n 'Write the service key JSON to a file and pass the file path instead.\\n' +\n 'Example: /path/to/service-key.json',\n );\n }\n\n if (!existsSync(trimmed)) {\n throw new Error(`Service key file not found: ${trimmed}`);\n }\n\n const raw = readFileSync(trimmed, 'utf-8');\n return ServiceKeyParser.parse(raw);\n}\n\n/**\n * Resolves a service key destination from the `ADT_SERVICE_KEY` environment\n * variable.\n *\n * The value can be a path to a JSON file or a raw JSON string (for backward\n * compatibility with CI pipelines that store the key as a secret JSON value).\n *\n * @returns A `Destination` object for the service-key plugin, or `null` if\n * `ADT_SERVICE_KEY` is not set.\n */\nexport function resolveServiceKeyFromEnv(): Destination | null {\n const raw = process.env['ADT_SERVICE_KEY'];\n\n if (!raw) {\n return null;\n }\n\n const trimmed = raw.trim();\n\n // Accept both a raw JSON string and a file path so that CI environments that\n // store the key as a secret JSON value continue to work without changes.\n let serviceKey: BTPServiceKey;\n if (trimmed.startsWith('{')) {\n serviceKey = ServiceKeyParser.parse(trimmed);\n } else {\n serviceKey = readServiceKey(trimmed);\n }\n\n return {\n type: '@abapify/adt-auth/plugins/service-key',\n options: {\n url: serviceKey.url,\n serviceKey,\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;AAYA,SAAgB,eAAe,UAAiC;CAC9D,MAAM,UAAU,SAAS,KAAK;CAE9B,IAAI,QAAQ,WAAW,GAAG,GACxB,MAAM,IAAI,MACR,2KAGF;CAGF,IAAI,CAAC,WAAW,OAAO,GACrB,MAAM,IAAI,MAAM,+BAA+B,SAAS;CAG1D,MAAM,MAAM,aAAa,SAAS,OAAO;CACzC,OAAO,iBAAiB,MAAM,GAAG;AACnC;;;;;;;;;;;AAYA,SAAgB,2BAA+C;CAC7D,MAAM,MAAM,QAAQ,IAAI;CAExB,IAAI,CAAC,KACH,OAAO;CAGT,MAAM,UAAU,IAAI,KAAK;CAIzB,IAAI;CACJ,IAAI,QAAQ,WAAW,GAAG,GACxB,aAAa,iBAAiB,MAAM,OAAO;MAE3C,aAAa,eAAe,OAAO;CAGrC,OAAO;EACL,MAAM;EACN,SAAS;GACP,KAAK,WAAW;GAChB;EACF;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"env.mjs","names":[],"sources":["../../src/utils/env.ts"],"sourcesContent":["import { readFileSync, existsSync } from 'node:fs';\nimport type { Destination } from '../auth-manager';\nimport { ServiceKeyParser, type BTPServiceKey } from '../types/service-key';\n\n/**\n * Read a BTP service key from a file path.\n *\n * @param filePath - Path to a JSON file containing the BTP service key\n * @returns Parsed and validated `BTPServiceKey`\n * @throws Error if the input looks like raw JSON (a file path is required)\n * @throws Error if the file does not exist\n */\nexport function readServiceKey(filePath: string): BTPServiceKey {\n const trimmed = filePath.trim();\n\n if (trimmed.startsWith('{')) {\n throw new Error(\n 'Expected a path to a JSON file, but received a raw JSON string.\\n' +\n 'Write the service key JSON to a file and pass the file path instead.\\n' +\n 'Example: /path/to/service-key.json',\n );\n }\n\n if (trimmed.includes('..')) {\n throw new Error(\n 'Service key path must not contain path traversal segments',\n );\n }\n\n if (!existsSync(trimmed)) {\n throw new Error(`Service key file not found: ${trimmed}`);\n }\n\n const raw = readFileSync(trimmed, 'utf-8');\n return ServiceKeyParser.parse(raw);\n}\n\n/**\n * Resolves a service key destination from the `ADT_SERVICE_KEY` environment\n * variable.\n *\n * The value can be a path to a JSON file or a raw JSON string (for backward\n * compatibility with CI pipelines that store the key as a secret JSON value).\n *\n * @returns A `Destination` object for the service-key plugin, or `null` if\n * `ADT_SERVICE_KEY` is not set.\n */\nexport function resolveServiceKeyFromEnv(): Destination | null {\n const raw = process.env['ADT_SERVICE_KEY'];\n\n if (!raw) {\n return null;\n }\n\n const trimmed = raw.trim();\n\n // Accept both a raw JSON string and a file path so that CI environments that\n // store the key as a secret JSON value continue to work without changes.\n let serviceKey: BTPServiceKey;\n if (trimmed.startsWith('{')) {\n serviceKey = ServiceKeyParser.parse(trimmed);\n } else {\n serviceKey = readServiceKey(trimmed);\n }\n\n return {\n type: '@abapify/adt-auth/plugins/service-key',\n options: {\n url: serviceKey.url,\n serviceKey,\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;AAYA,SAAgB,eAAe,UAAiC;CAC9D,MAAM,UAAU,SAAS,KAAK;CAE9B,IAAI,QAAQ,WAAW,GAAG,GACxB,MAAM,IAAI,MACR,2KAGF;CAGF,IAAI,QAAQ,SAAS,IAAI,GACvB,MAAM,IAAI,MACR,2DACF;CAGF,IAAI,CAAC,WAAW,OAAO,GACrB,MAAM,IAAI,MAAM,+BAA+B,SAAS;CAG1D,MAAM,MAAM,aAAa,SAAS,OAAO;CACzC,OAAO,iBAAiB,MAAM,GAAG;AACnC;;;;;;;;;;;AAYA,SAAgB,2BAA+C;CAC7D,MAAM,MAAM,QAAQ,IAAI;CAExB,IAAI,CAAC,KACH,OAAO;CAGT,MAAM,UAAU,IAAI,KAAK;CAIzB,IAAI;CACJ,IAAI,QAAQ,WAAW,GAAG,GACxB,aAAa,iBAAiB,MAAM,OAAO;MAE3C,aAAa,eAAe,OAAO;CAGrC,OAAO;EACL,MAAM;EACN,SAAS;GACP,KAAK,WAAW;GAChB;EACF;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.4.
|
|
6
|
+
"version": "0.4.44",
|
|
7
7
|
"description": "Authentication package for SAP ADT systems - supports multiple auth methods",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"types": "./dist/index.d.mts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"README.md"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@abapify/logger": "0.4.
|
|
24
|
+
"@abapify/logger": "0.4.44",
|
|
25
25
|
"open": "^11.0.4",
|
|
26
26
|
"proxy-agent": "^6.4.0"
|
|
27
27
|
},
|