The developer's guide to CLI authentication
API keys, token files, OAuth Device Flow, and Client Credentials compared. A practical guide to choosing the right authentication pattern for your CLI.
Developers spend the majority of their workday in the terminal, and the tools they use there (from gh and vercel to Claude Code and railway) have become primary interfaces. The rise of AI-powered coding agents has only accelerated this: when an LLM can read, edit, and commit code from a terminal session, the CLI isn't just a convenience. It's the product.
But here's the thing nobody warns you about when you ship a CLI: authentication is surprisingly hard to get right.
Web apps have decades of battle-tested patterns. You redirect to a login page, set a cookie, and you're done. CLIs don't have browsers. They don't have cookies. They run in environments where "open a URL" might not even be possible: Docker containers, SSH sessions, CI pipelines, or headless servers.
This article compares the three most common approaches to CLI authentication (API keys, token files, and the OAuth Device Flow) and lays out when each one makes sense, where each one breaks down, and what to do if your CLI needs to pass an enterprise security review.
Approach 1: API Keys
API keys are the default. Almost every developer tool starts here, and for good reason: they're dead simple. Generate a key in the dashboard, paste it into a config file or environment variable, and every request includes it as a header. No libraries, no flows, no redirects.
The developer experience for initial setup is excellent. Copy, paste, done. And for single-developer workflows (personal projects, local scripts, quick prototyping), API keys are often the right answer.
Where API keys break down
The trouble starts when you zoom out from the individual developer.
- They're long-lived secrets stored in plaintext. Most API keys don't expire unless manually rotated. They end up in
.bashrcfiles,.envfiles committed to repos, CI environment variables copied across pipelines, and Slack messages between teammates. A leaked key grants full access until someone notices and revokes it. - They can't express identity. An API key authenticates a machine, not a person. If three engineers on the same team all use a shared key, your audit log can't distinguish who did what. Enterprise customers care about this. A lot.
- They don't support SSO or MFA. If your customer's security policy requires that every login goes through Okta with multi-factor authentication, an API key is a compliance gap. There's no way to enforce an organization's auth policy on a static credential.
- Revocation is blunt. Rotating an API key invalidates every tool, script, and CI pipeline that uses it, all at once. There's no way to revoke a single session without breaking everything else.
For individual developers running their own tools? API keys are fine. For any B2B SaaS product that sells to teams, let alone enterprises, they become a liability.
When to use API keys
API keys still have a place, particularly for machine-to-machine authentication. Service accounts running in CI/CD, automated scripts that don't have a human in the loop, and integrations between backend systems are all reasonable use cases. The key distinction is that these are non-interactive contexts where there's no human to redirect through a browser.
If your CLI is primarily used by individual developers for personal automation and you don't need per-user identity, API keys might be all you need.
What implementing API keys actually involves
If you go this route, here's what a production-quality implementation looks like.
- Key generation. You need a cryptographically secure random generator producing keys with enough entropy that they can't be guessed or brute-forced. Most teams use 256 bits of randomness encoded as a base62 or hex string. You'll also want a consistent prefix scheme (like
sk_live_andsk_test_) so keys are identifiable at a glance and can be routed to the right environment. - Hashed storage. Never store raw API keys in your database. Just like passwords, keys should be hashed before storage so that a database breach doesn't immediately compromise every customer's credentials. Unlike passwords, API keys are high-entropy, so a fast hash like SHA-256 is acceptable (you don't need bcrypt). Store the hash, and only ever show the full key to the user once at creation time.
- Verification. Every incoming API request needs to hash the provided key and look it up against your store. This is on the hot path for every authenticated request, so it needs to be fast and reliable. You'll want an index on the hash column and a caching strategy for high-throughput use cases.
- A management interface. Your customers need a way to create keys, see which keys exist (with masked values), and revoke compromised or unused keys. Revocation needs to be immediate. You'll also want metadata on each key: who created it, when, what it's for, and when it was last used.
- Permission scoping. A single all-access key is a liability. Production implementations let customers create keys with specific permissions: read-only, scoped to certain resources, or limited to particular API operations. This means your API key system needs to integrate with whatever authorization model your application uses.
- Rotation and expiry. Ideally, keys should support expiration dates and graceful rotation, where a new key can be created and validated before the old one is revoked. Without this, every key rotation is a coordinated outage risk across every system that uses the key.
That's a meaningful amount of infrastructure. If your product already has a robust auth layer, some of these pieces may already exist. If not, it's worth understanding the full scope before committing to building it in-house.
Approach 2: Token files
Token files are the next step up in sophistication. Instead of a single static key, the user authenticates once (usually through a browser redirect), and the CLI stores an access token and refresh token on disk. Subsequent commands use the stored tokens, refreshing them automatically when they expire.
This is how tools like gcloud, aws, and gh work. You run something like mycli auth login, a browser opens, you log in, and a token gets written to ~/.config/mycli/credentials.json.
The advantages over API keys
Token files solve several of the API key problems. Tokens are short-lived (typically an hour), so a leaked access token has limited blast radius. Refresh tokens can be scoped and revoked individually. And because the initial authentication happens through a browser, you can enforce SSO and MFA during that step.
You also get per-user identity. Each developer authenticates as themselves, which means audit logs can attribute actions to real people.
Where token files break down
- They require a browser on the same machine. The standard OAuth Authorization Code flow opens a local browser, redirects to the identity provider, and catches the callback on
http://localhost. This works great on a developer's laptop. It does not work on a remote server, inside a Docker container, or in an SSH session, which is exactly where many CLI tools are used. - Credential storage is your problem. Where does the token file live? What permissions does it have? Is it encrypted at rest? Most CLIs write JSON to a dotfile in the user's home directory, which means anyone with access to that filesystem (including other processes) can read it. OS keychains exist, but integrating with Keychain on macOS, libsecret on Linux, and Credential Manager on Windows adds significant complexity.
- Multi-organization context switching is painful. If a user belongs to multiple organizations (common in B2B SaaS), the token file needs to handle multiple contexts. Tools like
kubectlhave solved this with config contexts, but building a robust multi-tenant token management system from scratch is non-trivial. - Refresh token rotation adds operational complexity. If you implement refresh token rotation (recommended by security best practices), you need to handle race conditions where two concurrent CLI invocations both try to use the same refresh token simultaneously.
When to use token files
Token files are a reasonable choice when your users primarily work on their local machines, when you need per-user identity, and when you're willing to invest in building the credential storage and refresh logic yourself. They're the right middle ground for tools that need more security than API keys but aren't yet serving enterprise customers with strict compliance requirements.
Approach 3: OAuth Device Flow
The OAuth Device Authorization Grant (defined in RFC 8628) was originally designed for smart TVs and IoT devices, environments where typing credentials is impractical. It turns out CLIs fit this description perfectly.
Here's how it works:
- The user runs
mycli login. - The CLI requests a device code from the authorization server.
- The terminal displays a short code and a URL: "Visit https://app.example.com/device and enter code: ABCD-EFGH"
- The user opens that URL on any device (their phone, their laptop, a browser on a different machine) and enters the code.
- They see a familiar login screen (SSO, MFA, the works) and authorize the CLI.
- Meanwhile, the CLI polls the authorization server. Once the user approves, it receives an access token and refresh token.
Why the Device Flow matters for CLIs
- It decouples the browser from the CLI. The user doesn't need a browser on the same machine where the CLI runs. They can SSH into a remote server, run
mycli login, and complete the authentication on their phone. This is a fundamentally better experience for remote development, containers, and CI debugging sessions. - It inherits all your web auth capabilities. Because the user authenticates through a standard browser flow, you get SSO, MFA, conditional access policies, and organization-level auth rules for free. Whatever your web app enforces, the CLI enforces too. A customer that requires Okta SSO with hardware MFA keys gets exactly that, even for terminal access.
- It provides per-user, per-session credentials. Each device flow produces its own token pair. Users can revoke individual sessions without affecting others. Administrators get clear audit trails.
- The user experience is surprisingly good. Developers are familiar with this flow from tools like GitHub CLI, Stripe CLI, and the VS Code remote extension. The two-step "visit URL, enter code" pattern is well-understood and requires zero configuration.
Security considerations
The Device Flow does introduce one consideration worth understanding: device code phishing. Since late 2024, sophisticated campaigns have exploited this flow by tricking users into entering attacker-generated codes on legitimate authorization pages. The user thinks they're authenticating their own device, but they're actually granting access to an attacker.
Mitigations include educating users to only enter codes they personally initiated, displaying clear context during the authorization step (showing which application and device are being authorized), and monitoring for anomalous device code usage patterns.
Short code expiry windows (typically 15 minutes) and rate-limited polling also limit the attack surface.
Approach 4: Client Credentials
The OAuth 2.0 Client Credentials grant (RFC 6749, Section 4.4) is designed for machine-to-machine authentication where there's no human user involved. A service authenticates with a client_id and client_secret, receives a short-lived access token (typically a signed JWT), and uses that token for subsequent API requests. When the token expires, the service re-authenticates to get a new one.
If this looks similar to API keys, it is, in the sense that both authenticate a machine rather than a person. The critical difference is that the secret itself never travels with individual API requests. The client_secret is only used during the token exchange; what travels over the wire for every subsequent call is a short-lived access token. If that token leaks, it expires within minutes or hours. If an API key leaks, it's valid until someone manually revokes it.
When Client Credentials make sense for CLIs
Client Credentials are the right choice for the non-interactive, machine-to-machine side of your CLI when your customers have specific security requirements.
- Enterprise security teams that require OAuth. Many large organizations have centralized OAuth infrastructure with token introspection, policy enforcement, and audit logging already in place. Client Credentials let your service plug directly into that infrastructure without custom integration work.
- Environments where short-lived tokens are mandatory. Some compliance frameworks require that credentials expire automatically. Client Credentials produce tokens with built-in expiry, which satisfies this requirement without relying on manual key rotation.
- Backend-to-backend integrations at scale. When your customers are building services that call your API at high volume, JWT validation (checking the signature against a published JWKS endpoint) can be done locally without a network call on every request. This is faster than looking up an API key in a database.
Where Client Credentials add friction
- They're more complex for your customers. Implementing a token exchange flow, caching tokens, and handling expiry is more work than pasting a key into an environment variable. For a quick integration or a small script, that overhead can be a real barrier.
- They require OAuth infrastructure on your side. You need an authorization server that can issue tokens, manage client registrations, and publish a JWKS endpoint. If you're already running OAuth for your web application, adding a Client Credentials grant is incremental. If not, it's a meaningful infrastructure investment.
- The client secret is still long-lived. While the access tokens are short-lived, the
client_secretused to obtain them doesn't expire automatically in most implementations. A leaked client secret still grants the ability to mint new tokens until it's rotated.
API keys vs. Client Credentials: Choosing between the two M2M approaches
Since both API keys and Client Credentials solve the same fundamental problem (authenticating a service rather than a person), it's worth understanding when to reach for which.
The practical differences come down to a few things.
- Token lifetime. API keys are typically long-lived. If one leaks, it's valid until someone notices and revokes it. Client Credentials produce tokens that expire (usually within an hour), so a leaked token has a much shorter window of usefulness. The client secret itself is still long-lived, but it never travels with individual API requests.
- Infrastructure requirements. API keys need a lookup table and a verification step. Client Credentials need an OAuth authorization server capable of issuing and validating tokens. If you're already running OAuth infrastructure for your web application (which you probably are if you support SSO), adding a Client Credentials grant is incremental. If you're not, it's a heavier lift than API keys.
- Standards and interoperability. Client Credentials follow a well-defined spec (RFC 6749, Section 4.4). This means enterprise customers who already have OAuth tooling, token introspection, and centralized policy enforcement can plug your service into their existing infrastructure without custom integration work. API keys are bespoke by nature, and every provider implements them slightly differently.
- Simplicity for your customers. API keys are easier to understand and use. Copy a string, put it in a header, done. Client Credentials require your customers to implement a token exchange flow, handle token caching, and manage expiry. For quick integrations and smaller teams, that extra complexity can be a real barrier to adoption.
Many mature platforms offer both options. API keys serve as the low-friction onboarding path: a developer can grab a key from the dashboard and make their first API call in under a minute. Client Credentials serve as the enterprise-grade path: an IT team can configure scoped, short-lived access that integrates with their existing security tooling. If you're early and need to pick one, API keys will get more customers through the door faster. If you're selling to enterprises that require OAuth-based access controls, Client Credentials may be non-negotiable.
!!For a more detailed comparison see API Keys vs M2M Applications: Differences, use cases, and how to decide.!!
How the four approaches compare
- Setup complexity. API keys win here. Just copy and paste. Token files require building an OAuth flow with a local HTTP server. The Device Flow needs an OAuth implementation but skips the local server entirely. Client Credentials need an OAuth token exchange and an authorization server, but the flow itself is straightforward.
- Works in headless environments. API keys and Client Credentials both work anywhere, since neither involves a human login step. Token files don't, since they need a local browser. The Device Flow works in any environment since the user authenticates on a separate device.
- SSO and MFA support. API keys and Client Credentials can't support these (and don't need to, since they authenticate machines, not people). Both token files and the Device Flow support them, since both authenticate through a browser.
- Enterprise audit trails. API keys identify a key, not a person. Client Credentials identify a service or organization. Both token files and the Device Flow tie actions to individual users.
- Credential lifetime. API keys live until manually revoked. Token files and the Device Flow both use short-lived access tokens with refresh capabilities. Client Credentials produce short-lived tokens, though the underlying secret is long-lived.
- Multi-organization support. API keys require generating separate keys per organization. Token files can store multiple contexts. The Device Flow integrates naturally with organization-scoped auth policies. Client Credentials tokens can carry organization claims natively.
What your enterprise customers will ask you
If you're building a B2B SaaS product with a CLI, here's what will come up during enterprise security reviews:
- "Does your CLI support our identity provider?" This means SSO, typically SAML or OIDC through Okta, Azure AD, or Google Workspace. API keys can't answer this question. The Device Flow can, because it delegates authentication to your standard web login flow, which can support any IdP.
- "Can we enforce MFA for CLI access?" Same story. If your web login requires MFA, and the CLI authenticates through the same web flow, MFA is automatically enforced.
- "How are credentials stored on developer machines?" With the Device Flow, you store short-lived access tokens and refresh tokens. Even if compromised, access tokens expire quickly, and refresh tokens can be individually revoked.
- "Can we see who did what?" Per-user authentication means every CLI action can be attributed to a real person in your audit log.
- "Can we deprovision users?" When an employee leaves and their identity provider account is deactivated, their CLI refresh tokens stop working at the next refresh attempt. With API keys, someone has to manually track down and revoke every key the departing employee used.
Picking the right approach
Here's a practical decision framework.
- If your CLI is used by individual developers for personal automation, and you don't sell to teams: start with API keys. They're simple, well-understood, and sufficient.
- If your CLI is part of a product that serves teams, and you need per-user identity for audit and access control: use the Device Flow. It gives you per-user auth, SSO support, and headless compatibility without the complexity of managing local browser redirects and credential storage.
- If you also need to support machine-to-machine access and your customers expect simple, static credentials: add API keys for service accounts. This covers CI/CD pipelines, quick integrations, and scripts where a developer just wants to paste a key and go.
- If your enterprise customers require OAuth-based access controls, short-lived tokens, or need to plug your service into their existing authorization infrastructure: add Client Credentials. This is the standards-compliant path for backend-to-backend integrations at scale.
Most mature developer tools end up offering multiple patterns. GitHub CLI, for example, uses the Device Flow for interactive login and personal access tokens for automation. The good news is you don't have to build any of these from scratch.
Secure your CLI authentication with WorkOS
WorkOS provides managed infrastructure for each pattern covered in this article: the Device Flow for interactive users, API keys for simple machine credentials, and M2M Applications for OAuth Client Credentials. All are scoped to organizations and share the same permission model.
Device Flow for interactive users
WorkOS AuthKit supports the OAuth Device Authorization Grant natively. If you're already using AuthKit for your web application, adding CLI authentication requires no additional configuration. The same login experience, SSO connections, MFA policies, and organization settings apply automatically.
Your CLI application requests a device code, displays the verification URL and code to the user, polls for completion, and receives standard access and refresh tokens. Users see the same AuthKit login screen they're familiar with from your web app, which means there's no separate onboarding for CLI access.
CLI Auth is available to all hosted AuthKit customers at no additional cost. Check out the CLI Auth documentation to get started, or read the Device Authorization Grant deep dive for more on the protocol itself.
API keys for simple machine-to-machine access
WorkOS API Keys handles the infrastructure covered in the "What implementing API keys actually involves" section above: key generation, hashed storage, verification, and a drop-in management widget your customers can use to create, view, and revoke keys without you building a single admin screen.
Keys are automatically scoped to organizations and can be restricted to specific permissions. A customer can create a read-only key for a reporting integration or a narrowly scoped key for a single automated workflow, all through the management widget. You define which permissions are available for API keys; your customers choose the ones they need.
This is the right choice when your customers want simple, static credentials they can drop into a config file or environment variable and start making API calls immediately.
M2M Applications for OAuth Client Credentials
For customers who need short-lived tokens, JWT validation, or have a requirement to use the OAuth Client Credentials flow, WorkOS offers M2M Applications. Your customer's service authenticates with a client_id and client_secret and receives a short-lived access token (a signed JWT) that includes an org_id claim identifying which organization the client is acting on behalf of.
This gives you the security benefits discussed earlier in this article: tokens expire quickly, so a leaked token has limited usefulness. Your API can validate tokens using WorkOS's JWKS endpoint without making a network call on every request, or use the Token Introspection API for synchronous checks. And because each M2M Application is tied to an organization, access is automatically scoped to the right tenant.
M2M Applications are the right choice when your customers prefer standards-based OAuth, when their security team requires short-lived tokens, or when they're building backend-to-backend integrations that need to fit into existing enterprise OAuth infrastructure.
Sign up for WorkOS today and get 1 million users per month for free (no credit card required).