
It is a staggering differential. Nearly 88% of model context protocol(MCP) servers require authentication to external services, yet only 8.5% implement proper mechanisms for securing those credentials.
Static secrets, including GitHub personal access tokens, database passwords, and API keys, are exported into .env files and left unchanged for months. Once leaked, these credentials remain valid until someone manually rotates them. If they are overprivileged, the blast radius extends far beyond the original workload.
The credentials an MCP server uses to access backend services carry the same operational risk as any other infrastructure secret. Treating them casually undermines the security of the entire workflow.
This article focuses on authentication mechanisms for MCP servers, why static tokens fail in the real world, and which authentication patterns are better at reducing the risk of exposure. For background and production architecture, see our security engineer’s introduction to MCP servers and secrets management and our guide on building secure and scalable MCP servers.
To understand secure MCP authentication in production, it is critical to distinguish between transport security and backend identity. According to the MCP specification, MCP communication operates across two layers(client-to-server and server-to-backend). Each layer requires a different authentication strategy, and confusing them leads to incomplete security controls.
This layer describes how AI clients or MCP hosts communicate with the MCP server. Locally, it uses a stdio process. The MCP server runs as a child process on the operating system, and trust is inherited from the local execution context. If a user can execute the binary that connects the client to the MCP server, they are effectively authorized at the transport level.
In remote deployments, where the MCP server is exposed over HTTP, the MCP authorization specification supports OAuth 2.1 for client authentication. These deployments may integrate with a server acting as an MCP authorization server or with third-party authorization servers(including Auth0 or Okta). Some implementations also rely on API keys issued by hosts such as Claude Desktop or OpenAI clients.
When engineers implement authentication at this layer, they often assume that it completes the security model. However, it does not address how the MCP server authenticates to external systems.
Layer 2 is where most authentication risks exist because the MCP server acts as the identity bridge to external backend services. Regardless of whether a local or remote server is used, this layer requires credentials to access these services.
This is the authentication surface that 88% of MCP servers depend on. The central question is: How does the MCP server safely authenticate to GitHub, PostgreSQL, AWS, or any external API?

Answering that requires a deliberate credential strategy. Depending on the service, that may involve using OAuth, workload identity, scoped API keys, or short-lived credentials.
This distinction between Layer 1 and Layer 2 matters because they solve different problems, and the MCP authorization spec primarily addresses transport-level authentication rather than backend credential management.

You cannot secure backend access solely by implementing OAuth at the transport layer. MCP authentication failures in production almost always originate at Layer 2, especially when static tokens are used.
It is not difficult to understand why developers default to static tokens. They are convenient. Most engineers already manage build systems, CI/CD pipelines, and application logic. So, creating additional infrastructure for secrets mechanism almost feels like an unnecessary overhead.
“I just need to generate a token and export it through the CLI or store it in a .env file." "It works locally, in CI, and in production." This mental model gives rise to common bad implementation patterns, including:
A typical MCP server configuration often looks like this:
Once the code works and integration succeeds, it feels like a job well done. However, these implementations create failure points.
Setting up static tokens with these poor patterns limits the ability to implement automated rotation. Token leaks would require manual updates across environments, pipelines, and, in some cases, multiple developer machines. This leads to delayed rotation, stale credentials, and extended exposure windows after system compromise.
Static tokens are frequently created with broad permissions because it is easier than designing scoped access.
For example, a GitHub personal access token may be granted full repository access when only repo:read is required. A database user may be granted GRANT ALL privileges when only SELECT is necessary. These patterns lead to credential misuse and expand the workload attack surface.
Manually distributing secrets amongst your developers introduces unsafe propagation methods. Tokens get shared via Slack messages, email threads, and WhatsApp messages. They get copied into CI configuration files and .env files. It then becomes unclear where your secrets live and which systems use them. A single hack into a developer's email could mean access to critical workloads.
With static tokens reused across environments and scattered among developers, it's difficult to get visibility into their usage. Questions such as Who used this token at 3 a.m.? or Was this access triggered by automation or a developer session? go unanswered. Without structured audit logging and scoped credentials, investigation turns into guesswork.
Instead of spending time remediating failures, that effort should go into building resilient systems with stronger authentication methods at the sever-backend layer.
When using OAuth for backend authentication, the MCP server participates in an authorization flow with the MCP client instead of storing long-lived tokens. In cases where no end user is involved, the server may instead use the client credentials flow to authenticate directly as an application.
In a typical OAuth exchange, the MCP server is registered with the backend provider using a client ID and client secret, either manually or through dynamic client registration. When a user or AI client needs access to a backend service, the MCP server delegates the user authorization request by redirecting the user to the provider to review and approve specified scopes. After approval, the MCP server receives an authorization code from the provider(which operates as both the authorization and resource server). This code is what gets exchanged for the short-lived access token, often accompanied by a refresh token.
The access token is scoped to the permissions the user requests and usually expires within a limited timeframe, typically between 1 and 24 hours. If continued access is required, the server can request a new access token using the refresh token without forcing the user through the full approval flow again.
A practical example is using a GitHub OAuth App instead of a PAT. It follows the same authorization code flow described above.

In the event of a breach, the risk profile changes:
Given these advantages, why is adoption still around 8.5 percent? Well, there are challenges with implementing this approach.
OAuth requires additional code implementation, including handling redirect URIs, state validation, retrieving authorization server metadata, authorization server discovery, token exchange logic, refresh workflows, and secure storage. And each of these has to be implemented separately for each backend provider. The more services that get integrated, the more code work multiplies.
For stdio-based local MCP servers, triggering a browser authorization flow during development interrupts the local testing loop. The process slows down because every restart might require repeated user interaction and authentication with the provider.
Not all backend services support OAuth. PostgreSQL databases and many legacy internal services still rely on static credentials. In these cases, OAuth is not an option.
Even with OAuth, tokens still need to be stored somewhere. If access or refresh tokens are stored insecurely, the system has replaced a long-lived secret with another poorly managed credential. Tools such as Doppler provide structured storage that allows the tokens to be fetched securely at runtime.
When these challenges are weighed against the benefits, OAuth often becomes the safer default when supported. However, it does not eliminate credential storage entirely. There are authentication patterns that remove the need for token storage.
Authentication via workload identity shifts from presenting stored tokens to proving identity through cryptographic attestation. One of the most common implementations is through OpenID Connect(OIDC). In this model, a cloud provider acts as the identity issuer. For example, when an MCP server pod running in Kubernetes needs to access an AWS service, a Kubernetes service account is preconfigured with a role that grants this access.
The cloud provider issues access tokens in the form of signed JSON Web Tokens(JWTs) that the MCP server presents to the target service. The service validates the signature against the provider's public keys and grants temporary access to the protected resources for 15–60 minutes.
Since the provider handles the lifecycle of the token, this approach presents several benefits:
However, this approach has limitations.
Firstly, Workload identity is not universally supported. Most SaaS APIs, including GitHub, do not accept OIDC-issued JWTs for API access.
Secondly, it is not practical for local development setups because it requires a managed platform, such as Kubernetes, or a cloud identity service. Workload identity is the most commonly used in AWS IAM roles, GCP service accounts, and Azure managed identities.
Since this approach isn't widely supported, an alternative is to use short-lived service tokens with Doppler.
Instead of embedding long-lived credentials directly into the MCP server, the server uses a Doppler service token to fetch the required backend credential at runtime.
For example, if a GitHub PAT is stored in Doppler, the MCP server could retrieve it when starting. The advantage is that the PAT exists only in memory during the lifetime of the MCP process. And when rotated in Doppler, the change propagates without requiring configuration file edits.
The aim is to move away from long-lived static tokens. Because they are more prone to misuse, they create predictable failure points.
In the MCP ecosystem, the server acts as an access point between an AI client and your critical systems. When credential management between the MCP server and backend services is weak, an attacker does not need to compromise the AI host or the MCP server itself. The authentication layer becomes the entry point.
Understanding how these failures occur makes it easier to prevent them.
When developers grant secrets excessive privileges and expose them to MCP tools, the system becomes susceptible to supply chain attacks. A token intended for read-only access may instead carry admin permissions.
Through prompt injection, an attacker can trick an AI tool into executing a command it wasn’t intended to execute. For example, if a server holds a GitHub token with full repository access, the AI tool can be persuaded to push malicious code directly into a production branch.
Prevention:
When credentials such as database passwords or API keys are stored as long-lived environment variables, they remain in process memory for the lifetime of the server. This increases the risk of exposure.
If the MCP server crashes and generates a memory dump, sensitive data held in memory may be captured. Attackers who gain access to that dump can extract and reuse those credentials.
Prevention:
To simplify setup processes, some teams use a single account token to handle requests from multiple engineers to the MCP server. This centralizes access under one identity. If that token is leaked, it can be used to access any repository or resource the shared account has permission to access.
Prevention:
When engineers leave, and their credentials are not revoked, those tokens become stale. In many cases, they remain unmanaged and continue to provide access long after the owner has left the organization. If those credentials are later compromised, they can be used to access sensitive systems undetected.
Prevention:
For debugging purposes, MCP servers may log every outgoing HTTP request. These logs usually get aggregated into SIEM platforms such as Splunk or Datadog. When a bearer token is included in the request header and not properly redacted, it can be captured in plain text within those logs. An attacker with basic log-view permissions can extract that token and reuse it.
Prevention:
Seeing how these failure points are exploited makes a deliberate authentication strategy necessary.
A critical part of securing your MCP servers is matching backend services to the authentication types they support. A mismatch, even with a powerful authentication mechanism, can still create breaches. It is therefore important to know when to use each type.
Appropriate when:
Appropriate when:
Appropriate when:
Appropriate when:
Static tokens fail because of how they are handled: no rotation policies, over-privileged assignment, and unsafe distribution methods. These practices make your MCP workflows insecure. The next step is to audit your existing servers and identify where static tokens can be replaced with OAuth or workload identity. Then, adopt Doppler as a secrets manager to enforce rotation policies, issue short-lived tokens, and maintain proper audit logging.



Trusted by the world’s best DevOps and security teams. Doppler is the secrets manager developers love.
