Skip to content

Headless Linux: OAuth can fall back to plaintext token when Secret Service has no login collection #424

Description

@javierburongarcia

Summary

On a headless Linux VM, td auth login can complete OAuth successfully but silently fall back to persisting the resulting access token in plaintext when the Secret Service daemon is reachable but its default login collection cannot be created or prompted for.

The warning is emitted, but the default outcome is still a working write-capable credential stored in ~/.config/todoist-cli/config.json.

Reproduction

Environment:

  • Todoist CLI: 1.76.1
  • Node: v22.22.3
  • OS: Ubuntu 25.10, kernel 6.17.0-40-generic
  • Headless VM accessed over SSH
  • gnome-keyring-daemon / org.freedesktop.secrets are running
  1. Run td auth login --no-browser-open --callback-port 8766 on the VM.

  2. Use an SSH local port forward from a Mac so the browser callback reaches the VM loopback listener.

  3. Complete OAuth in the Mac browser.

  4. Observe successful OAuth login followed by:

    Warning: system credential manager unavailable; access token saved as plaintext in ~/.config/todoist-cli/config.json
    

The underlying Secret Service failure is reproducible without touching a Todoist credential:

secret-tool: ... does not exist at path "/org/freedesktop/secrets/collection/login"

The VM's keyring service logs also show that its graphical prompt cannot start in the headless session:

couldn't initialize prompt: ... org.gnome.keyring.SystemPrompter exited with status 1

Expected behavior

A headless/SSH environment should not unexpectedly leave a newly authorized write-capable OAuth token on disk in plaintext.

At minimum, the CLI should clearly identify this as a missing/unusable Secret Service collection and offer an actionable, non-secret-bearing remediation.

Suggested directions

I understand that OS credential managers are a sensible default for desktop users. For headless Linux, could the CLI consider one or more of:

  1. Require an explicit opt-in before writing an OAuth token to the plaintext fallback (for example, --allow-plaintext-token), rather than only emitting a warning after it has happened.
  2. Detect the missing/unusable login collection specifically and explain that Secret Service exists but cannot create/unlock its default collection in the current session.
  3. Offer a documented headless credential-store strategy or extension point (for example, a command-based secret provider), without requiring a particular third-party vault.

TODOIST_API_TOKEN is a useful escape hatch, but it moves the responsibility of safe runtime retrieval entirely to the caller and does not help users understand why a successful OAuth flow was persisted insecurely.

Notes

  • OAuth itself worked correctly; this is specifically a local credential-storage behavior.
  • No OAuth codes, access tokens, client secrets, or authorization URLs are included here.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions