<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andrea Chiarelli</title>
    <description>The latest articles on DEV Community by Andrea Chiarelli (@andychiare).</description>
    <link>https://dev.to/andychiare</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2656%2Fme2.jpg</url>
      <title>DEV Community: Andrea Chiarelli</title>
      <link>https://dev.to/andychiare</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/andychiare"/>
    <language>en</language>
    <item>
      <title>Generative AI and Non-Determinism</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Tue, 31 Mar 2026 07:12:49 +0000</pubDate>
      <link>https://dev.to/andychiare/generative-ai-and-non-determinism-526l</link>
      <guid>https://dev.to/andychiare/generative-ai-and-non-determinism-526l</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“&lt;em&gt;The limits of my language are the limits of my world.&lt;/em&gt;”&lt;/p&gt;

&lt;p&gt;– Ludwig Wittgenstein&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We often hear that generative &lt;strong&gt;AI is non-deterministic&lt;/strong&gt; because it responds differently each time to the same prompt. This quality is both fascinating, because it is, in a sense, creative, and problematic, because it eludes our control in contexts where precision and predictability are essential.&lt;/p&gt;

&lt;p&gt;So, &lt;strong&gt;is non-determinism really what lies behind the processing of an LLM?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Non-Determinism?
&lt;/h2&gt;

&lt;p&gt;To understand what non-determinism is, we need to turn to the &lt;a href="https://en.wikipedia.org/wiki/Theory_of_computation" rel="noopener noreferrer"&gt;theory of computation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Imagine you’re in a maze. In a &lt;strong&gt;deterministic system&lt;/strong&gt;, you are a lone explorer. You come to a fork in the road and choose the right path. If the path is blocked, you must go back (good old &lt;a href="https://en.wikipedia.org/wiki/Backtracking" rel="noopener noreferrer"&gt;backtracking&lt;/a&gt;) and try the left path.&lt;/p&gt;

&lt;p&gt;In a &lt;strong&gt;non-deterministic system&lt;/strong&gt;, however, you possess the gift of ubiquity. When you reach the fork, you split into two people. One goes right and the other goes left.&lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://en.wikipedia.org/wiki/Michael_Sipser" rel="noopener noreferrer"&gt;Michael Sipser&lt;/a&gt;, the author of the book &lt;a href="https://en.wikipedia.org/wiki/Introduction_to_the_Theory_of_Computation" rel="noopener noreferrer"&gt;&lt;em&gt;Introduction to the Theory of Computation&lt;/em&gt;&lt;/a&gt;, this process is perfect parallelism: just one clone needs to find the exit for the entire machine to succeed. The computation of a non-deterministic machine is not expressed as a transition from one state to another, but rather as a transition from one state to a set of states. This theoretical &lt;em&gt;super-capacity&lt;/em&gt; is used to solve complex problems, such as those in the famous &lt;a href="https://en.wikipedia.org/wiki/NP_(complexity)" rel="noopener noreferrer"&gt;NP class&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In computer science, non-determinism is a model of logical perfection. &lt;strong&gt;If a solution exists, a non-deterministic machine will find it&lt;/strong&gt; because it explores every possible path without ever making a mistake. However, be careful: &lt;strong&gt;there is no such thing as a purely non-deterministic physical computer&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Many Faces of Non-Determinism
&lt;/h2&gt;

&lt;p&gt;Before returning to generative AI, we need to clarify the various concepts of non-determinism that are often conflated in everyday language. In fact, the term’s meaning changes significantly depending on the context.&lt;/p&gt;

&lt;p&gt;As we have seen, in computer science, non-determinism is a logical abstraction: a virtual perfect parallelism that allows us to solve a problem within a reasonable amount of time.&lt;/p&gt;

&lt;p&gt;In physics, non-determinism, or more accurately, &lt;a href="https://en.wikipedia.org/wiki/Quantum_indeterminacy" rel="noopener noreferrer"&gt;indeterminism&lt;/a&gt;, is the principle that it is impossible to simultaneously know two conjugate properties of a particle, such as position and momentum.&lt;/p&gt;

&lt;p&gt;Biology also has the &lt;a href="https://en.wikipedia.org/wiki/Indeterminism#Evolution_and_biology" rel="noopener noreferrer"&gt;concept of indeterminism&lt;/a&gt;, though it is linked to the role of chance in the evolution of living beings. This chance seems to be present in other aspects of nature, such as the weather, which are so difficult to predict that they appear non-deterministic. In reality, these are not non-deterministic systems, but rather highly complex deterministic systems, as &lt;a href="https://en.wikipedia.org/wiki/Chaos_theory" rel="noopener noreferrer"&gt;chaos theory&lt;/a&gt; explains.&lt;/p&gt;

&lt;p&gt;Generative AI is often defined as non-deterministic because of the &lt;em&gt;chaos effect&lt;/em&gt;: small variations in the input produce different outputs. However, as we will see, its nature is quite different.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI and Probability
&lt;/h2&gt;

&lt;p&gt;When you ask an LLM to answer a question or write code, the system doesn't use a non-deterministic approach to find the solution. The LLM simply… guesses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LLMs are probabilistic machines&lt;/strong&gt;, not non-deterministic machines. Their entire universe boils down to assigning a probability percentage to the next token. For example, if you write, “The cat is on the...”, the model isn’t &lt;em&gt;thinking&lt;/em&gt; about physical space. It calculates that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;carpet&lt;/em&gt; has an 80% probability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;lawn&lt;/em&gt; has a 15% probability.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike Sipser’s non deterministic machine, an LLM doesn’t traverse all logical branches. It chooses one based on a statistical distribution. This is a substantial difference!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A non-deterministic machine would &lt;em&gt;virtually&lt;/em&gt; always give the correct solution&lt;/strong&gt;. It would explore all possible branches of the computation to identify the correct one. If a solution is incorrect, that branch of the computation simply ends. There cannot be an output that is not a valid solution to the problem.&lt;/p&gt;

&lt;p&gt;The LLM follows &lt;strong&gt;likelihood&lt;/strong&gt;, not &lt;strong&gt;truth&lt;/strong&gt;. It can generate a grammatically perfect sentence that is factually false because those words &lt;em&gt;sound&lt;/em&gt; good together statistically. For instance, the LLM might tell you that the capital of Brazil is Rio de Janeiro because Rio is associated with Brazil much more often than Brasília in its training data. Statistically, Rio is the &lt;em&gt;more likely&lt;/em&gt; answer, even if it is logically incorrect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Is Imprecise AI Useful?
&lt;/h2&gt;

&lt;p&gt;If AI is fallible and imprecise, why is it revolutionizing the world? Why is it permeating our daily lives?&lt;/p&gt;

&lt;p&gt;Traditional, deterministic software is like a train traveling on tracks. It only does what it was programmed to do. It doesn’t make decisions beyond what the programmer intended, and it doesn’t evolve on its own. Traditional software expects input in a specific format. It cannot handle ambiguity or imprecision. For example, if you want to reschedule an appointment from today to tomorrow, you can do so by following one of a few predefined methods.&lt;/p&gt;

&lt;p&gt;In contrast, if you ask a chatbot “Move today’s appointment to tomorrow,” or “Move today’s meeting to tomorrow,” or even “&lt;em&gt;Tooday’s apointment goes to tomorow&lt;/em&gt;,” it uses its probabilistic nature to correctly interpret your request.&lt;/p&gt;

&lt;p&gt;This ability to handle unstructured data, such as analyzing the sentiment of an email, summarizing a document, or translating a text, cannot be reduced to simple, rigid rules. In these cases, probabilistic flexibility is the only way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigating Uncertainty
&lt;/h2&gt;

&lt;p&gt;For now, non-determinism remains a logical ideal of supreme efficiency. However, what we actually have at our disposal is an extraordinary probabilistic tool that &lt;strong&gt;is not intended to replace traditional deterministic software, but rather to enhance it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As developers and engineers, our task is not to eliminate AI's unpredictability, but rather to learn how to channel it. We must design deterministic systems around probabilistic engines that know when to be &lt;em&gt;intuitive&lt;/em&gt; and &lt;em&gt;imaginative&lt;/em&gt; and when to be precise.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>Common FAPI Misconceptions</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Fri, 20 Mar 2026 09:00:30 +0000</pubDate>
      <link>https://dev.to/auth0/common-fapi-misconceptions-121k</link>
      <guid>https://dev.to/auth0/common-fapi-misconceptions-121k</guid>
      <description>&lt;p&gt;For some time now, I've been interested in FAPI from both an Identity practitioner's and a developer's perspective. I've written a few posts on this topic on &lt;a href="https://auth0.com/blog" rel="noopener noreferrer"&gt;the Auth0 blog&lt;/a&gt; and created &lt;a href="https://auth0.com/resources/whitepapers/developers-guide-to-FAPI" rel="noopener noreferrer"&gt;a guide to FAPI&lt;/a&gt; with the support of colleagues who are much more experienced than I am. Surfing the web and talking to developers, however, I couldn't help but notice some misunderstandings about certain aspects of FAPI.&lt;br&gt;&lt;br&gt;
In this article, I'll summarize the most common and recurring ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Misconception 1: FAPI Is a New Protocol
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;FAPI is a security profile based on OAuth 2.1&lt;/strong&gt;, it is &lt;strong&gt;not a new protocol&lt;/strong&gt;, intended as an alternative to established standards like OAuth 2.0, SAML, or OpenID Connect (OIDC).&lt;/p&gt;

&lt;p&gt;It acts as a prescriptive blueprint that defines exactly which OAuth 2.0 and OIDC extensions must be used and how they must be configured. While the core OAuth 2.0 specification (&lt;a href="https://datatracker.ietf.org/doc/html/rfc6749" rel="noopener noreferrer"&gt;RFC 6749&lt;/a&gt;) is a flexible framework that provides a "toolbox" of flows and leaves security decisions to the implementer, FAPI removes this "dangerous flexibility" to ensure a secure-by-default posture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Misconception 2: FAPI Is for Banks and Financial Organizations
&lt;/h2&gt;

&lt;p&gt;The "F" in FAPI originally stood for "Financial," reflecting its initial goal to protect banking applications. However, the scope has expanded significantly. Today, FAPI is a general-purpose high-security profile for any industry handling sensitive, high-risk data. Major applications include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;E-Health:&lt;/strong&gt; Protecting Patient Health Information (PHI) in standards like &lt;a href="https://hl7.org/fhir/" rel="noopener noreferrer"&gt;HL7 FHIR&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E-Signing:&lt;/strong&gt; Securing the underlying API calls for legally binding digital signatures.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Government Services:&lt;/strong&gt; Managing Personally Identifiable Information (PII) and government services.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Misconception 3: FAPI 2.0 Is an Incremental Update of FAPI 1.0
&lt;/h2&gt;

&lt;p&gt;Some people mistakenly believe FAPI 2.0 is a minor version bump that maintains backward compatibility. This is incorrect. FAPI 2.0 is a complete redesign based on lessons learned from FAPI 1.0, specifically aimed at simplifying the developer experience.&lt;/p&gt;

&lt;p&gt;A key change is the removal of the &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/hybrid-flow" rel="noopener noreferrer"&gt;OpenID Connect Hybrid Flow&lt;/a&gt; used in FAPI 1.0 "Advanced," which required complex front-channel validations of ID Tokens. FAPI 2.0 replaces this with a hardened &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow" rel="noopener noreferrer"&gt;Authorization Code Flow&lt;/a&gt; where all tokens are delivered via the back-channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Misconception 4: OAuth 2.1 Makes FAPI Redundant
&lt;/h2&gt;

&lt;p&gt;As OAuth 2.1 incorporates modern best practices like &lt;a href="https://datatracker.ietf.org/doc/html/rfc7636" rel="noopener noreferrer"&gt;PKCE&lt;/a&gt; and the removal of the &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/implicit-flow-with-form-post" rel="noopener noreferrer"&gt;Implicit Flow&lt;/a&gt;, some wonder if FAPI 2.0 remains necessary. However, the difference between the two is significant.&lt;/p&gt;

&lt;p&gt;OAuth 2.1 is a "Best Current Practice" consolidation for the general community, while FAPI 2.0 is a "high-security profile" that mandates features OAuth 2.1 only recommends. Crucially, FAPI 2.0 is built on a &lt;a href="https://openid.net/specs/fapi-attacker-model-2_0-final.html" rel="noopener noreferrer"&gt;Formal Attacker Model&lt;/a&gt;, providing proof of security against specific threats that OAuth 2.1 does not formally address.&lt;/p&gt;

&lt;h2&gt;
  
  
  Misconception 5: You Can Use Any Type of Client with FAPI
&lt;/h2&gt;

&lt;p&gt;Standard OAuth 2.0 allows for both public (e.g., mobile, browser-based) and confidential (server-side) clients. However, while FAPI 1.0 Baseline specification allows for public clients, &lt;strong&gt;FAPI 1.0 Advanced and FAPI 2.0 explicitly excludes support for them&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FAPI 2.0 focuses on confidential clients&lt;/strong&gt; because they are capable of protecting a private key. This is the cornerstone of FAPI’s security model; a client cannot perform asymmetric authentication or prove possession of a sender-constrained token if it cannot keep its private key secure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Misconception 6: You Can’t Use Public Clients with FAPI
&lt;/h2&gt;

&lt;p&gt;As a consequence of the previous point, you may conclude that you can’t use public clients at all with FAPI. So, you can’t have mobile applications or SPAs in a FAPI-based system.&lt;/p&gt;

&lt;p&gt;Actually, this is not entirely true. You can still have public clients in a FAPI context provided that they don’t manage ID and access tokens. For example, you can have a SPA if you are using an architectural pattern like the &lt;a href="https://auth0.com/blog/the-backend-for-frontend-pattern-bff/" rel="noopener noreferrer"&gt;Backend for Frontend pattern&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Misconception 7: Pushed Authorization Requests Support Simply Shortens URLs
&lt;/h2&gt;

&lt;p&gt;Developers often view &lt;a href="https://auth0.com/blog/what-are-oauth-push-authorization-requests-par/" rel="noopener noreferrer"&gt;Pushed Authorization Requests&lt;/a&gt; (PAR) as merely an optional optimization for long URLs. In reality, FAPI 2.0 makes PAR mandatory because it moves the entire authorization request from the &lt;em&gt;risky&lt;/em&gt; environment of the browser to the secure back-channel.&lt;/p&gt;

&lt;p&gt;Standard GET requests expose sensitive data (scopes, identifiers) to browser history, server logs, and Referer headers. PAR solves this by having the client POST parameters directly to the server, receiving an opaque request_uri in return. This ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Confidentiality:&lt;/strong&gt; Sensitive parameters are never in the URL.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrity:&lt;/strong&gt; The server authenticates the request before the user is ever involved.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability:&lt;/strong&gt; It bypasses URL length limits that often break complex authorization requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;The journey through the FAPI landscape often involves navigating a complex mix of technical details and outdated assumptions. As this article has shown, FAPI is far more than an obscure security framework for banks; it is a critical, mature security profile that strips away the risky flexibility of standard OAuth 2.0 to offer a secure foundation for any system handling high-value data.&lt;/p&gt;

&lt;p&gt;From understanding that FAPI is a profile, not a new protocol, to recognizing the fundamental changes in FAPI 2.0 and its mandated use of features like Pushed Authorization Requests (PAR), a clear picture emerges. FAPI’s strict reliance on confidential clients and back-channel communications is a pragmatic response to known threat models, ensuring integrity and confidentiality where it matters most.&lt;/p&gt;

&lt;p&gt;Embracing FAPI is not about adding unnecessary complexity. It is about adopting a proven security posture necessary for protecting assets in e-Health, e-Signing, government services, and beyond. By moving past these common misconceptions, organizations can leverage FAPI to build truly robust and compliant high-security applications.&lt;/p&gt;

&lt;p&gt;Here is an infographic that summarizes the misconceptions discussed in this article. Keep it handy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdms4lk1ygtas81a32fkw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdms4lk1ygtas81a32fkw.jpg" alt="Infographic about the seven common misconceptions on FAPI" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>fapi</category>
      <category>oauth</category>
      <category>identity</category>
      <category>security</category>
    </item>
    <item>
      <title>Secure a C# MCP Server with Auth0</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Fri, 13 Mar 2026 09:59:59 +0000</pubDate>
      <link>https://dev.to/auth0/secure-a-c-mcp-server-with-auth0-4p0n</link>
      <guid>https://dev.to/auth0/secure-a-c-mcp-server-with-auth0-4p0n</guid>
      <description>&lt;p&gt;As the &lt;a href="https://modelcontextprotocol.io/" rel="noopener noreferrer"&gt;Model Context Protocol (MCP)&lt;/a&gt; gains traction, the transition from "localhost" experimentation to enterprise integration introduces a critical challenge: security. For developers building sophisticated integrations, treating every LLM request as an "admin" action is a significant risk.&lt;/p&gt;

&lt;p&gt;Luckily, the MCP specification supports authorization based on OAuth 2.1. So, basically, to protect your MCP server, you have to treat it as a resource server with some extra challenges in case you want to share it publicly to a wide audience.&lt;/p&gt;

&lt;p&gt;The .NET ecosystem can leverage the &lt;a href="https://csharp.sdk.modelcontextprotocol.io/" rel="noopener noreferrer"&gt;C# SDK for MCP&lt;/a&gt; to easily expose tools and resources to an AI-powered application. The SDK reached maturity recently &lt;a href="https://devblogs.microsoft.com/dotnet/release-v10-of-the-official-mcp-csharp-sdk/" rel="noopener noreferrer"&gt;supporting all the features defined by the specification&lt;/a&gt;, including security.&lt;/p&gt;

&lt;p&gt;This article explores a practical implementation of a secured MCP server. You will walk through a sample project and learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement three distinct tools: one available for public consumption and two gated behind specific permission sets.
&lt;/li&gt;
&lt;li&gt;Make the protected MCP server available to an MCP client, such as VSCode, leveraging Dynamic Client Registration (DCR) supported by Auth0.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Protect an MCP Server?
&lt;/h2&gt;

&lt;p&gt;In a standard local setup, the MCP server and the client often share the same security boundary. However, as soon as an MCP server is deployed as a shared service or connected to a multi-user LLM platform, that boundary shifts. Protecting the server becomes relevant for at least two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Preventing prompt injection escalation&lt;/strong&gt;. If a tool allows an LLM to interact with internal databases or APIs, a malicious prompt could attempt to trick the model into executing commands it shouldn't. By implementing strict authorization checks at the server level, you provide a defense in depth strategy. Even if the LLM is convinced to call a restricted tool, the MCP server will reject the request because the underlying user context lacks the necessary permissions.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource isolation and multi-tenancy&lt;/strong&gt;. Not every user interacting with an AI agent should have the same capabilities. For example, a junior developer might have access to a &lt;em&gt;ReadDocumentation&lt;/em&gt; tool, but only a lead engineer should be able to trigger a &lt;em&gt;DeployProduction&lt;/em&gt; tool. Without protection, the MCP server treats all requests as equal, which is unacceptable in any regulated or professional environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These reasons are what OAuth 2.1 support in the MCP specification aims to prevent. We will use it to protect a sample MCP server built with C# by integrating with Auth0.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To learn how Auth0 helps you secure MCP clients and servers, check out &lt;a href="https://auth0.com/ai/docs/mcp/intro/overview" rel="noopener noreferrer"&gt;Auth for MCP&lt;/a&gt;. For a detailed explanation of authorization support in MCP, read the &lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization" rel="noopener noreferrer"&gt;Authorization specification&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Build Your MCP Server in C
&lt;/h2&gt;

&lt;p&gt;Let’s begin our exploration by implementing a very simple MCP server that we will later extend and secure using Auth0.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;The MCP server project we are going to implement requires the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/download/dotnet/10.0" rel="noopener noreferrer"&gt;.NET SDK 10&lt;/a&gt; or later,
&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://csharp.sdk.modelcontextprotocol.io/" rel="noopener noreferrer"&gt;C# SDK for MCP&lt;/a&gt; package,
&lt;/li&gt;
&lt;li&gt;An Auth0 account. If you don’t have it, &lt;a href="https://a0.to/blog_signup" rel="noopener noreferrer"&gt;sign up for free here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create the MCP Server
&lt;/h3&gt;

&lt;p&gt;To create a simple MCP server, you can use the MCP server template project provided by Microsoft. Currently, the template is in preview, so you won’t find it in the builtin template set. You can download and install the package by running the following command in a terminal window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new &lt;span class="nb"&gt;install &lt;/span&gt;Microsoft.McpServer.ProjectTemplates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have downloaded the project template, you can create your MCP server project by running a command like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new mcpserver &lt;span class="nt"&gt;-n&lt;/span&gt; AspNetCoreMcpServer &lt;span class="nt"&gt;-t&lt;/span&gt; remote
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates a new MCP server project named &lt;code&gt;AspNetCoreMcpServer&lt;/code&gt; in a folder with the same name. The option &lt;code&gt;-t remote&lt;/code&gt; specifies to create an MCP server based on HTTP transport, which leads to actually creating an ASP.NET Core application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MCP servers can use &lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/transports" rel="noopener noreferrer"&gt;stdio or HTTP as transport protocols&lt;/a&gt;. OAuth protection for MCP servers is only designed for HTTP-based MCP servers. See &lt;a href="https://modelcontextprotocol.io/docs/tutorials/security/authorization#when-should-you-use-authorization" rel="noopener noreferrer"&gt;When Should You Use Authorization?&lt;/a&gt; for more information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Explore the project
&lt;/h3&gt;

&lt;p&gt;Let’s take a quick look at the project to understand its implementation.&lt;br&gt;&lt;br&gt;
Go to the project’s folder and open the &lt;code&gt;Program.cs&lt;/code&gt; file. You should see the the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Program.cs&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Add the MCP services: the transport to use (http) and the tools to register.&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcpServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithHttpTransport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTools&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RandomNumberTools&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapMcp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an ordinary .NET application that uses the MCP server service (&lt;code&gt;AddMcpServer&lt;/code&gt;). This service uses the HTTP transport (&lt;code&gt;WithHttpTransport&lt;/code&gt;) and exposes the tools implemented by the class &lt;code&gt;RandomNumberTools&lt;/code&gt; (&lt;code&gt;WithTools&amp;lt;RandomNumberTools&amp;gt;&lt;/code&gt;). The invocation of the &lt;code&gt;MapMcp()&lt;/code&gt; method initializes the MCP server.&lt;/p&gt;

&lt;p&gt;The C# SDK for MCP library takes charge of all the complexity of implementing the MCP protocol. You can stay focused on the tool implementation, which is pretty straightforward as well.&lt;br&gt;&lt;br&gt;
Open the &lt;code&gt;RandomNumberTools.cs&lt;/code&gt; file in the &lt;code&gt;Tools&lt;/code&gt; folder and take a look at the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Tools/RandomNumberTools.cs&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ModelContextProtocol.Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RandomNumberTools&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Generates a random number between the specified minimum and maximum values."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;GetRandomNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Minimum value (inclusive)"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Maximum value (exclusive)"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You see a very simple class implementing the method &lt;code&gt;GetRandomNumber()&lt;/code&gt;, which returns a random number within a range. The attribute &lt;code&gt;McpServerTool&lt;/code&gt; marks the &lt;code&gt;GetRandomNumber()&lt;/code&gt; method as an MCP tool. The &lt;code&gt;Description&lt;/code&gt; attributes describe what the tool does and what its parameters mean, very important to let the LLM understand when and how to use it.  In fact, clear descriptions of the tool's functionality help the LLM select the appropriate tool for a given task.&lt;/p&gt;

&lt;p&gt;That’s all! Your MCP server is ready to run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test your MCP server
&lt;/h3&gt;

&lt;p&gt;Since the MCP server we implemented is nothing more than an ASP.NET Core application, we can test it in several ways: you can use curl or the &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/test/http-files" rel="noopener noreferrer"&gt;.http file&lt;/a&gt; automatically generated with the project. Or you can use the &lt;a href="https://modelcontextprotocol.io/docs/tools/inspector" rel="noopener noreferrer"&gt;MCP Inspector&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'll integrate our MCP server directly into an MCP client like VSCode. This will come in handy later when we implement some advanced features.&lt;/p&gt;

&lt;p&gt;You can add your MCP server to VSCode by creating a &lt;code&gt;.vscode&lt;/code&gt; folder in your project’s root folder, and adding an &lt;code&gt;mcp.json&lt;/code&gt; file with the following JSON content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//.vscode/mcp.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"AspNetCoreMcpServer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:PORT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;inputs&lt;/span&gt;&lt;span class="s2"&gt;": []
}

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to replace the PORT placeholder with the port your MCP server listens to.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Look at &lt;a href="https://code.visualstudio.com/docs/copilot/customization/mcp-servers#_add-an-mcp-server" rel="noopener noreferrer"&gt;alternative ways to add your MCP server to VSCode&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you have added your MCP server to VSCode, set its chat window to agent mode and ask to get a random number. You should be prompted to authorize the use of the tool as shown in the following screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvyop35awnfo6ejzu73c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frvyop35awnfo6ejzu73c.png" alt="Authorization request for the random numbers tool in Copilot chat in VSCode" width="283" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you authorize it, you will get a random number:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fssslankyoogecbmzz5gu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fssslankyoogecbmzz5gu.png" alt="Response of the random numbers tool in Copilot chat" width="283" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cool! You have a new working MCP server in your VSCode instance!&lt;/p&gt;

&lt;h2&gt;
  
  
  Protect Your MCP Server
&lt;/h2&gt;

&lt;p&gt;Now let's move on to the real focus of this article: protecting your MCP server from unauthorized access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define the tools to protect
&lt;/h3&gt;

&lt;p&gt;Currently, anyone installing your MCP server can use its only tool, &lt;code&gt;get_random_number&lt;/code&gt;. This tool is publicly accessible because there is no restriction on it. You will leave it so, but you will also add two new tools that will require some form of authorization.&lt;/p&gt;

&lt;p&gt;Specifically, you will add a tool that gets the current state of a hypothetical system and another one that sets the state of this system. The system is fictitious, of course, but you can think of it as a kind of processor that can be found in different states.&lt;/p&gt;

&lt;p&gt;To implement these tools, add a new file named &lt;code&gt;SystemTools.cs&lt;/code&gt; to the &lt;code&gt;Tools&lt;/code&gt; folder with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Tools/SystemTools.cs&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ModelContextProtocol.Server&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;SystemState&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Ready&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Waiting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Stopped&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SystemTools&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;SystemState&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;AllStates&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetValues&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SystemState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;SystemState&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_currentState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Returns the current state of the fictitious system. Possible states are: Ready, Waiting, Running, Stopped."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetSystemState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_currentState&lt;/span&gt; &lt;span class="p"&gt;??=&lt;/span&gt; &lt;span class="n"&gt;AllStates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AllStates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;()!;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sets the current state of the fictitious system. Valid states are: Ready, Waiting, Running, Stopped."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;SetSystemState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The new state to set. Must be one of: Ready, Waiting, Running, Stopped."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;SystemState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_currentState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$"System state set to '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_currentState&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You see that the tool &lt;code&gt;GetSystemState()&lt;/code&gt; picks a random state the first time it is called and stores it into a private static variable, &lt;code&gt;_currentState&lt;/code&gt;. Any subsequent call to the tool will return the value stored in that variable.&lt;/p&gt;

&lt;p&gt;The tool &lt;code&gt;SetSystemState()&lt;/code&gt; changes the value of the state stored in the &lt;code&gt;_currentState&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;You will protect these two new tools by requiring that the user has specific permissions to use each of them.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#roles" rel="noopener noreferrer"&gt;MCP specification&lt;/a&gt;, your MCP server is nothing but a resource server in the context of OAuth, and VSCode is just an OAuth client. So actually all you are going to implement is a well-known scenario: you will use Auth0 as the authorization server, which will authenticate the user and provide the access token to the MCP client (VSCode) in order to access the tools exposed by your MCP server. The access token will include the required permissions to access the protected tools.&lt;/p&gt;

&lt;p&gt;However, you may have a little concern now. You want to share your MCP server as a package that anyone can download and use in their VSCode instance. How can they register their VSCode instance with Auth0 as required for any OAuth client? Well, the MCP specification supports &lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#client-registration-approaches" rel="noopener noreferrer"&gt;a few ways to do this&lt;/a&gt;. You will use &lt;a href="https://datatracker.ietf.org/doc/html/rfc7591" rel="noopener noreferrer"&gt;Dynamic Client Registration (DCR)&lt;/a&gt;, which allows an MCP client (VSCode) to self-register with the authorization server (Auth0). Be aware that DCR raises security concerns, as we will mention later.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure your Auth0 tenant
&lt;/h3&gt;

&lt;p&gt;First of all, you need to configure Auth0 at the tenant level. In fact, you need to enable &lt;a href="https://auth0.com/docs/get-started/applications/dynamic-client-registration" rel="noopener noreferrer"&gt;Dynamic Client Registration&lt;/a&gt; to allow MCP clients to register. You also need to enable &lt;a href="https://auth0.com/ai/docs/mcp/guides/resource-param-compatibility-profile" rel="noopener noreferrer"&gt;Resource Parameter Compatibility Profile&lt;/a&gt; as required by the &lt;a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization#resource-parameter-implementation" rel="noopener noreferrer"&gt;MCP specification&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep in mind that these are tenant level settings, which means they affect all the applications registered in that tenant.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To enable those settings, go to &lt;a href="https://manage.auth0.com/#/tenant" rel="noopener noreferrer"&gt;&lt;em&gt;Settings&lt;/em&gt;&lt;/a&gt; on the left menu and then select the &lt;em&gt;Advanced&lt;/em&gt; tab. Scroll down to the &lt;em&gt;Settings&lt;/em&gt; section and enable the &lt;em&gt;Dynamic Client Registration (DCR)&lt;/em&gt; and &lt;em&gt;Resource Parameter Compatibility Profile&lt;/em&gt; toggles, as shown in the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tlpn79sjl2k25wdkbvs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3tlpn79sjl2k25wdkbvs.png" alt="Dynamic Client Registration option on the Auth0 dashboard" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As mentioned before, you need to enable Dynamic Client Registration to allow VSCode to self-register with Auth0 the first time a user interacts with your MCP server. This allows any client to register with Auth0 with &lt;strong&gt;potential risks&lt;/strong&gt; such as resource exhaustion or unauthorized access attempts. Read &lt;a href="https://auth0.com/ai/docs/mcp/guides/registering-your-mcp-client-application#dynamic-client-registration-dcr" rel="noopener noreferrer"&gt;Register your MCP Client Application&lt;/a&gt; to learn more about the dangers of an open DCR endpoint and possible solutions.&lt;/p&gt;

&lt;p&gt;Beyond the static registration of an MCP client, an alternative approach is based on &lt;a href="https://auth0.com/blog/cimd-vs-dcr-mcp-registration/" rel="noopener noreferrer"&gt;Client ID Metadata Document (CIMD)&lt;/a&gt;, a recent standard whose implementation is in progress.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In addition to the previous tenant-level settings, you also need to &lt;a href="https://auth0.com/docs/authenticate/identity-providers/promote-connections-to-domain-level" rel="noopener noreferrer"&gt;promote the connections&lt;/a&gt; the MCP clients will use to domain level. To this purpose, go to the &lt;em&gt;Authentication&lt;/em&gt; menu item of your dashboard and select the connection to configure. For our current example, select the &lt;em&gt;Username-Password-Authentication&lt;/em&gt; connection under &lt;em&gt;Database&lt;/em&gt;, and scroll down until you see the setting &lt;em&gt;Promote Connection to Domain Level&lt;/em&gt;. Enable it as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frsdbdaugjqa1utxwqihb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frsdbdaugjqa1utxwqihb.png" alt="Promote Connection to Domain Level setting in the Auth0 dashboard" width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! You have prepared your Auth0 tenant for self-registrations of MCP clients.&lt;/p&gt;

&lt;h3&gt;
  
  
  Register your MCP Server
&lt;/h3&gt;

&lt;p&gt;Now you need to register your MCP server.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, from the OAuth point of view, an MCP server is nothing but a resource server or API server. This means that you can register it with Auth0 &lt;a href="https://auth0.com/docs/get-started/auth0-overview/set-up-apis" rel="noopener noreferrer"&gt;as a standard API&lt;/a&gt;. The only requirement is to make sure to set your MCP server base URL as the audience. This is &lt;a href="https://datatracker.ietf.org/doc/html/rfc9728#section-3.3" rel="noopener noreferrer"&gt;a requirement of the Protected Resource Metadata specification&lt;/a&gt;. So, in your case, if your MCP server listens to &lt;code&gt;http://localhost:5678&lt;/code&gt;, use this URL as the audience.&lt;/p&gt;

&lt;p&gt;Once you have created your API on the Auth0 dashboard, scroll down in the &lt;em&gt;Settings&lt;/em&gt; tab to reach the &lt;em&gt;Access Token Settings&lt;/em&gt; section. Here, select the &lt;em&gt;RFC 9068&lt;/em&gt; format as shown in the following picture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzv41xbfc20z2lqn0lrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzv41xbfc20z2lqn0lrb.png" alt="Enabling the RFC 9068 access token profile in the Auth0 dashboard" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read the &lt;a href="https://auth0.com/docs/secure/tokens/access-tokens/access-token-profiles" rel="noopener noreferrer"&gt;documentation to learn more about access token profiles&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then scroll down more to the &lt;em&gt;RBAC Settings&lt;/em&gt; section and enable both &lt;em&gt;RBAC&lt;/em&gt; and &lt;em&gt;Add Permissions in the Access Token&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20yx4s6dcwhxv1n60hzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20yx4s6dcwhxv1n60hzu.png" alt="Enabling RBAC and permissions in access tokens in the Auth0 dashboard" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These settings will include the user’s permissions in the access token so your MCP server will be able to make authorization decisions to let users access the tools.&lt;/p&gt;

&lt;p&gt;Save the settings and go to the &lt;em&gt;Permissions&lt;/em&gt; tab. Here you should add the &lt;code&gt;tool:getsystemstate&lt;/code&gt; and &lt;code&gt;tool:setsystemstate&lt;/code&gt; permissions as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqtssnxz7pcm7tn62mhb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqtssnxz7pcm7tn62mhb.png" alt="List of permissions associated with the API" width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Follow the instructions in &lt;a href="https://auth0.com/docs/get-started/apis/add-api-permissions" rel="noopener noreferrer"&gt;this document to learn how to add permissions to an API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally, go to the &lt;em&gt;Application Access&lt;/em&gt; tab and select the &lt;em&gt;Allow&lt;/em&gt; value for the application access policy for user access, as you can see in the picture below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vqw170c3ogrj9d5jjxl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vqw170c3ogrj9d5jjxl.png" alt="Enabling application access policy on the Auth0 dashboard" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This setting allows any self-registered MCP client to access your MCP server on behalf of the user. To learn more about this setting, read the &lt;a href="https://auth0.com/docs/get-started/apis/api-access-policies-for-applications" rel="noopener noreferrer"&gt;API Access Policy documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now your MCP server is configured on the Auth0 side.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secure your MCP Server
&lt;/h3&gt;

&lt;p&gt;Let's complete the MCP server protection by modifying the current code.&lt;/p&gt;

&lt;p&gt;First, add an &lt;code&gt;appsettings.json&lt;/code&gt; file with the current content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//appsettings.json&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Auth0"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_DOMAIN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Audience"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_MCP_SERVER_BASE_URL"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"McpServer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"BaseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_MCP_SERVER_BASE_URL"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_DOMAIN&lt;/code&gt; with your Auth0 tenant domain, and &lt;code&gt;YOUR_MCP_SERVER_BASE_URL&lt;/code&gt; with the actual URL of your MCP server. Remember that &lt;code&gt;YOUR_MCP_SERVER_BASE_URL&lt;/code&gt; &lt;a href="https://datatracker.ietf.org/doc/html/rfc9728#section-3.3" rel="noopener noreferrer"&gt;must also correspond to the audience you registered in Auth0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now you will add support for the access token validation and protection on the two MCP tools you added to the initial project.&lt;/p&gt;

&lt;p&gt;Install the middleware to manage JWT tokens with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet package add Microsoft.AspNetCore.Authentication.JwtBearer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the current content of the &lt;code&gt;Program.cs&lt;/code&gt; file with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Program.cs&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Authentication.JwtBearer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.IdentityModel.Tokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ModelContextProtocol.AspNetCore.Authentication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;authority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"https://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Auth0:Domain"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s"&gt;/"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;audience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Auth0:Audience"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mcpServerBaseUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"McpServer:BaseUrl"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultChallengeScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;McpAuthenticationDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultAuthenticateScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JwtBearerDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJwtBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authority&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TokenValidationParameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;TokenValidationParameters&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ValidateIssuer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ValidateAudience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ValidateLifetime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ValidateIssuerSigningKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ValidAudience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ValidIssuer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authority&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;JwtBearerEvents&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;OnTokenValidated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Token validated successfully."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;OnAuthenticationFailed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Authentication failed: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResourceMetadata&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mcpServerBaseUrl&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;audience&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;AuthorizationServers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;authority&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;ScopesSupported&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"tool:getsystemstate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tool:setsystemstate"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcpServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithHttpTransport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorizationFilters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTools&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RandomNumberTools&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapMcp&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;RequireAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mcpServerBaseUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down this code to understand what it does.&lt;/p&gt;

&lt;p&gt;Basically, it adds support for authenticating client requests as in any ordinary ASP.NET Core Web API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Program.cs&lt;/span&gt;

&lt;span class="c1"&gt;//...existing code...&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultChallengeScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;McpAuthenticationDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultAuthenticateScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JwtBearerDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJwtBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authority&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TokenValidationParameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;TokenValidationParameters&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ValidateIssuer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ValidateAudience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ValidateLifetime&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ValidateIssuerSigningKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ValidAudience&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ValidIssuer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authority&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;JwtBearerEvents&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;OnTokenValidated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Token validated successfully."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;OnAuthenticationFailed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Authentication failed: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;//...existing code...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code sets up the access token validation logic through &lt;code&gt;AddJwtBearer()&lt;/code&gt;. The &lt;code&gt;OnTokenValidated&lt;/code&gt; and &lt;code&gt;OnAuthenticationFailed&lt;/code&gt; event managers do nothing special apart from showing their message to the console, but you can use them for any customization you may need.&lt;/p&gt;

&lt;p&gt;Just after &lt;code&gt;AddJwtBearer()&lt;/code&gt; invocation, you find &lt;code&gt;AddMcp()&lt;/code&gt;, as highlighted below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Program.cs&lt;/span&gt;

&lt;span class="c1"&gt;//...existing code...&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...existing code...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddJwtBearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...existing code...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResourceMetadata&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mcpServerBaseUrl&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;audience&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;AuthorizationServers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;authority&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;ScopesSupported&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"tool:getsystemstate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tool:setsystemstate"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;//...existing code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;AddMcp()&lt;/code&gt; method defines the &lt;a href="https://datatracker.ietf.org/doc/html/rfc9728#name-protected-resource-metadata" rel="noopener noreferrer"&gt;Protected Resource Metadata (PRM)&lt;/a&gt; document for this MCP server. This document provides useful information for the client about the capabilities of the MCP server and which authorization server to contact to access protected tools.&lt;/p&gt;

&lt;p&gt;This document will be dynamically exposed at the URL &lt;code&gt;YOUR_MCP_SERVER_BASE_URL/.well-known/oauth-protected-resource&lt;/code&gt;, where &lt;code&gt;YOUR_MCP_SERVER_BASE_URL&lt;/code&gt; is the base URL of your MCP server. The document will contain the following JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"YOUR_MCP_SERVER_BASE_URL"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"authorization_servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://YOUR_DOMAIN"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bearer_methods_supported"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"header"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scopes_supported"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"tool:getsystemstate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"tool:setsystemstate"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, enforce authorization with the code highlighted below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Program.cs&lt;/span&gt;

&lt;span class="c1"&gt;//...existing code...&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;//👈 new code&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMcpServer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithHttpTransport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorizationFilters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;//👈 new code&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTools&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RandomNumberTools&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapMcp&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;RequireAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;//👈 changed code&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mcpServerBaseUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;//👈 changed code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You added the authorization middleware service (&lt;code&gt;AddAuthorization()&lt;/code&gt;) and support for authorization filters (&lt;code&gt;AddAuthorizationFilters()&lt;/code&gt;). Also, you applied authorization to the MCP endpoints (&lt;code&gt;RequireAuthorization()&lt;/code&gt;) and forced the server to use the base URL defined in the &lt;code&gt;appsettings.json&lt;/code&gt; file. The last change is not strictly required, but it ensures that you have one source of truth for the MCP base URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add authorization policy
&lt;/h3&gt;

&lt;p&gt;You added a generic support for authorization with &lt;code&gt;AddAuthorization()&lt;/code&gt;. This makes sure that only authenticated users access the protected tools. But our initial intent is to allow users to access each specific tool based on their permissions.&lt;/p&gt;

&lt;p&gt;You can do this by defining a couple of authorization policies based on the existence of specific permissions in the access token. To this purpose, replace the &lt;code&gt;AddAuthorization()&lt;/code&gt; invocation with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Program.cs&lt;/span&gt;

&lt;span class="c1"&gt;//...existing code...&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthorization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetSystemStatePolicy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RequireClaim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tool:getsystemstate"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SetSystemStatePolicy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RequireClaim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"tool:setsystemstate"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;//...existing code...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code defines two authorization policies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GetSystemStatePolicy&lt;/code&gt; based on the &lt;code&gt;tool:getsystemstate&lt;/code&gt; value for the claim &lt;code&gt;permissions&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SetSystemStatePolicy&lt;/code&gt; based on the &lt;code&gt;tool:setsystemstate&lt;/code&gt; value for the claim &lt;code&gt;permissions&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s complete the protection of the tools by applying the authorization filter with the specific policy, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Tools/SystemTools.cs&lt;/span&gt;

&lt;span class="c1"&gt;//...existing code...&lt;/span&gt;

&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SystemTools&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...existing code...&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Returns the current state of the fictitious system. Possible states are: Ready, Waiting, Running, Stopped."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GetSystemStatePolicy"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;  &lt;span class="c1"&gt;//👈 new code&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetSystemState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//...existing code...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;McpServerTool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sets the current state of the fictitious system. Valid states are: Ready, Waiting, Running, Stopped."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SetSystemStatePolicy"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;  &lt;span class="c1"&gt;//👈 new code&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;SetSystemState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The new state to set. Must be one of: Ready, Waiting, Running, Stopped."&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;SystemState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//...existing code...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your MCP server is secure now!&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Your Secure MCP Server
&lt;/h2&gt;

&lt;p&gt;Now it’s time to test your brand new secure MCP server!&lt;/p&gt;

&lt;p&gt;Run your server with the &lt;code&gt;dotnet run&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Then, in your VSCode instance, &lt;a href="https://code.visualstudio.com/docs/copilot/customization/mcp-servers#_manage-mcp-servers" rel="noopener noreferrer"&gt;restart your MCP server&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszwrz0m7xcozmjor0qzl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fszwrz0m7xcozmjor0qzl.png" alt="Start an MCP server in VSCode" width="605" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a few seconds, you will see a popup message like the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j9hsx8l701i3fmz18mz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6j9hsx8l701i3fmz18mz.png" alt="MCP server requests authentication in VSCode" width="316" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This message is the effect of the &lt;code&gt;RequireAuthorization()&lt;/code&gt; execution you added to the server. It sent a 401 HTTP status message to VSCode with a reference to the Protected Resource Metadata document, where VSCode found all the needed info to start the user authentication and authorization process.&lt;/p&gt;

&lt;p&gt;Once you have allowed the authentication flow, you will get the &lt;a href="https://auth0.com/blog/anatomy-of-an-oauth2-authorization-request/" rel="noopener noreferrer"&gt;authorization request&lt;/a&gt; that VSCode is about to send to Auth0, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnewm5m4s1vx0os0p97o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnewm5m4s1vx0os0p97o.png" alt="Authorization request warning for MCP server in VSCode" width="567" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After user authentication, you will be requested to authorize VSCode to access your profile:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mfoyx0nlgcz9fmoy85i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7mfoyx0nlgcz9fmoy85i.png" alt="Authorization consent prompt for an MCP server on the Auth0 side" width="800" height="834"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you accept the request, you will be redirected back to your VSCode instance.&lt;/p&gt;

&lt;p&gt;You didn’t notice it, but under the hood, your VSCode instance automatically registered with Auth0 using DCR. You can verify this by going to your Auth0 dashboard and look in the list of the &lt;a href="https://manage.auth0.com/#/applications" rel="noopener noreferrer"&gt;registered applications&lt;/a&gt;. You will see VSCode among them, and it is marked as a third-party application, as you can see below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdapytz8fqtpnypekl1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvdapytz8fqtpnypekl1i.png" alt="VSCode is automatically registered as a client application on the Auth0 dashboard" width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now your VSCode instance has an access token to access the tools implemented by your MCP server.&lt;/p&gt;

&lt;p&gt;Make sure your chat window is in Agent mode and &lt;a href="https://code.visualstudio.com/docs/copilot/agents/agent-tools#_enable-tools-for-chat" rel="noopener noreferrer"&gt;list the tools&lt;/a&gt; provided by your MCP server. You will see only the &lt;code&gt;get_random_number&lt;/code&gt; tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp04v1aj76vk6khf2z539.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp04v1aj76vk6khf2z539.png" alt="List only Random Numbers tool for the MCP server on VSCode" width="618" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why? Doesn’t VSCode have an access token?&lt;/p&gt;

&lt;p&gt;VSCode has an access token, but without the required permissions to access the protected tools. So you can only access the public tool as before the protection implementation.&lt;/p&gt;

&lt;p&gt;To test your MCP server properly, you should assign one or more of the required permissions to your user profile on Auth0. Follow the instructions in &lt;a href="https://auth0.com/docs/manage-users/access-control/configure-core-rbac/rbac-users/assign-permissions-to-users" rel="noopener noreferrer"&gt;Assign Permissions to Users&lt;/a&gt; to assign the &lt;code&gt;tool:getsystemstate&lt;/code&gt; permission to your user profile.&lt;/p&gt;

&lt;p&gt;Use the command palette to &lt;a href="https://code.visualstudio.com/docs/copilot/customization/mcp-servers#_manage-mcp-servers" rel="noopener noreferrer"&gt;sign out and restart&lt;/a&gt; your MCP server in VSCode. Then authenticate and check again the list of tools available to you. This time you should also see the &lt;code&gt;get_system_state&lt;/code&gt; tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnq1ma49cpxyj0adzzesc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnq1ma49cpxyj0adzzesc.png" alt="List two tools for the MCP server on VSCode" width="618" height="172"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ask something involving the state of the fictitious system in the chat window, as shown in the following screenshot:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8b2jpmoo0xadvfcara4l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8b2jpmoo0xadvfcara4l.png" alt="Running the Get System State tool of the MCP server in the Copilot chat of VSCode" width="269" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should be able to use the tool as expected.&lt;/p&gt;

&lt;p&gt;Now, feel free to play with the permissions to get access to the other tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s Recap
&lt;/h2&gt;

&lt;p&gt;After a long journey, this article provided a comprehensive guide on securing a C# MCP server using Auth0, transforming it from a local experiment into an enterprise-ready resource server.&lt;/p&gt;

&lt;p&gt;The journey covered the following main steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Building the Base MCP Server:&lt;/strong&gt; You quickly set up a basic, unsecured MCP server using the C# SDK for MCP and the MCP server template, exposing a simple &lt;code&gt;GetRandomNumber()&lt;/code&gt; tool.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defining Protected Tools:&lt;/strong&gt; You introduced two new tools, &lt;code&gt;GetSystemState()&lt;/code&gt; and &lt;code&gt;SetSystemState()&lt;/code&gt;, which require specific authorization, setting the stage for security implementation.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth0 Tenant Configuration:&lt;/strong&gt; You configured the Auth0 tenant by enabling &lt;strong&gt;Dynamic Client Registration (DCR)&lt;/strong&gt; to allow MCP clients (like VSCode) to self-register, and enabling the &lt;strong&gt;Resource Parameter Compatibility Profile&lt;/strong&gt; as required by the MCP specification. We also promoted necessary connections to the domain level.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registering the MCP Server as an API:&lt;/strong&gt; The MCP server was registered with Auth0 as a standard API (Resource Server), ensuring its base URL was set as the audience. You configured it to use &lt;strong&gt;RFC 9068 access tokens&lt;/strong&gt; and enabled &lt;strong&gt;Role-Based Access Control (RBAC)&lt;/strong&gt; to include permissions in the access token.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Securing the Server Code:&lt;/strong&gt; You updated the C# application to:

&lt;ul&gt;
&lt;li&gt;Include necessary configuration via &lt;code&gt;appsettings.json&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Integrate the &lt;code&gt;JwtBearer&lt;/code&gt; authentication middleware for access token validation against Auth0.
&lt;/li&gt;
&lt;li&gt;Define the &lt;strong&gt;Protected Resource Metadata (PRM)&lt;/strong&gt; document using &lt;code&gt;AddMcp()&lt;/code&gt;, providing clients with authorization server and scope information.
&lt;/li&gt;
&lt;li&gt;Implement authorization policies based on specific permissions.
&lt;/li&gt;
&lt;li&gt;Apply these policies to the respective tools using the &lt;code&gt;[Authorize]&lt;/code&gt; attribute.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Testing the Secure Server:&lt;/strong&gt; Finally, you tested the secured server by integrating it with VSCode. The process demonstrated the DCR flow, user authentication, and the crucial step of assigning user permissions within Auth0 to grant access to the newly protected tools.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;By following these steps, you successfully implemented an OAuth 2.1-compliant security layer, ensuring that access to sensitive tools is gated by proper user permissions validated through Auth0.&lt;/p&gt;

&lt;p&gt;You can download the final version of this project from this &lt;a href="https://github.com/auth0-samples/auth0-ai-samples" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;, looking into the &lt;a href="https://github.com/auth0-samples/auth0-ai-samples/tree/main/auth-for-mcp/aspnetcore-mcp-server" rel="noopener noreferrer"&gt;auth-for-mcp/aspnetcore-mcp-server&lt;/a&gt; folder.&lt;/p&gt;

&lt;p&gt;To learn more about using Auth0 to secure your AI agents check out &lt;a href="http://a0.to/ai-content" rel="noopener noreferrer"&gt;Auth0 for AI Agents&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Strengthening OAuth 2.0 with FAPI 2.0</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Tue, 24 Feb 2026 08:27:26 +0000</pubDate>
      <link>https://dev.to/auth0/strengthening-oauth-20-with-fapi-20-oea</link>
      <guid>https://dev.to/auth0/strengthening-oauth-20-with-fapi-20-oea</guid>
      <description>&lt;p&gt;OAuth 2.0 has long been the cornerstone of modern authorization. It is a consolidated technology that powers everything from social logins to complex enterprise integrations. Developers appreciate its flexibility, which allows them to adapt the framework to various scenarios like traditional web applications, single page applications, desktop and mobile environments.&lt;/p&gt;

&lt;p&gt;However, this flexibility is a double-edged sword. Because OAuth 2.0 is a framework rather than a rigid protocol, it provides a collection of specifications that developers can combine in different ways. In high-risk and regulated sectors like banking, healthcare, and insurance, this freedom creates a significant surface area for configuration errors and interoperability issues. This is where the FAPI profile steps in, providing a strict set of rules to ensure the highest levels of security and trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flexibility Paradox of OAuth 2.0
&lt;/h2&gt;

&lt;p&gt;The fundamental challenge with "vanilla" OAuth 2.0 is the vast number of optional features. While the &lt;a href="https://datatracker.ietf.org/doc/html/rfc6749" rel="noopener noreferrer"&gt;core specification&lt;/a&gt; defines the basic grant types, it leaves many security decisions to the implementer. For a developer building a low-risk internal tool, these choices are manageable. But for an architect designing a system for &lt;a href="https://en.wikipedia.org/wiki/Open_banking" rel="noopener noreferrer"&gt;Open Banking&lt;/a&gt; or sensitive patient records, the stakes are much higher.&lt;/p&gt;

&lt;p&gt;In high-assurance environments, the phrase "&lt;em&gt;with great power comes great responsibility&lt;/em&gt;" applies literally. Security vulnerabilities often arise not from flaws in the individual specifications, but from how they are combined. For example, a system might use the &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow" rel="noopener noreferrer"&gt;Authorization Code Flow&lt;/a&gt; but fail to implement &lt;a href="https://datatracker.ietf.org/doc/html/rfc7636" rel="noopener noreferrer"&gt;Proof Key for Code Exchange (PKCE)&lt;/a&gt; or use insecure redirect URIs. FAPI 2.0 solves this by removing the "optional" from security best practices and mandating a "secure by default" posture.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is FAPI 2.0?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://openid.net/specs/fapi-2_0-security-02.html" rel="noopener noreferrer"&gt;FAPI 2.0&lt;/a&gt; is a security profile developed by the OpenID Foundation. Unlike its predecessor (FAPI 1.0), which was often criticized for its complexity, FAPI 2.0 focuses on simplicity and robust interoperability. It is not a new protocol. Instead, it is &lt;strong&gt;a prescriptive profile&lt;/strong&gt; that defines exactly which OAuth 2.0 and OpenID Connect extensions must be used and how they must be configured.&lt;/p&gt;

&lt;p&gt;The profile is built upon a formal attacker model that assumes a high-threat environment where attackers might control the network or have the ability to intercept front-channel messages. By adhering to FAPI 2.0, organizations ensure their authorization servers and clients meet a standardized level of resistance against modern attack vectors like session injection, token theft, and authorization request tampering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardening OAuth 2.0
&lt;/h2&gt;

&lt;p&gt;The key security enhancements introduced by FAPI 2.0 to strengthen the OAuth 2.0 framework focus on three main areas: hardening the authorization request, eliminating bearer token risks, and improving client authentication. Here are the most relevant protection mechanisms FAPI 2.0 enforces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pushed Authorization Requests (PAR):&lt;/strong&gt; FAPI 2.0 mandates &lt;a href="https://a0.to/pushed-authorization-6da4c7" rel="noopener noreferrer"&gt;PAR&lt;/a&gt; to mitigate the risks of sending sensitive authorization parameters (like scopes and PKCE challenges) over the insecure front-channel (browser). With PAR, the client sends these parameters via a secure back-channel POST request before redirection. The server then issues a short-lived &lt;code&gt;request_uri&lt;/code&gt; used in the subsequent browser redirect, ensuring sensitive data never appears in browser history or logs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sender-Constrained Tokens:&lt;/strong&gt; FAPI 2.0 moves away from vulnerable bearer tokens by enforcing "sender-constrained" tokens, which are cryptographically bound to the client that requested them. This prevents token misuse even if stolen. The primary mechanisms for this are Mutual TLS (mTLS), which binds the token to the client's X.509 certificate, and &lt;a href="https://a0.to/dpop-d188dc" rel="noopener noreferrer"&gt;Demonstration of Proof-of-Possession (DPoP)&lt;/a&gt;, a more flexible, application-level solution where the client proves possession of a private key using a signed JWT with every request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Asymmetric Client Authentication:&lt;/strong&gt; FAPI 2.0 replaces shared client secrets with asymmetric authentication, typically using Private Key JWT. The client signs a JWT with its private key, which the authorization server verifies with the client's registered public key. This method removes the risk of client secrets being leaked during transmission.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Role of FAPI Certification
&lt;/h2&gt;

&lt;p&gt;A critical component of the FAPI ecosystem is the &lt;a href="https://openid.net/certification/" rel="noopener noreferrer"&gt;OpenID Foundation's certification program&lt;/a&gt;. Because security depends on correct implementation, the foundation provides a suite of automated tests that vendors and organizations can use to verify their compliance. This certification provides a common ground for interoperability, ensuring that a FAPI-compliant client from one vendor can work seamlessly and securely with a FAPI-compliant authorization server from another.&lt;/p&gt;

&lt;p&gt;Choosing certified components reduces the burden of security auditing and provides assurance that the system adheres to the industry's highest standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  References and Further Reading
&lt;/h2&gt;

&lt;p&gt;For more technical details on the specifications mentioned, consult the following official resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://openid.net/specs/fapi-security-profile-2_0-final.html" rel="noopener noreferrer"&gt;OpenID FAPI 2.0 Security Profile - Final Specification&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://datatracker.ietf.org/doc/html/rfc9126" rel="noopener noreferrer"&gt;OAuth 2.0 Pushed Authorization Requests (RFC 9126)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://datatracker.ietf.org/doc/html/rfc9449" rel="noopener noreferrer"&gt;OAuth 2.0 Demonstration of Proof-of-Possession (RFC 9449)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openid.net/specs/fapi-attacker-model-2_0-final.html" rel="noopener noreferrer"&gt;FAPI 2.0 Attacker Model&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To learn more about FAPI 2.0 from a developer’s perspective, download the free ebook &lt;a href="https://a0.to/fapi-book-26d859" rel="noopener noreferrer"&gt;A Developer’s Guide to FAPI&lt;/a&gt;, which provides a detailed overview of the various security measures to make your application FAPI-ready.&lt;/p&gt;

</description>
      <category>oauth</category>
      <category>security</category>
      <category>identity</category>
      <category>fapi</category>
    </item>
    <item>
      <title>What a Developer Advocate Is Not</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Fri, 03 Oct 2025 16:20:32 +0000</pubDate>
      <link>https://dev.to/andychiare/what-a-developer-advocate-is-not-1fd4</link>
      <guid>https://dev.to/andychiare/what-a-developer-advocate-is-not-1fd4</guid>
      <description>&lt;p&gt;Someone: "What’s your job?"&lt;/p&gt;

&lt;p&gt;Me: "I'm a developer advocate."&lt;/p&gt;

&lt;p&gt;Someone: "Oh, cool! Like... what do you do exactly?"&lt;/p&gt;

&lt;p&gt;Me: "Well, my job is to make it easier for developers to adopt and use a product."&lt;/p&gt;

&lt;p&gt;Someone: "Oh, so you work in sales?"&lt;/p&gt;

&lt;p&gt;Me: "No, no. I don't sell the product. I help developers use it effectively, for example, by providing code samples, tutorials, articles, and so on."&lt;/p&gt;

&lt;p&gt;Someone: "So are you a blogger? Or a technical writer?"&lt;/p&gt;

&lt;p&gt;Me: "Well… maybe partly, but that's not all. I support the community and try to understand the pain points developers experience with the product so I can provide that feedback internally to improve it."&lt;/p&gt;

&lt;p&gt;Someone: "Oh, right. Uhm… well, that's interesting..."&lt;/p&gt;

&lt;p&gt;I’ve had many conversations like this. And often, the other person walks away with a very confused idea about the kind of work I do. I know, it's not easy to explain what I do for a living in just a few words. That's why I wrote &lt;a href="https://leanpub.com/i-am-a-developer-advocate" rel="noopener noreferrer"&gt;a little book&lt;/a&gt; that tries to explain it. In this article, however, I want to focus on what a developer advocate is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Developer Advocate Is Not…
&lt;/h2&gt;

&lt;p&gt;It's natural to try to relate a new concept to something you already know. We always do it whenever we have to learn something new. Keeping that familiar concept in mind helps us understand the similarities and differences.&lt;/p&gt;

&lt;p&gt;While understanding a new concept by looking at &lt;strong&gt;what it's not&lt;/strong&gt; is not the most efficient process, it's the most natural way to frame it, at least initially. So, let's define what a developer advocate is not by comparing this role to a few common assumptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  …a Salesperson
&lt;/h2&gt;

&lt;p&gt;The goal of a developer advocate is not to sell a product. Although they aim to &lt;em&gt;influence&lt;/em&gt;, their purpose is not to sell. Rather, they seek to improve the product so that it meets the real needs of developers. This influence moves in two directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Outward&lt;/strong&gt; (from the company to the developer community), to teach developers how to best use the product.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inward&lt;/strong&gt; (from the developer community to the company), to convey their feedback to the company and encourage improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Developers don't trust marketing promises; they trust other developers who have experience with a product and understand its technical details. A developer advocate builds this trust through technical competence, not sales techniques.&lt;/p&gt;

&lt;h2&gt;
  
  
  …a Copywriter
&lt;/h2&gt;

&lt;p&gt;Although writing is one of their core skills, the role of a developer advocate differs from that of a copywriter. The fundamental difference lies in the purpose of the communication. Content created by a developer advocate is meant to stimulate interaction and feedback. Their work is not about promoting a brand; rather, it is about starting a technical conversation with the developer community. Their work is part of an ongoing dialogue aimed at improving the developer's experience with the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  …a Blogger
&lt;/h2&gt;

&lt;p&gt;Another common oversimplification is confusing a developer advocate with a blogger. While they may manage a blog and write articles, this is just one activity among many and one communication channel among many. Unlike bloggers, whose primary focus is content creation, developer advocates have the crucial responsibility of inbound advocacy. Their job doesn't end when a post is published. Rather, it continues by actively gathering comments, criticism, and suggestions from the community and bringing them back internally to influence product decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  …a Technical Writer
&lt;/h2&gt;

&lt;p&gt;There is a clear distinction between a developer advocate and a technical writer. Technical writers create a product's documentation, which, by nature, has an impersonal style and follows specific rules to guide users. Technical documentation implies one-way communication from the company to the user. In contrast, a developer advocate's content is personal and carries the author's imprint. Its goal is to establish a human connection. Their goal is not only to explain "how to do something," but also to stimulate dialogue, answer questions, and build trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  …a (Mere) Presenter
&lt;/h2&gt;

&lt;p&gt;Although presentation skills are important, a developer advocate is more than just a presenter of technical content. They are, first and foremost, experienced developers. Their credibility comes from their ability to write code, understand development complexities, and “feel the pain” of other developers, not just from their public speaking abilities. Presentations at conferences or in videos are merely a means of communication and are not the essence of the role. Without direct, in-depth development experience, they would be unable to earn the community's trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Now that we've ruled out some of the professions it can be confused with, you probably want to know what a developer advocate is. What do they do? What value do they bring to a company that builds products for developers?&lt;/p&gt;

&lt;p&gt;It would take too long to explain here. Find out by downloading my booklet “&lt;a href="https://leanpub.com/i-am-a-developer-advocate" rel="noopener noreferrer"&gt;I Am a Developer Advocate&lt;/a&gt;”. &lt;strong&gt;It's free!&lt;/strong&gt; And let me know what you think!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://leanpub.com/i-am-a-developer-advocate" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7c4jynkwemi09dbjf9uw.jpg" alt="I Am a Developer Advocate" width="640" height="801"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>career</category>
      <category>role</category>
      <category>developers</category>
    </item>
    <item>
      <title>Being a Developer in the Vibe Coding Era</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Tue, 26 Aug 2025 15:07:44 +0000</pubDate>
      <link>https://dev.to/andychiare/being-a-developer-in-the-vibe-coding-era-3me8</link>
      <guid>https://dev.to/andychiare/being-a-developer-in-the-vibe-coding-era-3me8</guid>
      <description>&lt;p&gt;I was looking at results of the &lt;a href="https://survey.stackoverflow.co/2025" rel="noopener noreferrer"&gt;Stackoverflow Developer Survey 2025&lt;/a&gt;, and one aspect caught my attention: the percentages relating to developers' confidence in &lt;a href="https://survey.stackoverflow.co/2025/ai#2-accuracy-of-ai-tools" rel="noopener noreferrer"&gt;the accuracy of AI tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqt5ussgo1vqos8lvb6y5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqt5ussgo1vqos8lvb6y5.png" alt="Stats about the trust developers have in the accuracy of AI tools according to Stackoverflow Developer Survey 2025" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As mentioned in the comment on the results related to this question, &lt;strong&gt;more developers do not trust AI tools than trust them&lt;/strong&gt;. In my opinion, this is good news, although I am concerned about that &lt;strong&gt;3% who have a high level of trust in AI tools&lt;/strong&gt;. This is not to demonize AI tools, but rather to address the evolving role of developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developers and Libraries
&lt;/h2&gt;

&lt;p&gt;Some time ago, I wrote an article about whether &lt;a href="https://auth0.com/blog/zen-and-the-art-of-identity-management/" rel="noopener noreferrer"&gt;using a library or framework exempts developers from understanding the specifics of the underlying tools and technologies&lt;/a&gt;. I addressed this issue by referring to the cult book &lt;a href="https://dev.toZen_and_the_Art_of_Motorcycle_Maintenance"&gt;Zen and the Art of Motorcycle Maintenance&lt;/a&gt; by Robert M. Pirsig. Paraphrasing the book, I identified three types of developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;classic developer&lt;/strong&gt;, who cannot resist the curiosity to see how a tool works. If possible, they will even venture into modifying it. This is what we commonly call a &lt;strong&gt;hacker&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;romantic developer&lt;/strong&gt;, who just uses the library or framework as it is. He is more interested in achieving his desired result and appreciates the library more for how easily it allows him to do so than for how it works internally. The details of the tool are none of his business.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;pragmatic developer&lt;/strong&gt; uses the library to solve a problem but does not overlook the advantage of understanding how it works internally. They may not know all the internal details of the library, but they have a high-level understanding of its architecture and can delve deeper if necessary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking at the data on developers' trust in AI tools, I was reminded of that article. I thought that, nowadays, &lt;strong&gt;that classification and the need for pragmatic rather than romantic developers is even more relevant&lt;/strong&gt;. If we want to translate this classification in terms of trust, the classic developer does not trust the tool; the pragmatic developer trusts it to a certain extent but tries to keep everything under control; and the romantic developer has blind faith in the tool.&lt;/p&gt;

&lt;p&gt;If blindly trusting a traditional library has its drawbacks, then &lt;strong&gt;how can we trust code generated by an AI tool?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Towards a New Way of Being Developers?
&lt;/h2&gt;

&lt;p&gt;With the rise of &lt;a href="https://en.wikipedia.org/wiki/Vibe_coding" rel="noopener noreferrer"&gt;vibe coding&lt;/a&gt;, a new &lt;strong&gt;Romanticism of software development&lt;/strong&gt; seems to have emerged, generating widespread enthusiasm. However, if code written by a developer is often unreliable, how can we trust code generated by an AI tool that has no real understanding of what it is doing? I am referring, of course, to tools based on LLMs, the current mainstream of AI.&lt;/p&gt;

&lt;p&gt;The answer is that &lt;strong&gt;we should not trust it. Ever!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Should we give up on AI code generation tools? Absolutely not! We should use them because they can improve the quality of our code more than increase the production speed.&lt;/p&gt;

&lt;p&gt;However, &lt;strong&gt;we should avoid becoming romantic developers&lt;/strong&gt; who get excited because we can accomplish in a couple of hours what would have taken days or even weeks a few months ago. We must be pragmatic and &lt;strong&gt;verify that the generated code does what we expected&lt;/strong&gt;. If we want to take it a step further and become classic developers who understand how the tool generated the code, even better. At the very least, it will help us formulate better questions.&lt;/p&gt;

&lt;p&gt;The developer's job is not—and never has been—to produce code. Their job is to provide a solution that works as expected. As a &lt;em&gt;vibe coder&lt;/em&gt;, &lt;strong&gt;you are responsible for verifying the code generated by the AI tool&lt;/strong&gt;. You must understand the code and modify it if necessary. Some of the time saved by using the AI tool will need to be spent reviewing the results.&lt;/p&gt;

&lt;p&gt;In short, you should never trust what an AI tool proposes. You must always analyze and validate the results. This means that you cannot stop learning programming languages, their best practices, design patterns, and the technologies on which your application is based. You will also need to learn the new things that the generated code will probably include.&lt;/p&gt;

&lt;p&gt;Keep in mind that if something goes wrong—rarely does everything go smoothly—you are responsible for the code generated by the AI tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Developer Is Dead. Long Live the Developer.
&lt;/h2&gt;

&lt;p&gt;To those who think that AI will replace developers, I would say that they are right. In a few years—or even months—there won't be any more developers. &lt;strong&gt;There will be no more developers as we understood them until a few years ago&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once the &lt;em&gt;Romanticism of software development&lt;/em&gt; has passed, we will find that the role of developers has been redefined and probably expanded. We will likely have a new title, such as "code manager" or "coded solution provider."&lt;/p&gt;

&lt;p&gt;However, &lt;strong&gt;there will still be a need for technical skills in code review and validation&lt;/strong&gt;: high-level skills that minimize bugs and security issues in code generated by AI tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We cannot afford to be romantic developers who blindly trust the code generated by AI tools&lt;/strong&gt;. Professional developers should never accept code produced by an AI tool without question. They must question it, employing the &lt;strong&gt;critical thinking&lt;/strong&gt; that many believe is threatened by AI. After all, the saying "&lt;strong&gt;Trust, but verify&lt;/strong&gt;" still holds true.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vibecoding</category>
      <category>developer</category>
      <category>programming</category>
    </item>
    <item>
      <title>.NET MAUI Authentication on Windows: The OpenID Connect Challenge</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Tue, 06 May 2025 10:36:39 +0000</pubDate>
      <link>https://dev.to/auth0/net-maui-authentication-on-windows-the-openid-connect-challenge-40h2</link>
      <guid>https://dev.to/auth0/net-maui-authentication-on-windows-the-openid-connect-challenge-40h2</guid>
      <description>&lt;p&gt;I'm not an expert desktop and mobile developer. My experience in this field is from a while ago, particularly in mobile development, where I wouldn't claim professional competence. Most of the time, I've created desktop console applications supporting backend processing. Still, I know how hard it is to build native applications with a GUI, especially when cross-platform support is required.&lt;/p&gt;

&lt;p&gt;When I heard about &lt;a href="https://dotnet.microsoft.com/it-it/apps/maui" rel="noopener noreferrer"&gt;.NET MAUI&lt;/a&gt;, I immediately thought it would be an ambitious and very interesting project. Several development teams dream of having a single codebase (or so) for applications that can run on different platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  .NET MAUI and OpenID Connect
&lt;/h2&gt;

&lt;p&gt;I had the chance to play with MAUI to explore the integration with &lt;a href="https://auth0.com/" rel="noopener noreferrer"&gt;Auth0&lt;/a&gt; authentication, which is based on &lt;a href="https://openid.net/developers/how-connect-works/" rel="noopener noreferrer"&gt;OpenID Connect&lt;/a&gt;, a standard authentication protocol. However, since my first attempt around mid-2022, I immediately ran into issues on the Windows platform.&lt;/p&gt;

&lt;p&gt;As you may know, OpenID Connect is a browser-based authentication protocol. This means that authentication is performed by opening the Identity Provider's login page in a system browser window. Auth0's experience is similar to what happens when you access a website using your Google or Facebook account.&lt;br&gt;
While you might consider using an embedded WebView in your native application, &lt;a href="https://datatracker.ietf.org/doc/html/rfc8252#section-8.12" rel="noopener noreferrer"&gt;best practices&lt;/a&gt; recommend launching authentication in a separate system browser window. Among other things, this allows you to benefit from &lt;a href="https://auth0.com/blog/what-is-and-how-does-single-sign-on-work/" rel="noopener noreferrer"&gt;Single Sign-On&lt;/a&gt;. But let's not digress.&lt;/p&gt;

&lt;p&gt;.NET MAUI leverages the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.maui.authentication.webauthenticator" rel="noopener noreferrer"&gt;WebAuthenticator&lt;/a&gt; class to initiate the browser-based OpenID Connect authentication flow. While this approach provides a seamless experience for user login on platforms like Android, iOS, and macOS, developers encounter significant challenges with Windows when implementing secure authentication in their .NET MAUI applications. This was true three years ago, and &lt;a href="https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/communication/authentication?view=net-maui-9.0&amp;amp;tabs=windows" rel="noopener noreferrer"&gt;it is still true&lt;/a&gt; at the time of writing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1uv83wefk7qybvv6xl2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1uv83wefk7qybvv6xl2.png" alt="WebAuthenticator does not work on Windows" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Frustrating Experience
&lt;/h2&gt;

&lt;p&gt;You know, I'm not the only one surprised by this issue. If you take a look at the &lt;a href="https://github.com/microsoft/WindowsAppSDK/issues/441" rel="noopener noreferrer"&gt;long threads&lt;/a&gt; around &lt;a href="https://github.com/dotnet/maui/issues/2702" rel="noopener noreferrer"&gt;this issue&lt;/a&gt;, you can see many developers are complaining about the missing support for a very common feature such as authentication in Microsoft's own operating system!&lt;/p&gt;

&lt;p&gt;But developers are not sitting idly by. Some have rolled up their sleeves and found workarounds. My colleagues on the SDK team have also &lt;a href="https://github.com/auth0/auth0-oidc-client-net/pull/287" rel="noopener noreferrer"&gt;implemented a solution inspired by the one adopted by WinUIEx&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to this workaround implemented by the &lt;a href="https://github.com/auth0/auth0-oidc-client-net/tree/master/src/Auth0.OidcClient.MAUI" rel="noopener noreferrer"&gt;Auth0 MAUI SDK&lt;/a&gt;, I was able to create some sample apps, write &lt;a href="https://auth0.com/blog/add-authentication-to-dotnet-maui-apps-with-auth0" rel="noopener noreferrer"&gt;some tutorials&lt;/a&gt;, and add a MAUI template to the &lt;a href="https://github.com/auth0/auth0-dotnet-templates" rel="noopener noreferrer"&gt;Auth0 Templates for .NET&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;Everything worked fine until .NET 9. With the latest release, once again, OpenID Connect integration stopped working in applications migrated to .NET 9, and &lt;a href="https://github.com/microsoft/WindowsAppSDK/issues/441#issuecomment-2585843963" rel="noopener noreferrer"&gt;the threads restarted&lt;/a&gt; (if ever they have actually stopped).&lt;/p&gt;

&lt;p&gt;We also had &lt;a href="https://community.auth0.com/t/add-authentication-to-net-maui-apps-with-auth0/86929/137" rel="noopener noreferrer"&gt;some&lt;/a&gt; &lt;a href="https://community.auth0.com/t/add-auth0-authentication-to-blazor-hybrid-apps-in-net-maui/101159/28" rel="noopener noreferrer"&gt;requests&lt;/a&gt; for &lt;a href="https://community.auth0.com/t/add-authentication-to-net-maui-apps-with-auth0/86929/130" rel="noopener noreferrer"&gt;help&lt;/a&gt; in the Auth0 Community forum.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Ongoing Journey of .NET MAUI Authentication on Windows
&lt;/h2&gt;

&lt;p&gt;Bottom line, MAUI developers had to revise their workarounds to fix the OIDC integration once again. My colleagues on the SDK team were also great in finding a way to fix the issue once again and &lt;a href="https://auth0.com/blog/announcing-a-new-release-of-the-auth0-oidc-client-for-net/" rel="noopener noreferrer"&gt;update the MAUI SDK to support OIDC in .NET 9&lt;/a&gt;. On my side, I took the opportunity to update the &lt;a href="https://github.com/auth0/auth0-dotnet-templates" rel="noopener noreferrer"&gt;Auth0 Templates for .NET&lt;/a&gt; package just in these days and provide you with a working &lt;a href="https://github.com/auth0/auth0-dotnet-templates/blob/main/docs/auth0maui.md" rel="noopener noreferrer"&gt;.NET MAUI template for Windows&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From some ongoing &lt;a href="https://github.com/dotnet/maui/discussions/27981" rel="noopener noreferrer"&gt;discussions&lt;/a&gt;, it seems like there is a glimmer of light for the future. It goes without saying that every MAUI developer is looking forward to the resolution of this complicated relationship between MAUI, OpenID Connect, and Windows.&lt;/p&gt;

&lt;p&gt;And you? What has been your experience integrating OpenID Connect authentication in MAUI applications on Windows? Share your story below.👇&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>maui</category>
      <category>openid</category>
      <category>authentication</category>
    </item>
    <item>
      <title>Letter to Blazor</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Mon, 10 Mar 2025 17:34:33 +0000</pubDate>
      <link>https://dev.to/andychiare/letter-to-blazor-9h1</link>
      <guid>https://dev.to/andychiare/letter-to-blazor-9h1</guid>
      <description>&lt;p&gt;Dear Blazor, I met you when you were still in your infancy. I started playing with you when you could barely stand up...&lt;/p&gt;

&lt;h2&gt;
  
  
  The Promise
&lt;/h2&gt;

&lt;p&gt;Even though you were still frail, your ambition was exciting: &lt;strong&gt;to allow developers to create web, desktop, and mobile applications using only C# and Razor&lt;/strong&gt;. Not bad at all. It was a bit like dreaming of becoming an astronaut when you grow up. 🧑‍🚀&lt;/p&gt;

&lt;p&gt;You started with the web, promising to dethrone JavaScript and do much better than that old-timer. You promised that &lt;strong&gt;developers only need to know C# and Razor&lt;/strong&gt; to create efficient web applications, whether they run on the server or the browser. No more complications to make a SPA communicate with the server. You, developer, create your components in Razor and all the logic in C#. The framework will take care of transforming everything into an application that runs on a web server or in a browser. Don't worry. &lt;strong&gt;Everything will happen behind the scenes in a linear and transparent manner&lt;/strong&gt;. 🚀&lt;/p&gt;

&lt;h2&gt;
  
  
  Old Memories
&lt;/h2&gt;

&lt;p&gt;Who knows why, but those promises brought to my mind something already seen: memories that I thought I had completely erased resurfaced. &lt;strong&gt;The promise of &lt;a href="https://en.wikipedia.org/wiki/ASP.NET_Web_Forms" rel="noopener noreferrer"&gt;Web Forms&lt;/a&gt; resurfaced in my memory&lt;/strong&gt;: a unified paradigm for web development, whether the application logic runs on the server or the client. The framework would have taken care of doing the right thing. This promise created quite a few disappointments at that time. Many developers who were approaching Web programming for the first time got confused. But perhaps now things have changed, times are different, we have different technologies. &lt;em&gt;Blazor is not Web Forms&lt;/em&gt;. 🤔&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Wonderful World
&lt;/h2&gt;

&lt;p&gt;You, Blazor, have made it possible to create fantastic applications that run on the server and on the browser. You enabled &lt;strong&gt;components that can be shared between the server and the client&lt;/strong&gt;. And your followers are not few. You have enchanted many. Not just those who hated JavaScript - that was easy - you also attracted those who were familiar with JavaScript. In short, your dream was within reach.&lt;/p&gt;

&lt;p&gt;You enabled the reuse of the same Razor components in MAUI to create desktop and mobile applications, as you promised. Many .NET developers have started to idolize you. 😍&lt;/p&gt;

&lt;p&gt;With .NET 8, you've gone further. Not only can developers use the same programming model to build server and client applications, but now &lt;strong&gt;they can decide at the level of individual components whether it should run on the server or the client&lt;/strong&gt;. They can even decide that a component starts as a server component and becomes a client component. Crazy stuff! 🤯&lt;/p&gt;

&lt;h2&gt;
  
  
  A Crack in the Wall
&lt;/h2&gt;

&lt;p&gt;Even though I have followed your growth, I can't say I know you well. I only played with you in a limited scope. I have a narrow view of you, I admit it. But the little I got to know about you left me a bit puzzled.&lt;/p&gt;

&lt;p&gt;Ok. Many of the promises you made, you kept. But from what I could see, there are aspects that resemble me - no offense - more like a big deception. 😒&lt;/p&gt;

&lt;p&gt;You promised that &lt;strong&gt;we wouldn't have to worry about JavaScript anymore&lt;/strong&gt;, but every now and then there are things that you don't allow us to do. And so we inevitably have to communicate with the old man through &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/" rel="noopener noreferrer"&gt;JSInterop&lt;/a&gt;, and it's not a walk in the park.&lt;/p&gt;

&lt;p&gt;You promised a unified programming model, where we didn't have to come up with tricks to circumvent programming problems that the not-so-simple coexistence of JavaScript and HTML has accustomed us to. I would have been &lt;strong&gt;a unified and consistent model where we create a component and don't need to worry about how and where it will be rendered and executed&lt;/strong&gt;. The framework will handle everything.&lt;/p&gt;

&lt;p&gt;So, we write an interactive auto component that needs to make an HTTP call to the server. Follow all the simple rules, but &lt;a href="https://stackoverflow.com/questions/77503849/in-the-new-blazor-web-app-template-using-net-8-how-do-i-make-an-http-request-f" rel="noopener noreferrer"&gt;it doesn't work&lt;/a&gt;. Why? You also have to register &lt;code&gt;HttpClient&lt;/code&gt; on the server. But why? Due to &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/call-web-api?view=aspnetcore-8.0#service-abstractions-for-web-api-calls" rel="noopener noreferrer"&gt;how the framework works under the hood&lt;/a&gt;. 😨&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;what about the combination of render mode and interactivity management?&lt;/strong&gt; How should I go about making my application today? Interactive WebAssembly or Interactive Server? But maybe it's better Global Interactive Auto, so it takes care of everything. The best way to get a headache is the day something doesn't work and you can't go back. As someone said, &lt;a href="https://www.reddit.com/r/dotnet/comments/1folj0j/comment/lorluuv/" rel="noopener noreferrer"&gt;Blazor is absolutely amazing, until it’s not&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then, you want to add support for authentication. A fairly common operation. Which application is without authentication nowadays? You enter a nightmare. &lt;strong&gt;How do I transfer the authentication state between the server and the client parts of a Blazor application?&lt;/strong&gt; Well, just &lt;a href="https://auth0.com/blog/auth0-authentication-blazor-web-apps/#Syncing-the-Authentication-State" rel="noopener noreferrer"&gt;implement a few classes here and there&lt;/a&gt;. Why? &lt;strong&gt;Didn't you promise us that there wouldn't be any more need for tricks and gimmicks to do simple things?&lt;/strong&gt; Yes, but this depends on how the framework works. 😞&lt;/p&gt;

&lt;p&gt;Ok, now &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-9.0&amp;amp;tabs=visual-studio#manage-authentication-state-in-blazor-web-apps" rel="noopener noreferrer"&gt;with .NET 9 you've simplified it&lt;/a&gt;, but only after your supporters went crazy trying to get the server and client to communicate about the authentication state. And in any case, it’s still a developer’s burden to tell you what to do to make them transfer the serialized authentication state. But &lt;strong&gt;wasn't the framework supposed to take care of these details?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Big Deception?
&lt;/h2&gt;

&lt;p&gt;What about the programming model you promised us? You had promised us that it was no longer necessary to know how JavaScript works, its interaction with HTML, etc. You had promised us a simple and unified model — &lt;strong&gt;a model that abstracts the underlying complications&lt;/strong&gt;: HTTP, HTML, JavaScript, AJAX, etc.&lt;/p&gt;

&lt;p&gt;I feel like &lt;strong&gt;you replaced a complex model based on standard stuff with YOUR model&lt;/strong&gt;. Now, instead of knowing how HTTP, HTML, and JavaScript work, I have to understand how YOU work internally to create my applications. And then I wonder: &lt;strong&gt;where is the gain?&lt;/strong&gt; 🤔&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; &lt;strong&gt;This letter is not meant to be a complaint against the work done by the Blazor team&lt;/strong&gt;. It is merely an invitation to not trust abstractions when they are pushed too far. Not all complexity can be hidden behind an abstraction. Even if the model proposed by an abstraction seems to work, you always need to have an idea of how things work under the hood when they don't behave as expected. &lt;strong&gt;Be enthusiastic, yet pragmatic&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>blazor</category>
      <category>dotnet</category>
      <category>programming</category>
    </item>
    <item>
      <title>Securing Blazor in All Its Flavors</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Thu, 27 Feb 2025 09:36:02 +0000</pubDate>
      <link>https://dev.to/auth0/securing-blazor-in-all-its-flavors-2819</link>
      <guid>https://dev.to/auth0/securing-blazor-in-all-its-flavors-2819</guid>
      <description>&lt;p&gt;Since its first official release in .NET 3.0, &lt;a href="https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor" rel="noopener noreferrer"&gt;Blazor&lt;/a&gt; has made a solemn promise to .NET developers: allow them to build web applications using just C# and the .NET platform, with no JavaScript and very little HTML. But Blazor’s goal was even more ambitious: &lt;a href="https://devblogs.microsoft.com/dotnet/blazor-server-in-net-core-3-0-scenarios-and-performance/#what%E2%80%99s-next-for-blazor" rel="noopener noreferrer"&gt;becoming the reference framework for all types of applications, not just web&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Since then, the Blazor framework has gained considerable popularity among .NET developers, but not without some problems, inevitably due to its evolution. The framework's changes since its first release have significantly impacted developers' work, who have had to adapt their applications to accommodate its evolution.&lt;/p&gt;

&lt;p&gt;Furthermore, while the idea of ​​having a single framework to develop any type of application may seem great, this can hide pitfalls and complications when implementing standards-based authentication and authorization, such as those based on &lt;a href="https://openid.net/developers/how-connect-works/" rel="noopener noreferrer"&gt;OpenID Connect&lt;/a&gt; (OIDC) and &lt;a href="https://datatracker.ietf.org/doc/html/rfc6749" rel="noopener noreferrer"&gt;OAuth 2.0&lt;/a&gt;, for example. As you may know, you should use &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/which-oauth-2-0-flow-should-i-use" rel="noopener noreferrer"&gt;different flows to authenticate users&lt;/a&gt; based on the application type: server-rendered web application, single-page application, or native application.&lt;/p&gt;

&lt;p&gt;In this article, we will focus on implementing authentication based on the different types of applications you can build with Blazor. We will provide guidance on best practices and direct you to practical implementations using Auth0.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Blazor “Flavors”
&lt;/h2&gt;

&lt;p&gt;We mentioned Blazor’s evolution earlier. Let’s briefly recap what it is about.&lt;/p&gt;

&lt;p&gt;In the beginning, Blazor was released with two &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/hosting-models" rel="noopener noreferrer"&gt;hosting models&lt;/a&gt;: Blazor Server and Blazor WebAssembly, also known as Blazor WASM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blazor Server&lt;/strong&gt; applications are intended to run on the server, and the UI part sent to the browser communicates with the server part over a &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/signalr/introduction" rel="noopener noreferrer"&gt;SignalR&lt;/a&gt; connection using &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/websockets" rel="noopener noreferrer"&gt;WebSocket&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blazor WASM&lt;/strong&gt; applications are compiled into &lt;a href="https://webassembly.org/" rel="noopener noreferrer"&gt;WebAssembly&lt;/a&gt; and run entirely on the browser. While you can host a Blazor WASM application on any web server (&lt;em&gt;standalone Blazor WASM&lt;/em&gt;), a specific hosting model called &lt;em&gt;ASP.NET Core hosted model&lt;/em&gt; allows you to simply host your application &lt;em&gt;within&lt;/em&gt; an ASP.NET Core application, giving you more control over some server-side functionality.&lt;/p&gt;

&lt;p&gt;With .NET 6.0, a new hosting model was introduced: &lt;strong&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/hybrid" rel="noopener noreferrer"&gt;Blazor Hybrid&lt;/a&gt;&lt;/strong&gt;. This hosting model allows you to use the Blazor framework to build desktop and mobile applications using a hybrid approach, i.e., Razor components run directly in the native application.&lt;/p&gt;

&lt;p&gt;With .NET 8.0, a significant change was introduced: &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes" rel="noopener noreferrer"&gt;render modes&lt;/a&gt;. This revolutionized the way the hosting model was understood until now. With the Server and WASM hosting models, either the entire application ran on the server, or it all ran on the client. With render modes, the components run on the server or client, not the entire application. Or rather, the developer can decide the level of granularity with which to run the various parts of their Blazor application on the server or client. This is a great opportunity to build high-performance web applications, but it also complicates things quite a bit in different contexts, such as authentication and authorization.&lt;/p&gt;

&lt;p&gt;At the same time, the introduction of render modes practically deprecates the Blazor Server hosting model. From now on, Blazor applications that support render modes are called &lt;strong&gt;Blazor Web Apps&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With the proliferation of all these &lt;em&gt;flavors&lt;/em&gt; of Blazor, what is the best approach to implementing OIDC and OAuth 2.0 for authentication and authorization?&lt;/p&gt;

&lt;h2&gt;
  
  
  Blazor Server
&lt;/h2&gt;

&lt;p&gt;While Blazor Server may be considered deprecated, legacy applications based on this hosting model exist. From the OAuth 2.0 and OpenID Connect perspective, a Blazor Server application is a server-rendered application and should therefore be considered a &lt;a href="https://auth0.com/docs/get-started/applications/confidential-and-public-applications#confidential-applications" rel="noopener noreferrer"&gt;confidential client&lt;/a&gt;. It will use the &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow" rel="noopener noreferrer"&gt;Authorization Code flow&lt;/a&gt; to obtain ID and access tokens.&lt;/p&gt;

&lt;p&gt;In code terms, this means that your Blazor application will use the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnecthandler" rel="noopener noreferrer"&gt;OpenID Connect middleware&lt;/a&gt;, as in the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;  
  &lt;span class="p"&gt;{&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CookieAuthenticationDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultChallengeScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OpenIdConnectDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="p"&gt;})&lt;/span&gt;  
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCookie&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOpenIdConnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myOidc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;  
  &lt;span class="p"&gt;{&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"https://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Oidc:Domain"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Oidc:ClientId"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientSecret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Oidc:ClientSecret"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use Auth0 for authentication, you can leverage the &lt;a href="https://github.com/auth0/auth0-aspnetcore-authentication" rel="noopener noreferrer"&gt;Auth0 ASP.NET Core Authentication SDK&lt;/a&gt; to simplify things.&lt;/p&gt;

&lt;p&gt;Read &lt;a href="https://a0.to/blazor-webapp-with-authentication" rel="noopener noreferrer"&gt;this article to learn how to add Auth0 authentication to your Blazor Server application&lt;/a&gt;. As an additional resource, &lt;a href="https://a0.to/blazor-logout-problem" rel="noopener noreferrer"&gt;this article explains how to overcome an issue with logout that affects Blazor Server&lt;/a&gt; due to SignalR's behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blazor WebAssembly
&lt;/h2&gt;

&lt;p&gt;Since Blazor WASM applications run entirely in the browser, they can be considered single-page applications, i.e., &lt;a href="https://auth0.com/docs/get-started/applications/confidential-and-public-applications#public-applications" rel="noopener noreferrer"&gt;public clients&lt;/a&gt;. In this case, you must use the &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-pkce" rel="noopener noreferrer"&gt;Authorization Code flow with PKCE&lt;/a&gt; to get ID and access tokens.&lt;/p&gt;

&lt;p&gt;In terms of implementation, you need a specific WebAssembly package for your application, the &lt;a href="https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Authentication" rel="noopener noreferrer"&gt;Microsoft.AspNetCore.Components.WebAssembly.Authentication&lt;/a&gt; package. Your code for configuring OIDC will be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOidcAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Oidc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProviderOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProviderOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to add a reference to the &lt;code&gt;AuthenticationService.js&lt;/code&gt; script in the &lt;code&gt;index.html&lt;/code&gt; file of your application, as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    ...
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"_framework/blazor.webassembly.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read &lt;a href="https://a0.to/blazor-webassembly-apps" rel="noopener noreferrer"&gt;this article for more details on how to add Auth0 authentication to your Blazor WASM application&lt;/a&gt;. The article uses the ASP.NET Core hosted model, but the authentication configuration is the same even for standalone Blazor WebAssembly applications. To learn about &lt;a href="https://a0.to/web-apps-for-blazor-wasm" rel="noopener noreferrer"&gt;building Blazor WASM applications to host in Azure Static Web Apps, read this article&lt;/a&gt;. Finally, take a look at &lt;a href="https://a0.to/rbac-in-blazor-apps" rel="noopener noreferrer"&gt;this article to learn how to use Auth0’s Role-Based Access Control (RBAC)&lt;/a&gt; in both Server and WASM hosting models.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Chaos of Blazor Render Modes
&lt;/h2&gt;

&lt;p&gt;As you can see, identifying the correct authentication approach with the Server and WASM hosting models is quite straightforward: the first follows the confidential client approach, and the second follows the public client approach.&lt;/p&gt;

&lt;p&gt;The introduction of render modes complicates things a bit. Indeed, in this case, part of an application runs on the server, and part runs on the client. To complicate the situation even more, with the &lt;em&gt;interactive auto render mode&lt;/em&gt;, a component is initially rendered on the server and then on the client. What category does a Blazor application fall into in this case? Is it a confidential client or a public client?&lt;/p&gt;

&lt;p&gt;Actually, there is no definitive general answer to this question. Much depends on how the components and their related render modes are combined in a specific application. A conservative approach to implementing OIDC authentication in a Blazor Web App is to consider it a confidential client. This entails that you can reuse the same code to configure a Blazor Server application.&lt;/p&gt;

&lt;p&gt;However, this opens a new problem: how do you determine whether or not a user can see a specific component based on its authentication state? If a user authenticates after the browser downloads a WASM component, how does the browser know the new user authentication state?&lt;/p&gt;

&lt;p&gt;To solve this problem, you need to keep the server-side and client-side &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/security/authentication-state" rel="noopener noreferrer"&gt;authentication states&lt;/a&gt; in sync. However, while you have to manually implement authentication state sync in .NET 8.0, .NET 9.0 simplifies this approach by providing native support for authentication state synchronization.&lt;/p&gt;

&lt;p&gt;Read &lt;a href="https://a0.to/auth0-authentication-blazor" rel="noopener noreferrer"&gt;this article to learn the details of how to add support to Blazor Web Apps authentication&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, problems are not just related to authentication. You should also take care of how to call remote APIs from your WASM components. Since you consider your Blazor Web App a confidential client, your WASM components do not receive any access token to call APIs. How can you deal with this scenario?&lt;/p&gt;

&lt;p&gt;Don't be tempted to make the access token available to WASM components. As &lt;a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps" rel="noopener noreferrer"&gt;security best practices&lt;/a&gt; suggest, the best approach is to implement the &lt;a href="https://a0.to/the-backend-for-frontend-pattern-bff" rel="noopener noreferrer"&gt;Backend for Frontend (BFF)&lt;/a&gt; pattern. This &lt;a href="https://auth0.com/blog/call-protected-api-from-blazor-web-app/" rel="noopener noreferrer"&gt;article shows how to call APIs by implementing BFF in Blazor&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blazor Hybrid Apps
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, Blazor Hybrid allows you to reuse Razor components and the skills learned with the Blazor framework to create UIs for desktop and mobile applications. In this context, your Blazor application is hosted by a native application, typically developed with &lt;a href="https://dotnet.microsoft.com/it-it/apps/maui" rel="noopener noreferrer"&gt;MAUI&lt;/a&gt;. In fact, the combination of Blazor and MAUI is also often called &lt;strong&gt;Blazor MAUI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this scenario, authentication is handled entirely by the native component of the Blazor MAUI application. That is, the OIDC configuration and the authentication process occur in the native code of MAUI. The Blazor part simply creates an authentication state based on the user authentication result. Here is an example of an authentication state definition for a Blazor MAUI application using the &lt;a href="https://github.com/auth0/auth0-oidc-client-net/tree/master/src/Auth0.OidcClient.MAUI" rel="noopener noreferrer"&gt;Auth0 MAUI SDK&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Auth0AuthenticationStateProvider&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AuthenticationStateProvider&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ClaimsPrincipal&lt;/span&gt; &lt;span class="n"&gt;currentUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ClaimsPrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ClaimsIdentity&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Auth0Client&lt;/span&gt; &lt;span class="n"&gt;auth0Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Auth0AuthenticationStateProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Auth0Client&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;auth0Client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AuthenticationState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAuthenticationStateAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AuthenticationState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;LogInAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;loginTask&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;LogInAsyncCore&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;NotifyAuthenticationStateChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loginTask&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;loginTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AuthenticationState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;LogInAsyncCore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;LoginWithAuth0Async&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;currentUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AuthenticationState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ClaimsPrincipal&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;LoginWithAuth0Async&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;authenticatedUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ClaimsPrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ClaimsIdentity&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;loginResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;auth0Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoginAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;loginResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;authenticatedUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loginResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;authenticatedUser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LogOut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;auth0Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogoutAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;currentUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ClaimsPrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ClaimsIdentity&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;NotifyAuthenticationStateChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AuthenticationState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read &lt;a href="https://a0.to/authentication-to-blazor-hybrid-apps" rel="noopener noreferrer"&gt;this article for more details about adding Auth0 authentication to a Blazor MAUI application&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the Auth0 Templates
&lt;/h2&gt;

&lt;p&gt;As you may have understood by now, there are many moving parts in the process of integrating OIDC and OAuth 2.0 with the many flavors of Blazor. Understanding why and how each approach works is important. It allows you to identify the source of any problems and understand why something goes wrong.&lt;/p&gt;

&lt;p&gt;Once you understand which approach to take for each Blazor flavor, you just need to correctly implement the integration for each new application.&lt;/p&gt;

&lt;p&gt;Auth0 provides you with project templates to make this job easier. The &lt;a href="https://www.nuget.org/packages/Auth0.Templates" rel="noopener noreferrer"&gt;Auth0 Templates for .NET package&lt;/a&gt; includes a few project templates for Blazor that you can use with the .NET CLI, Visual Studio, or JetBrains Rider.&lt;/p&gt;

&lt;p&gt;For example, the &lt;a href="https://github.com/auth0/auth0-dotnet-templates/blob/main/docs/auth0blazor.md" rel="noopener noreferrer"&gt;Auth0 Blazor Web Application template&lt;/a&gt; allows you to scaffold a Blazor Web App application with Auth0 authentication embedded with just a command like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new auth0blazor &lt;span class="nt"&gt;-o&lt;/span&gt; MyBlazorWebApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have the &lt;a href="https://github.com/auth0/auth0-cli" rel="noopener noreferrer"&gt;Auth0 CLI&lt;/a&gt; installed on your machine and &lt;a href="https://github.com/auth0/auth0-cli?tab=readme-ov-file#authenticating-to-your-tenant" rel="noopener noreferrer"&gt;logged in to your Auth0 tenant&lt;/a&gt;, you can also get your application automatically registered with Auth0 and ready to run in less than one minute.&lt;/p&gt;

&lt;p&gt;Alternatively, you can use your preferred IDE, such as Visual Studio, to generate your application and register it manually:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzszqf504ff2gq8klu8cl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzszqf504ff2gq8klu8cl.png" alt="Auth0 Blazor Web App template" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The package also provides a legacy &lt;a href="https://github.com/auth0/auth0-dotnet-templates/blob/main/docs/auth0blazorserver.md" rel="noopener noreferrer"&gt;Blazor Server template&lt;/a&gt; and a &lt;a href="https://github.com/auth0/auth0-dotnet-templates/blob/main/docs/auth0blazorwasm.md" rel="noopener noreferrer"&gt;Blazor WebAssembly template&lt;/a&gt; in the ASP.NET Core hosted model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;At this point, you have a comprehensive overview of the different flavors of Blazor available so far and the issues you face when adding support for OIDC and OAuth 2.0-based authentication and authorization.&lt;/p&gt;

&lt;p&gt;If you use Auth0 as your identity provider, follow the Blazor resources mentioned here. They will allow you to understand how to integrate authentication and authorization correctly. Furthermore, the Auth0 Templates for .NET package will allow you to have applications ready to use in minutes.&lt;/p&gt;

&lt;p&gt;Leave your experience using authentication and authorization with Blazor in the comments.&lt;/p&gt;

</description>
      <category>blazor</category>
      <category>authentication</category>
      <category>oauth</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Pushed Authorization Requests in .NET 9: Why and How to Use Them</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Wed, 15 Jan 2025 09:30:49 +0000</pubDate>
      <link>https://dev.to/oktadev/pushed-authorization-requests-in-net-9-why-and-how-to-use-them-53ba</link>
      <guid>https://dev.to/oktadev/pushed-authorization-requests-in-net-9-why-and-how-to-use-them-53ba</guid>
      <description>&lt;p&gt;You may have heard that &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-9.0?view=aspnetcore-9.0#openidconnecthandler-adds-support-for-pushed-authorization-requests-par" rel="noopener noreferrer"&gt;.NET 9.0 brings Pushed Authorization Requests (PAR) support&lt;/a&gt; in ASP.NET Core. If you are wondering what the heck Pushed Authorization Requests are, why you should use this feature, and how to use it in your .NET applications, well, this article is for you.&lt;/p&gt;

&lt;p&gt;PAR enhances the security posture of your ASP.NET Core application that uses OpenID Connect and OAuth 2.0. You should care about this security improvement, especially if you build applications for the financial, healthcare, insurance, and other data-sensitive sectors, where it is a requirement.&lt;/p&gt;

&lt;p&gt;Before you start, you need some basic knowledge about &lt;a href="https://auth0.com/docs/authenticate/protocols/openid-connect-protocol" rel="noopener noreferrer"&gt;OpenID Connect&lt;/a&gt; and &lt;a href="https://auth0.com/docs/authenticate/protocols/oauth" rel="noopener noreferrer"&gt;OAuth 2.0&lt;/a&gt;. You don’t need to be an expert; just a high-level knowledge of these protocols is enough to understand why and how PAR works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basic OpenID Connect Flow
&lt;/h2&gt;

&lt;p&gt;Let’s start by recalling how a basic OpenID Connect (OIDC) flow works. By basic flow, I mean a flow involving a classic server-rendered web application, i.e., in the context of .NET, a classic ASP.NET Core MVC application, a Razor Pages application, or a Blazor Server application. In the scenario involving this type of application, the flow we use is the &lt;a href="https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow" rel="noopener noreferrer"&gt;Authorization Code flow&lt;/a&gt;, which we are going to describe using this diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbmd5eh994f7ux86c9fv3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbmd5eh994f7ux86c9fv3.png" alt="Basic OpenID Connect flow" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the scenario illustrated by the diagram, we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;browser&lt;/strong&gt;, which is the tool used by the user to access our web application.
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;web application&lt;/strong&gt;, called client application in the diagram because it’s a client of the authorization server in the OAuth 2.0 and OIDC context.
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;authorization server&lt;/strong&gt;, which is responsible for authenticating the user and authorizing the application.
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;API server&lt;/strong&gt;, which is the protected server our web application wants to access using an &lt;a href="https://auth0.com/docs/secure/tokens/access-tokens" rel="noopener noreferrer"&gt;access token&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following the numbered arrows, we can describe the flow as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user uses their browser to access the web application. The application finds that the user is not authenticated and builds an authorization request for the browser; then, it redirects the browser with this authorization request to the authorization server for authentication.
&lt;/li&gt;
&lt;li&gt;The browser sends the authorization request to the authorization endpoint of the authorization server, and the user authenticates. As a result of the authentication, the browser receives an authorization code from the authorization server.
&lt;/li&gt;
&lt;li&gt;The browser provides the authorization code to the web application.
&lt;/li&gt;
&lt;li&gt;The web application calls the token endpoint of the authorization server and provides the authorization code. As a response, the authorization server returns an &lt;a href="https://a0.to/devto/id-token-access-token-what-is-the-difference" rel="noopener noreferrer"&gt;ID and access tokens&lt;/a&gt;.
&lt;/li&gt;
&lt;li&gt;The web application uses the ID token to get information about the user and the access token to call the protected API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a simplified description of the OIDC flow for authenticating the user and authorizing the web application to access the protected API server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using OIDC in ASP.NET Core
&lt;/h2&gt;

&lt;p&gt;As you can see from the previous section, there are many moving parts behind the user authentication and application authorization with OIDC. In .NET, you don’t need to take care of all these details. What you usually need to do is to correctly configure the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnecthandler" rel="noopener noreferrer"&gt;OpenID Connect middleware&lt;/a&gt;. In practice, the following code in your &lt;code&gt;Program.cs&lt;/code&gt; file works the magic in the basic scenario:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;  
  &lt;span class="p"&gt;{&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CookieAuthenticationDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultChallengeScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OpenIdConnectDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="p"&gt;})&lt;/span&gt;  
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCookie&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOpenIdConnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auth0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;  
  &lt;span class="p"&gt;{&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"https://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Auth0:Domain"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Auth0:ClientId"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientSecret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Auth0:ClientSecret"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code snippet gets the configuration parameters of the authorization server — Auth0 in the example — from the &lt;code&gt;appsettings.json&lt;/code&gt; file, or even better, from environment variables. You don’t need to do anything else to enable your web application to get the ID and access token. Everything happens behind the scenes!&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Potential Issues
&lt;/h2&gt;

&lt;p&gt;You probably already knew all this, but maybe you never wondered what problems can arise with this well-proven flow. There are two points in the OIDC flow that can have potential security and privacy issues, as highlighted in the following diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fie8tn8l8t8glbcowhj7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fie8tn8l8t8glbcowhj7l.png" alt="Potential authorization request issues " width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first point is when the web application redirects the browser to the authorization server to authenticate the user. The second point is when the browser calls the authorization endpoint of the authorization server. Both points are related to the authorization request prepared by the web application and sent by the browser to the authorization server.&lt;/p&gt;

&lt;p&gt;The main issues that can affect the authorization request are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The authorization request could be altered&lt;/strong&gt;. Usually, the web application builds the authorization request on behalf of the browser and then redirects the browser to the authorization server. A malicious actor, such as a browser extension or malware, can modify, add, or remove one or more parameters of this request before sending it to the authorization server.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No guarantee of the request's provenance&lt;/strong&gt;. As we said, the web application builds the authorization request on behalf of the browser. However, there is no guarantee that an authorization request was built by the web application. Anyone can build it once they know some basic data, such as the client ID and the redirect URI.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No guarantee of confidentiality&lt;/strong&gt;. Although the browser sends the authorization request to the authorization server via HTTPS, the request parameters can be intercepted by third-party applications, such as proxies, load balancers, or even browser plugins. A malicious network component of this type can inject or change the request parameters, not to mention that the request itself can be logged.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser limitations&lt;/strong&gt;. Finally, a very complex query string in the authorization request may incur possible browser limitations on URL length.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Entering Pushed Authorization Requests (PAR)
&lt;/h2&gt;

&lt;p&gt;While the issues pointed out above may appear as non-crucial issues in ordinary authentication contexts, they become significant in contexts where privacy and data protection are paramount.&lt;br&gt;&lt;br&gt;
Here comes &lt;a href="https://datatracker.ietf.org/doc/html/rfc9126" rel="noopener noreferrer"&gt;PAR&lt;/a&gt;, an extension of OAuth 2.0 (and then of OIDC) that enhances the protocol’s security level to solve those issues and protect the authorization request’s exposure.&lt;/p&gt;

&lt;p&gt;We will not dive deep into the details of this extension. Read &lt;a href="https://a0.to/devto/what-are-oauth-push-authorization-requests" rel="noopener noreferrer"&gt;this article to learn more about how Pushed Authorization Requests work&lt;/a&gt;. Anyway, to have a high-level idea, let’s follow this new flow in this diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fha7t0ckqzcqeyor3v8y7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fha7t0ckqzcqeyor3v8y7.png" alt="OAuth 2.0 Pushed Authorization Requests flow" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;As usual, the user accesses the web application with their browser.
&lt;/li&gt;
&lt;li&gt;The web application builds an authorization request, but this time, it sends the request to the authorization server using the new PAR endpoint instead of sending it to the browser. The web application uses the PAR endpoint to &lt;em&gt;register&lt;/em&gt; the authorization request and get an identifier for this request (the request URI).
&lt;/li&gt;
&lt;li&gt;Instead of the typical authorization request, the browser will get a URL with the request identifier as a parameter. No risk of sensitive detail exposure or modification this time; just an identifier saying nothing to a potential attacker.
&lt;/li&gt;
&lt;li&gt;When the authorization server receives the request with the request URI from the browser, it retrieves the previously registered authorization request and processes it as if the browser sent it.
&lt;/li&gt;
&lt;li&gt;From now on, the usual flow continues.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;PAR guarantees more security and confidentiality when managing authorization requests. It’s part of a set of specifications that fall under the umbrella of &lt;a href="https://openid.net/wg/fapi/" rel="noopener noreferrer"&gt;FAPI&lt;/a&gt;, an OpenID initiative that aims to strengthen the protocols to make them compatible with the more restrictive needs typical of financial, insurance, healthcare, and governmental contexts, among other contexts.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to Use PAR in .NET 9
&lt;/h2&gt;

&lt;p&gt;Now that we have an idea of ​​what PAR is and how it works let's see how to use it in .NET 9.&lt;br&gt;&lt;br&gt;
To add PAR support to your ASP.NET Core application, your OIDC configuration in &lt;code&gt;Program.cs&lt;/code&gt; should look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;  
  &lt;span class="p"&gt;{&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CookieAuthenticationDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultChallengeScheme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OpenIdConnectDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
  &lt;span class="p"&gt;})&lt;/span&gt;  
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddCookie&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  
 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddOpenIdConnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auth0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;  
  &lt;span class="p"&gt;{&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Authority&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"https://&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Auth0:Domain"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Auth0:ClientId"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientSecret&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Auth0:ClientSecret"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  
 &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can you see the difference with the code shown earlier? No? Can't you see it?&lt;/p&gt;

&lt;p&gt;Actually there is no difference! .NET supports PAR by default, with a fallback to the traditional flow in case the authorization server does not support PAR. That’s amazing, don’t you? You get more security with no additional code.&lt;/p&gt;

&lt;p&gt;You may say, “If I don’t need to add code, why should I care about PAR and how it works?” Actually, you should be aware of what happens under the hood. When things go wrong, having an idea of what is happening at a lower level can be helpful for debugging and detecting the cause of an issue.&lt;br&gt;&lt;br&gt;
Assume you are used to checking the traditional OIDC flow when a problem with authentication arises. You migrate your application to .NET 9 without any need to change code and everything works fine. One day you have a problem with user authentication and analyze the HTTP traffic to try understanding what’s going on, and you don’t find the classic authorization request. I bet you'll have a hard time figuring out what happened, since you didn't change your code.&lt;br&gt;&lt;br&gt;
If you were already prepared, you wouldn't have been surprised by this new flow.&lt;/p&gt;
&lt;h2&gt;
  
  
  Controlling PAR Configuration
&lt;/h2&gt;

&lt;p&gt;We said that PAR is supported by default in .NET 9. But what if you don’t want to use it? How can you tell your application to just use the classic OIDC flow? You can do it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PushedAuthorizationBehavior&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PushedAuthorizationBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Disable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;PushedAuthorizationBehavior&lt;/code&gt; option allows you to configure PAR behavior. In the previous example, you disable it, forcing your application to fall back to the classic OIDC flow. You can also force your application to use PAR with the following settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PushedAuthorizationBehavior&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PushedAuthorizationBehavior&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Require&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, your application will use the PAR flow. If the authorization server does not support PAR, your application will not fall back to the classic OIDC flow but will throw an exception.&lt;/p&gt;

&lt;p&gt;Finally, you can handle a new event, &lt;code&gt;onPushAuthorization&lt;/code&gt;, to customize the authorization request before it is sent to the authorization server. The following example shows how you can create a handler for this event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OpenIdConnectEvents&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="n"&gt;OnPushAuthorization&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;  
  &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We started by analyzing the basic OIDC flow and discovered a few possible security and privacy issues with authorization requests. Then, we introduced PAR and learned how it works. We also learned that .NET 9 introduces PAR support for ASP.NET Core applications by default without requiring us to write code. However, we can configure and customize PAR behavior programmatically by using a couple of options.&lt;br&gt;&lt;br&gt;
I hope you now have a clearer understanding of what PAR is, why using it, and how to use it in ASP.NET Core starting with .NET 9.0.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>authentication</category>
      <category>security</category>
      <category>oauth</category>
    </item>
    <item>
      <title>Sessions, Tokens, and Rock'n'Roll</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Sun, 19 Nov 2023 23:00:00 +0000</pubDate>
      <link>https://dev.to/andychiare/sessions-tokens-and-rocknroll-4pdc</link>
      <guid>https://dev.to/andychiare/sessions-tokens-and-rocknroll-4pdc</guid>
      <description>&lt;p&gt;&lt;em&gt;Does dealing with tokens eliminate the need for cookie-based sessions?&lt;/em&gt; You will find plenty of questions like this on &lt;a href="https://stackoverflow.com/questions/43452896/authentication-jwt-usage-vs-session" rel="noopener noreferrer"&gt;Stack Overflow&lt;/a&gt;, &lt;a href="https://www.reddit.com/r/node/comments/v7a1fc/should_i_use_sessions_or_jwt/" rel="noopener noreferrer"&gt;reddit&lt;/a&gt;, and other developer communities. Very often, developers confuse sessions, cookies, tokens, and other similar concepts that are somewhat related to authentication and authorization. Let’s try to get some clarity.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: This is not a complete guide on sessions, cookies, and tokens. This is an attempt to clarify the basic concepts on which sessions, cookies, and tokens are grounded and dispel the most common misunderstandings on the topic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Is a Session?
&lt;/h2&gt;

&lt;p&gt;Let's start by clarifying what a session is. A session is &lt;strong&gt;a sequence of interactions between a user or a client application and a server&lt;/strong&gt;. A session starts with the initial interaction and lasts until explicitly closed or after a given period of inactivity. A session can also last indefinitely if no specific termination rule is defined.&lt;/p&gt;

&lt;p&gt;Usually, &lt;strong&gt;sessions have an associated state&lt;/strong&gt; containing specific data, for example, the user's name, email, and other data related to the current user's activity.&lt;/p&gt;

&lt;p&gt;From a conceptual point of view, this is what you need to know about a session. But you may be thinking about how to implement it, and that's probably where the problems start.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Before moving on, let us point out that the implementation of a session depends on the specific communication protocol. In this article, &lt;strong&gt;only HTTP-based sessions will be addressed&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Sessions and Cookies
&lt;/h2&gt;

&lt;p&gt;On the Web, a session is a sequence of HTTP requests from a client and related responses from the server. However, by design, each HTTP request is independent of all others. That means that &lt;strong&gt;the HTTP protocol itself can't keep information between requests&lt;/strong&gt;. In other words, it's a &lt;a href="https://en.wikipedia.org/wiki/Stateless_protocol" rel="noopener noreferrer"&gt;stateless protocol&lt;/a&gt;. How can a server recognize that a group of requests is coming from a specific client? How can it maintain the state between independent calls? Here's where cookies traditionally come in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Here are the cookies!
&lt;/h3&gt;

&lt;p&gt;I guess you know what a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies" rel="noopener noreferrer"&gt;cookie&lt;/a&gt; is. Either as a user or as a developer, you have dealt with them directly or indirectly. Although, as a user, you may dislike them for their sometimes intrusive use, as a developer, you may have appreciated their usefulness.&lt;/p&gt;

&lt;p&gt;Often, the names assigned to these artifacts seem to attribute &lt;a href="https://www.merriam-webster.com/dictionary/thaumaturgy" rel="noopener noreferrer"&gt;thaumaturgic&lt;/a&gt; properties to them: authentication cookies, tracking cookies, session cookies. But beyond that, &lt;strong&gt;a cookie is just a small block of data created by the server and stored on the client, your web browser&lt;/strong&gt;. Nothing more. The information stored in the cookie makes it special.&lt;/p&gt;

&lt;h3&gt;
  
  
  Session cookies
&lt;/h3&gt;

&lt;p&gt;The typical approach to building HTTP-based sessions is for &lt;strong&gt;the server to send the browser a cookie with a unique ID: the session ID&lt;/strong&gt;. That cookie, which magically becomes the &lt;strong&gt;session cookie&lt;/strong&gt;, will be sent back to the server with any subsequent request. This causes the server to finally recognize HTTP requests coming from the same client and thus making up a session. In addition, &lt;strong&gt;the server can use the session ID to store and retrieve data&lt;/strong&gt; related to the current user's activity: that's how the session state is maintained!&lt;/p&gt;

&lt;p&gt;Session cookies rely on the native ability of web browsers to manage them without the developer's intervention. Of course, they should have the &lt;code&gt;HttpOnly&lt;/code&gt; and &lt;code&gt;Secure&lt;/code&gt; attribute set, and &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html" rel="noopener noreferrer"&gt;other requirements must be met to ensure security&lt;/a&gt;, but this is beyond the scope of the topics to be discussed here.&lt;/p&gt;

&lt;p&gt;The main takeaway is that &lt;strong&gt;cookies allow tracking clients' requests so that they can build a session with a state stored on the server side&lt;/strong&gt;. Usually, the session cookie only contains the session ID value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros and cons of cookie-based sessions
&lt;/h3&gt;

&lt;p&gt;Now that you understand how cookie-based sessions work, let's summarize the main advantages and disadvantages of this approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Cookies are &lt;strong&gt;tied to a domain&lt;/strong&gt;, that is, the browser sends them back only to the server that created them. They can be configured to be shared with subdomains, but by default, they can't be shared with other domains.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They &lt;strong&gt;require little storage&lt;/strong&gt; on the browser. As a result, they have a low impact on the amount of data transmitted in each individual request.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They are &lt;strong&gt;automatically managed by the browser&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Without proper precautions, cookies &lt;strong&gt;can be exploited to perform &lt;a href="https://auth0.com/blog/cross-site-request-forgery-csrf/" rel="noopener noreferrer"&gt;CSRF&lt;/a&gt; and &lt;a href="https://auth0.com/blog/cross-site-scripting-xss/" rel="noopener noreferrer"&gt;XSS attacks&lt;/a&gt;&lt;/strong&gt; and improperly access active user sessions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You may run into &lt;strong&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;CORS&lt;/a&gt; problems when building apps across multiple domains&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;They have a &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Scalability" rel="noopener noreferrer"&gt;scalability&lt;/a&gt; problem&lt;/strong&gt;. If you replicate the server to spread its high workload, you need to centralize the session state management. In fact, the server that receives a request with a cookie might be different from the server that generated it. This increases the complexity of each interaction.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sessions and Tokens
&lt;/h2&gt;

&lt;p&gt;Now you know that requests sent by a browser to a server are bundled into sessions using cookies. So far, so good!&lt;/p&gt;

&lt;p&gt;But the Web is not just for browsers. Mobile and desktop clients, also known as native clients, can make HTTP requests as well. Leaving out clients that use an embedded browser to render web pages, &lt;strong&gt;native clients have no support for cookies&lt;/strong&gt;. So the problem arises again: how do you build a session from a sequence of stateless HTTP requests? This is a job for tokens. 😎&lt;/p&gt;

&lt;h3&gt;
  
  
  Meet the token
&lt;/h3&gt;

&lt;p&gt;Before going ahead to see how tokens can help you build HTTP-based sessions, let's try to understand what a token is. Generally speaking, &lt;a href="https://en.wikipedia.org/wiki/Token" rel="noopener noreferrer"&gt;"token" is an extremely generic term&lt;/a&gt;. However, in the context of this article, a token is just a string, a sequence of generated characters. In particular, it's &lt;strong&gt;a unique string that identifies requests coming from the same client&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What are you saying? Do you have the feeling of a &lt;em&gt;deja vu&lt;/em&gt;? Well, actually, the basic idea of building sessions here is the same as the cookie-based one. The token value is the session ID. The native client must send it to the server along with each request.&lt;/p&gt;

&lt;p&gt;Unlike the cookie case, however, you don't have a standard specification that tells you how to store and manage tokens. You can send them to the server as a header, in the query string, or in the request's body. You don't even have a standard tool that takes charge of automatically managing tokens, as the browser does with cookies. &lt;strong&gt;As a developer of the native application, it's your responsibility to take care of the tokens according to what the server expects&lt;/strong&gt;. So, you have to store the token sent by the server somewhere in your application, maybe just keeping it in a variable, but possibly you would save it to storage. You have to add the token to each request sent to the server. If your application interacts with different servers, you have to take care to send the right token to the right server. In other words, &lt;strong&gt;you have to replicate much of the work that browsers automatically do with cookies&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On the server side, everything remains pretty similar to the cookie-based session management. The server maintains the session state, and the session ID is the key to retrieving it. The only difference is that the session ID is not passed in a cookie but is the token's value.&lt;/p&gt;

&lt;h3&gt;
  
  
  JWTs
&lt;/h3&gt;

&lt;p&gt;The type of token described so far is what is known as an &lt;strong&gt;opaque token&lt;/strong&gt;, that is, a token whose value has no specific meaning for the client application. But in the magical world of tokens, there are some "talking" ones, such as &lt;a href="https://a0.to/dev-to/jwt-intro" rel="noopener noreferrer"&gt;JSON Web Tokens (JWT)&lt;/a&gt;. These are tokens containing encoded data that the client application can decode and read. This feature opens new horizons in session management: the &lt;strong&gt;inversion of state maintenance&lt;/strong&gt;! Why keep the session state on the server if it can be kept on the application client?&lt;/p&gt;

&lt;p&gt;Think about this for a moment. Keeping the session state on the server comes at a cost in terms of both storage and performance. Also, if the client needs certain information about the current session, for example, the user's profile or other technical data, it has to make calls to the server. Then there is the &lt;a href="https://en.wikipedia.org/wiki/Scalability" rel="noopener noreferrer"&gt;system scalability&lt;/a&gt; problem: if you replicate the server to spread its high workload, you need to centralize the session state management. In fact, the server instance that receives a request with a token might be different from the server instance that generated that token. Remember that this detail also applies to session cookies. In short, server-side session management is a bit of a hassle for the server.&lt;/p&gt;

&lt;p&gt;JWTs can solve these problems. They &lt;strong&gt;encode into the token the information&lt;/strong&gt; (or at least part of it) &lt;strong&gt;that the server would store in the session state&lt;/strong&gt;. The client application will have the user's data and other stuff at its disposal as soon as the session starts and no longer needs to request them from the server for the duration of the current session. This frees some server memory and lightens its workload a bit.&lt;/p&gt;

&lt;p&gt;Also, the application client sends the token back to the server when it makes a request. The server instance receiving that request will get the session state from the token, and the scalability problem is solved as well. That's fantastic! 🎉&lt;/p&gt;



&lt;h3&gt;
  
  
  JWTs and browsers
&lt;/h3&gt;

&lt;p&gt;The good news is that JWTs are not specific to native clients. Web applications can also use them. You can &lt;strong&gt;use JWTs in your Single Page Application&lt;/strong&gt; to lighten the server workload and promote server scalability.&lt;/p&gt;

&lt;p&gt;Of course, you will need to do some additional work on your SPA's code. You have to decode the JWT and verify its signature to make sure it comes from the right server. You have to store it somewhere to prevent losing it after a page refresh. You also have to add the JWT to each request sent to the server and take care of sending the right token to the right server, in case you talk with multiple servers.&lt;/p&gt;

&lt;p&gt;Wait! Isn't part of this stuff automatically handled by the browser when you use cookies? What about &lt;strong&gt;storing your JWT in a cookie&lt;/strong&gt;? That's a great idea! You still have the session information at your fingertips and leverage the standard cookie behavior to handle the interaction with the server. Awesome! 🙌&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While storing a JWT into a cookie allows you to delegate part of the token management to the browser, you should also consider cookies' disadvantages, as mentioned earlier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Pros and cons of token-based sessions
&lt;/h3&gt;

&lt;p&gt;Let's summarize the main benefits and drawbacks of using tokens to track your sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Tokens are &lt;strong&gt;platform-independent&lt;/strong&gt;: they can be used by web applications as well as native apps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JWT tokens &lt;strong&gt;promote server scalability&lt;/strong&gt;, since they can relieve the server of the responsibility of maintaining the session state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tokens &lt;strong&gt;can be saved in different types of storage&lt;/strong&gt;: they can be stored in memory, in cookies, in the browser's session storage, or in native secure storage.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Tokens &lt;strong&gt;may be exposed to greater security risk&lt;/strong&gt; since there are no standard protection mechanisms like those applied by browsers to cookies, and their management is left to the developer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JWT tokens' &lt;strong&gt;size can become considerable&lt;/strong&gt; if a lot of state information is maintained. This can affect the transmission of data to the server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;server can't invalidate a JWT&lt;/strong&gt; sent to the client, so its data can become outdated until a new request is made or the JWT expires.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ID Tokens and Sessions
&lt;/h2&gt;

&lt;p&gt;Do you know the &lt;a href="https://en.wikipedia.org/wiki/Law_of_the_instrument" rel="noopener noreferrer"&gt;law of the hammer&lt;/a&gt;? It says that when &lt;em&gt;you have a hammer, everything looks like a nail&lt;/em&gt;. Maybe that's what happens to some developers when they discover JWTs. Let me explain briefly.&lt;/p&gt;

&lt;p&gt;When does a session start? Usually, after the first request is sent to the server. In its response, the server sends a brand new session ID (through a cookie or as a token), and everything begins.&lt;/p&gt;

&lt;p&gt;And what is one of the most common first requests sent to a server by an application? An authentication request.&lt;/p&gt;

&lt;p&gt;If your application uses &lt;a href="https://auth0.com/docs/authenticate/protocols/openid-connect-protocol" rel="noopener noreferrer"&gt;OpenID Connect (OIDC)&lt;/a&gt; for user authentication, it will receive a JWT as a confirmation of successful authentication and, optionally, some data about the user. &lt;strong&gt;Can you consider this JWT a session token?&lt;/strong&gt; If you are an OIDC-experienced developer, you know that &lt;strong&gt;the answer is "no!"&lt;/strong&gt;. But once I was asked by a developer: "Why do you still need cookies if you get a JWT from the authentication server?"&lt;/p&gt;

&lt;p&gt;Let's clarify that the JWT sent by the OIDC provider is a token the named ID token, that has a very specific task: to prove that the user has been successfully authenticated. It's not meant for session management. You should still use cookies or yet another token to manage your application's session.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To learn more about the &lt;a href="https://a0.to/dev-to/id-token-access-token" rel="noopener noreferrer"&gt;nature of ID tokens and their correct use, check out this article&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When your app receives an ID token, it knows that the user has been successfully authenticated, and it can start a session — but with its own session ID.&lt;/p&gt;

&lt;h3&gt;
  
  
  Session &amp;amp; session
&lt;/h3&gt;

&lt;p&gt;Consider the following picture, which describes a common scenario involving OIDC authentication:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F6Sssi1FjgSVRfIte8X4wdR%2F5472d7e6845c3f20bf048b393c70f84d%2Foidc-scenario.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fimages.ctfassets.net%2F23aumh6u8s0i%2F6Sssi1FjgSVRfIte8X4wdR%2F5472d7e6845c3f20bf048b393c70f84d%2Foidc-scenario.png" alt="OpenID Connect scenario"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this scenario, you have two active sessions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The session with the OpenID Connect provider. This session starts with user authentication and tracks all interactions with the authentication server.&lt;/li&gt;
&lt;li&gt;The session with your own server. This session starts when your application receives the ID token and tracks all the interactions with your own server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What is the relationship between the two sessions?&lt;/p&gt;

&lt;p&gt;They are largely independent of each other. If the session with your own server expires or is closed, all the data related to this session should be destroyed, including your ID token. However, the end of your session normally has no effect on the session with the OIDC provider. It can continue to be alive. This can be convenient because it allows the user to log back in to your app without having to re-enter their credentials as long as the session with the OIDC provider is active.&lt;/p&gt;

&lt;p&gt;What happens if the session with the OIDC provider expires? As said before, the two sessions are independent of each other so, in practice, nothing happens to your server's session, unless you have specific requirements. In this case, you have to look for a solution to keep the two sessions in sync.&lt;/p&gt;

&lt;p&gt;Remember, &lt;strong&gt;your ID token has nothing to do with your session apart from telling you that you can start it&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access Tokens and Sessions
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://auth0.com/docs/authenticate/protocols/oauth" rel="noopener noreferrer"&gt;OAuth 2&lt;/a&gt; context, access tokens allow a client application to access a resource, such as an API, on behalf of the user.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To learn more about &lt;a href="https://a0.to/dev-to/id-token-access-token" rel="noopener noreferrer"&gt;access tokens and the difference with ID tokens, check out this article&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a client-server interaction, access tokens are credentials indicating that the client application is authorized to send requests to the server. Fundamentally, they have a different role from a session token. That is, their job is not to keep track of requests made by the same client and possibly enable session state management.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analogies with session tokens
&lt;/h3&gt;

&lt;p&gt;Within certain limits, however, the use of an access token can resemble that of a session token. It is &lt;strong&gt;sent to the server with each request&lt;/strong&gt; and the &lt;strong&gt;server uses it to retrieve information&lt;/strong&gt; about the permissions granted to the client for this set of interactions. This is true both when the access token is an opaque token and when &lt;a href="https://datatracker.ietf.org/doc/html/rfc9068" rel="noopener noreferrer"&gt;it is a JWT&lt;/a&gt;. Note the analogy with session state retrieval described earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Differences from session tokens
&lt;/h3&gt;

&lt;p&gt;Unlike session tokens, the information represented by access tokens is critical to client and server security. One strategy to mitigate the risk of theft and unauthorized use of an access token is to &lt;strong&gt;reduce its validity to a limited time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Of course, this contrasts with the classic concept of a session as the set of potentially infinite interactions between the client and the server. However, the access token expiration must necessarily result in the current session's termination: the current token is no longer valid, and the user must authenticate again to obtain another access token for the client application. This would mean starting a new session.&lt;/p&gt;

&lt;p&gt;Fortunately, OAuth2 provides &lt;a href="https://auth0.com/docs/secure/tokens/refresh-tokens" rel="noopener noreferrer"&gt;refresh tokens&lt;/a&gt;, that is, tokens designed to renew an expired access token without user intervention. They provide a compromise between security needs and user experience.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To learn more about &lt;a href="https://a0.to/dev-to/refresh-tokens" rel="noopener noreferrer"&gt;how refresh tokens work, read this article&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In addition, access tokens and refresh tokens &lt;strong&gt;can be used even when the user has no current active session&lt;/strong&gt;. This is the so-called &lt;em&gt;offline access&lt;/em&gt; mode. Think of an application that sends tweets or emails on your behalf on a scheduled basis: once you authorize it, you don't need to have an active session on that app when the scheduled job is triggered.&lt;/p&gt;

&lt;p&gt;These features highlight how an access token does not have the typical characteristics of a classic session token. Its value is not simply a mechanism for grouping calls to a server and retrieving information related to user activity. It serves a very different purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  What About Rock’n’Roll?
&lt;/h2&gt;

&lt;p&gt;If you reached this section, you have learned a lot about sessions, cookies, and tokens.&lt;/p&gt;

&lt;p&gt;At this point, you may be wondering: what does rock'n'roll have to do with this? Well, to tell you the truth, it only served to... set the tone for the title of the article.&lt;/p&gt;

&lt;p&gt;But in fact, there is some relevance. The interactions between a client and a server, the going back and forth between the two actors somewhat reminds of a kind of dance. Incidentally, the interactions between the client and the OIDC provider for user authentication are sometimes called &lt;em&gt;the authentication dance&lt;/em&gt;. It may not be quite rock'n'roll, but it is still dancing. 💃🕺🏻&lt;/p&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this journey, you explored the session concept in the HTTP context and how it can be implemented using cookies or tokens. Beyond the main pros and cons that each approach brings, you learned how having a token does not mean that it necessarily has anything to do with session management. The case of ID and access tokens is an example: each of them has its own specific role in authentication and authorization.&lt;/p&gt;

</description>
      <category>cookies</category>
      <category>jwt</category>
      <category>token</category>
      <category>session</category>
    </item>
    <item>
      <title>Auth0 Templates for .NET</title>
      <dc:creator>Andrea Chiarelli</dc:creator>
      <pubDate>Tue, 12 Sep 2023 08:47:46 +0000</pubDate>
      <link>https://dev.to/oktadev/auth0-templates-for-net-4mni</link>
      <guid>https://dev.to/oktadev/auth0-templates-for-net-4mni</guid>
      <description>&lt;p&gt;If you are a .NET developer and have been using &lt;a href="https://auth0.com/" rel="noopener noreferrer"&gt;Auth0 by Okta&lt;/a&gt;'s authentication and authorization services, chances are that you've come across &lt;a href="https://auth0.com/blog/authors/andrea-chiarelli/" rel="noopener noreferrer"&gt;one of my articles&lt;/a&gt;. 😎&lt;/p&gt;

&lt;p&gt;Over the past few years, I have tried to explain in simple and, most importantly, practical terms how to integrate Auth0 into the various types of applications you can build with .NET. My goal has been to simplify the experience of .NET developers approaching Auth0.&lt;/p&gt;

&lt;p&gt;The topic of authentication and authorization is complex, and untangling the details of protocols like &lt;a href="https://openid.net/developers/how-connect-works/" rel="noopener noreferrer"&gt;OIDC&lt;/a&gt; and &lt;a href="https://oauth.net/2/" rel="noopener noreferrer"&gt;OAuth2&lt;/a&gt; is not easy. Fortunately, &lt;a href="https://auth0.com/docs/libraries" rel="noopener noreferrer"&gt;SDKs&lt;/a&gt; provide a great deal of help in abstracting the complexity inherent in the protocols. But even &lt;a href="https://auth0.com/blog/choose-the-right-dotnet-sdk-for-auth0/" rel="noopener noreferrer"&gt;knowing which SDK to use&lt;/a&gt; based on the type of application and how to write the code to integrate Auth0 when an SDK is not available requires some familiarity with both the Auth0 product and the infrastructure provided by .NET.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here are the Auth0 Templates for .NET!
&lt;/h2&gt;

&lt;p&gt;To simplify the development experience for .NET developers, I tried to go beyond creating tutorials and started the &lt;a href="https://a0.to/dev-to/auth0-templates-prj" rel="noopener noreferrer"&gt;Auth0 Templates for .NET project&lt;/a&gt;, a package of .NET project templates with everything needed to support Auth0's authentication and authorization services! 🎉&lt;/p&gt;

&lt;p&gt;For your next .NET application using Auth0, you only need to select the right template and provide the configuration parameters from the Auth0 dashboard. You can even not provide the configuration parameters if you have the &lt;a href="https://github.com/auth0/auth0-cli" rel="noopener noreferrer"&gt;Auth0 CLI&lt;/a&gt; installed on your machine. But let's go one step at a time.&lt;/p&gt;

&lt;p&gt;First, you need to install &lt;a href="https://a0.to/dev-to/auth0-templates-pkg" rel="noopener noreferrer"&gt;the NuGet package&lt;/a&gt;. You can do this by running the following command in a terminal window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new &lt;span class="nb"&gt;install &lt;/span&gt;Auth0.Templates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the package is installed, you will have six templates to create your own applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auth0 MVC Application&lt;/li&gt;
&lt;li&gt;Auth0 Web API Application&lt;/li&gt;
&lt;li&gt;Auth0 Blazor Web App&lt;/li&gt;
&lt;li&gt;Auth0 Blazor Server Application&lt;/li&gt;
&lt;li&gt;Auth0 Blazor WebAssembly Application&lt;/li&gt;
&lt;li&gt;Auth0 MAUI Application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is a work in progress, and more templates will be added in future releases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create your Auth0-enabled .NET Project with the .NET CLI
&lt;/h2&gt;

&lt;p&gt;You can use these templates from the command line, through the &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/tools/" rel="noopener noreferrer"&gt;.NET CLI&lt;/a&gt;, or in Visual Studio.&lt;/p&gt;

&lt;p&gt;Say you want to create a new Blazor Server application that uses Auth0 as its Identity provider. The first step is to &lt;a href="https://auth0.com/docs/get-started/auth0-overview/create-applications" rel="noopener noreferrer"&gt;register your app with Auth0&lt;/a&gt;. During this first step, you will get some settings that you will use to set up your app through the templates.&lt;/p&gt;

&lt;p&gt;Then, on your machine, run the following command in a terminal window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new auth0blazorserver &lt;span class="nt"&gt;--domain&lt;/span&gt; &amp;lt;YOUR_AUTH0_DOMAIN&amp;gt; &lt;span class="nt"&gt;--client-id&lt;/span&gt; &amp;lt;YOUR_CLIENT_ID&amp;gt; &lt;span class="nt"&gt;-o&lt;/span&gt; MyBlazorApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You specify the &lt;code&gt;auth0blazorserver&lt;/code&gt; template as the template to use. Then, you pass your Auth0 domain and your client ID, values that come from the registration step. Finally, you indicate the folder where the new application will be created.&lt;/p&gt;

&lt;p&gt;Voila! After a few seconds, you will find a new Blazor Server project in the folder you specified with all the code to support authentication via Auth0! 🪄&lt;/p&gt;

&lt;h2&gt;
  
  
  Add the power of the Auth0 CLI
&lt;/h2&gt;

&lt;p&gt;If you have the &lt;a href="https://github.com/auth0/auth0-cli" rel="noopener noreferrer"&gt;Auth0 CLI&lt;/a&gt; installed on your machine and logged in to your Auth0 tenant, you can even skip the registration step. You can simply run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new auth0blazorserver &lt;span class="nt"&gt;-o&lt;/span&gt; MyBlazorApp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Auth0 CLI will automatically register you application with Auth0 and configure it on your behalf. You can have a working .NET application in less than a minute! :🧞‍♂️&lt;/p&gt;

&lt;h2&gt;
  
  
  Create your Auth0-enabled .NET Project with Visual Studio
&lt;/h2&gt;

&lt;p&gt;If you prefer to use Visual Studio, you just need to select the Auth0 template from the list, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbj1tuxaljahudn86c2k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbj1tuxaljahudn86c2k.png" alt="Select Auth0 template from Visual Studio for Windows" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, provide the required settings as shown in the following picture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqbo9j4iqg7cqmje6si9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftqbo9j4iqg7cqmje6si9.png" alt="Auth0 settings in Visual Studio for Windows" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, due to a Visual Studio limitation in running external script, you can't leverage the Auth0 CLI magic to automatically register your application in this case.&lt;/p&gt;

&lt;p&gt;You can also use the templates in Visual Studio for Mac and JetBrains Rider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay Tuned!
&lt;/h2&gt;

&lt;p&gt;I'd like the &lt;a href="https://a0.to/dev-to/auth0-templates-prj" rel="noopener noreferrer"&gt;Auth0 Templates for .NET project&lt;/a&gt; to become the starting point for building any .NET application with Auth0 embedded. I plan to add more templates and improve the developer experience in the near future.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://a0.to/dev-to/auth0-templates-pkg" rel="noopener noreferrer"&gt;Give it a try&lt;/a&gt; and let me know what do you think. Your feedback is welcome, as well as your contribution. 🙏&lt;/p&gt;

</description>
      <category>auth0</category>
      <category>dotnet</category>
      <category>template</category>
      <category>authentication</category>
    </item>
  </channel>
</rss>
