If you've ever built or used a command-line interface (CLI) tool, you know that authentication can be a tricky puzzle to solve. Getting this authentication flow right makes the difference between a tool people love using and one they constantly fight with.
In this post, I'll explore what seems to be the most practical approach to CLI authentication: combining initial authentication (either through email/password or OAuth) with a dual-token system. While other methods certainly work for specific use cases, this approach appears to be a sweet spot for many applications.
🛡️ An Effective Authentication Pattern for CLI Apps
After researching various authentication methods for CLI applications, this approach seems most balanced for modern tools:
The Dual-Token Pattern:
- Initial Authentication via email + password or OAuth
- Token-Based Authorization with two distinct tokens:
- Short-lived access token (15-60 minutes)
- Longer-lived refresh token (days to weeks)
This dual-token approach provides solid security while not constantly interrupting users with annoying re-authentication prompts.
🔄 How It Works: The Authentication Flow
Let's explore how this pattern works in a typical CLI application:
📝 1. Initial Authentication
When a user first runs an authenticated command, they're prompted to authenticate:
# User attempts to access protected resource
$ myapp resources list
Not authenticated. Please log in first.
# User authenticates with email and password
$ myapp login
Email: [email protected]
Password: ********
Login successful!
Behind the scenes, the CLI sends these credentials to the authentication server, which validates them and returns both an access token and a refresh token.
💡 Pro Tip: You could also use OAuth here if the service supports it. In that case, the CLI would open a browser window where the user can complete authentication through a standard web login flow.
💾 2. Token Storage
After authentication, both tokens need to be stored securely. A common approach is to store them in a config file in the user's home directory:
~/.config/myapp/credentials.json
The structure typically looks something like this:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"access_expires": 1682349600
}
⚠️ Security Note: Always set appropriate file permissions (0600) to prevent other users from accessing these tokens!
For increased security, other options include:
- 🔒 Storing tokens only in memory (though this requires keeping the CLI running)
- ⏱️ Setting shorter refresh token expiration (e.g., 12 hours instead of days)
- 🗝️ Using the system's secure keychain rather than a plain file
🌐 3. Making Authenticated Requests
With tokens securely stored, the CLI can now make authenticated requests to the API:
- Before making any request, check if the access token has expired
- If expired, automatically use the refresh token to get a new access token
- If refresh fails (e.g., refresh token expired), prompt for login
- Otherwise, include the valid access token in the API request
This automatic refresh mechanism is what makes this approach so user-friendly. When functioning correctly, users rarely need to manually log in. The CLI handles token management behind the scenes, and everything just works.
🎯 Conclusion
Combining email/password (or browser-based OAuth) authentication with the dual-token approach seems to provide a good balance of security and usability for most applications. This approach offers several benefits:
- 🔒 Limited security exposure through token expiration
- 😌 Seamless user experience with behind-the-scenes token refreshes
- 🎮 Server-side control over session management
- 💻 Consistent behaviour across MacOS, Windows, and Linux
This is just a starting point, though. Depending on security requirements, this basic flow could be enhanced by using system keychains instead of config files, implementing MFA for sensitive operations, or adding device registration capabilities.
Key Takeaway: The beauty of the dual-token approach is how adaptable it is - starting simple and adding security layers as needs evolve seems like a sensible path.
Have you tried implementing CLI authentication? Did you find storing tokens in .config
directories sufficient for your needs? Let me know in the comments!
P.S. Remember that authentication is just one piece of a secure CLI. No matter which approach you choose, always use HTTPS, implement proper error handling, and follow secure coding practices throughout your application.