惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
GRAHAM CLULEY
P
Privacy International News Feed
L
LINUX DO - 热门话题
C
Cisco Blogs
T
Tor Project blog
AWS News Blog
AWS News Blog
K
Kaspersky official blog
C
Cybersecurity and Infrastructure Security Agency CISA
Cyberwarzone
Cyberwarzone
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CERT Recently Published Vulnerability Notes
P
Proofpoint News Feed
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
Lohrmann on Cybersecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
Project Zero
Project Zero
Attack and Defense Labs
Attack and Defense Labs
Latest news
Latest news
The Last Watchdog
The Last Watchdog
Apple Machine Learning Research
Apple Machine Learning Research
Simon Willison's Weblog
Simon Willison's Weblog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cloudbric
Cloudbric
Last Week in AI
Last Week in AI
S
Security Affairs
Google DeepMind News
Google DeepMind News
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
有赞技术团队
有赞技术团队
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
P
Palo Alto Networks Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Security @ Cisco Blogs
V
V2EX
S
Secure Thoughts
人人都是产品经理
人人都是产品经理
月光博客
月光博客
博客园_首页
T
Troy Hunt's Blog
爱范儿
爱范儿
N
News and Events Feed by Topic
Hugging Face - Blog
Hugging Face - Blog
雷峰网
雷峰网
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Privacy & Cybersecurity Law Blog
V
Vulnerabilities – Threatpost

Duende Software Official Site

Duende Software Duende Software Duende Software Duende Software Duende Software Duende Software Stop AI Bots from Wasting Your Server How Duende IdentityServer Filters Claims (And Why It Matters) Core vs Extended Protocols in Duende IdentityServer v8: What You Get and When You Need More Your IdentityServer v8 Upgrade Checklist: A Quick Pre-Flight Guide Setting Up SAML Single Sign-On in ASP.NET with Duende IdentityServer Your Identity, Your Terms: Duende's Modular Identity Infrastructure and v8.x Release Duende Spring Launch '26: Identity Infrastructure That Expands With You SAML and OpenID Connect (OIDC): Coexistence, Not Competition The 9 Components of SAML You Need to Know, Ranked by Importance The Cost of NOT Implementing Financial-Grade Security Token Issuer Isolation: Why It Matters for Security and Compliance The Composable Identity Pattern: Build What You Need, Skip What You Don't Multi-Brand Identity: When Your Company Needs More Than One Face Post-Quantum Cryptography in .NET 10: A Practical Guide The field Keyword in C# 14: Write Less, Validate More The Real Cost of Build vs. Buy for Identity OAuth 2.1 Made Simple: The Only Flows You Need Beyond localhost: Multi-Instance ASP.NET Core Deployment with .NET 10 Harden Your .NET JSON Deserialization with System.Text.Json and JsonSerializerOptions.Strict ASP.NET Core Cookie Size Limits in Production: Causes and Fixes The Emergency Stop Button - Implementing Immediate Token Revocation in .NET 10 The 2025 OWASP Top 10 and IdentityServer Update Guidance for CVE-2026-40372 - ASP.NET Data Protection Why a Standard JWT Access Token Matters The Identity Governance Checklist You Wish You Had Six Months Ago The History and Future of SAML: Why a 20-Year-Old Protocol Still Matters The Cookie Apocalypse Already Happened Verify - Open Source Sponsorship Why Identity Is Infrastructure, Not a Feature Extending Duende IdentityServer Server-Side Sessions with Dynamic User Metadata Give Your AI Coding Assistant Duende Expertise with Agent Skills and MCP Server Triggering User Registration via OpenID Connect with Duende IdentityServer Improving .NET Security Code with C# 14 Property Extensions Developing Audit Logs with Duende IdentityServer Events Patch Releases: Addressing CVE-2026-26127 in Microsoft.BCL.Memory Client-Initiated Backchannel Authentication (CIBA) in ASP.NET Core 10 with Duende Identity Server Rate Limiting IdentityServer Endpoints It's Probably DNS - Can You Dig It? Security Lingo Explained: Encode vs Encrypt vs Hash Implementing Zero Trust with Resource Isolation Security Lingo Explained: JWT DPoP Security for .NET APIs with JwtBearer Extensions v1.0.0 Announcing the Duende IdentityServer4 Migration Analysis Tool BenchmarkDotNet - Open Source Sponsorship Security Lingo Explained: PAR Why Signing Key Rotation Matters in OpenID Connect and Duende IdentityServer Security Lingo Explained: OP Duende Year-End Review 2025 Security Lingo Explained: BCP Security Lingo Explained: DPoP Security Lingo Explained: Auth Secure frontend apps with the BFF Pattern Scaling with Duende IdentityServer, MCP, and AI Duende IdentityServer v7.4 is now available Duende BFFv4 is now available Securing OpenAPI and Swagger UI with OAuth in .NET 10 Building a Federation Gateway with Duende IdentityServer: Strategies and Considerations for Identity Orchestration
Duende Software
Maarten Balliauw · 2026-07-07 · via Duende Software Official Site
Summary: MCP servers and native apps bind to random ports at startup, which breaks IdentityServer's default exact-match redirect URI validation. Duende IdentityServer ships a built-in opt-in validator (AddAppAuthRedirectUriValidator) that accepts any port when the client registers http://127.0.0.1 as its redirect URI and has PKCE required. This aligns with RFC 8252's requirement that authorization servers allow any loopback port. If you need localhost or IPv6 loopback support beyond 127.0.0.1, a custom IRedirectUriValidator covers those cases.

If you're building OAuth 2.0 authentication for a Model Context Protocol (MCP) server, you've likely hit a wall: your server binds to a random port at startup, but IdentityServer's default redirect URI validation requires an exact match. http://127.0.0.1:12345/callback is not the same as http://127.0.0.1:54321/callback, and the authorization request fails.

This post explains why this happens, what the standards say, and how to configure Duende IdentityServer to handle it correctly.

Why MCP Servers Use Ephemeral Ports

MCP servers are local tools that run on a developer's machine and expose AI model capabilities to clients like Claude Desktop or VS Code extensions. When an MCP server needs to complete an OAuth flow, it opens a local HTTP listener to receive the authorization code callback. Because multiple MCP servers may run simultaneously, each one asks the OS for any available port rather than hardcoding one.

This is the same pattern used by native desktop apps, CLI tools, and developer utilities. The redirect URI looks like http://127.0.0.1:{random_port}/callback, where the port changes every time the server starts.

What RFC 8252 Requires for Loopback Redirect URIs

RFC 8252 (OAuth 2.0 for Native Apps) addresses this directly. Section 7.3 states:

"The authorization server MUST allow any port to be specified at the time of the request for loopback IP redirect URIs, to accommodate clients that obtain an available ephemeral port from the operating system at the time of the request."

The RFC also specifies that loopback redirect URIs should use the IP literal 127.0.0.1 (or [::1] for IPv6), not the hostname localhost. This is not just a style preference. Section 8.3 explains the security reason: localhost can be overridden in a system's name resolution configuration to point to a different address. An attacker with local access could redirect the callback to a process they control. Using 127.0.0.1 avoids that risk entirely.

OAuth 2.1 (currently in draft) reinforces this position and explicitly discourages http://localhost for native client redirect URIs for the same reason.

How Duende IdentityServer Validates Loopback Redirect URIs

By default, IdentityServer uses StrictRedirectUriValidator, which requires an exact string match between the registered redirect URI and the one in the authorization request. This is the right default for web applications, where redirect URIs are stable and predictable.

For native apps and MCP servers, IdentityServer ships a second validator: StrictRedirectUriValidatorAppAuth, registered via the AddAppAuthRedirectUriValidator extension method. This validator extends the strict behavior with one addition: if the client has RequirePkce = true and a registered redirect URI of exactly http://127.0.0.1, it accepts any http://127.0.0.1:{port}/{path} URI at runtime.

Here is the relevant logic:

C#

// From StrictRedirectUriValidatorAppAuth
// For redirect URIs:
result = baseResult || (context.Client.RequirePkce 
    && context.Client.RedirectUris.Contains("http://127.0.0.1") 
    && IsLoopback(context.RequestedUri));

// For post-logout redirect URIs:
result = baseResult || (client.RequirePkce 
    && client.PostLogoutRedirectUris.Contains("http://127.0.0.1") 
    && IsLoopback(requestedUri));

The IsLoopback method validates that the runtime URI is well-formed: it must use the http scheme, the 127.0.0.1 host, and a valid port number (0-65535). The path is allowed to vary.

Configuring Duende IdentityServer for Port-Agnostic Loopback Redirects

Register the AppAuth validator when setting up IdentityServer:

C#

builder.Services.AddIdentityServer()
    // ... other configuration
    .AddAppAuthRedirectUriValidator();

Configure your client with RequirePkce = true and register http://127.0.0.1 as the redirect URI (no port):

C#

new Client
{
    ClientId = "mcp-server",
    ClientName = "My MCP Server",
    AllowedGrantTypes = GrantTypes.Code,
    RequirePkce = true,
    RequireClientSecret = false, // public client
    RedirectUris = { "http://127.0.0.1" },
    PostLogoutRedirectUris = { "http://127.0.0.1" },
    AllowedScopes = { "openid", "profile", "api" }
}

At runtime, your MCP server can use any port:

Shell

http://127.0.0.1:12345/callback   // works
http://127.0.0.1:54321/callback   // works
http://127.0.0.1:8080/callback    // works

Limitations of AddAppAuthRedirectUriValidator

The built-in AppAuth validator only matches http://127.0.0.1. It does not support:

  • http://localhost:{port} (hostname, not IP literal)
  • http://[::1]:{port} (IPv6 loopback)

If your MCP client sends a localhost or IPv6 redirect URI, the AppAuth validator will reject it. This is intentional and aligns with RFC 8252 and the OAuth 2.1 draft's security guidance.

If you need to support localhost or [::1] anyway (for example, because a third-party client library hardcodes it), you can implement a custom IRedirectUriValidator.

Custom IRedirectUriValidator for localhost and IPv6 Loopback Support

Implement IRedirectUriValidator and override the loopback check to accept any loopback address:

C#

public class LoopbackRedirectUriValidator : StrictRedirectUriValidator
{
    private static readonly string[] LoopbackHosts = 
        ["127.0.0.1", "localhost", "[::1]"];

    public override async Task<bool> IsRedirectUriValidAsync(
        RedirectUriValidationContext context)
    {
        // Check the standard strict match first
        if (await base.IsRedirectUriValidAsync(context))
            return true;

        // Fall back to loopback port-agnostic match
        return context.Client.RequirePkce 
            && IsLoopbackUri(context.RequestedUri, context.Client.RedirectUris);
    }

    public override async Task<bool> IsPostLogoutRedirectUriValidAsync(
        string requestedUri, Client client)
    {
        if (await base.IsPostLogoutRedirectUriValidAsync(requestedUri, client))
            return true;

        return client.RequirePkce 
            && IsLoopbackUri(requestedUri, client.PostLogoutRedirectUris);
    }

    private static bool IsLoopbackUri(
        string requestedUri, 
        IEnumerable<string> registeredUris)
    {
        if (!Uri.TryCreate(requestedUri, UriKind.Absolute, out var requested))
            return false;

        if (!string.Equals(requested.Scheme, "http", StringComparison.OrdinalIgnoreCase))
            return false;

        if (!LoopbackHosts.Contains(requested.Host, StringComparer.OrdinalIgnoreCase))
            return false;

        // Check that the registered URIs include a loopback base URI
        return registeredUris.Any(registered =>
            Uri.TryCreate(registered, UriKind.Absolute, out var reg)
            && LoopbackHosts.Contains(reg.Host, StringComparer.OrdinalIgnoreCase)
            && string.Equals(reg.Scheme, "http", StringComparison.OrdinalIgnoreCase));
    }
}

Register it with the generic extension method:

C#

builder.Services.AddIdentityServer()
    .AddRedirectUriValidator<LoopbackRedirectUriValidator>();

Security note: If you enable localhost matching, document why and review whether your deployment environment allows DNS or hosts-file manipulation. For most developer-machine scenarios, the risk is acceptable. For production authorization servers issuing tokens to sensitive resources, prefer 127.0.0.1 only.

Decision Guide

flowchart TD
    A[MCP / native app needs loopback redirect] --> B{Client sends 127.0.0.1?}
    B -- Yes --> C[Use AddAppAuthRedirectUriValidator\nRegister 'http://127.0.0.1' as redirect URI]
    B -- No --> D{Acceptable to require 127.0.0.1?}
    D -- Yes --> E[Update client library to use 127.0.0.1\nThen use AddAppAuthRedirectUriValidator]
    D -- No --> F[Implement custom IRedirectUriValidator\nwith LoopbackRedirectUriValidator pattern]

Summary

Duende IdentityServer supports port-agnostic loopback redirect URIs for native apps and MCP servers. Call AddAppAuthRedirectUriValidator() during setup, register http://127.0.0.1 as the client's redirect URI, and set RequirePkce = true. The built-in validator handles the rest.

If you need localhost or IPv6 loopback support, the custom IRedirectUriValidator pattern above gives you full control without touching the rest of the validation pipeline.

References