DEV Community

Cover image for Getting Authentication Right is Critical to Running MCP Servers
Juan Antonio Osorio for Stacklok

Posted on • Edited on

6 1

Getting Authentication Right is Critical to Running MCP Servers

Any time you’re connecting APIs, you have to consider security implications. The same is true for Model Context Protocol Servers. In our experience, it’s important to have a strong separation of authentication (verifying who you are) and authorization (deciding what you can do).

We built ToolHive to make it easier and more secure to run MCP Servers. In this post, we’ll explore why ToolHive leverages OpenID Connect (OIDC) for authentication, how it distinguishes authn vs authz, and real-world scenarios it enables – from Google sign-ins to Kubernetes service tokens. (A follow-up post will dive into ToolHive’s built-in authorization framework in detail.)

Authentication vs Authorization in ToolHive

It’s crucial to understand the difference between authentication (authn) and authorization (authz). Authentication is about identity – proving who is making a request (e.g. via a login or token). Authorization is about permissions – determining if that identity is allowed to perform a given action. ToolHive treats these as separate concerns.

In practice, this means ToolHive first ensures it knows who the caller is (authenticating via OIDC), and only then applies rules about what that caller can do (ToolHive’s own permission model). By explicitly separating authn and authz, ToolHive can rely on well-proven identity systems for authentication and avoid conflating identity with access control. This leads to a cleaner, more secure design.

Why OpenID Connect for Authentication?

OpenID Connect (OIDC) is a widely adopted protocol for user authentication built on OAuth 2.0. It provides a standard way to get an identity token (usually a JWT) that confirms a user’s identity, issued by a trusted Identity Provider (IdP). In other words, OIDC lets ToolHive outsource the heavy lifting of verifying identities to providers like Google, GitHub, or corporate SSO systems, and then consume a verifiable assertion of the user’s identity (How OpenID Connect Works - OpenID Foundation). This has several advantages:

  • Standard and Interoperable – OIDC is an interoperable auth protocol based on OAuth2, simplifying identity verification. Because of this, ToolHive can integrate with any OIDC-compliant identity provider without custom code. The protocol even supports features like discovery and JWT signature verification out-of-the-box, making integration easier and less error-prone.

  • Proven and Secure – By using OIDC, ToolHive leverages the security of battle-tested identity systems. OIDC providers handle the user login UI, multi-factor auth, password storage, etc., so ToolHive itself never sees raw passwords or credentials. ToolHive simply trusts the ID token from the provider if it’s correctly signed and not expired.

  • Decoupled Identity Management – Adopting OIDC means ToolHive doesn’t have to be its own identity service. Instead of reinventing user management, it “piggybacks” on existing identity solutions. For organizations, this is huge: you can plug ToolHive into your existing SSO/IdP (Google, Microsoft Entra ID, Okta, etc.) and instantly use those identities.

  • Flexible for Humans and Services – OIDC isn’t just for interactive user login. It also works great for service-to-service auth via JWTs. ToolHive’s use of OIDC means it can authenticate both end-users and other services in a unified way (more on this below). The common factor is that identity is conveyed through a trusted OIDC token, whether it came from an OAuth web flow or from a service account in Kubernetes.

OIDC Use Cases in ToolHive

How does this OIDC-based authentication play out in practice? Here are two common scenarios where ToolHive’s approach shines:

🔐 User Login via Google (OIDC) – Imagine a developer wants to run an MCP server that requires user authentication, and they prefer using their Google credentials. Because Google’s identity platform supports OpenID Connect, ToolHive can delegate the login to Google. The user would be redirected to sign in with Google, Google would then provide an ID token to ToolHive representing the user’s Google identity (email, user ID, etc.). ToolHive trusts this token (after verifying its signature and issuer). The end result: the user is authenticated and can use the MCP server using their Google account.

🤖 Service-to-Service Auth with Kubernetes – In some cases, you might want to call an MCP server from a service running in a Kubernetes cluster. With ToolHive’s OIDC support this can be done fairly seamlessly. Kubernetes can issue service account tokens that are actually OIDC JWTs (specifically, bound service account tokens are OIDC identity token). This means a microservice in your cluster can present its Kubernetes JWT to ToolHive; ToolHive will recognize and validate it as an OIDC token. The beauty here is that the service doesn’t need a separate API key or password – its cluster-issued token is enough to prove its identity. This is perfect for scenarios like an internal app calling an MCP server managed by ToolHive; the auth is secure, token-based, and fully automated by existing infrastructure.

How ToolHive Builds on the MCP Spec’s Recommendations

If you’ve read the MCP specification, you might know that the MCP world has been gravitating towards OAuth 2.x for securing tool access. The latest MCP spec (as of early 2025) actually standardizes an OAuth 2.1-based authorization flow for MCP servers. In essence, the spec suggests that an MCP server should use OAuth 2.1 so that clients can obtain tokens to access it, rather than using simplistic API keys. This is a great step for security!

However, the spec’s approach couples authentication and authorization together in the OAuth flow. It essentially envisions the MCP server itself acting as both the Authorization Server and Resource Server in OAuth terms. This means an MCP server developer would need to implement OAuth endpoints (authorization, token, client registration, etc.) and manage tokens, which is non-trivial. In other words, every MCP server might end up reinventing identity and auth if following the spec naively – a recipe for inconsistent implementations and potential security issues.

ToolHive’s philosophy is to leverage existing best practices instead of coming up with our own thing. Rather than turning every MCP tool into an OAuth mini-server, ToolHive acts as the gateway that handles auth on behalf of the MCP servers it runs. It can use OIDC for authentication (as described earlier) to validate the user or service’s identity, and then applies its own authorization logic to decide if that identity can access a given tool. This clean separation aligns with best practices and the principle of least privilege. The MCP spec’s OAuth guidance is not thrown away. In fact, ToolHive aligns with it by using the OIDC/OAuth standards.

What does it look like?

ToolHive has built-in support for OIDC token validation. Enabling it in your server would look as follows:

$ thv run \
    --oidc-audience "some-audience" \
    --oidc-client-id "some-client" \
    --oidc-issuer https://some-oidc-issuer.com/ \
    --oidc-jwks-url https://some-oidc-issuer.com/path/to/jws \
    $MY_MCP_SERVER
Enter fullscreen mode Exit fullscreen mode

Using those flags, the MCP server proxy that ToolHive spawns will validate the OIDC token with the relevant information.

Let’s look at this in a Kubernetes setup. Let’s say our deployment looks as follows:

  • Service account name: my-service
  • Namespace: mcp-servers
  • Audience expected by the MCP server: toolhive
  • The OIDC issuer for your cluster (usually the Kubernetes API server): https://kubernetes.default.svc
  • The JWKS URL (location of public keys used to verify the JWT signature): https://kubernetes.default.svc/openid/v1/jwks

The ToolHive pod would look as follows:

apiVersion: v1       
kind: Pod                    
metadata:        
  labels:                                    
    app: thv-everything          
    app.kubernetes.io/name: thv-everything  
  name: thv-everything
  namespace: mcp-servers          
spec:                                        
  containers:    
    - args:                          
        - run                
        - --foreground=true                      
        - --port=8080  
        - --transport=stdio
        - --name=mcp-everything
        - --oidc-audience "toolhive"
        - --oidc-issuer=https://kubernetes.default.svc
        - --oidc-client-id=my-service.mcp-servers.svc.cluster.local
        - --oidc-jwks-url=https://kubernetes.default.svc/openid/v1/jwks
        - docker.io/mcp/everything               
      image: ghcr.io/stackloklabs/toolhive:latest
      name: toolhive     
      serviceAccountName: toolhive-sa
      ports:                                 
        - containerPort: 8080
          name: http                                             
          protocol: TCP
Enter fullscreen mode Exit fullscreen mode

This will only allow requests coming from a pod that has the my-service service account in the mcp-servers namespace.

Did we ever mention we have Kubernetes support? We do! In fact, this will spawn an MCP server instance in Kubernetes. We’ll talk about this in detail later on.

What’s next?

With OIDC, ToolHive is able to validate identities, which is a great first step. However, this is not the end of the road when locking down a deployment. Authorization or Access Control is a very important piece that needs to be considered. Authorization itself can be challenging, but the ToolHive team decided to give it a go anyways!

Stay tuned for a follow-up post, where we’ll dive into ToolHive’s built-in authorization framework. We’ll see how ToolHive defines and checks permissions for MCP servers, ensuring that once a user/service is authenticated via OIDC, they only get to do what they’re allowed to. By separating authn and authz, ToolHive makes it easier to reason about security in your AI toolchain – and ultimately, to keep your MCP servers both easy and secure.

Top comments (0)