Skip to content

security: auth token stored as plaintext JSON — use OS keychain #7

Description

@samsonajulor

Summary

The authentication token is stored in plaintext at ~/.quikdb-frame/auth.json. While file permissions are 0600 (owner only), the token is fully readable as plain JSON and will appear in unencrypted disk backups.

Current behavior

internal/deploy/auth.go:132-137

func SaveAuth(auth *AuthConfig) error {
    dir := filepath.Join(homeDir(), configDir)
    os.MkdirAll(dir, 0700)
    data, _ := json.MarshalIndent(auth, "", "  ")  // plaintext, human-readable
    return os.WriteFile(filepath.Join(dir, configFile), data, 0600)
}

Contents of ~/.quikdb-frame/auth.json:

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresAt": "2027-01-01T00:00:00Z"
}

Fix

Use the OS-native credential store via the zalando/go-keyring package:

import "github.com/zalando/go-keyring"

const service = "quikdb-frame"

func SaveAuth(auth *AuthConfig) error {
    return keyring.Set(service, "token", auth.Token)
}

func LoadAuth() (*AuthConfig, error) {
    token, err := keyring.Get(service, "token")
    if err != nil { return nil, err }
    return &AuthConfig{Token: token}, nil
}

This uses Keychain on macOS, Secret Service on Linux, and Credential Manager on Windows.

Severity

LOW — mitigated by file permissions, but unencrypted backups remain a risk.

Metadata

Metadata

Assignees

No one assigned

    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