diff --git a/.changeset/violet-emus-wait.md b/.changeset/violet-emus-wait.md new file mode 100644 index 00000000..a373d4db --- /dev/null +++ b/.changeset/violet-emus-wait.md @@ -0,0 +1,5 @@ +--- +"@cipherstash/cli": minor +--- + +Fixed issue where the wizard was checking CipherStash auth based on path and now leverages the auth npm package. diff --git a/packages/cli/src/commands/auth/login.ts b/packages/cli/src/commands/auth/login.ts index 50189d99..0833ac4a 100644 --- a/packages/cli/src/commands/auth/login.ts +++ b/packages/cli/src/commands/auth/login.ts @@ -2,14 +2,6 @@ import auth from '@cipherstash/auth' import * as p from '@clack/prompts' const { beginDeviceCodeFlow, bindClientDevice } = auth -// TODO(CIP-2996): @cipherstash/auth@0.35.0 (latest on npm as of 2026-04-17) -// writes the device-code token to a profile dir that later auth reads do not -// find, so subsequent CLI invocations fail to resolve credentials. The fix is -// upstream — bump this catalog pin once a newer @cipherstash/auth that -// aligns the write + read paths ships. Do not paper over it in the CLI: -// divergent profile-dir logic across tools is exactly what caused the -// regression in the first place. - // TODO: pull from the CTS API export const regions = [ { value: 'us-east-1.aws', label: 'us-east-1 (Virginia, USA)' }, @@ -53,7 +45,7 @@ export async function login(region: string, _referrer: string | undefined) { s.start('Waiting for authorization...') const auth = await pending.pollForToken() - s.stop('Authenticated! Token saved to ~/.cipherstash/auth.json') + s.stop('Authenticated!') p.log.info( `Token expires at: ${new Date(auth.expiresAt * 1000).toISOString()}`, diff --git a/packages/cli/src/commands/wizard/lib/prerequisites.ts b/packages/cli/src/commands/wizard/lib/prerequisites.ts index 1f15e384..17742b7b 100644 --- a/packages/cli/src/commands/wizard/lib/prerequisites.ts +++ b/packages/cli/src/commands/wizard/lib/prerequisites.ts @@ -1,6 +1,6 @@ import { existsSync } from 'node:fs' import { resolve } from 'node:path' -import { homedir } from 'node:os' +import auth from '@cipherstash/auth' interface PrerequisiteResult { ok: boolean @@ -12,18 +12,17 @@ interface PrerequisiteResult { * 1. CipherStash authentication exists * 2. stash.config.ts exists in the project */ -export function checkPrerequisites(cwd: string): PrerequisiteResult { +export async function checkPrerequisites( + cwd: string, +): Promise { const missing: string[] = [] - // Check CipherStash auth - const authPath = resolve(homedir(), '.cipherstash', 'auth.json') - if (!existsSync(authPath)) { + if (!(await hasCredentials())) { missing.push( 'Not authenticated with CipherStash. Run: npx @cipherstash/cli auth login', ) } - // Check stash.config.ts if (!findStashConfig(cwd)) { missing.push( 'No stash.config.ts found. Run: npx @cipherstash/cli db install', @@ -33,6 +32,23 @@ export function checkPrerequisites(cwd: string): PrerequisiteResult { return { ok: missing.length === 0, missing } } +// Ask @cipherstash/auth to resolve credentials via its own profile logic +// rather than probing a hardcoded path — the on-disk layout has shifted +// between auth versions and duplicating it in the CLI is what caused +// CIP-2996 in the first place. +async function hasCredentials(): Promise { + try { + await auth.AutoStrategy.detect().getToken() + return true + } catch (error) { + const code = (error as { code?: string } | null)?.code + if (code === 'NOT_AUTHENTICATED' || code === 'MISSING_WORKSPACE_CRN') { + return false + } + throw error + } +} + /** Walk up from cwd to find stash.config.ts. */ function findStashConfig(startDir: string): string | undefined { let dir = resolve(startDir) diff --git a/packages/cli/src/commands/wizard/run.ts b/packages/cli/src/commands/wizard/run.ts index a29ba08a..3c52a6a5 100644 --- a/packages/cli/src/commands/wizard/run.ts +++ b/packages/cli/src/commands/wizard/run.ts @@ -44,7 +44,7 @@ export async function run(options: RunOptions) { ) // Phase 1: Prerequisites - const prereqs = checkPrerequisites(options.cwd) + const prereqs = await checkPrerequisites(options.cwd) if (!prereqs.ok) { trackPrerequisiteMissing(prereqs.missing) p.log.error('Missing prerequisites:')