PromptHub
Developer Tools Infrastructure & DevOps

Stop Wrestling with OAuth! tsidp Is the Secret Weapon for Tailscale Networks

B

Bright Coding

Author

15 min read
41 views
Stop Wrestling with OAuth! tsidp Is the Secret Weapon for Tailscale Networks

Stop Wrestling with OAuth! tsidp Is the Secret Weapon for Tailscale Networks

Every developer knows the pain. You've built something beautiful—a sleek internal dashboard, a powerful API gateway, a collaboration tool your team desperately needs. Then comes the authentication requirement. Suddenly, you're drowning in OAuth 2.0 flows, JWT validation libraries, and the endless nightmare of identity provider configuration. Hours turn into days. Your core project gathers dust while you wrestle with protocols that were designed by committee, not by people who actually ship code.

What if I told you there's a way to eliminate this friction entirely? What if your existing Tailscale network—already securing your infrastructure with zero-config VPN magic—could become your identity provider? No more external auth services. No more monthly bills for user management. No more vendor lock-in from giants who treat your login flow as a data harvesting opportunity.

Enter tsidp, the experimental but rapidly maturing OIDC identity provider from Tailscale. This isn't just another auth tool. It's a paradigm shift: your tailnet becomes your identity backbone. In this deep dive, I'll expose exactly how tsidp works, why it's causing a stir among infrastructure-savvy developers, and how you can deploy it in production today. Buckle up—your authentication headaches are about to vanish.

What is tsidp?

tsidp (Tailscale Identity Provider) is an OpenID Connect (OIDC) and OAuth 2.0 Identity Provider server that transforms your Tailscale network into a full-fledged authentication authority. Created by Tailscale as an experimental community project, it leverages the cryptographic identity guarantees already present in your tailnet to issue standard OIDC tokens that any OIDC-compatible application can consume.

Here's the genius: Tailscale already knows who you are. Every node in your tailnet is cryptographically verified, every user is authenticated through your existing identity provider (Google Workspace, Okta, Microsoft Entra, or Tailscale's own system). tsidp simply bridges this rock-solid identity layer into the standard OIDC ecosystem that thousands of applications already support.

Why is this trending now? Three forces are converging:

  • The rise of secure internal tools: Post-2020, every company became a software company. Internal tools proliferated, and each needed auth. Developers are exhausted from reinventing this wheel.
  • Zero Trust architecture mandates: Security teams demand identity verification for every request, not just perimeter defense. tsidp delivers this without adding network complexity.
  • The MCP revolution: The Model Context Protocol for AI agent communication requires authenticated connections. tsidp is explicitly designed to support MCP authorization with Dynamic Client Registration.

⚠️ Heads up: tsidp carries a [!CAUTION] flag—it's experimental and actively developed. Breaking changes may occur. But don't let "experimental" scare you. The foundation is Tailscale's battle-tested tsnet library, and early adopters are already running it in production with remarkable stability.

Key Features That Make tsidp Irresistible

Let's dissect what makes tsidp technically compelling. This isn't marketing fluff—these are architectural advantages that solve real problems.

Native Tailscale Integration

tsidp runs as a node on your tailnet, not as an external service. This means it inherits Tailscale's NAT traversal, automatic HTTPS certificates via Let's Encrypt, and MagicDNS resolution. No port forwarding. No DNS configuration. No certificate management. Your IdP URL becomes something like https://idp.your-tailnet.ts.net—automatically secured and globally reachable if you enable Funnel.

Dynamic Client Registration (DCR)

Traditional OIDC providers require manual client registration—creating entries for each application, generating client IDs and secrets, configuring redirect URIs. tsidp supports OAuth 2.0 Dynamic Client Registration, allowing applications to self-register. This is essential for modern architectures where services spin up dynamically in Kubernetes or serverless environments.

RFC 8693 Token Exchange (STS)

The -enable-sts flag activates OAuth Token Exchange, letting you trade one token type for another. This enables sophisticated patterns: exchanging a Tailscale-derived identity token for a service-specific token with narrowed scopes, or implementing step-up authentication where additional verification unlocks elevated privileges.

Application Capability Grants

Tailscale's grant system controls access to tsidp's administrative functions. Using HuJSON (Human JSON) in your ACLs, you define precisely which users can access the admin UI, register clients dynamically, or receive custom claims. These grants are evaluated per-request and apply immediately—no service restarts required.

Flexible Deployment Models

Run it via Docker with pre-built images from GitHub Container Registry, build your own container, or execute directly with Go. The -funnel flag extends reach beyond your tailnet to SaaS applications, while -local-port enables secure local development without any network exposure.

Custom Claims Injection

Through the extraClaims mechanism in capability grants, you can embed arbitrary data—booleans, strings, numbers, arrays—directly into ID tokens and userinfo responses. This eliminates the need for secondary user database lookups in many scenarios.

Use Cases Where tsidp Absolutely Dominates

1. Internal Tool Authentication Without the Enterprise Tax

Your team built a Grafana instance, a JupyterHub deployment, and a custom admin panel. Traditionally, you'd pay Okta or Auth0 per user monthly, or struggle with each tool's bespoke LDAP integration. With tsidp, your Tailscale identity becomes the single source of truth. Every team member already has Tailscale installed. Authentication becomes invisible.

2. Proxmox VE Cluster SSO

Virtualization infrastructure demands privileged access control. The tsidp repository includes a dedicated Proxmox configuration guide. Instead of managing separate user databases across your Proxmox nodes or relying on Microsoft's complex AD integration, point Proxmox at your tsidp instance. Your existing Tailscale users gain immediate, auditable access with proper role mapping.

3. MCP-Enabled AI Agent Ecosystems

The Model Context Protocol is reshaping how AI agents interact with tools and data. But authorization in distributed agent systems is a nightmare—how do you authenticate a server that spins up to handle one request? tsidp's Dynamic Client Registration and full MCP Authorization spec compliance solve this. The repository includes working examples for both MCP Client/Server and MCP Client/Gateway Server topologies.

4. SaaS Application Bridging with Funnel

Need to use a cloud-hosted service that supports custom OIDC, but your security policy forbids external IdPs? Enable --funnel on tsidp, and your tailnet-hosted identity provider becomes globally accessible. The SaaS application sees a standard OIDC endpoint; your security team sees complete control and auditability within Tailscale's infrastructure.

5. Development Environment Parity

The -local-port flag lets developers run tsidp entirely locally. No tailnet required for initial testing. Configure your application to authenticate against localhost:8080, iterate rapidly, then deploy to production with identical configuration—just change the URL to https://idp.your-tailnet.ts.net.

Step-by-Step Installation & Setup Guide

Ready to deploy? Here's the complete path from zero to authenticated.

Prerequisites Checklist

Before touching any commands, verify:

  • MagicDNS enabled in your Tailscale admin console (Settings → DNS)
  • HTTPS certificates enabled (Settings → DNS → HTTPS Certificates)
  • A Tailscale authentication key or OAuth client secret ready
  • Docker and Docker Compose installed (recommended path)
  • Administrative access to modify your tailnet's ACL/grants

Step 1: Obtain Authentication Credentials

Navigate to your Tailscale admin console Keys page. Create an auth key with appropriate tags, or create an OAuth client with Auth Keys → Write scope for enhanced security.

Step 2: Deploy with Docker Compose

Create compose.yaml with this exact configuration:

services:
  tsidp:
    container_name: tsidp
    image: ghcr.io/tailscale/tsidp:latest
    volumes:
      - tsidp-data:/data
    environment:
      - TAILSCALE_USE_WIP_CODE=1 # tsidp is experimental - needed while version <1.0.0
      - TS_STATE_DIR=/data # store persistent tsnet and tsidp state
      - TS_HOSTNAME=idp # Hostname on tailnet (becomes idp.your-tailnet.ts.net)
      - TSIDP_ENABLE_STS=1 # Enable OAuth token exchange
      # Optional: Tailscale auth key for automatic node registration
      # - TS_AUTHKEY=tskey-auth-xxxxx
volumes:
  tsidp-data:

⚠️ Critical: The tsidp-data volume is not optional for production. Without persistent state, every restart triggers re-registration, invalidates sessions, and destroys dynamic client registrations. Serverless deployments without persistent storage are explicitly discouraged.

Step 3: Launch and Verify

# Start the service
docker compose up -d

# Monitor initialization (watch for certificate generation)
docker compose logs -f

Initial TLS certificate provisioning may take 2-3 minutes. Once stable, browse to https://idp.your-tailnet.ts.net—substituting your actual tailnet name—to confirm operation.

Step 4: Configure Application Capability Grants

Without grants, all administrative access is denied by default. Add to your tailnet's HuJSON ACL:

"grants": [
  {
    "src": ["group:admin"],
    "dst": ["tag:tsidp"],
    "app": {
      "tailscale.com/cap/tsidp": [
        {
          "allow_admin_ui": true,
          "allow_dcr": true,
          "users": ["*"],
          "resources": ["*"]
        }
      ]
    }
  }
]

Apply via the admin console—no tsidp restart needed.

Alternative: OAuth Client Secret Setup

For enhanced security, replace TS_AUTHKEY with:

environment:
  - TS_AUTHKEY=tskey-client-xxxxx
  - TS_ADVERTISE_TAGS=tag:tsidp

Ensure tag:tsidp exists in your ACL's tagOwners section.

REAL Code Examples from the Repository

Let's examine actual implementation patterns from the tsidp codebase and documentation.

Example 1: Core Docker Compose Deployment

The repository's primary deployment mechanism is this Docker Compose configuration. Let's dissect it thoroughly:

services:
  tsidp:
    container_name: tsidp
    image: ghcr.io/tailscale/tsidp:latest
    volumes:
      - tsidp-data:/data    # Named volume for persistent state storage
    environment:
      # Required: Enables experimental code paths while tsidp < v1.0.0
      - TAILSCALE_USE_WIP_CODE=1
      
      # Maps to -dir flag: Where tsnet stores node keys and tsidp keeps OIDC data
      - TS_STATE_DIR=/data
      
      # Maps to -hostname flag: Determines MagicDNS name (idp.your-tailnet.ts.net)
      - TS_HOSTNAME=idp
      
      # Maps to -enable-sts flag: Activates RFC 8693 Token Exchange
      # Essential for MCP authorization and advanced token transformation
      - TSIDP_ENABLE_STS=1
      
      # Optional automatic registration - comment out for interactive auth
      # - TS_AUTHKEY=tskey-auth-xxxxx
volumes:
  tsidp-data:  # Docker-managed volume survives container recreation

Why this matters: The TS_STATE_DIR environment variable maps directly to the -dir CLI flag. This directory stores two distinct state types: tsnet's WireGuard node keys (enabling tailnet participation without re-authentication) and tsidp's OIDC operational data (registered clients, signing keys, session state). The separation of concerns between Tailscale's network layer and the OIDC application layer is clean, but both require persistence.

Example 2: Capability Grant with Custom Claims

This HuJSON snippet from the README demonstrates tsidp's sophisticated authorization model:

"grants": [
  {
    // Very permissive and suitable only for testing.
    "src": ["*"],
    "dst": ["*"],

    // Example of a grant for tsidp:
    "app": {
      "tailscale.com/cap/tsidp": [
        {
          // allow access to UI
          "allow_admin_ui": true,

          // allow dynamic client registration
          "allow_dcr": true,

          // Secure Token Service (STS) controls
          // Which users can request token exchanges
          "users":     ["*"],
          // Which resource servers can be targeted
          "resources": ["*"],

          // extraClaims are included in the id_token
          // recommend: keep this small and simple
          "extraClaims": {
            "bools": true,
            "strings": "Mon Jan 2 15:04:05 MST 2006",
            "numbers": 180,
            "array1": [1,2,3],
            "array2": ["one", "two", "three"]
          },

          // include extraClaims data in /userinfo response
          "includeInUserInfo": true,
        },
      ],
    },
  },
],

Deep dive: This grant uses Tailscale's application capability system, a relatively recent addition to the platform. The src and dst fields use standard Tailscale ACL semantics—here wildcarded for testing, but typically restricted to specific users, groups, or tags. The app block is where tsidp-specific magic happens.

The extraClaims object is particularly powerful. These values become part of the JWT id_token that tsidp issues. Applications receiving this token can extract bools, strings, numbers, or arrays without additional API calls. The includeInUserInfo flag mirrors these claims in the UserInfo endpoint response, ensuring compatibility with applications that don't parse ID tokens directly.

Example 3: Direct Go Execution for Development

For contributors or those avoiding containers, the repository supports direct Go execution:

# Clone the Tailscale repository
$ git clone https://github.com/tailscale/tsidp.git
$ cd tsidp

# run with default values for flags
# TAILSCALE_USE_WIP_CODE=1: Required experimental flag
# TS_AUTHKEY: Your tailnet authentication credential
# TSNET_FORCE_LOGIN=1: Forces re-authentication, useful when switching tailnets
$ TAILSCALE_USE_WIP_CODE=1 TS_AUTHKEY={YOUR_TAILSCALE_AUTHKEY} TSNET_FORCE_LOGIN=1 go run .

Development insight: The TSNET_FORCE_LOGIN=1 environment variable is invaluable during iterative development. Without it, tsnet caches authentication state and may silently connect to the wrong tailnet or use expired credentials. This flag ensures clean authentication on every startup. Note that go run . compiles and executes in one step—slower than a prebuilt binary but essential when modifying source.

Example 4: OAuth Client Secret Configuration

The repository documents modern OAuth-based authentication:

environment:
  # tskey-client- prefix identifies OAuth client secrets vs traditional auth keys
  - TS_AUTHKEY=tskey-client-xxxxx
  # REQUIRED when using OAuth client secrets: advertises tag for ACL evaluation
  - TS_ADVERTISE_TAGS=tag:tsidp

Security architecture: OAuth client secrets represent a significant security upgrade. Traditional auth keys grant node-level access permanently. OAuth clients can be scoped (here, limited to Auth Keys → Write), rotated independently, and audited through Tailscale's OAuth console. The TS_ADVERTISE_TAGS requirement ensures the node appears in your ACL with predictable identity, enabling fine-grained dst restrictions in grants.

Advanced Usage & Best Practices

Production Hardening

Never deploy without persistent storage. The README's [!WARNING] about serverless deployments exists because operators have learned this painfully. Use Docker volumes, bind mounts to redundant storage, or Kubernetes PVCs with appropriate backup policies.

Certificate Monitoring

Initial TLS provisioning delays are normal but can surprise automation. Implement health checks that tolerate 3-5 minute startup times, or pre-provision certificates by running tsidp interactively before enabling dependent services.

Grant Minimal Permissions

The example grant with "src": ["*"] is marked "suitable only for testing" for good reason. Production grants should:

  • Restrict src to specific groups (group:platform-team)
  • Limit dst to tagged tsidp instances (tag:tsidp)
  • Scope users and resources in STS configurations
  • Keep extraClaims minimal—every byte increases token size

Funnel Considerations

Enabling --funnel exposes tsidp to the public internet. This is necessary for SaaS integration but increases attack surface. Combine with strict capability grants and consider funnel-specific monitoring. The trade-off: external accessibility versus tailnet-only security.

Logging Strategy

Use -log debug during initial deployment, then revert to info. The -debug-all-requests and -debug-tsnet flags capture sensitive data—never enable in production. Structured logging integration would be a welcome community contribution.

Comparison with Alternatives

Capability tsidp Keycloak Auth0 Okta Authentik
Infrastructure required Tailscale node only Java application server SaaS only SaaS only Docker container
Cost for internal use Free (Tailscale plan) Free (self-hosted) $23+/month $15+/user/month Free (self-hosted)
Tailnet-native identity ✅ Built-in ❌ Manual federation ❌ External ❌ External ❌ Manual config
Dynamic Client Registration ✅ Native ✅ Complex setup ✅ Enterprise tier ✅ Limited ✅ Partial
RFC 8693 Token Exchange ✅ Flag-enabled ❌ Not native ❌ Not native ❌ Not native ❌ Not native
MCP Authorization ready ✅ Documented ❌ Unknown ❌ Unknown ❌ Unknown ❌ Unknown
Operational complexity Low Very High Low Low Medium
Certificate management Automatic (Tailscale) Manual/Let's Encrypt Handled by vendor Handled by vendor Manual/Let's Encrypt
Network reachability Tailnet + optional Funnel Requires exposure Global SaaS Global SaaS Requires exposure

The verdict: Choose tsidp when you're already invested in Tailscale, value infrastructure simplicity, need MCP support, or want to eliminate per-user SaaS costs for internal tools. Choose alternatives when you need broader protocol support (SAML, LDAP without Tailscale), have complex federation requirements, or aren't using Tailscale's network layer.

FAQ

Is tsidp production-ready despite the experimental label?

Many operators run tsidp in production successfully. The experimental designation primarily signals potential API changes before v1.0.0. Pin your deployment to specific image digests rather than :latest, monitor the repository for breaking changes, and maintain capability grant backups.

Can I use tsidp without Docker?

Absolutely. The repository supports direct Go execution and custom container builds via make docker-image. However, Docker provides the smoothest experience with automatic volume management and environment variable mapping.

What happens if my tsidp container restarts without persistent storage?

Catastrophic state loss. The node re-registers with a new Tailscale identity, invalidating all existing OIDC client registrations and user sessions. Dynamic clients must re-register. This is why the README explicitly warns against serverless deployments.

How do I authenticate SaaS applications outside my tailnet?

Enable the --funnel flag or TSIDP_USE_FUNNEL=1 environment variable. This exposes tsidp through Tailscale Funnel with public HTTPS endpoints. Your SaaS application configures tsidp like any standard OIDC provider.

Can I migrate from another OIDC provider to tsidp?

Migration is straightforward for applications using standard OIDC discovery. Update the issuer URL to your tsidp endpoint, reconfigure client credentials (or use Dynamic Client Registration), and verify claim mappings. The extraClaims system helps replicate custom assertions from legacy providers.

Does tsidp support multi-factor authentication?

Indirectly, through Tailscale's identity layer. If your tailnet requires MFA for Tailscale login (via your configured identity provider), that MFA assurance propagates through tsidp. tsidp itself doesn't implement separate MFA—that's a feature, not a limitation, as it centralizes security policy.

Where do I get help or report issues?

File issues on the GitHub repository. This is a community project without formal Tailscale support SLAs. Engage constructively—the maintainers are responsive to well-documented bug reports and feature requests.

Conclusion

Authentication infrastructure has been a necessary evil for too long—complex, expensive, and operationally draining. tsidp inverts this paradigm by leveraging identity guarantees you already possess through Tailscale, transforming them into standard OIDC tokens that any modern application consumes effortlessly.

The experimental label shouldn't deter pragmatic adopters. The foundation is solid tsnet engineering, the deployment model is elegantly simple, and the feature set—Dynamic Client Registration, RFC 8693 Token Exchange, MCP authorization support—rivals enterprise platforms costing thousands annually.

My assessment? If you're running Tailscale and building internal tools, tsidp deserves immediate evaluation. The time savings on auth infrastructure alone justify the setup effort. As the project matures toward v1.0.0, early adopters will have shaped its direction and mastered its patterns.

Ready to eliminate your authentication complexity? Head to the tsidp repository on GitHub, deploy your instance today, and join the growing community of developers who've discovered that the best identity provider was hiding in their tailnet all along.


Have you deployed tsidp in production? Share your configuration patterns and lessons learned—the community thrives on real-world battle stories.

Comments (0)

Comments are moderated before appearing.

No comments yet. Be the first to share your thoughts!

Support us! ☕