dnsimple / dnsimple/cli

Store API tokens in the OS keyring instead of plaintext credentials.yml

Open
#31 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Go
Stars
3
Forks
0
Avg merge
8h 39m
Merged PRs (30d)
9

Description

Summary

Today the CLI stores DNSimple API tokens in plaintext at ~/Library/Application Support/dnsimple/credentials.yml (and equivalents on Linux/Windows). Anyone with read access to that file — including malware running as the user, accidental backups, screen-shared terminals running cat, etc. — gets the token.

We should move tokens out of the file and into the OS keyring (macOS Keychain, Linux Secret Service, Windows Credential Manager), with the on-disk file storing only non-sensitive metadata (host, account ID, context name, user email).

Motivation

  • Tokens are bearer credentials with full account access. Plaintext-on-disk is the lowest possible bar.
  • Users on shared workstations, devs running with set -x, and folks who commit dotfiles have all hit this category of leak in other CLIs.
  • This is the standard pattern for CLIs that handle long-lived API tokens (gh, aws, op, glab, doctl).

Reference: how gh does it

The gh CLI uses the same pattern we'd want:

  • ~/.config/gh/hosts.yml stores hostname → user → non-sensitive metadata.
  • The actual OAuth tokens live in the OS keyring, keyed by (hostname, user).
  • A plaintext fallback exists for environments without a keyring (containers, headless CI), gated behind a configuration option.
  • Resolution chain at runtime: env var → keyring(host, active user) → keyring(host) (legacy fallback).

The relevant code is in cli/cli internal/keyring and internal/config (AuthConfig.ActiveToken, TokenFromKeyringForUser). The underlying library is zalando/go-keyring (or 99designs/keyring, depending on the abstraction we want).

Proposed change

  1. Add a keyring package wrapping a vendor library (likely zalando/go-keyring for its smaller surface area; revisit if we hit platform gaps).
  2. Update the credentials file schema (post-#28) to store everything except the token. The keyring entry is keyed by (host, account_id) or (context_name) — to be decided in implementation.
  3. Migrate existing plaintext tokens into the keyring on first run, then strip them from the file. Migration should be silent on success and leave a .bak file for one cycle.
  4. Add a fallback mode for environments where the keyring is unavailable (CI, containers without dbus, SSH sessions without an unlocked keychain). The fallback should be opt-in, not automatic, and emit a warning the first time it's used. Suggested gate: DNSIMPLE_KEYRING=plaintext env var.
  5. Honour DNSIMPLE_TOKEN env var override exactly as today — env always wins over keyring.

Open questions

  • Library choice. zalando/go-keyring is simpler; 99designs/keyring (which gh uses) supports more backends including encrypted file fallback. Decision deferred to implementation.
  • Keyring entry key. (host, account_id) is the most explicit but couples the keyring schema to the credentials schema. context_name is cleaner but means renaming a context requires re-keying the keyring. Decision deferred.
  • Headless / CI behaviour. Hard-fail or fall back to plaintext with a warning? Recommend the env-var-gated fallback in step 4.
  • Migration of existing plaintext tokens. Run on first load after upgrade, or on first explicit auth login? Recommend first load, with the .bak safety net.

Dependencies

  • Blocked on dnsimple/dnsimple-cli#28. The named-context refactor is the right time to redesign the storage layer. Doing this before #28 lands would mean two disruptive credentials migrations back-to-back.

References

  • dnsimple/dnsimple-cli#28 — named authentication contexts (blocker)
  • dnsimple/dnsimple-cli#25 — the original auth fix that surfaced the credentials storage discussion

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the current credentials.yml handling and the named-context refactor in issue #28, which blocks this work. Trace how DNSIMPLE_TOKEN and stored credentials are resolved today. Done means tokens are handled through the agreed keyring design, migration and fallback behavior are specified, and tests cover the security-sensitive paths.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
authentication, cli, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.