stack/packages/stack-cli/src/lib/config.ts
Mantra 7f8e3df852
feat: add anonRefreshToken to CLI auth flow and enhance session management (#1303)
- Extended `CliAuthAttempt` with `anonRefreshToken` and a migration.
- CLI `POST /auth/cli` accepts optional `anon_refresh_token` (must be an
anonymous user's refresh token for the current project).
- `POST /auth/cli/complete` supports `mode` `check` (anonymous vs none),
`claim-anon-session` (issue tokens for the linked anonymous session),
and `complete` (bind the browser session's refresh token to the
attempt). Completing clears `anonRefreshToken` on the row. We do **not**
merge anonymous account data into the signed-in user (that behavior was
removed as a security risk; the anonymous user remains unchanged).
- Template CLI confirmation page, stack-cli optional
`STACK_CLI_ANON_REFRESH_TOKEN`, SDK/spec updates, and e2e coverage.

<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* CLI login supports attaching anonymous sessions and a multi-mode
confirm/claim/check flow; CLI tools now surface login codes and remove
anon token after use.
  * Added interactive CLI auth demo page and a CLI simulator script.
* Client libraries: prompt flow accepts an optional anon token and a
promptLink(url, loginCode) callback.

* **Tests**
* Expanded end-to-end coverage for anonymous CLI sessions,
claim/complete/poll flows, upgrades, and error cases.

* **Documentation**
* Updated prompt CLI docs/spec to describe new options and callback
signature.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-14 02:09:35 +00:00

38 lines
1.1 KiB
TypeScript

import * as fs from "fs";
import * as path from "path";
import * as os from "os";
const CONFIG_PATH = process.env.STACK_CLI_CONFIG_PATH ?? path.join(os.homedir(), ".config", "stack-auth", "credentials.json");
type ConfigKey = "STACK_CLI_REFRESH_TOKEN" | "STACK_CLI_ANON_REFRESH_TOKEN" | "STACK_API_URL" | "STACK_DASHBOARD_URL";
function readConfigJson(): Record<string, string> {
try {
return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
} catch {
return {};
}
}
function writeConfigJson(data: Record<string, string>): void {
fs.mkdirSync(path.dirname(CONFIG_PATH), { recursive: true });
fs.writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2) + "\n", { mode: 0o600 });
}
export function readConfigValue(key: ConfigKey): string | undefined {
const config = readConfigJson();
return config[key];
}
export function writeConfigValue(key: ConfigKey, value: string): void {
const config = readConfigJson();
config[key] = value;
writeConfigJson(config);
}
export function removeConfigValue(key: ConfigKey): void {
const config = readConfigJson();
delete config[key];
writeConfigJson(config);
}