<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Kévin Chalet&#39;s blog</title>
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://kevinchalet.com/"/>
  <updated>2022-03-17T20:24:50.000Z</updated>
  <id>https://kevinchalet.com/</id>
  
  <author>
    <name>Kévin Chalet</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Introducing the OpenIddict client</title>
    <link href="https://kevinchalet.com/2022/02/25/introducing-the-openiddict-client/"/>
    <id>https://kevinchalet.com/2022/02/25/introducing-the-openiddict-client/</id>
    <published>2022-02-25T21:00:00.000Z</published>
    <updated>2022-03-17T20:24:50.000Z</updated>
    
    <content type="html"><![CDATA[<p>When I unveiled the <a href="https://github.com/openiddict/openiddict-core/issues/736" target="_blank" rel="external">OpenIddict 3.0 roadmap three years ago</a>, I mentioned that having an OpenIddict client would be a very nice addition but that implementing it as part of 3.0 wasn&#39;t realistic. Today, I&#39;m very happy to announce that <strong>the OpenIddict client will ship as part of OpenIddict&#39;s next major version</strong>.</p><h2 id="Why-a-new-client"><a href="#Why-a-new-client" class="headerlink" title="Why a new client?"></a>Why a new client?</h2><p>A few client libraries already exist for .NET, including:</p><ul><li>For ASP.NET Core, <a href="https://github.com/dotnet/aspnetcore/tree/main/src/Security/Authentication/OAuth/src" target="_blank" rel="external">an OAuth 2.0 base handler developed by Microsoft</a> that we massively use for the <a href="https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers" target="_blank" rel="external">aspnet-contrib project</a> (more than 80 OAuth 2.0 services are supported at the time of writing!)</li><li>For ASP.NET Core, <a href="https://github.com/dotnet/aspnetcore/tree/main/src/Security/Authentication/OpenIdConnect/src" target="_blank" rel="external">an OpenID Connect handler developed by Microsoft</a>.</li><li>For ASP.NET 4.x/OWIN, <a href="https://github.com/aspnet/AspNetKatana/tree/dev/src/Microsoft.Owin.Security.OpenIdConnect" target="_blank" rel="external">an OpenID Connect middleware maintained by Microsoft</a>.</li><li>For console and GUI applications, <a href="https://github.com/IdentityModel/IdentityModel.OidcClient" target="_blank" rel="external">a certified OpenID Connect client developed by Duende Software</a>.</li><li>For Blazor WASM applications, <a href="https://github.com/dotnet/aspnetcore/tree/main/src/Components/WebAssembly/WebAssembly.Authentication/src" target="_blank" rel="external">an OpenID Connect integration based on the oidc-client-js library</a>.</li></ul><p>So why do we need another one? Am I reinventing the wheel by introducing another OIDC client stack?</p><p>Well, that&#39;s definitely a legit question. First, I have to say that these implementations work just fine and that I&#39;ve used them happily other the years. Actually, I even contributed <a href="https://github.com/aspnet/Security/pulls?q=is%3Apr+author%3Akevinchalet+is%3Aclosed" target="_blank" rel="external">to some of these implementations multiple times</a> so I have a fairly good experience working with them.</p><p>In a nutshell, here&#39;s what motivated me:</p><ul><li><p>While all these libraries offer very specialized implementations, <strong>none of them offers a unified experience</strong> that allowing sharing a common code base usable on, say, ASP.NET Core and Blazor WASM applications: what you learned about the ASP.NET Core OIDC handler is not applicable to Blazor WASM, that uses a completely different OIDC client stack under the hood.</p></li><li><p><strong>The OIDC integration for Blazor WASM uses <a href="https://github.com/IdentityModel/oidc-client-js" target="_blank" rel="external">oidc-client-js</a>, that was archived in June 2021 and is no longer supported</strong>. While the Blazor OIDC wrapper itself is supported by Microsoft, the library it uses under the hood is not and won&#39;t receive any bug or security fix, which is a bit surprising to me, considering its author is still sponsored by Microsoft.</p></li><li><p>While it&#39;s supported by Microsoft, the Blazor OIDC wrapper doesn&#39;t actually get much love and <a href="https://github.com/dotnet/aspnetcore/issues/39144" target="_blank" rel="external">suffers from annoying design issues that affect OpenIddict users</a> that the team is unwilling to fix.</p></li><li><p>The OAuth 2.0 base handler for ASP.NET Core offers a straightforward API for creating derived OAuth 2.0 clients that we successfully used in the aspnet-contrib social providers. Unfortunately, its simplicity comes at a cost: <strong>it&#39;s not composable</strong>. Concretely, it means that every time we need to customize something (e.g adding a parameter to the token request), we end up duplicating more code than what we should (e.g to add a token request parameter, you also need to take care of sending the HTTP request and handling the response, which is something the OAuth 2.0 base handler does for you if you don&#39;t override the <code>OAuthHandler&lt;T&gt;.ExchangeCodeAsync(OAuthCodeExchangeContext context)</code> method).</p></li><li><p>There&#39;s also another downside with the OAuth 2.0 base handler: <strong>it doesn&#39;t support OpenID Connect</strong>. Yet, we received contributions to add OAuth 2.0 providers that also support OpenID Connect: while ASP.NET Core has a dedicated OIDC handler that is more appropriate for these cases, many people like the simpler registration story the OAuth 2.0 base handler and the aspnet-contrib providers offer. Retrospectively, we shouldn&#39;t have accepted these contributions as these providers don&#39;t benefit from the additional security checks a real OpenID Connect implementation requires (e.g <code>nonce</code>, <code>at_hash</code> or <code>c_hash</code> validation, etc.)</p></li><li><p>The OAuth 2.0 base handler doesn&#39;t support <a href="https://datatracker.ietf.org/doc/html/rfc8414" target="_blank" rel="external">the OAuth 2.0 Authorization Server Metadata</a> specification that backported the OpenID Connect server discovery feature to the OAuth 2.0 world and that allows finding the location of the authorization and token endpoints dynamically, without having to hardcode them. While the adoption is slow, I&#39;d expect more and more services to support it in the next few years.</p></li></ul><a id="more"></a><h2 id="Can-you-tell-me-more-about-this-new-client"><a href="#Can-you-tell-me-more-about-this-new-client" class="headerlink" title="Can you tell me more about this new client?"></a>Can you tell me more about this new client?</h2><p>Put simply, the OpenIddict client is closely modeled after the OpenIddict server and validation stacks and reuses many of their core concepts, including their extremely powerful events model, their modular approach and the host/transport-agnostic design of the core server and validation packages.</p><p>Concretely:</p><ul><li><p>The OpenIddict client <strong>will be usable with any OAuth 2.0 and OpenID Connect server</strong> (based on OpenIddict or not) and will heavily use client/server negotiation to adapt its validation routines to the targeted authorization servers to guarantee maximum compatibility while ensuring the best level of security.</p></li><li><p>The OpenIddict client <strong>currently supports ASP.NET Core 2.1 and higher</strong> and native support for ASP.NET 4.x/OWIN will be added in the next few weeks. I also started working on a Blazor WASM prototype and while there&#39;ll be limitations (e.g Blazor WASM&#39;s encryption/signing support is extremely limited), things are really promising.</p></li><li><p>The <strong>code, implicit and hybrid flows are all natively supported</strong> (except <code>response_type=token</code>, which is unsafe) and OpenIddict has built-in logic to select the best flow to use (unlike the ASP.NET Core OIDC handler that uses the hardcoded <code>response_type=id_token</code>). It&#39;s worth noting the OpenIddict client was designed to allow implementing additional flows (like the OpenID Connect Client-Initiated Backchannel Authentication Flow) without requiring massive design changes.</p></li><li><p>The OpenIddict client <strong>will allow supporting multiple static authorization servers</strong> (I currently don&#39;t plan on adding dynamic client registration support, but it&#39;s something that may be added in a future version).</p></li><li><p>The OpenIddict client <strong>uses a different approach for handling authorization callbacks</strong>: while the OAuth 2.0 and OIDC handlers developed by Microsoft typically handle everything for you by flowing the <code>ClaimsPrincipal</code> extracted from identity tokens/userinfo responses to another authentication handler (in most cases, an instance of the cookies authentication handler), the OpenIddict client won&#39;t have this logic and will offer exactly the same approach as the OpenIddict server stack by encouraging users to be involved in the creation of the authentication cookie during the callback handling.</p></li></ul><p>Concretely, the OpenIddict client will offer a pass-through mode to allow handling callbacks in a custom MVC action or minimal API handler. Here&#39;s an example of such an action:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div></pre></td><td class="code"><pre><div class="line">[HttpGet(<span class="string">"~/signin-oidc"</span>), HttpPost(<span class="string">"~/signin-oidc"</span>)]</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">async</span> Task&lt;ActionResult&gt; <span class="title">Callback</span>(<span class="params"></span>)</span></div><div class="line">&#123;</div><div class="line">    <span class="comment">// Retrieve the authorization data validated by OpenIddict as part of the callback handling.</span></div><div class="line">    <span class="keyword">var</span> result = <span class="keyword">await</span> HttpContext.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme);</div><div class="line"></div><div class="line">    <span class="comment">// Multiple strategies exist to handle OAuth 2.0/OpenID Connect callbacks, each with their pros and cons:</span></div><div class="line">    <span class="comment">//</span></div><div class="line">    <span class="comment">//   * Directly using the tokens to perform the necessary action(s) on behalf of the user, which is suitable</span></div><div class="line">    <span class="comment">//     for applications that don't need a long-term access to the user's resources or don't want to store</span></div><div class="line">    <span class="comment">//     access/refresh tokens in a database or in an authentication cookie (which has security implications).</span></div><div class="line">    <span class="comment">//     It is also suitable for applications that don't need to authenticate users but only need to perform</span></div><div class="line">    <span class="comment">//     action(s) on their behalf by making API calls using the access tokens returned by the remote server.</span></div><div class="line">    <span class="comment">//</span></div><div class="line">    <span class="comment">//   * Storing the external claims/tokens in a database (and optionally keeping the essentials claims in an</span></div><div class="line">    <span class="comment">//     authentication cookie so that cookie size limits are not hit). For the applications that use ASP.NET</span></div><div class="line">    <span class="comment">//     Core Identity, the UserManager.SetAuthenticationTokenAsync() API can be used to store external tokens.</span></div><div class="line">    <span class="comment">//</span></div><div class="line">    <span class="comment">//     Note: in this case, it's recommended to use column encryption to protect the tokens in the database.</span></div><div class="line">    <span class="comment">//</span></div><div class="line">    <span class="comment">//   * Storing the external claims/tokens in an authentication cookie, which doesn't require having</span></div><div class="line">    <span class="comment">//     a user database but may be affected by the cookie size limits enforced by most browser vendors</span></div><div class="line">    <span class="comment">//     (e.g Safari for macOS and Safari for iOS/iPadOS enforce a per-domain 4KB limit for all cookies).</span></div><div class="line">    <span class="comment">//</span></div><div class="line">    <span class="comment">//     Note: this is the approach used here, but the external claims are first filtered to only persist</span></div><div class="line">    <span class="comment">//     a few claims like the user identifier. The same approach is used to store the access/refresh tokens.</span></div><div class="line"></div><div class="line">    <span class="comment">// Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint,</span></div><div class="line">    <span class="comment">// result.Principal.Identity will represent an unauthenticated identity and won't contain any claim.</span></div><div class="line">    <span class="comment">//</span></div><div class="line">    <span class="comment">// Such identities cannot be used as-is to build an authentication cookie in ASP.NET Core (as the</span></div><div class="line">    <span class="comment">// antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but</span></div><div class="line">    <span class="comment">// the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls.</span></div><div class="line">    <span class="keyword">if</span> (result.Principal.Identity <span class="keyword">is</span> not ClaimsIdentity &#123; IsAuthenticated: <span class="literal">true</span> &#125;)</div><div class="line">    &#123;</div><div class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> InvalidOperationException(<span class="string">"The external authorization data cannot be used for authentication."</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">// Build an identity based on the external claims and that will be used to create the authentication cookie.</span></div><div class="line">    <span class="comment">//</span></div><div class="line">    <span class="comment">// By default, all claims extracted during the authorization dance are available. The claims collection stored</span></div><div class="line">    <span class="comment">// in the cookie can be filtered out or mapped to different names depending the claim name or its issuer.</span></div><div class="line">    <span class="keyword">var</span> claims = result.Principal.Claims</div><div class="line">        .Select(claim =&gt; claim <span class="keyword">switch</span></div><div class="line">        &#123;</div><div class="line">            <span class="comment">// Applications can map non-standard claims issued by specific issuers to a standard equivalent.</span></div><div class="line">            &#123; Type: <span class="string">"non_standard_user_id"</span>, Issuer: <span class="string">"https://example.com/"</span> &#125;</div><div class="line">                =&gt; <span class="keyword">new</span> Claim(Claims.Subject, claim.Value, claim.ValueType, claim.Issuer),</div><div class="line"></div><div class="line">            _ =&gt; claim</div><div class="line">        &#125;)</div><div class="line">        .Where(claim =&gt; claim <span class="keyword">switch</span></div><div class="line">        &#123;</div><div class="line">            <span class="comment">// Preserve the "name" and "sub" claims.</span></div><div class="line">            &#123; Type: Claims.Name or Claims.Subject &#125; =&gt; <span class="literal">true</span>,</div><div class="line"></div><div class="line">            <span class="comment">// Applications that use multiple client registrations can filter claims based on the issuer.</span></div><div class="line">            &#123; Type: <span class="string">"custom_claim"</span>, Issuer: <span class="string">"https://example.com/"</span> &#125; =&gt; <span class="literal">true</span>,</div><div class="line"></div><div class="line">            <span class="comment">// Don't preserve the other claims.</span></div><div class="line">            _ =&gt; <span class="literal">false</span></div><div class="line">        &#125;);</div><div class="line"></div><div class="line">    <span class="keyword">var</span> identity = <span class="keyword">new</span> ClaimsIdentity(claims,</div><div class="line">        authenticationType: CookieAuthenticationDefaults.AuthenticationScheme,</div><div class="line">        nameType: Claims.Name,</div><div class="line">        roleType: Claims.Role);</div><div class="line"></div><div class="line">    <span class="comment">// If needed, the tokens returned by the authorization server can be stored in the authentication cookie.</span></div><div class="line">    <span class="comment">// To make cookies less heavy, tokens that are not used can be filtered out before creating the cookie.</span></div><div class="line">    <span class="keyword">var</span> tokens = result.Properties.GetTokens().Where(token =&gt; token <span class="keyword">switch</span></div><div class="line">    &#123;</div><div class="line">        <span class="comment">// Preserve the access and refresh tokens returned in the token response, if available.</span></div><div class="line">        &#123;</div><div class="line">            Name: OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken or</div><div class="line">                  OpenIddictClientAspNetCoreConstants.Tokens.BackchannelRefreshToken</div><div class="line">        &#125; =&gt; <span class="literal">true</span>,</div><div class="line"></div><div class="line">        <span class="comment">// Ignore the other tokens.</span></div><div class="line">        _ =&gt; <span class="literal">false</span></div><div class="line">    &#125;);</div><div class="line"></div><div class="line">    <span class="keyword">var</span> properties = <span class="keyword">new</span> AuthenticationProperties</div><div class="line">    &#123;</div><div class="line">        RedirectUri = result.Properties.RedirectUri</div><div class="line">    &#125;;</div><div class="line"></div><div class="line">    properties.StoreTokens(tokens);</div><div class="line"></div><div class="line">    <span class="comment">// Note: "return SignIn(...)" cannot be directly used in this case, as the cookies handler doesn't allow</span></div><div class="line">    <span class="comment">// redirecting from an endpoint that doesn't match the path set in CookieAuthenticationOptions.LoginPath.</span></div><div class="line">    <span class="comment">// For more information about this restriction, visit https://github.com/dotnet/aspnetcore/issues/36934.</span></div><div class="line">    <span class="keyword">await</span> HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, <span class="keyword">new</span> ClaimsPrincipal(identity), properties);</div><div class="line"></div><div class="line">    <span class="keyword">return</span> Redirect(properties.RedirectUri ?? <span class="string">"/"</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>By doing that, <strong>developers will have greater control over what&#39;s actually stored in their authentication cookies</strong> without requiring a claims mapping feature similar to what&#39;s used by the MSFT OIDC handler, that can sometimes be hard to use when you need to restore claims that are removed by the OIDC handler to help ensure the resulting authentication cookies don&#39;t hit cookie size limits.</p><ul><li><p>The OpenIddict client <strong>will be fully suitable for delegation-only scenarios where authentication is not needed</strong> or where the identification of the user who authorized the client is not possible (typically, OAuth 2.0-only servers that don&#39;t have a userinfo endpoint). In this case, no information about the user will be available but the tokens will still be available to perform any API call on his behalf.</p></li><li><p>Currently, only the authorization/authentication part is fully implemented: userinfo will come in the next few weeks and logout support at a later date.</p></li></ul><h2 id="What-39-s-next"><a href="#What-39-s-next" class="headerlink" title="What&#39;s next?"></a>What&#39;s next?</h2><p>I plan on working on a prototype evaluating the OpenIddict client as a potential replacement of the OAuth 2.0 base handler developed by Microsoft for the aspnet-contrib social providers. While the OpenIddict client is certainly a bit more complex, doing so would have interesting advantages:</p><ul><li><p>By being natively compatible with multiple environments (ASP.NET Core, ASP.NET 4.x, Blazor WASM), a broader audience of developers could be reached at no additional cost. This could be a nice migration option for those using <a href="https://github.com/TerribleDev/OwinOAuthProviders" target="_blank" rel="external">the OWIN OAuth providers</a> (by which the aspnet-contrib providers were initially inspired), that are sadly no longer under active development.</p></li><li><p>Unlike the OAuth 2.0 base handlers, the OpenIddict client natively supports the <code>client_secret_basic</code> client authentication method, which would save us from having code that is needed just to attach the client credentials to the <code>Authorization</code> header of token requests in all the providers that don&#39;t support <code>client_secret_post</code>.</p></li><li><p>By being natively composable, the events model could be used to eliminate code is not strictly required for the social providers to work (e.g if we need to simply attach a custom parameter, we shouldn&#39;t have to also send the HTTP request and handle the response in each provider).</p></li><li><p>The story for social providers supporting both OAuth 2.0 and OpenID Connect would be much better than with the OAuth 2.0 base handler, as they would automatically benefit from all the validation checks implemented in the core OpenIddict client without requiring custom code.</p></li><li><p>As the OpenIddict client supports both static and dynamic server configuration, social providers that implement discovery could be easily updated to use dynamic configuration, which is not possible with the OAuth 2.0 base handler (that requires hardcoded endpoints).</p></li></ul><p>This will certainly be points I&#39;ll discuss with <a href="https://github.com/martincostello" target="_blank" rel="external">Martin Costello</a> - who co-owns the aspnet-contrib OAuth 2.0 providers with me - once I have a fully working prototype.</p><p>You can see the OpenIddict client <a href="https://github.com/openiddict/openiddict-core/blob/dev/sandbox/OpenIddict.Sandbox.AspNetCore.Client/Startup.cs" target="_blank" rel="external">in action in the sandbox project</a>. <strong>If you&#39;re interesting in sharing your feedback, please don&#39;t hesitate to do so</strong> by posting a message <a href="https://github.com/openiddict/openiddict-core/issues/1387" target="_blank" rel="external">in the dedicated GitHub discussion</a>.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;When I unveiled the &lt;a href=&quot;https://github.com/openiddict/openiddict-core/issues/736&quot;&gt;OpenIddict 3.0 roadmap three years ago&lt;/a&gt;, I mentioned that having an OpenIddict client would be a very nice addition but that implementing it as part of 3.0 wasn&amp;#39;t realistic. Today, I&amp;#39;m very happy to announce that &lt;strong&gt;the OpenIddict client will ship as part of OpenIddict&amp;#39;s next major version&lt;/strong&gt;.&lt;/p&gt;&lt;h2 id=&quot;Why-a-new-client&quot;&gt;&lt;a href=&quot;#Why-a-new-client&quot; class=&quot;headerlink&quot; title=&quot;Why a new client?&quot;&gt;&lt;/a&gt;Why a new client?&lt;/h2&gt;&lt;p&gt;A few client libraries already exist for .NET, including:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;For ASP.NET Core, &lt;a href=&quot;https://github.com/dotnet/aspnetcore/tree/main/src/Security/Authentication/OAuth/src&quot;&gt;an OAuth 2.0 base handler developed by Microsoft&lt;/a&gt; that we massively use for the &lt;a href=&quot;https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers&quot;&gt;aspnet-contrib project&lt;/a&gt; (more than 80 OAuth 2.0 services are supported at the time of writing!)&lt;/li&gt;&lt;li&gt;For ASP.NET Core, &lt;a href=&quot;https://github.com/dotnet/aspnetcore/tree/main/src/Security/Authentication/OpenIdConnect/src&quot;&gt;an OpenID Connect handler developed by Microsoft&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;For ASP.NET 4.x/OWIN, &lt;a href=&quot;https://github.com/aspnet/AspNetKatana/tree/dev/src/Microsoft.Owin.Security.OpenIdConnect&quot;&gt;an OpenID Connect middleware maintained by Microsoft&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;For console and GUI applications, &lt;a href=&quot;https://github.com/IdentityModel/IdentityModel.OidcClient&quot;&gt;a certified OpenID Connect client developed by Duende Software&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;For Blazor WASM applications, &lt;a href=&quot;https://github.com/dotnet/aspnetcore/tree/main/src/Components/WebAssembly/WebAssembly.Authentication/src&quot;&gt;an OpenID Connect integration based on the oidc-client-js library&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;So why do we need another one? Am I reinventing the wheel by introducing another OIDC client stack?&lt;/p&gt;&lt;p&gt;Well, that&amp;#39;s definitely a legit question. First, I have to say that these implementations work just fine and that I&amp;#39;ve used them happily other the years. Actually, I even contributed &lt;a href=&quot;https://github.com/aspnet/Security/pulls?q=is%3Apr+author%3Akevinchalet+is%3Aclosed&quot;&gt;to some of these implementations multiple times&lt;/a&gt; so I have a fairly good experience working with them.&lt;/p&gt;&lt;p&gt;In a nutshell, here&amp;#39;s what motivated me:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;While all these libraries offer very specialized implementations, &lt;strong&gt;none of them offers a unified experience&lt;/strong&gt; that allowing sharing a common code base usable on, say, ASP.NET Core and Blazor WASM applications: what you learned about the ASP.NET Core OIDC handler is not applicable to Blazor WASM, that uses a completely different OIDC client stack under the hood.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;The OIDC integration for Blazor WASM uses &lt;a href=&quot;https://github.com/IdentityModel/oidc-client-js&quot;&gt;oidc-client-js&lt;/a&gt;, that was archived in June 2021 and is no longer supported&lt;/strong&gt;. While the Blazor OIDC wrapper itself is supported by Microsoft, the library it uses under the hood is not and won&amp;#39;t receive any bug or security fix, which is a bit surprising to me, considering its author is still sponsored by Microsoft.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;While it&amp;#39;s supported by Microsoft, the Blazor OIDC wrapper doesn&amp;#39;t actually get much love and &lt;a href=&quot;https://github.com/dotnet/aspnetcore/issues/39144&quot;&gt;suffers from annoying design issues that affect OpenIddict users&lt;/a&gt; that the team is unwilling to fix.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The OAuth 2.0 base handler for ASP.NET Core offers a straightforward API for creating derived OAuth 2.0 clients that we successfully used in the aspnet-contrib social providers. Unfortunately, its simplicity comes at a cost: &lt;strong&gt;it&amp;#39;s not composable&lt;/strong&gt;. Concretely, it means that every time we need to customize something (e.g adding a parameter to the token request), we end up duplicating more code than what we should (e.g to add a token request parameter, you also need to take care of sending the HTTP request and handling the response, which is something the OAuth 2.0 base handler does for you if you don&amp;#39;t override the &lt;code&gt;OAuthHandler&amp;lt;T&amp;gt;.ExchangeCodeAsync(OAuthCodeExchangeContext context)&lt;/code&gt; method).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;There&amp;#39;s also another downside with the OAuth 2.0 base handler: &lt;strong&gt;it doesn&amp;#39;t support OpenID Connect&lt;/strong&gt;. Yet, we received contributions to add OAuth 2.0 providers that also support OpenID Connect: while ASP.NET Core has a dedicated OIDC handler that is more appropriate for these cases, many people like the simpler registration story the OAuth 2.0 base handler and the aspnet-contrib providers offer. Retrospectively, we shouldn&amp;#39;t have accepted these contributions as these providers don&amp;#39;t benefit from the additional security checks a real OpenID Connect implementation requires (e.g &lt;code&gt;nonce&lt;/code&gt;, &lt;code&gt;at_hash&lt;/code&gt; or &lt;code&gt;c_hash&lt;/code&gt; validation, etc.)&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;The OAuth 2.0 base handler doesn&amp;#39;t support &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc8414&quot;&gt;the OAuth 2.0 Authorization Server Metadata&lt;/a&gt; specification that backported the OpenID Connect server discovery feature to the OAuth 2.0 world and that allows finding the location of the authorization and token endpoints dynamically, without having to hardcode them. While the adoption is slow, I&amp;#39;d expect more and more services to support it in the next few years.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
    
    </summary>
    
      <category term="Development" scheme="https://kevinchalet.com/categories/development/"/>
    
    
      <category term="asp.net core" scheme="https://kevinchalet.com/tags/asp-net-core/"/>
    
      <category term="aspnet-contrib" scheme="https://kevinchalet.com/tags/aspnet-contrib/"/>
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
  </entry>
  
  <entry>
    <title>ASP.NET Core 6 and authentication servers: the real bait and switch is not the one you think</title>
    <link href="https://kevinchalet.com/2021/05/24/asp-net-core-6-and-authentication-servers-the-real-bait-and-switch-is-not-the-one-you-think/"/>
    <id>https://kevinchalet.com/2021/05/24/asp-net-core-6-and-authentication-servers-the-real-bait-and-switch-is-not-the-one-you-think/</id>
    <published>2021-05-24T18:30:00.000Z</published>
    <updated>2021-06-04T11:51:21.000Z</updated>
    
    <content type="html"><![CDATA[<p>Earlier this month, <a href="https://devblogs.microsoft.com/aspnet/asp-net-core-6-and-authentication-servers/" target="_blank" rel="external">Barry Dorrans – the Security PM for .NET – announced</a> that the ASP.NET Core templates would be updated to target Duende IdentityServer5 as part of the .NET 6 effort and that the ASP.NET team was considering creating a development-only tool for testing OpenID Connect integration in .NET 7.</p><p>By looking at the reactions under the blog post, <a href="https://github.com/dotnet/aspnetcore/issues/32494" target="_blank" rel="external">on GitHub</a> or <a href="https://www.reddit.com/r/dotnet/comments/n8euul/aspnet_core_6_and_authentication_servers_status/" target="_blank" rel="external">on Reddit</a>, the least we can say is that people are always quite passionate when it comes to how Microsoft treats these topics. Some like that decision while a few others feel &quot;betrayed&quot; and consider this move a &quot;bait and switch&quot;:</p><img src="/2021/05/24/asp-net-core-6-and-authentication-servers-the-real-bait-and-switch-is-not-the-one-you-think/github-post-1.png" alt="github-post-1.png"><p>Despite this move, <strong>my personal opinion hasn&#39;t changed</strong>: I was very happy when Microsoft announced they would use IdentityServer4 in 2018 and the commitment to use the new dual-licensed version of IdentityServer in .NET 6 (instead of writing their own OIDC server from scratch) is, in my opinion, a step in the right direction in changing their relationship with the .NET community: Microsoft can&#39;t provide everything out-of-the-box and promoting third-party projects is a great approach.</p><p>The fact Microsoft keeps sponsoring the IdentityServer project – for $12,000/year at the time of writing – is also an excellent signal sent to the community. Even if you&#39;ve never liked Microsoft&#39;s love/hate relationship with OSS, it would be very hard to deny that the company has positively changed in this regard.</p><a id="more"></a> <img src="/2021/05/24/asp-net-core-6-and-authentication-servers-the-real-bait-and-switch-is-not-the-one-you-think/github-post-2.png" alt="github-post-2.png"><p>That said, I&#39;d be lying if I didn&#39;t admit I was quite disappointed when reading that blog post: OpenIddict – the OpenID Connect server I maintain – wasn&#39;t even mentioned as a potential free and open source alternative that can be used on-premises or hosted in a cloud application.</p><p><strong>This omission is not an oversight: Barry and the rest of the ASP.NET team know my work</strong> and we informally discussed potential options that could have helped secure their supply chain, should they have included something based on OpenIddict in the ASP.NET templates: <strong>even options like making OpenIddict a co-owned project were on the table</strong>.</p><p>This omission has a reason, clearly explained in the blog post: Microsoft wants you to use their cloud offers: <a href="https://azure.microsoft.com/en-us/services/active-directory/" target="_blank" rel="external">Azure AD</a> and <a href="https://azure.microsoft.com/en-us/services/active-directory/external-identities/b2c/" target="_blank" rel="external">Azure AD B2C</a>. In fact, <a href="https://github.com/dotnet/AspNetCore.Docs/issues/22355" target="_blank" rel="external">the ASP.NET team is already working on migration guides</a> to help users move to Azure AD as part of their .NET 6 effort (fun fact: the thread has been locked so that only members of the .NET team can reply).</p><p>The crucial thing to note here is that Azure AD – or the equivalents offered by Okta, Auth0, Firebase or Amazon – are not just OpenID Connect servers like IdentityServer or OpenIddict (that typically rely on a membership stack like ASP.NET Core Identity): <strong>they also aim at replacing your users database</strong>.</p><p>Relying on a SaaS product to take care of your users database is not a bad idea in itself: most of the security aspects are managed for you by the cloud vendors, that will transparently maintain the software and hardware, keep your users database safe and try their best to protect your application against bad actors. This argument alone is often enough to justify the move to cloud solutions like Azure AD:</p><img src="/2021/05/24/asp-net-core-6-and-authentication-servers-the-real-bait-and-switch-is-not-the-one-you-think/twitter-post.png" alt="twitter-post.png"><blockquote><p>Microsoft already has a team and a product in that area, Azure Active Directory, which allows 500,000 objects for free. The ASP.NET team feels a managed cloud solution remains the best practical option for developers – the security is managed, you don’t store credentials locally with the risks that presents.</p><footer><strong>Barry Dorrans</strong><cite>ASP.NET Core 6 and Authentication Servers</cite></footer></blockquote><p>After all, Azure AD has a free plan with up to 500,000 users (*), so this should be a no-brainer, right? Not so fast: these services are always free for a reason: <strong>their business model relies on the fact a fraction of their customers will end up exceeding the limits of the free plan</strong>. At that point, customers basically have three options:</p><ul><li>Remove as many users as needed to avoid reaching the limit (e.g by removing &quot;inactive&quot; accounts, but this typically only works for a while).</li><li>Pay for the service. <a href="https://azure.microsoft.com/en-us/pricing/details/active-directory/" target="_blank" rel="external">At the time of writing, Microsoft charges an insanely high $6/user rate for those on a paid plan</a>.</li><li>Move to a different vendor with a different pricing model.</li></ul><p>The thing is moving to a different vendor is never an easy task as cloud vendors typically do their best to make that a difficult process: while you&#39;re generally allowed to export non-sensitive parts of your users database (e.g to re-import it in a different service), there&#39;s a thing you often have no control over: <strong>password hashes</strong>:</p><ul><li>Azure AD and Okta don&#39;t allow you to retrieve them. In practice, this means you&#39;ll need to reset all your user passwords, which is generally horrible from a user experience perspective.</li><li>Auth0 offers an option to export users with their passwords, <a href="https://auth0.com/docs/support/export-data" target="_blank" rel="external">but this requires contacting the support</a>. To deter you from doing that, their documentation explicitly says that &quot;they are unable to accept or guarantee requests for exports at a specific time and date&quot;. Oh and guess what? It&#39;s not possible if you&#39;re on a free plan! And now that <a href="https://auth0.com/blog/okta-acquisition-announcement/" target="_blank" rel="external">Auth0 was acquired by Okta for $6,5B</a>, who knows how long this ability will stay?</li></ul><p><strong>While cloud-based identity offers are captive environments, things are completely different with IdentityServer and OpenIddict, that are often used in conjunction with ASP.NET Core Identity</strong>: you own the database and user passwords are stored in a format that could even be implemented in a non-.NET environment (after all, it uses PBKDF under the hood).</p><p>When using an open-source solution you can run locally, you have full control over your data. With a cloud offer, the story is completely different. <strong>Don&#39;t get lured by free plans: if you&#39;re interested in a cloud offer, make sure the pricing model of paid plans is adequate</strong> for your case before even considering it (and even then, be prepared for a potential price increase at any point).</p><p>(*) Actually, the free plan&#39;s limit is 50,000 objects. If you need additional entries, you have to ask the Azure support to extend the limit, which is something they do at their own discretion.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Earlier this month, &lt;a href=&quot;https://devblogs.microsoft.com/aspnet/asp-net-core-6-and-authentication-servers/&quot;&gt;Barry Dorrans – the Security PM for .NET – announced&lt;/a&gt; that the ASP.NET Core templates would be updated to target Duende IdentityServer5 as part of the .NET 6 effort and that the ASP.NET team was considering creating a development-only tool for testing OpenID Connect integration in .NET 7.&lt;/p&gt;&lt;p&gt;By looking at the reactions under the blog post, &lt;a href=&quot;https://github.com/dotnet/aspnetcore/issues/32494&quot;&gt;on GitHub&lt;/a&gt; or &lt;a href=&quot;https://www.reddit.com/r/dotnet/comments/n8euul/aspnet_core_6_and_authentication_servers_status/&quot;&gt;on Reddit&lt;/a&gt;, the least we can say is that people are always quite passionate when it comes to how Microsoft treats these topics. Some like that decision while a few others feel &amp;quot;betrayed&amp;quot; and consider this move a &amp;quot;bait and switch&amp;quot;:&lt;/p&gt;&lt;img src=&quot;/2021/05/24/asp-net-core-6-and-authentication-servers-the-real-bait-and-switch-is-not-the-one-you-think/github-post-1.png&quot; alt=&quot;github-post-1.png&quot;&gt;&lt;p&gt;Despite this move, &lt;strong&gt;my personal opinion hasn&amp;#39;t changed&lt;/strong&gt;: I was very happy when Microsoft announced they would use IdentityServer4 in 2018 and the commitment to use the new dual-licensed version of IdentityServer in .NET 6 (instead of writing their own OIDC server from scratch) is, in my opinion, a step in the right direction in changing their relationship with the .NET community: Microsoft can&amp;#39;t provide everything out-of-the-box and promoting third-party projects is a great approach.&lt;/p&gt;&lt;p&gt;The fact Microsoft keeps sponsoring the IdentityServer project – for $12,000/year at the time of writing – is also an excellent signal sent to the community. Even if you&amp;#39;ve never liked Microsoft&amp;#39;s love/hate relationship with OSS, it would be very hard to deny that the company has positively changed in this regard.&lt;/p&gt;
    
    </summary>
    
      <category term="General" scheme="https://kevinchalet.com/categories/general/"/>
    
    
      <category term="asp.net core" scheme="https://kevinchalet.com/tags/asp-net-core/"/>
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="asp.net core identity" scheme="https://kevinchalet.com/tags/asp-net-core-identity/"/>
    
      <category term="auth0" scheme="https://kevinchalet.com/tags/auth0/"/>
    
      <category term="azure ad" scheme="https://kevinchalet.com/tags/azure-ad/"/>
    
      <category term="okta" scheme="https://kevinchalet.com/tags/okta/"/>
    
  </entry>
  
  <entry>
    <title>OpenIddict 3.0 general availability</title>
    <link href="https://kevinchalet.com/2020/12/23/openiddict-3-0-general-availability/"/>
    <id>https://kevinchalet.com/2020/12/23/openiddict-3-0-general-availability/</id>
    <published>2020-12-23T21:00:00.000Z</published>
    <updated>2020-12-23T20:30:31.000Z</updated>
    
    <content type="html"><![CDATA[<img src="/2020/12/23/openiddict-3-0-general-availability/christmas-tree.jpg" alt="christmas-tree.jpg"><p>One year and a half after releasing the first OpenIddict 3.0 alpha bits, <strong>I&#39;m very pleased to announce that 3.0 is now generally available</strong>!</p><p>This release is unquestionably OpenIddict&#39;s most exciting milestone, as both the server and validation stacks were rewritten from scratch to adopt a whole new event-oriented model, which should give OpenIddict 3.0 <strong>much more flexibility and extensibility than the aspnet-contrib OpenID Connect server and validation middleware it replaces</strong>.</p><p>If you&#39;re interested in reading more about the changes introduced in this release, I wrote a few blog posts describing the most important ones:</p><ul><li><a href="/2020/02/18/creating-an-openid-connect-server-proxy-with-openiddict-3-0-s-degraded-mode/" title="Creating an OpenID Connect server proxy with OpenIddict 3.0's degraded mode">Creating an OpenID Connect server proxy with OpenIddict 3.0's degraded mode</a></li><li><a href="/2020/03/03/adding-openiddict-3-0-to-an-owin-application/" title="Adding OpenIddict 3.0 to an OWIN application">Adding OpenIddict 3.0 to an OWIN application</a></li><li><a href="/2020/06/11/introducing-openiddict-3-0-beta1/" title="Introducing OpenIddict 3.0 beta1">Introducing OpenIddict 3.0 beta1</a></li><li><a href="/2020/07/08/openiddict-3-0-beta2-is-out/" title="OpenIddict 3.0 beta2 is out">OpenIddict 3.0 beta2 is out</a></li><li><a href="/2020/08/03/introducing-localization-support-in-openiddict-3-0-beta3/" title="Introducing localization support in OpenIddict 3.0 beta3">Introducing localization support in OpenIddict 3.0 beta3</a> (<em>note: localization support had to be removed in rc1 to keep the error descriptions returned by OpenIddict standard-compliant</em>)</li><li><a href="/2020/10/02/introducing-quartz-net-support-and-new-languages-in-openiddict-3-0-beta4/" title="Introducing Quartz.NET support and new languages in OpenIddict 3.0 beta4">Introducing Quartz.NET support and new languages in OpenIddict 3.0 beta4</a></li><li><a href="/2020/10/03/using-the-orchardcore-openid-management-feature-with-an-existing-openiddict-deployment/" title="Using the OrchardCore OpenID management feature with an existing OpenIddict deployment">Using the OrchardCore OpenID management feature with an existing OpenIddict deployment</a></li><li><a href="/2020/10/27/openiddict-3-0-beta6-is-out/" title="OpenIddict 3.0 beta6 is out">OpenIddict 3.0 beta6 is out</a></li><li><a href="/2020/11/17/introducing-openiddict-3-0-s-first-release-candidate-version/" title="Introducing OpenIddict 3.0's first release candidate version">Introducing OpenIddict 3.0's first release candidate version</a></li></ul><div class="note undefined"><p>In OpenIddict 3.0 RTM, the Data Protection formatter used to create and read tokens serialized using ASP.NET Core Data Protection was fixed to use the same serialization versions as OpenIddict 2.x so that tokens created with OpenIddict 2.x can be read and used with OpenIddict 3.0. This change will impact users of the beta and RC versions that opted in for the Data Protection format (typically, OrchardCore users). Fortunately, this only requires retrieving new refresh tokens by starting a new authorization flow.</p></div><p>As announced in <a href="/2020/06/11/introducing-openiddict-3-0-beta1/" title="Introducing OpenIddict 3.0 beta1">Introducing OpenIddict 3.0 beta1</a>, <strong>the OpenIddict 2.x and aspnet-contrib OpenID Connect server/validation middleware are now considered legacy/obsolete packages and won&#39;t receive bug fixes or security updates</strong>.</p><p>Whether you&#39;re still on ASP.NET 4.x (via OWIN/Katana), ASP.NET Core 2.1 on .NET Framework or .NET Core, ASP.NET Core 3.1 or are already using ASP.NET Core 5.0, migrating to OpenIddict 3.0 should never be a blocking factor, thanks to its broader-than-ever platforms support!</p><p>If you need additional time to migrate to OpenIddict 3.0 and are interesting in chatting about your options, feel free to ping me at <a href="mailto:contact@kevinchalet.com">contact@kevinchalet.com</a>.</p><p>I&#39;d like to thank <a href="https://github.com/sebastienros" target="_blank" rel="external">Sébastien Ros</a>, <a href="https://github.com/mridentity" target="_blank" rel="external">mridentity</a>, <a href="https://github.com/GDreyV" target="_blank" rel="external">Andrew</a>, <a href="https://github.com/gustavdw" target="_blank" rel="external">gustavdw</a>, <a href="https://github.com/Gillardo" target="_blank" rel="external">Gillardo</a>, <a href="https://github.com/DovydasNavickas" target="_blank" rel="external">Dovydas Navickas</a> and <a href="https://github.com/schmitch" target="_blank" rel="external">Christian Schmitt</a> for <a href="https://github.com/sponsors/kevinchalet" target="_blank" rel="external">sponsoring me on GitHub</a>, which allows me to dedicate more time to OpenIddict! You guys have no idea how much I appreciate that.</p><p>Merry Christmas everyone!</p>]]></content>
    
    <summary type="html">
    
      &lt;img src=&quot;/2020/12/23/openiddict-3-0-general-availability/christmas-tree.jpg&quot; alt=&quot;christmas-tree.jpg&quot;&gt;&lt;p&gt;One year and a half after releasin
    
    </summary>
    
      <category term="Development" scheme="https://kevinchalet.com/categories/development/"/>
    
    
      <category term="asp.net core" scheme="https://kevinchalet.com/tags/asp-net-core/"/>
    
      <category term="aspnet-contrib" scheme="https://kevinchalet.com/tags/aspnet-contrib/"/>
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
  </entry>
  
  <entry>
    <title>Introducing OpenIddict 3.0&#39;s first release candidate version</title>
    <link href="https://kevinchalet.com/2020/11/17/introducing-openiddict-3-0-s-first-release-candidate-version/"/>
    <id>https://kevinchalet.com/2020/11/17/introducing-openiddict-3-0-s-first-release-candidate-version/</id>
    <published>2020-11-17T21:00:00.000Z</published>
    <updated>2020-11-17T20:29:40.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="What-changed"><a href="#What-changed" class="headerlink" title="What changed?"></a>What changed?</h2><p>All the changes introduced in this release can be found <a href="https://github.com/openiddict/openiddict-core/issues?q=milestone%3A3.0.0-rc1" target="_blank" rel="external">on GitHub</a>.</p><p>While this release almost exclusively focused on fixing bugs and eliminating pain points, an important change affected our localization story: starting with OpenIddict 3.0 RC1, <strong>the error descriptions returned by OpenIddict itself will no longer be translated</strong>.</p><p>Thanks to <a href="https://github.com/openiddict/openiddict-core/issues/1164" target="_blank" rel="external">an external report</a>, I discovered that the localization support introduced in OpenIddict 3.0 beta3 was causing issues when returned as part of the standard <code>WWW-Authenticate</code> response header. Worse, it was violating the OAuth 2.0 core specification, that requires that all <code>error_description</code>s (not just the ones returned in <code>WWW-Authenticate</code>) only include US-ASCII characters: even diacritics like <code>é</code> (that are common in French) are not allowed. In practice, that means we can sadly only return English error descriptions and that&#39;s why localization support had to be removed in this release.</p><p>That said, I still believe having localized content is extremely useful and makes a library like OpenIddict more inclusive and developer-friendly. As such, we&#39;ll opt for a different approach in the next RC: the OpenIddict server and validation stacks will be updated to always include a unique <code>error_uri</code> pointing to <a href="http://openiddict.com/" target="_blank" rel="external">openiddict.com</a>, which will allow documenting every returned error over time.</p><h2 id="What-39-s-next"><a href="#What-39-s-next" class="headerlink" title="What&#39;s next?"></a>What&#39;s next?</h2><p><strong>OpenIddict 3.0 RTM is expected to ship mid-December</strong>. Before that, a second release candidate version should be released early December.</p><p>OpenIddict 2.x and the aspnet-contrib OAuth 2.0/OpenID Connect server/extensions will no longer be supported and won&#39;t receive free security updates/bug fixes as soon as 3.0 is officially released (<strong>only users benefiting from a support contract will still receive updates</strong>). As such, it is very important for users of these packages to start evaluating OpenIddict 3.0 RC1 as soon as possible, so that potential bugs affecting their scenarios can be fixed in the RTM version.</p>]]></content>
    
    <summary type="html">
    
      In this post, learn more about the changes introduced in OpenIddict 3.0 rc1.
    
    </summary>
    
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
  </entry>
  
  <entry>
    <title>OpenIddict 3.0 beta6 is out</title>
    <link href="https://kevinchalet.com/2020/10/27/openiddict-3-0-beta6-is-out/"/>
    <id>https://kevinchalet.com/2020/10/27/openiddict-3-0-beta6-is-out/</id>
    <published>2020-10-27T19:00:00.000Z</published>
    <updated>2020-11-02T17:27:40.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="What-changed"><a href="#What-changed" class="headerlink" title="What changed?"></a>What changed?</h2><p>All the changes introduced in this release can be found <a href="https://github.com/openiddict/openiddict-core/issues?q=milestone%3A3.0.0-beta6" target="_blank" rel="external">on GitHub</a>, but here&#39;s a recap of the most important ones:</p><h3 id="OpenIddict-now-supports-response-type-permissions"><a href="#OpenIddict-now-supports-response-type-permissions" class="headerlink" title="OpenIddict now supports response type permissions"></a>OpenIddict now supports response type permissions</h3><p>In previous versions, the <code>response_type</code> values an application was allowed to use were always inferred from the grant types. For instance, if a client was granted the <code>authorization_code</code> grant type permission, it was automatically allowed to use <code>response_type=code</code> (that corresponds to the &quot;pure&quot; authorization code). If it was granted the <code>implicit</code> permission, it was also allowed to use <code>response_type=id_token</code> and depending on its type (confidential or public), <code>response_type=id_token token</code> and <code>response_type=token</code>.</p><p>While convenient, this approach lacked granularity. In OpenIddict 3.0 beta6, this mechanism is replaced by explicit response type permissions. Response type permissions are granted exactly the same way as the other permissions:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">await</span> manager.CreateAsync(<span class="keyword">new</span> OpenIddictApplicationDescriptor</div><div class="line">&#123;</div><div class="line">    ClientId = <span class="string">"mvc"</span>,</div><div class="line">    ClientSecret = <span class="string">"901564A5-E7FE-42CB-B10D-61EF6A8F3654"</span>,</div><div class="line">    ConsentType = ConsentTypes.Explicit,</div><div class="line">    DisplayName = <span class="string">"MVC client application"</span>,</div><div class="line">    DisplayNames =</div><div class="line">    &#123;</div><div class="line">        [CultureInfo.GetCultureInfo(<span class="string">"fr-FR"</span>)] = <span class="string">"Application cliente MVC"</span></div><div class="line">    &#125;,</div><div class="line">    PostLogoutRedirectUris =</div><div class="line">    &#123;</div><div class="line">        <span class="keyword">new</span> Uri(<span class="string">"https://localhost:44381/signout-callback-oidc"</span>)</div><div class="line">    &#125;,</div><div class="line">    RedirectUris =</div><div class="line">    &#123;</div><div class="line">        <span class="keyword">new</span> Uri(<span class="string">"https://localhost:44381/signin-oidc"</span>)</div><div class="line">    &#125;,</div><div class="line">    Permissions =</div><div class="line">    &#123;</div><div class="line">        Permissions.Endpoints.Authorization,</div><div class="line">        Permissions.Endpoints.Logout,</div><div class="line">        Permissions.Endpoints.Token,</div><div class="line">        Permissions.GrantTypes.AuthorizationCode,</div><div class="line">        Permissions.GrantTypes.RefreshToken,</div><div class="line">        Permissions.ResponseTypes.Code, <span class="comment">// New permission</span></div><div class="line">        Permissions.Scopes.Email,</div><div class="line">        Permissions.Scopes.Profile,</div><div class="line">        Permissions.Scopes.Roles,</div><div class="line">        Permissions.Prefixes.Scope + <span class="string">"demo_api"</span></div><div class="line">    &#125;,</div><div class="line">    Requirements =</div><div class="line">    &#123;</div><div class="line">        Requirements.Features.ProofKeyForCodeExchange</div><div class="line">    &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>Introducing response type permissions was also a good opportunity for removing the OpenIddict-specific <code>hybrid</code> client type, that was used by confidential clients that also wanted to retrieve an access token directly from the authorization endpoint. In beta6, the <code>hybrid</code> type is no longer supported and must be replaced by explicit <code>Permissions.ResponseTypes.CodeIdTokenToken</code> or <code>Permissions.ResponseTypes.CodeToken</code> permissions.</p><a id="more"></a><p>Users who have many clients to migrate can use this script to infer the &quot;best&quot; response type permissions based on the already allowed grant types:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">using</span> System;</div><div class="line"><span class="keyword">using</span> System.Collections.Generic;</div><div class="line"><span class="keyword">using</span> System.Linq;</div><div class="line"><span class="keyword">using</span> System.Threading.Tasks;</div><div class="line"><span class="keyword">using</span> Microsoft.EntityFrameworkCore;</div><div class="line"><span class="keyword">using</span> Microsoft.Extensions.DependencyInjection;</div><div class="line"><span class="keyword">using</span> OpenIddict.Abstractions;</div><div class="line"><span class="keyword">using</span> <span class="keyword">static</span> OpenIddict.Abstractions.OpenIddictConstants;</div><div class="line"></div><div class="line"><span class="keyword">namespace</span> <span class="title">MigrationScript</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">Program</span></div><div class="line">    &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">async</span> Task <span class="title">Main</span>(<span class="params"><span class="keyword">string</span>[] args</span>)</span></div><div class="line">        &#123;</div><div class="line">            <span class="keyword">var</span> services = <span class="keyword">new</span> ServiceCollection();</div><div class="line"></div><div class="line">            services.AddOpenIddict()</div><div class="line">                .AddCore(options =&gt;</div><div class="line">                &#123;</div><div class="line">                    options.UseEntityFrameworkCore()</div><div class="line">                           .UseDbContext&lt;DbContext&gt;();</div><div class="line">                &#125;);</div><div class="line"></div><div class="line">            services.AddDbContext&lt;DbContext&gt;(options =&gt;</div><div class="line">            &#123;</div><div class="line">                options.UseSqlServer(<span class="string">"Server=(localdb)\\mssqllocaldb;Database=aspnet5-openiddict-sample-12340be6-0442-4622-b782-a7412bb7d045;Trusted_Connection=True;MultipleActiveResultSets=true"</span>);</div><div class="line">                options.UseOpenIddict();</div><div class="line">            &#125;);</div><div class="line"></div><div class="line">            <span class="keyword">using</span> <span class="keyword">var</span> provider = services.BuildServiceProvider();</div><div class="line">            <span class="keyword">using</span> <span class="keyword">var</span> scope = provider.CreateScope();</div><div class="line">            <span class="keyword">var</span> manager = scope.ServiceProvider.GetRequiredService&lt;IOpenIddictApplicationManager&gt;();</div><div class="line"></div><div class="line">            <span class="keyword">foreach</span> (<span class="function"><span class="keyword">var</span> application <span class="keyword">in</span> <span class="keyword">await</span> <span class="title">GetApplicationsAsync</span>(<span class="params">manager</span>))</span></div><div class="line">            &#123;</div><div class="line">                <span class="comment">// Ignore applications that already have at least one response type permission.</span></div><div class="line">                <span class="keyword">var</span> permissions = <span class="keyword">new</span> HashSet&lt;<span class="keyword">string</span>&gt;(<span class="keyword">await</span> manager.GetPermissionsAsync(application), StringComparer.Ordinal);</div><div class="line">                <span class="keyword">if</span> (permissions.Any(permission =&gt; permission.StartsWith(Permissions.Prefixes.ResponseType)))</div><div class="line">                &#123;</div><div class="line">                    <span class="keyword">continue</span>;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="comment">// If a application was granted the authorization code grant permission, allow it to use the code response type.</span></div><div class="line">                <span class="keyword">if</span> (<span class="keyword">await</span> manager.HasPermissionAsync(application, Permissions.GrantTypes.AuthorizationCode))</div><div class="line">                &#123;</div><div class="line">                    permissions.Add(Permissions.ResponseTypes.Code);</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="keyword">if</span> (<span class="keyword">await</span> manager.HasPermissionAsync(application, Permissions.GrantTypes.Implicit))</div><div class="line">                &#123;</div><div class="line">                    <span class="comment">// Allow all applications that were granted the implicit</span></div><div class="line">                    <span class="comment">// grant permission to use the "id_token" response types.</span></div><div class="line">                    permissions.Add(Permissions.ResponseTypes.IdToken);</div><div class="line"></div><div class="line">                    <span class="comment">// Applications that were also granted the code grant type permission</span></div><div class="line">                    <span class="comment">// are allowed to use the "code id_token" response type permission.</span></div><div class="line">                    <span class="keyword">if</span> (<span class="keyword">await</span> manager.HasPermissionAsync(application, Permissions.GrantTypes.AuthorizationCode))</div><div class="line">                    &#123;</div><div class="line">                        permissions.Add(Permissions.ResponseTypes.CodeIdToken);</div><div class="line">                    &#125;</div><div class="line"></div><div class="line">                    <span class="comment">// Allow public and hybrid clients to retrieve an access token directly from the</span></div><div class="line">                    <span class="comment">// authorization endpoint by granting them the "id_token token" and "token" permissions.</span></div><div class="line">                    <span class="keyword">if</span> (<span class="keyword">await</span> manager.HasClientTypeAsync(application, ClientTypes.Public) ||</div><div class="line">                        <span class="keyword">await</span> manager.HasClientTypeAsync(application, <span class="string">"hybrid"</span>))</div><div class="line">                    &#123;</div><div class="line">                        permissions.Add(Permissions.ResponseTypes.IdTokenToken);</div><div class="line">                        permissions.Add(Permissions.ResponseTypes.Token);</div><div class="line">                    &#125;</div><div class="line"></div><div class="line">                    <span class="comment">// Hybrid applications that were also granted the code grant type permission</span></div><div class="line">                    <span class="comment">// are allowed to use the "code id_token token" and "code token" types.</span></div><div class="line">                    <span class="keyword">if</span> (<span class="keyword">await</span> manager.HasClientTypeAsync(application, <span class="string">"hybrid"</span>) &amp;&amp;</div><div class="line">                        <span class="keyword">await</span> manager.HasPermissionAsync(application, Permissions.GrantTypes.AuthorizationCode))</div><div class="line">                    &#123;</div><div class="line">                        permissions.Add(Permissions.ResponseTypes.CodeIdTokenToken);</div><div class="line">                        permissions.Add(Permissions.ResponseTypes.CodeToken);</div><div class="line">                    &#125;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="keyword">var</span> descriptor = <span class="keyword">new</span> OpenIddictApplicationDescriptor();</div><div class="line">                <span class="keyword">await</span> manager.PopulateAsync(descriptor, application);</div><div class="line"></div><div class="line">                <span class="comment">// If the application is an hybrid client, replace it by a confidential client.</span></div><div class="line">                <span class="comment">// Hybrid client types were removed in OpenIddict 3.0 beta6 and replaced by</span></div><div class="line">                <span class="comment">// response type permissions, that allow defining whether a confidential client</span></div><div class="line">                <span class="comment">// is allowed to retrieved an access token from the authorization endpoint</span></div><div class="line">                <span class="comment">// (i.e without having to send its client credentials to retrieve an access token).</span></div><div class="line">                <span class="keyword">if</span> (<span class="keyword">await</span> manager.HasClientTypeAsync(application, <span class="string">"hybrid"</span>))</div><div class="line">                &#123;</div><div class="line">                    descriptor.Type = ClientTypes.Confidential;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="comment">// Replace the permissions on the descriptor.</span></div><div class="line">                descriptor.Permissions.Clear();</div><div class="line">                descriptor.Permissions.UnionWith(permissions);</div><div class="line"></div><div class="line">                <span class="comment">// Update the application.</span></div><div class="line">                <span class="keyword">await</span> manager.PopulateAsync(application, descriptor);</div><div class="line">                <span class="keyword">await</span> manager.UpdateAsync(application);</div><div class="line">            &#125;</div><div class="line"></div><div class="line">            <span class="keyword">static</span> <span class="keyword">async</span> Task&lt;List&lt;<span class="keyword">object</span>&gt;&gt; GetApplicationsAsync(IOpenIddictApplicationManager manager)</div><div class="line">            &#123;</div><div class="line">                <span class="keyword">var</span> applications = <span class="keyword">new</span> List&lt;<span class="keyword">object</span>&gt;();</div><div class="line"></div><div class="line">                <span class="keyword">await</span> <span class="keyword">foreach</span> (<span class="keyword">var</span> application <span class="keyword">in</span> manager.ListAsync())</div><div class="line">                &#123;</div><div class="line">                    applications.Add(application);</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="keyword">return</span> applications;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><h3 id="The-hybrid-flow-is-no-longer-enabled-when-enabling-both-the-authorization-code-and-implicit-flows"><a href="#The-hybrid-flow-is-no-longer-enabled-when-enabling-both-the-authorization-code-and-implicit-flows" class="headerlink" title="The hybrid flow is no longer enabled when enabling both the authorization code and implicit flows"></a>The hybrid flow is no longer enabled when enabling both the authorization code and implicit flows</h3><p>In previous versions, the hybrid flow was automatically enabled when enabling both the authorization code and implicit flows. In beta6, it now must be enabled explicitly, like the other flows:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        options.AllowHybridFlow();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><h3 id="Rolling-refresh-tokens-are-now-enabled-by-default-and-allow-for-a-configurable-leeway"><a href="#Rolling-refresh-tokens-are-now-enabled-by-default-and-allow-for-a-configurable-leeway" class="headerlink" title="Rolling refresh tokens are now enabled by default and allow for a configurable leeway"></a>Rolling refresh tokens are now enabled by default and allow for a configurable leeway</h3><p>In 3.0 beta6, refresh tokens were revamped and the following changes were made:</p><ul><li><p>The rolling refresh tokens mechanism now allows for a small leeway to avoid revoking entire token chains when concurrent requests are received (by default, it is set to 15 seconds but can be changed using <code>OpenIddictServerBuilder.SetRefreshTokenReuseLeeway()</code>).</p></li><li><p>The rolling refresh tokens mechanism no longer revokes previously issued tokens, it just marks refresh tokens as redeemed when they are used.</p></li><li><p>Rolling refresh tokens are now enabled by default to make OpenIddict compliant with <a href="https://tools.ietf.org/html/draft-ietf-oauth-security-topics-16#section-4.13.2" target="_blank" rel="external">the OAuth 2.0 best current practices, that require implementing either rotation or sender-constrained tokens</a>. Developers who prefer disabling rolling refresh tokens can do so by calling <code>OpenIddictServerBuilder.DisableRollingRefreshTokens()</code>.</p></li><li><p>The refresh token lifetime extension mechanism was removed: even when rolling refresh tokens are disabled, a new refresh token will now be generated and returned for each refresh token request, which guarantees that the client application always gets a refresh tokens (and subsequent access tokens) containing the latest claims.</p></li></ul><h3 id="The-EF-Core-and-EF-6-entities-now-use-DateTime-instead-of-DateTimeOffset"><a href="#The-EF-Core-and-EF-6-entities-now-use-DateTime-instead-of-DateTimeOffset" class="headerlink" title="The EF Core and EF 6 entities now use DateTime instead of DateTimeOffset"></a>The EF Core and EF 6 entities now use <code>DateTime</code> instead of <code>DateTimeOffset</code></h3><p>Due to incomplete support in the Oracle MySQL and Npgsql Entity Framework providers, <a href="https://github.com/openiddict/openiddict-core/issues/1115" target="_blank" rel="external">the EF Core and EF 6 entities no longer use <code>DateTimeOffset</code></a>. Developers migrating to OpenIddict 3.0 beta6 will need to add a migration to move from <code>DateTimeOffset</code> to <code>DateTime</code>.</p><h3 id="Proof-Key-for-Code-Exchange-PKCE-can-now-be-enforced-globally"><a href="#Proof-Key-for-Code-Exchange-PKCE-can-now-be-enforced-globally" class="headerlink" title="Proof Key for Code Exchange (PKCE) can now be enforced globally"></a>Proof Key for Code Exchange (PKCE) can now be enforced globally</h3><p>Previous versions of OpenIddict 3.0 already allowed making PKCE mandatory per-client, but in beta6, there&#39;s now a global option:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// Force all client applications to use Proof Key for Code Exchange (PKCE).</span></div><div class="line">        options.RequireProofKeyForCodeExchange();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><h3 id="Tokens-no-longer-contain-a-jti-claim"><a href="#Tokens-no-longer-contain-a-jti-claim" class="headerlink" title="Tokens no longer contain a jti claim"></a>Tokens no longer contain a <code>jti</code> claim</h3><p>To make tokens lighter, a <code>jti</code> claim is no longer generated and persisted in the tokens generated by OpenIddict 3.0 beta6.</p><p>Special thanks to <a href="https://github.com/johanndev" target="_blank" rel="external">Johann Wimmer</a> <a href="https://github.com/openiddict/openiddict-core/pull/1116" target="_blank" rel="external">for his great contribution</a> to the tests projects, that are now compiled with nullable reference types enabled!</p>]]></content>
    
    <summary type="html">
    
      In this post, learn more about the changes introduced in OpenIddict 3.0 beta6.
    
    </summary>
    
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
      <category term="owin" scheme="https://kevinchalet.com/tags/owin/"/>
    
  </entry>
  
  <entry>
    <title>Using the OrchardCore OpenID management feature with an existing OpenIddict deployment</title>
    <link href="https://kevinchalet.com/2020/10/03/using-the-orchardcore-openid-management-feature-with-an-existing-openiddict-deployment/"/>
    <id>https://kevinchalet.com/2020/10/03/using-the-orchardcore-openid-management-feature-with-an-existing-openiddict-deployment/</id>
    <published>2020-10-03T19:45:00.000Z</published>
    <updated>2021-07-29T18:32:21.000Z</updated>
    
    <content type="html"><![CDATA[<p>With the recent announcement that <a href="https://leastprivilege.com/2020/10/01/the-future-of-identityserver/" target="_blank" rel="external">IdentityServer is transitioning to a commercial model</a>, some of you may already be looking for alternatives. <strong>I&#39;d like to reiterate that I encourage every IdentityServer user to keep using it</strong> <em>and pay for it</em>, but I also realize that not everyone will be able to afford an IdentityServer licence (whose cheapest plan is $1,500/year). As such, it was not a surprise to receive many mails from IdentityServer users evaluating their options soon after the announcement.</p><p>Even though both implement the OpenID Connect and OAuth 2.0 specifications, OpenIddict and IdentityServer4 are very different under the hood and have different approaches: IdentityServer4 was designed as a general and ready-to-use identity provider for ASP.NET Core while OpenIddict 3.0 aims at offering a fully modular OpenID Connect server and validation stack that can be used not only on ASP.NET Core 2.1 (.NET Framework and .NET Core), 3.1 and 5.0, but also in legacy ASP.NET &gt;= 4.6.1 applications using OWIN/Katana.</p><p>Internally, the two server stacks don&#39;t have much in common: IdentityServer4 uses a traditional and linear services-based approach (with request/token validators and response generators) while OpenIddict 3.0 uses a completely different model based on events and asynchronous events handlers: each handler is responsible of a very specific task (e.g ensuring a <code>redirect_uri</code> is not malformed and doesn&#39;t include a fragment) and all the handlers can be removed, reordered and even replaced by custom handlers for those who need to override the default logic used by OpenIddict 3.0.</p><p>Unlike IdentityServer4, OpenIddict doesn&#39;t ship with any membership stack integration, which is something developers must implement themselves. In the OpenIddict world, this is typically done by adding an authorization controller that serves as bridge between the user authentication part (generally implemented using ASP.NET Core Identity) and the OpenID Connect authorization part. Concretely, OpenIddict only knows about <code>ClaimsPrincipal</code> instances and you&#39;re responsible of providing <code>ClaimsPrincipal</code>s containing the claims you want OpenIddict to use in your tokens.</p><p>You probably guessed it by now: OpenIddict 3.0 is very different from IdentityServer4 and those hoping for a free look-alike will likely be disappointed. Fortunately, implementing an OpenID Connect server using OpenIddict is not hard, and generally consists in copying a few files from the <a href="https://github.com/openiddict/openiddict-samples" target="_blank" rel="external">openiddict-samples</a> repository.</p><p>There&#39;s however something that IdentityServer4 users will likely miss: an equivalent of the popular <code>Skoruba.IdentityServer4.Admin</code> project that offers a GUI to manage users, roles, clients and authorizations. While there&#39;s indeed no direct equivalent in OpenIddict itself, the OrchardCore project has a native OpenID module based on OpenIddict that includes a clients management feature. This feature is still young and quite limited (read, not as complete as <code>Skoruba.IdentityServer4.Admin</code>), but it&#39;s actively developed and contributions are always welcome.</p><p>In this blog post, I&#39;ll demonstrate how this management GUI can be used with an existing OpenIddict server (typically located in another project).</p><a id="more"></a><p>So here we go:</p><h3 id="Download-the-OpenIddict-samples-solution"><a href="#Download-the-OpenIddict-samples-solution" class="headerlink" title="Download the OpenIddict samples solution"></a>Download the OpenIddict samples solution</h3><p>For that, clone the <code>https://github.com/openiddict/openiddict-samples</code> repository.</p><p>For this tutorial, we&#39;ll use the <a href="https://github.com/openiddict/openiddict-samples/tree/dev/samples/Velusia/Velusia.Server" target="_blank" rel="external">Velusia.Server</a> demo, a very typical ASP.NET Core application that implements code flow support. Launch it via Visual Studio or using <code>dotnet run</code> and make sure the <code>openiddict-velusia-sample</code> database is correctly created by Entity Framework Core in your local <code>(localdb)\MSSQLLocalDB</code> SQL Server instance.</p><h3 id="Create-a-new-empty-ASP-NET-Core-project-reference-the-OrchardCore-CMS-metapackage-and-register-the-OrchardCore-services-and-middleware"><a href="#Create-a-new-empty-ASP-NET-Core-project-reference-the-OrchardCore-CMS-metapackage-and-register-the-OrchardCore-services-and-middleware" class="headerlink" title="Create a new empty ASP.NET Core project, reference the OrchardCore CMS metapackage and register the OrchardCore services and middleware"></a>Create a new empty ASP.NET Core project, reference the OrchardCore CMS metapackage and register the OrchardCore services and middleware</h3><p>To simplify things, we&#39;ll opt for the &quot;complete&quot; OrchardCore CMS approach, that offers a GUI-based setup experience. For that, reference the following package:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">Project</span> <span class="attr">Sdk</span>=<span class="string">"Microsoft.NET.Sdk.Web"</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">PropertyGroup</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">TargetFramework</span>&gt;</span>net5.0<span class="tag">&lt;/<span class="name">TargetFramework</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">PropertyGroup</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">ItemGroup</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"OrchardCore.Application.Cms.Targets"</span> <span class="attr">Version</span>=<span class="string">"1.0.0"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">ItemGroup</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;/<span class="name">Project</span>&gt;</span></div></pre></td></tr></table></figure><p>Then, update your <code>Startup</code> class to register the OrchardCore CMS services and middleware:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Builder;</div><div class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Hosting;</div><div class="line"><span class="keyword">using</span> Microsoft.Extensions.DependencyInjection;</div><div class="line"><span class="keyword">using</span> Microsoft.Extensions.Hosting;</div><div class="line"></div><div class="line"><span class="keyword">namespace</span> <span class="title">OrchardCoreDemo</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Startup</span></div><div class="line">    &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">ConfigureServices</span>(<span class="params">IServiceCollection services</span>)</span></div><div class="line">        &#123;</div><div class="line">            services.AddOrchardCms();</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configure</span>(<span class="params">IApplicationBuilder app, IWebHostEnvironment env</span>)</span></div><div class="line">        &#123;</div><div class="line">            <span class="keyword">if</span> (env.IsDevelopment())</div><div class="line">            &#123;</div><div class="line">                app.UseDeveloperExceptionPage();</div><div class="line">            &#125;</div><div class="line"></div><div class="line">            app.UseOrchardCore();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>At this point, you&#39;re ready to launch the application and configure OrchardCore:</p><img src="/2020/10/03/using-the-orchardcore-openid-management-feature-with-an-existing-openiddict-deployment/setup-screen.png" alt="setup-screen.png"><p>Fill in the form and press &quot;finish setup&quot;.</p><div class="note tip"><p>The database configured at this stage will only be used by OrchardCore. We&#39;ll later configure the OpenID module to use Entity Framework Core and SQL Server with the database created by the <code>Velusia.Server</code> demo application.</p></div><h3 id="Enable-the-OpenID-management-feature-in-the-features-admin"><a href="#Enable-the-OpenID-management-feature-in-the-features-admin" class="headerlink" title="Enable the OpenID management feature in the features admin"></a>Enable the OpenID management feature in the features admin</h3><p>Once set up, log in and click on &quot;Dashboard&quot; to access the administration interface. Then, click on &quot;Features&quot; under &quot;Configuration&quot;:</p><img src="/2020/10/03/using-the-orchardcore-openid-management-feature-with-an-existing-openiddict-deployment/features-menu.png" alt="features-menu.png"><p>In the features admin, type &quot;OpenID&quot; to filter the available features and enable the &quot;OpenID management interface&quot; feature:</p><img src="/2020/10/03/using-the-orchardcore-openid-management-feature-with-an-existing-openiddict-deployment/features-admin.png" alt="features-admin.png"><h3 id="Reference-the-OpenIddict-Entity-Framework-Core-stores-and-register-them"><a href="#Reference-the-OpenIddict-Entity-Framework-Core-stores-and-register-them" class="headerlink" title="Reference the OpenIddict Entity Framework Core stores and register them"></a>Reference the OpenIddict Entity Framework Core stores and register them</h3><p>At this stage, if you visit the client applications administration page, you should see an empty list:</p><img src="/2020/10/03/using-the-orchardcore-openid-management-feature-with-an-existing-openiddict-deployment/empty-applications-list.png" alt="empty-applications-list.png"><p>That&#39;s expected: by default, the management interface points to the local OrchardCore database and not (yet) to <code>openiddict-velusia-sample</code>.</p><p>To fix that, we&#39;ll need to reference the OpenIddict Entity Framework Core package, so that the management UI can directly communicate with the <code>openiddict-velusia-sample</code> database. For that, reference the following packages:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">Project</span> <span class="attr">Sdk</span>=<span class="string">"Microsoft.NET.Sdk.Web"</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">PropertyGroup</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">TargetFramework</span>&gt;</span>net5.0<span class="tag">&lt;/<span class="name">TargetFramework</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">PropertyGroup</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">ItemGroup</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"Microsoft.EntityFrameworkCore.SqlServer"</span> <span class="attr">Version</span>=<span class="string">"5.0.0"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"OpenIddict.EntityFrameworkCore"</span> <span class="attr">Version</span>=<span class="string">"3.1.0"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"OrchardCore.Application.Cms.Targets"</span> <span class="attr">Version</span>=<span class="string">"1.0.0"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">ItemGroup</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;/<span class="name">Project</span>&gt;</span></div></pre></td></tr></table></figure><p>Final part: adding an EF Core <code>DbContext</code> pointing to the <code>openiddict-velusia-sample</code> database and registering the OpenIddict EF Core stores using the OrchardCore APIs:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Builder;</div><div class="line"><span class="keyword">using</span> Microsoft.AspNetCore.Hosting;</div><div class="line"><span class="keyword">using</span> Microsoft.EntityFrameworkCore;</div><div class="line"><span class="keyword">using</span> Microsoft.Extensions.DependencyInjection;</div><div class="line"><span class="keyword">using</span> Microsoft.Extensions.Hosting;</div><div class="line"></div><div class="line"><span class="keyword">namespace</span> <span class="title">OrchardCoreDemo</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">MyContext</span> : <span class="title">DbContext</span></div><div class="line">    &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="title">MyContext</span>(<span class="params">DbContextOptions&lt;MyContext&gt; options</span>)</span></div><div class="line">            : <span class="title">base</span>(<span class="params">options</span>)</div><div class="line">        &#123;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Startup</span></div><div class="line">    &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">ConfigureServices</span>(<span class="params">IServiceCollection services</span>)</span></div><div class="line">        &#123;</div><div class="line">            services.AddOrchardCms()</div><div class="line">                .ConfigureServices(services =&gt;</div><div class="line">                &#123;</div><div class="line">                    services.AddDbContext&lt;MyContext&gt;(options =&gt;</div><div class="line">                    &#123;</div><div class="line">                        options.UseSqlServer(<span class="string">"Server=(localdb)\\mssqllocaldb;Database=openiddict-velusia-sample;Trusted_Connection=True;MultipleActiveResultSets=true"</span>);</div><div class="line">                        options.UseOpenIddict();</div><div class="line">                    &#125;);</div><div class="line"></div><div class="line">                    services.AddOpenIddict()</div><div class="line">                        .AddCore()</div><div class="line">                        .UseEntityFrameworkCore()</div><div class="line">                        .UseDbContext&lt;MyContext&gt;();</div><div class="line">                &#125;, order: <span class="number">10000</span>);</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configure</span>(<span class="params">IApplicationBuilder app, IWebHostEnvironment env</span>)</span></div><div class="line">        &#123;</div><div class="line">            <span class="keyword">if</span> (env.IsDevelopment())</div><div class="line">            &#123;</div><div class="line">                app.UseDeveloperExceptionPage();</div><div class="line">            &#125;</div><div class="line"></div><div class="line">            app.UseOrchardCore();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>Restart the application and visit the applications administration page. If everything is correctly configured, you should see the <code>MVC client application</code> entry corresponding to the <code>Velusia.Client</code> demo application registered by <code>Velusia.Server</code>.</p><img src="/2020/10/03/using-the-orchardcore-openid-management-feature-with-an-existing-openiddict-deployment/applications-list.png" alt="applications-list.png"><p>From here, you can easily update the application entry by clicking on &quot;Edit&quot;:</p><img src="/2020/10/03/using-the-orchardcore-openid-management-feature-with-an-existing-openiddict-deployment/application-edit-view.png" alt="application-edit-view.png"><p>Voilà!</p>]]></content>
    
    <summary type="html">
    
      In this post, discover how to use OrchardCore&#39;s OpenID module with an existing server.
    
    </summary>
    
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
      <category term="cms" scheme="https://kevinchalet.com/tags/cms/"/>
    
      <category term="orchard" scheme="https://kevinchalet.com/tags/orchard/"/>
    
      <category term="orchardcore" scheme="https://kevinchalet.com/tags/orchardcore/"/>
    
  </entry>
  
  <entry>
    <title>Introducing Quartz.NET support and new languages in OpenIddict 3.0 beta4</title>
    <link href="https://kevinchalet.com/2020/10/02/introducing-quartz-net-support-and-new-languages-in-openiddict-3-0-beta4/"/>
    <id>https://kevinchalet.com/2020/10/02/introducing-quartz-net-support-and-new-languages-in-openiddict-3-0-beta4/</id>
    <published>2020-10-02T14:00:00.000Z</published>
    <updated>2020-11-02T17:27:20.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="What-changed"><a href="#What-changed" class="headerlink" title="What changed?"></a>What changed?</h2><p>All the changes introduced in this release can be found <a href="https://github.com/openiddict/openiddict-core/issues?q=milestone%3A3.0.0-beta4" target="_blank" rel="external">on GitHub</a>, but here&#39;s a recap of the most important ones:</p><h3 id="OpenIddict-now-ships-with-a-native-Quartz-NET-3-2-integration"><a href="#OpenIddict-now-ships-with-a-native-Quartz-NET-3-2-integration" class="headerlink" title="OpenIddict now ships with a native Quartz.NET 3.2 integration"></a>OpenIddict now ships with a native Quartz.NET 3.2 integration</h3><p>To help with housecleaning and remove authorization and token entries that are no longer valid from the database, OpenIddict now comes with a new <code>OpenIddict.Quartz</code> plugin that you can enable in a few lines of code.</p><p>First, you need to register the Quartz.NET services and configure it to use DI and an in-memory store:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">services.AddQuartz(options =&gt;</div><div class="line">&#123;</div><div class="line">    options.UseMicrosoftDependencyInjectionJobFactory();</div><div class="line">    options.UseSimpleTypeLoader();</div><div class="line">    options.UseInMemoryStore();</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>Then, you need to enable the Quartz/.NET generic host integration.</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">services.AddQuartzHostedService(options =&gt; options.WaitForJobsToComplete = <span class="literal">true</span>);</div></pre></td></tr></table></figure><p>Finally, you&#39;ll need to register the OpenIddict job:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddCore(options =&gt; options.UseQuartz());</div></pre></td></tr></table></figure><p>Once registered, the OpenIddict job will start removing invalid entries approximately 2 minutes after the application starts and will do that every hour (assuming your app is still running).</p><div class="note tip"><p>To keep track of recently expired entries, the OpenIddict job will only remove authorizations and tokens whose lifespan is of at least 14 days. This value can be changed via <code>OpenIddictQuartzBuilder</code> but cannot be less than 10 minutes.</p></div><p><strong>Special thanks to Quartz.NET&#39;s maintainer, <a href="https://twitter.com/markolahma" target="_blank" rel="external">Marko Lahma</a></strong>, for reviewing the PR that added this feature and for the great job he did with Quartz.NET 3.2 (that was released earlier today).</p><h3 id="OpenIddict-now-speaks-12-languages"><a href="#OpenIddict-now-speaks-12-languages" class="headerlink" title="OpenIddict now speaks 12 languages!"></a>OpenIddict now speaks 12 languages!</h3><p>Thanks to fantastic contributions from the community, 10 additional languages have been added in this release:</p><table><thead><tr><th>Language</th><th>Contributor</th></tr></thead><tbody><tr><td>Arabic</td><td><a href="https://github.com/hishamco" target="_blank" rel="external">Hisham Bin Ateya</a></td></tr><tr><td>Deutsch</td><td><a href="https://github.com/johanndev" target="_blank" rel="external">Johann Wimmer</a></td></tr><tr><td>Dutch</td><td><a href="https://github.com/maartenba" target="_blank" rel="external">Maarten Balliauw</a></td></tr><tr><td>Gujarati</td><td><a href="https://github.com/a-patel" target="_blank" rel="external">Ashish Patel</a></td></tr><tr><td>Hindi</td><td><a href="https://github.com/a-patel" target="_blank" rel="external">Ashish Patel</a></td></tr><tr><td>Italian</td><td><a href="https://github.com/ilmax" target="_blank" rel="external">Massimiliano Donini</a></td></tr><tr><td>Simplified Chinese</td><td><a href="https://github.com/kinosang" target="_blank" rel="external">Chino Chang</a></td></tr><tr><td>Spanish</td><td><a href="https://github.com/Bartmax" target="_blank" rel="external">Bart Calixto</a></td></tr><tr><td>Traditional Chinese</td><td><a href="https://github.com/kinosang" target="_blank" rel="external">Chino Chang</a></td></tr><tr><td>Turkish</td><td><a href="https://github.com/serkanz" target="_blank" rel="external">Serkan Zengin</a></td></tr></tbody></table><h3 id="options-EnableAuthorizationEndpointCaching-and-options-EnableLogoutEndpointCaching-have-a-new-name"><a href="#options-EnableAuthorizationEndpointCaching-and-options-EnableLogoutEndpointCaching-have-a-new-name" class="headerlink" title="options.EnableAuthorizationEndpointCaching() and options.EnableLogoutEndpointCaching() have a new name"></a><code>options.EnableAuthorizationEndpointCaching()</code> and <code>options.EnableLogoutEndpointCaching()</code> have a new name</h3><p>Feedback indicated that <code>options.EnableAuthorizationEndpointCaching()</code> and <code>options.EnableLogoutEndpointCaching()</code>&#39;s current name was not ideal. To fix that, these methods were renamed to <code>options.EnableAuthorizationRequestCaching()</code> and <code>options.EnableLogoutRequestCaching()</code>.</p><p><strong>This post is also a good opportunity to thank <a href="https://github.com/sebastienros" target="_blank" rel="external">Sébastien Ros</a>, <a href="https://github.com/GDreyV" target="_blank" rel="external">Andrew</a> and <a href="https://github.com/mridentity" target="_blank" rel="external">Mr.i</a></strong>, who now <a href="https://github.com/sponsors/kevinchalet" target="_blank" rel="external">sponsor me on GitHub</a>. Thank you very much!</p>]]></content>
    
    <summary type="html">
    
      In this post, learn more about the changes introduced in OpenIddict 3.0 beta4.
    
    </summary>
    
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
  </entry>
  
  <entry>
    <title>Introducing localization support in OpenIddict 3.0 beta3</title>
    <link href="https://kevinchalet.com/2020/08/03/introducing-localization-support-in-openiddict-3-0-beta3/"/>
    <id>https://kevinchalet.com/2020/08/03/introducing-localization-support-in-openiddict-3-0-beta3/</id>
    <published>2020-08-03T17:30:00.000Z</published>
    <updated>2020-11-02T17:27:11.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="What-changed"><a href="#What-changed" class="headerlink" title="What changed?"></a>What changed?</h2><p>All the changes introduced in this release can be found <a href="https://github.com/openiddict/openiddict-core/issues?q=milestone%3A3.0.0-beta3" target="_blank" rel="external">on GitHub</a>, but here&#39;s a recap of the most important ones:</p><h3 id="The-core-server-and-validation-features-now-offer-built-in-localization-support"><a href="#The-core-server-and-validation-features-now-offer-built-in-localization-support" class="headerlink" title="The core, server and validation features now offer built-in localization support"></a>The core, server and validation features now offer built-in localization support</h3><p>As announced in a previous post, <strong>OpenIddict was fully updated to use the <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization" target="_blank" rel="external">.NET Platform localization extensions</a> to return localized error descriptions and validation messages</strong>, which will be particularly useful in multilingual projects like <a href="https://github.com/OrchardCMS/OrchardCore" target="_blank" rel="external">Orchard Core</a>.</p><p>At the time of writing, <strong>OpenIddict 3.0 beta3 comes with English and French resources</strong>, but additional languages will likely be added in the next versions.</p><p>To enable localization support in ASP.NET Core, simply register the request localization middleware (ideally, put it at the top of your <code>Configure(IApplicationBuilder app)</code> method):</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">app.UseRequestLocalization(options =&gt;</div><div class="line">&#123;</div><div class="line">    options.AddSupportedCultures(<span class="string">"en-US"</span>, <span class="string">"fr-FR"</span>);</div><div class="line">    options.AddSupportedUICultures(<span class="string">"en-US"</span>, <span class="string">"fr-FR"</span>);</div><div class="line">    options.SetDefaultCulture(<span class="string">"en-US"</span>);</div><div class="line">&#125;);</div></pre></td></tr></table></figure><div class="note tip"><p>ASP.NET 4.x users can enable globalization support <a href="https://docs.microsoft.com/en-us/previous-versions/bz9tc508(v=vs.140" target="_blank" rel="external">in web.config by setting <code>uiCulture</code> and <code>culture</code> to <code>auto</code></a>. Alternatively, ASP.NET 4.x and OWIN users can use <a href="https://github.com/pableess/dotnet-localization-ex" target="_blank" rel="external">Owin.Localization</a>, which is a backport of the ASP.NET Core middleware.</p></div><p>Conversely, developers who don&#39;t need localization support and prefer keeping their deployments as small as possible can remove the localized satellite assemblies by simply adding <code>&lt;SatelliteResourceLanguages&gt;en&lt;/SatelliteResourceLanguages&gt;</code> in their <code>.csproj</code> file.</p><h3 id="OpenIddict-is-now-decorated-with-nullable-reference-types-annotations"><a href="#OpenIddict-is-now-decorated-with-nullable-reference-types-annotations" class="headerlink" title="OpenIddict is now decorated with nullable reference types annotations"></a>OpenIddict is now decorated with nullable reference types annotations</h3><p>Following a general trend across the .NET community, <strong>all the OpenIddict libraries are now compiled with nullable reference types support enabled</strong>.</p><div class="note info"><p>Due to a bug in the Roslyn compiler, the non-generic <code>IOpenIddictApplicationManager</code>, <code>IOpenIddictAuthorizationManager</code>, <code>IOpenIddictScopeManager</code> and <code>IOpenIddictTokenManager</code> interfaces deliberately don&#39;t expose any nullable annotation. You can track the resolution of this bug <a href="https://github.com/dotnet/roslyn/issues/46494" target="_blank" rel="external">here</a>.</p></div><h3 id="Authorizations-are-no-longer-revoked-when-detecting-a-rolling-refresh-token-replay"><a href="#Authorizations-are-no-longer-revoked-when-detecting-a-rolling-refresh-token-replay" class="headerlink" title="Authorizations are no longer revoked when detecting a rolling refresh token replay"></a>Authorizations are no longer revoked when detecting a rolling refresh token replay</h3><p>In previous versions of OpenIddict, sending a rolling refresh token twice always resulted in the revocation of the entire tokens chain and the revocation of the associated authorization. To make the rolling tokens option more convenient to use, <strong>the authorization associated to a refresh token is no longer revoked when it is sent multiple times</strong>.</p><h3 id="DisableSlidingExpiration-was-renamed-to-DisableSlidingRefreshTokenExpiration"><a href="#DisableSlidingExpiration-was-renamed-to-DisableSlidingRefreshTokenExpiration" class="headerlink" title="DisableSlidingExpiration() was renamed to DisableSlidingRefreshTokenExpiration()"></a><code>DisableSlidingExpiration()</code> was renamed to <code>DisableSlidingRefreshTokenExpiration()</code></h3><p>To make clearer the fact the sliding expiration mechanism applies to refresh tokens, the <code>DisableSlidingExpiration()</code> helper was renamed to <code>DisableSlidingRefreshTokenExpiration()</code> in OpenIddict 3.0 beta3.</p>]]></content>
    
    <summary type="html">
    
      In this post, learn more about the changes introduced in OpenIddict 3.0 beta3.
    
    </summary>
    
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
      <category term="owin" scheme="https://kevinchalet.com/tags/owin/"/>
    
  </entry>
  
  <entry>
    <title>OpenIddict 3.0 beta2 is out</title>
    <link href="https://kevinchalet.com/2020/07/08/openiddict-3-0-beta2-is-out/"/>
    <id>https://kevinchalet.com/2020/07/08/openiddict-3-0-beta2-is-out/</id>
    <published>2020-07-08T17:30:00.000Z</published>
    <updated>2020-11-02T17:27:05.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="What-changed"><a href="#What-changed" class="headerlink" title="What changed?"></a>What changed?</h2><p>All the changes introduced in this release can be found <a href="https://github.com/openiddict/openiddict-core/issues?q=milestone%3A3.0.0-beta2" target="_blank" rel="external">on GitHub</a>, but here&#39;s a recap of the most important ones:</p><h3 id="System-Linq-Async-was-removed-from-the-OpenIddict-Core-dependencies"><a href="#System-Linq-Async-was-removed-from-the-OpenIddict-Core-dependencies" class="headerlink" title="System.Linq.Async was removed from the OpenIddict.Core dependencies"></a><code>System.Linq.Async</code> was removed from the <code>OpenIddict.Core</code> dependencies</h3><p>Due to <strong>a namespace conflict between Entity Framework Core and <code>System.Linq.Async</code></strong> (maintained by the RX team), we had to remove the dependency the <code>OpenIddict.Core</code> package had on this package, as explained <a href="https://github.com/openiddict/openiddict-core/issues/980" target="_blank" rel="external">in this GitHub thread</a>.</p><p>This is not specific to OpenIddict and other popular libraries like <code>Stripe.NET</code> <a href="https://github.com/stripe/stripe-dotnet/issues/1974" target="_blank" rel="external">had to make a similar decision</a>. As such, <strong>I strongly encourage the EF and RX teams to work together to find a better solution to this issue</strong>, that has a serious impact on the .NET ecosystem.</p><h3 id="The-automatic-initialization-logic-was-removed-from-the-MongoDB-integration"><a href="#The-automatic-initialization-logic-was-removed-from-the-MongoDB-integration" class="headerlink" title="The automatic initialization logic was removed from the MongoDB integration"></a>The automatic initialization logic was removed from the MongoDB integration</h3><p>In OpenIddict 1.x/2.x, the 4 collections used by the MongoDB stores were automatically initialized during the first use of the stores.</p><p>In OpenIddict 3.0 beta2, <strong>this logic was removed for performance reasons and consistency</strong> with the other stores. Instead, MongoDB users are encouraged to initialize their database once using a setup script. The following C# script can be used to initialize the 4 OpenIddict collections:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">using</span> System.Threading;</div><div class="line"><span class="keyword">using</span> System.Threading.Tasks;</div><div class="line"><span class="keyword">using</span> Microsoft.Extensions.DependencyInjection;</div><div class="line"><span class="keyword">using</span> Microsoft.Extensions.Options;</div><div class="line"><span class="keyword">using</span> MongoDB.Driver;</div><div class="line"><span class="keyword">using</span> OpenIddict.MongoDb;</div><div class="line"><span class="keyword">using</span> OpenIddict.MongoDb.Models;</div><div class="line"></div><div class="line"><span class="keyword">namespace</span> <span class="title">MongoDbSetup</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">Program</span></div><div class="line">    &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">async</span> Task <span class="title">Main</span>(<span class="params"><span class="keyword">string</span>[] args</span>)</span></div><div class="line">        &#123;</div><div class="line">            <span class="keyword">var</span> services = <span class="keyword">new</span> ServiceCollection();</div><div class="line">            services.AddOpenIddict()</div><div class="line">                .AddCore(options =&gt; options.UseMongoDb());</div><div class="line"></div><div class="line">            services.AddSingleton(<span class="keyword">new</span> MongoClient(<span class="string">"mongodb://localhost:27017"</span>).GetDatabase(<span class="string">"openiddict"</span>));</div><div class="line"></div><div class="line">            <span class="keyword">var</span> provider = services.BuildServiceProvider();</div><div class="line">            <span class="keyword">var</span> context = provider.GetRequiredService&lt;IOpenIddictMongoDbContext&gt;();</div><div class="line">            <span class="keyword">var</span> options = provider.GetRequiredService&lt;IOptionsMonitor&lt;OpenIddictMongoDbOptions&gt;&gt;().CurrentValue;</div><div class="line">            <span class="keyword">var</span> database = <span class="keyword">await</span> context.GetDatabaseAsync(CancellationToken.None);</div><div class="line"></div><div class="line">            <span class="keyword">var</span> applications = database.GetCollection&lt;OpenIddictMongoDbApplication&gt;(options.ApplicationsCollectionName);</div><div class="line">            <span class="keyword">await</span> applications.Indexes.CreateManyAsync(<span class="keyword">new</span>[]</div><div class="line">            &#123;</div><div class="line">                <span class="keyword">new</span> CreateIndexModel&lt;OpenIddictMongoDbApplication&gt;(</div><div class="line">                    Builders&lt;OpenIddictMongoDbApplication&gt;.IndexKeys.Ascending(application =&gt; application.ClientId),</div><div class="line">                    <span class="keyword">new</span> CreateIndexOptions</div><div class="line">                    &#123;</div><div class="line">                        Unique = <span class="literal">true</span></div><div class="line">                    &#125;),</div><div class="line"></div><div class="line">                <span class="keyword">new</span> CreateIndexModel&lt;OpenIddictMongoDbApplication&gt;(</div><div class="line">                    Builders&lt;OpenIddictMongoDbApplication&gt;.IndexKeys.Ascending(application =&gt; application.PostLogoutRedirectUris),</div><div class="line">                    <span class="keyword">new</span> CreateIndexOptions</div><div class="line">                    &#123;</div><div class="line">                        Background = <span class="literal">true</span></div><div class="line">                    &#125;),</div><div class="line"></div><div class="line">                <span class="keyword">new</span> CreateIndexModel&lt;OpenIddictMongoDbApplication&gt;(</div><div class="line">                    Builders&lt;OpenIddictMongoDbApplication&gt;.IndexKeys.Ascending(application =&gt; application.RedirectUris),</div><div class="line">                    <span class="keyword">new</span> CreateIndexOptions</div><div class="line">                    &#123;</div><div class="line">                        Background = <span class="literal">true</span></div><div class="line">                    &#125;)</div><div class="line">            &#125;);</div><div class="line"></div><div class="line">            <span class="keyword">var</span> authorizations = database.GetCollection&lt;OpenIddictMongoDbAuthorization&gt;(options.AuthorizationsCollectionName);</div><div class="line">            <span class="keyword">await</span> authorizations.Indexes.CreateOneAsync(<span class="keyword">new</span> CreateIndexModel&lt;OpenIddictMongoDbAuthorization&gt;(</div><div class="line">                Builders&lt;OpenIddictMongoDbAuthorization&gt;.IndexKeys</div><div class="line">                    .Ascending(authorization =&gt; authorization.ApplicationId)</div><div class="line">                    .Ascending(authorization =&gt; authorization.Scopes)</div><div class="line">                    .Ascending(authorization =&gt; authorization.Status)</div><div class="line">                    .Ascending(authorization =&gt; authorization.Subject)</div><div class="line">                    .Ascending(authorization =&gt; authorization.Type),</div><div class="line">                <span class="keyword">new</span> CreateIndexOptions</div><div class="line">                &#123;</div><div class="line">                    Background = <span class="literal">true</span></div><div class="line">                &#125;));</div><div class="line"></div><div class="line">            <span class="keyword">var</span> scopes = database.GetCollection&lt;OpenIddictMongoDbScope&gt;(options.ScopesCollectionName);</div><div class="line">            <span class="keyword">await</span> scopes.Indexes.CreateOneAsync(<span class="keyword">new</span> CreateIndexModel&lt;OpenIddictMongoDbScope&gt;(</div><div class="line">                Builders&lt;OpenIddictMongoDbScope&gt;.IndexKeys.Ascending(scope =&gt; scope.Name),</div><div class="line">                <span class="keyword">new</span> CreateIndexOptions</div><div class="line">                &#123;</div><div class="line">                    Unique = <span class="literal">true</span></div><div class="line">                &#125;));</div><div class="line"></div><div class="line">            <span class="keyword">var</span> tokens = database.GetCollection&lt;OpenIddictMongoDbToken&gt;(options.TokensCollectionName);</div><div class="line">            <span class="keyword">await</span> tokens.Indexes.CreateManyAsync(<span class="keyword">new</span>[]</div><div class="line">            &#123;</div><div class="line">                <span class="keyword">new</span> CreateIndexModel&lt;OpenIddictMongoDbToken&gt;(</div><div class="line">                    Builders&lt;OpenIddictMongoDbToken&gt;.IndexKeys.Ascending(token =&gt; token.ReferenceId),</div><div class="line">                    <span class="keyword">new</span> CreateIndexOptions&lt;OpenIddictMongoDbToken&gt;</div><div class="line">                    &#123;</div><div class="line">                        <span class="comment">// Note: partial filter expressions are not supported on Azure Cosmos DB.</span></div><div class="line">                        <span class="comment">// As a workaround, the expression and the unique constraint can be removed.</span></div><div class="line">                        PartialFilterExpression = Builders&lt;OpenIddictMongoDbToken&gt;.Filter.Exists(token =&gt; token.ReferenceId),</div><div class="line">                        Unique = <span class="literal">true</span></div><div class="line">                    &#125;),</div><div class="line"></div><div class="line">                <span class="keyword">new</span> CreateIndexModel&lt;OpenIddictMongoDbToken&gt;(</div><div class="line">                    Builders&lt;OpenIddictMongoDbToken&gt;.IndexKeys</div><div class="line">                        .Ascending(token =&gt; token.ApplicationId)</div><div class="line">                        .Ascending(token =&gt; token.Status)</div><div class="line">                        .Ascending(token =&gt; token.Subject)</div><div class="line">                        .Ascending(token =&gt; token.Type),</div><div class="line">                    <span class="keyword">new</span> CreateIndexOptions</div><div class="line">                    &#123;</div><div class="line">                        Background = <span class="literal">true</span></div><div class="line">                    &#125;)</div><div class="line">            &#125;);</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><a id="more"></a><h3 id="Authorization-codes-are-now-reference-tokens"><a href="#Authorization-codes-are-now-reference-tokens" class="headerlink" title="Authorization codes are now reference tokens"></a>Authorization codes are now reference tokens</h3><p>With <code>response_mode=form_post</code> being impacted by same-site, some applications are moving back to the query response mode (the default mode for the authorization code flow). To avoid token length issues with clients using <code>response_mode=query</code> and having strict query string limits, <strong>authorization codes are now reference tokens in OpenIddict 3.0 beta2</strong>.</p><p>As part of this change, the <code>options.UseReferenceTokens()</code> setting was also split into <code>options.UseReferenceAccessTokens()</code> and <code>options.UseReferenceRefreshTokens()</code> for more flexibility and <code>options.UseRollingTokens()</code> was renamed to <code>options.UseRollingRefreshTokens()</code> for clarity.</p><h3 id="The-OpenIddict-server-Data-Protection-integration-now-offers-more-options"><a href="#The-OpenIddict-server-Data-Protection-integration-now-offers-more-options" class="headerlink" title="The OpenIddict server Data Protection integration now offers more options"></a>The OpenIddict server Data Protection integration now offers more options</h3><p>In OpenIddict 3.0 beta1, the OpenIddict server Data Protection integration allowed you to opt out of the ASP.NET Core Data Protection token format when creating new tokens thanks to the <code>options.PreferDefaultTokenFormat()</code> option.</p><p>In beta2, this option was split into multiple options for more control over the token format (JWT or Data Protection) used to produce the different token types supported by OpenIddict:</p><ul><li><code>options.PreferDefaultAccessTokenFormat()</code>.</li><li><code>options.PreferDefaultAuthorizationCodeFormat()</code>.</li><li><code>options.PreferDefaultDeviceCodeFormat()</code>.</li><li><code>options.PreferDefaultRefreshTokenFormat()</code>.</li><li><code>options.PreferDefaultUserCodeFormat()</code>.</li></ul>]]></content>
    
    <summary type="html">
    
      In this post, learn more about the changes introduced in OpenIddict 3.0 beta2.
    
    </summary>
    
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
      <category term="owin" scheme="https://kevinchalet.com/tags/owin/"/>
    
  </entry>
  
  <entry>
    <title>Introducing OpenIddict 3.0 beta1</title>
    <link href="https://kevinchalet.com/2020/06/11/introducing-openiddict-3-0-beta1/"/>
    <id>https://kevinchalet.com/2020/06/11/introducing-openiddict-3-0-beta1/</id>
    <published>2020-06-11T19:00:00.000Z</published>
    <updated>2020-10-26T16:32:30.000Z</updated>
    
    <content type="html"><![CDATA[<p>For over a year now, <strong>I&#39;ve been working on what is for me the most exciting OpenIddict release: OpenIddict 3.0 beta1</strong>, whose server and validation features have been almost completely rewritten. You can find all the 3.0 beta1 packages on <a href="https://www.nuget.org/profiles/openiddict" target="_blank" rel="external">NuGet.org</a>.</p><h2 id="What-changed"><a href="#What-changed" class="headerlink" title="What changed?"></a>What changed?</h2><p>Some of the major changes introduced in this release were described in the <a href="https://github.com/openiddict/openiddict-core/issues/736" target="_blank" rel="external">OpenIddict 3.0 roadmap</a> (the detailed list <a href="https://github.com/openiddict/openiddict-core/issues?q=milestone%3A3.0.0-beta1" target="_blank" rel="external">can be found on GitHub</a>), but here&#39;s a recap of the most important ones:</p><h3 id="The-aspnet-contrib-OAuth-2-0-server-validation-introspection-handlers-and-OpenIddict-were-merged-into-a-single-codebase"><a href="#The-aspnet-contrib-OAuth-2-0-server-validation-introspection-handlers-and-OpenIddict-were-merged-into-a-single-codebase" class="headerlink" title="The aspnet-contrib OAuth 2.0 server/validation/introspection handlers and OpenIddict were merged into a single codebase"></a>The aspnet-contrib OAuth 2.0 server/validation/introspection handlers and OpenIddict were merged into a single codebase</h3><p><strong>This is the most important change of this release</strong>: ASOS – the low-level OpenID Connect server framework used in OpenIddict 1.0/2.0 – and the aspnet-contrib validation and introspection middleware were all merged into OpenIddict.</p><p>To ensure OpenIddict can be used as a replacement for ASOS (for which people usually write their own persistence layer, at least to validate things like <code>client_id</code> and <code>redirect_uri</code>), a new degraded mode was introduced to allow using the OpenIddict server components without all the additional logic that relies on the OpenIddict managers/stores (e.g client authentication, client validation, token storage).</p><p>For more information about this feature, read my previous post: <a href="/2020/02/18/creating-an-openid-connect-server-proxy-with-openiddict-3-0-s-degraded-mode/" title="Creating an OpenID Connect server proxy with OpenIddict 3.0's degraded mode">Creating an OpenID Connect server proxy with OpenIddict 3.0's degraded mode</a>.</p><h3 id="OpenIddict-has-been-decoupled-from-ASP-NET-Core-and-now-natively-supports-OWIN-Katana-and-ASP-NET-4-x"><a href="#OpenIddict-has-been-decoupled-from-ASP-NET-Core-and-now-natively-supports-OWIN-Katana-and-ASP-NET-4-x" class="headerlink" title="OpenIddict has been decoupled from ASP.NET Core and now natively supports OWIN/Katana and ASP.NET 4.x"></a>OpenIddict has been decoupled from ASP.NET Core and now natively supports OWIN/Katana and ASP.NET 4.x</h3><p>In OpenIddict 1.0/2.0, the core and the EF 6/EF Core/MongoDB stores were already decoupled from ASP.NET Core. <strong>In 3.0, the server and validation features have been revamped to avoid depending on ASP.NET Core</strong>. Instead, ASP.NET Core integration is now provided by separate packages named <code>OpenIddict.Server.AspNetCore</code> and <code>OpenIddict.Validation.AspNetCore</code>. For convenience, a metapackage named <code>OpenIddict.AspNetCore</code> can be referenced to import the OpenIddict core, server and validation packages and their ASP.NET Core integration with a single <code>PackageReference</code>.</p><p>Unlike the previous versions, <strong>OpenIddict 3.0 will also support multiple ASP.NET Core versions: 2.1, 3.1 and 5.0</strong>.</p><p>Decoupling OpenIddict from ASP.NET Core was also a great opportunity to make it natively compatible with OWIN/Katana, <strong>which will allow using its server or validation features in any ASP.NET (non-core) 4.6.1 application</strong>. OWIN/Katana integration is provided by the <code>OpenIddict.Server.Owin</code> and <code>OpenIddict.Validation.Owin</code> packages, which follows the same pattern as the ASP.NET Core hosts.</p><p>Here&#39;s the framework/runtime combinations that will be officially supported in 3.0:</p><table><thead><tr><th>Web framework version</th><th>.NET runtime version</th></tr></thead><tbody><tr><td>ASP.NET Core 2.1</td><td>.NET Framework 4.6.1</td></tr><tr><td>ASP.NET Core 2.1</td><td>.NET Framework 4.7.2</td></tr><tr><td>ASP.NET Core 2.1</td><td>.NET Framework 4.8</td></tr><tr><td>ASP.NET Core 2.1</td><td>.NET Core 2.1</td></tr><tr><td></td><td></td></tr><tr><td>ASP.NET Core 3.1</td><td>.NET Core 3.1</td></tr><tr><td></td><td></td></tr><tr><td>ASP.NET Core 5.0</td><td>.NET 5.0</td></tr><tr><td></td><td></td></tr><tr><td>OWIN/Katana 4.1</td><td>.NET Framework 4.6.1</td></tr><tr><td>OWIN/Katana 4.1</td><td>.NET Framework 4.7.2</td></tr><tr><td>OWIN/Katana 4.1</td><td>.NET Framework 4.8</td></tr></tbody></table><p>For more information about OWIN/Katana and ASP.NET 4.x support, read this dedicated post: <a href="/2020/03/03/adding-openiddict-3-0-to-an-owin-application/" title="Adding OpenIddict 3.0 to an OWIN application">Adding OpenIddict 3.0 to an OWIN application</a>.</p><a id="more"></a><h3 id="OpenIddict-now-uses-JSON-Web-Token-JWT-as-the-default-token-format"><a href="#OpenIddict-now-uses-JSON-Web-Token-JWT-as-the-default-token-format" class="headerlink" title="OpenIddict now uses JSON Web Token (JWT) as the default token format"></a>OpenIddict now uses JSON Web Token (JWT) as the default token format</h3><p>In OpenIddict 1.0/2.0, the ASP.NET Core Data Protection stack is always used to encrypt the authorization codes, refresh tokens and access tokens, unless you explicitly opt for JWT access tokens with <code>options.UseJsonWebTokens()</code>.</p><p>In 3.0, <strong>OpenIddict will now use encrypted JWT as the default token format for all the token types</strong>. Developers who need to disable access token encryption will be able to do so using <code>options.DisableAccessTokenEncryption()</code>:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        options.DisableAccessTokenEncryption();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><div class="note info"><p>Unlike previous versions, the OpenIddict 3.0 validation handler now supports JWT and introspection. <strong>Developers who use JWT access tokens in 2.0 and the JWT bearer middleware developed by Microsoft are strongly encouraged to move to the OpenIddict validation handler</strong>, that provides a simpler configuration story and includes dedicated logic to ensure tokens produced by OpenIddict 1.0/2.0 can still be used when migrating to OpenIddict 3.0.</p></div><p><strong>ASP.NET Core Data Protection tokens are still supported in 3.0</strong> and even recommended if you migrate from 1.0/2.0 (to ensure tokens produced by older versions can still be read after migrating to 3.0) or if you enable device flow support, as they provide additional protection against token leakage. To enable Data Protection support, simply call <code>options.UseDataProtection()</code> in both the server and validation options:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        options.UseDataProtection();</div><div class="line">    &#125;)</div><div class="line"></div><div class="line">    .AddValidation(options =&gt;</div><div class="line">    &#123;</div><div class="line">        options.UseDataProtection();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><div class="note tip"><p>ASP.NET Core Data Protection support is provided by the <code>OpenIddict.Server.DataProtection</code> and <code>OpenIddict.Validation.DataProtection</code> packages. These packages are referenced by the <code>OpenIddict.AspNetCore</code> metapackage but not by <code>OpenIddict.Owin</code> for naming and layering reasons.</p><p>Unlike the rest of ASP.NET Core, Data Protection is still compatible with the .NET Framework and thus can be used in ASP.NET 4.x applications. To enable ASP.NET Core Data Protection with the OWIN host, reference <code>OpenIddict.Server.DataProtection</code> and <code>OpenIddict.Validation.DataProtection</code>.</p></div><h3 id="JSON-NET-was-replaced-by-System-Text-Json"><a href="#JSON-NET-was-replaced-by-System-Text-Json" class="headerlink" title="JSON.NET was replaced by System.Text.Json"></a>JSON.NET was replaced by System.Text.Json</h3><p><strong>All the OpenIddict libraries have been updated to use <code>System.Text.Json</code></strong> (developed by Microsoft) instead of JSON.NET.</p><p>While it&#39;s a relatively new library (that still lacks features like <a href="https://github.com/dotnet/runtime/blob/master/src/libraries/System.Text.Json/docs/writable_json_dom_spec.md" target="_blank" rel="external">a writable DOM</a>), the performance boost it offers is impressive and makes <code>System.Text.Json</code> a fantastic candidate for replacing JSON.NET in OpenIddict.</p><p>Here&#39;s a tiny benchmark showing the difference between deserializing a <code>OpenIdConnectMessage</code> – the type used in OpenIddict 2.0 – with JSON.NET and deserializing its equivalent in 3.0 (where <code>OpenIddictMessage</code> was optimized and is now internally powered by <code>System.Text.Json</code>).</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line">[MarkdownExporterAttribute.GitHub, MemoryDiagnoser]</div><div class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Benchmark</span></div><div class="line">&#123;</div><div class="line">    [Benchmark(Baseline = <span class="literal">true</span>)]</div><div class="line">    <span class="function"><span class="keyword">public</span> OpenIdConnectMessage <span class="title">OpenIddict20</span>(<span class="params"></span>)</span></div><div class="line">    &#123;</div><div class="line">        <span class="keyword">return</span> JsonConvert.DeserializeObject&lt;OpenIdConnectMessage&gt;(<span class="string">@"&#123;</span></div><div class="line">  ""redirect_uris"": [</div><div class="line">    ""https://client.example.org/callback"",</div><div class="line">    ""https://client.example.org/callback2""</div><div class="line">  ],</div><div class="line">  ""client_name"": ""My Example Client"",</div><div class="line">  ""token_endpoint_auth_method"": ""client_secret_basic"",</div><div class="line">  ""logo_uri"": ""https://client.example.org/logo.png"",</div><div class="line">  ""jwks_uri"": ""https://client.example.org/my_public_keys.jwks"",</div><div class="line">  ""example_extension_parameter"": ""example_value""</div><div class="line">&#125;");</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    [Benchmark]</div><div class="line">    <span class="function"><span class="keyword">public</span> OpenIddictMessage <span class="title">OpenIddict30</span>(<span class="params"></span>)</span></div><div class="line">    &#123;</div><div class="line">        <span class="keyword">return</span> JsonSerializer.Deserialize&lt;OpenIddictMessage&gt;(<span class="string">@"&#123;</span></div><div class="line">  ""redirect_uris"": [</div><div class="line">    ""https://client.example.org/callback"",</div><div class="line">    ""https://client.example.org/callback2""</div><div class="line">  ],</div><div class="line">  ""client_name"": ""My Example Client"",</div><div class="line">  ""token_endpoint_auth_method"": ""client_secret_basic"",</div><div class="line">  ""logo_uri"": ""https://client.example.org/logo.png"",</div><div class="line">  ""jwks_uri"": ""https://client.example.org/my_public_keys.jwks"",</div><div class="line">  ""example_extension_parameter"": ""example_value""</div><div class="line">&#125;");</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>As you can see, the performance gain is excellent: not only it is 35% faster, but it also allocates much less memory!</p><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.264 (2004/?/20H1)</div><div class="line">Intel Core i9-9900K CPU 3.60GHz (Coffee Lake), 1 CPU, 16 logical and 8 physical cores</div><div class="line">.NET Core SDK=5.0.100-preview.4.20258.7</div><div class="line">  [Host]     : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT</div><div class="line">  DefaultJob : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT</div></pre></td></tr></table></figure><table><thead><tr><th>Method</th><th style="text-align:right">Mean</th><th style="text-align:right">Error</th><th style="text-align:right">StdDev</th><th style="text-align:right">Ratio</th><th style="text-align:right">Gen 0</th><th style="text-align:right">Gen 1</th><th style="text-align:right">Gen 2</th><th style="text-align:right">Allocated</th></tr></thead><tbody><tr><td>OpenIddict20</td><td style="text-align:right">4.510 μs</td><td style="text-align:right">0.0725 μs</td><td style="text-align:right">0.0642 μs</td><td style="text-align:right">1.00</td><td style="text-align:right">0.8469</td><td style="text-align:right">0.0229</td><td style="text-align:right">-</td><td style="text-align:right">6.92 KB</td></tr><tr><td>OpenIddict30</td><td style="text-align:right">2.948 μs</td><td style="text-align:right">0.0160 μs</td><td style="text-align:right">0.0149 μs</td><td style="text-align:right">0.65</td><td style="text-align:right">0.2136</td><td style="text-align:right">-</td><td style="text-align:right">-</td><td style="text-align:right">1.77 KB</td></tr></tbody></table><p>... and serialization is even more impressive!</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div></pre></td><td class="code"><pre><div class="line">[MarkdownExporterAttribute.GitHub, MemoryDiagnoser]</div><div class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Benchmark</span></div><div class="line">&#123;</div><div class="line">    [Benchmark(Baseline = <span class="literal">true</span>)]</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">string</span> <span class="title">OpenIddict20</span>(<span class="params"></span>)</span></div><div class="line">    &#123;</div><div class="line">        <span class="keyword">return</span> JsonConvert.SerializeObject(<span class="keyword">new</span> OpenIdConnectMessage</div><div class="line">        &#123;</div><div class="line">            [<span class="string">"redirect_uri"</span>] = <span class="keyword">new</span>[]</div><div class="line">            &#123;</div><div class="line">                <span class="string">"https://client.example.org/callback"</span>,</div><div class="line">                <span class="string">"https://client.example.org/callback2"</span></div><div class="line">            &#125;,</div><div class="line">            [<span class="string">"client_name"</span>] = <span class="string">"My Example Client"</span>,</div><div class="line">            [<span class="string">"token_endpoint_auth_method"</span>] = <span class="string">"client_secret_basic"</span>,</div><div class="line">            [<span class="string">"logo_uri"</span>] = <span class="string">"https://client.example.org/logo.png"</span>,</div><div class="line">            [<span class="string">"jwks_uri"</span>] = <span class="string">"https://client.example.org/my_public_keys.jwks"</span>,</div><div class="line">            [<span class="string">"example_extension_parameter"</span>] = <span class="string">"example_value"</span></div><div class="line">        &#125;);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    [Benchmark]</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">string</span> <span class="title">OpenIddict30</span>(<span class="params"></span>)</span></div><div class="line">    &#123;</div><div class="line">        <span class="keyword">return</span> JsonSerializer.Serialize(<span class="keyword">new</span> OpenIddictMessage</div><div class="line">        &#123;</div><div class="line">            [<span class="string">"redirect_uri"</span>] = <span class="keyword">new</span>[]</div><div class="line">            &#123;</div><div class="line">                <span class="string">"https://client.example.org/callback"</span>,</div><div class="line">                <span class="string">"https://client.example.org/callback2"</span></div><div class="line">            &#125;,</div><div class="line">            [<span class="string">"client_name"</span>] = <span class="string">"My Example Client"</span>,</div><div class="line">            [<span class="string">"token_endpoint_auth_method"</span>] = <span class="string">"client_secret_basic"</span>,</div><div class="line">            [<span class="string">"logo_uri"</span>] = <span class="string">"https://client.example.org/logo.png"</span>,</div><div class="line">            [<span class="string">"jwks_uri"</span>] = <span class="string">"https://client.example.org/my_public_keys.jwks"</span>,</div><div class="line">            [<span class="string">"example_extension_parameter"</span>] = <span class="string">"example_value"</span></div><div class="line">        &#125;);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.264 (2004/?/20H1)</div><div class="line">Intel Core i9-9900K CPU 3.60GHz (Coffee Lake), 1 CPU, 16 logical and 8 physical cores</div><div class="line">.NET Core SDK=5.0.100-preview.4.20258.7</div><div class="line">  [Host]     : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT</div><div class="line">  DefaultJob : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT</div></pre></td></tr></table></figure><table><thead><tr><th>Method</th><th style="text-align:right">Mean</th><th style="text-align:right">Error</th><th style="text-align:right">StdDev</th><th style="text-align:right">Ratio</th><th style="text-align:right">Gen 0</th><th style="text-align:right">Gen 1</th><th style="text-align:right">Gen 2</th><th style="text-align:right">Allocated</th></tr></thead><tbody><tr><td>OpenIddict20</td><td style="text-align:right">4.209 μs</td><td style="text-align:right">0.0667 μs</td><td style="text-align:right">0.0714 μs</td><td style="text-align:right">1.00</td><td style="text-align:right">0.9918</td><td style="text-align:right">0.0076</td><td style="text-align:right">-</td><td style="text-align:right">8.11 KB</td></tr><tr><td>OpenIddict30</td><td style="text-align:right">1.361 μs</td><td style="text-align:right">0.0257 μs</td><td style="text-align:right">0.0275 μs</td><td style="text-align:right">0.32</td><td style="text-align:right">0.1621</td><td style="text-align:right">-</td><td style="text-align:right">-</td><td style="text-align:right">1.34 KB</td></tr></tbody></table><div class="note warn"><p>In most cases, this change should be completely transparent, but if you manually use JSON.NET to serialize or deserialize <code>OpenIdConnectMessage</code>, <code>OpenIdConnectRequest</code> or <code>OpenIdConnectResponse</code> instances, consider moving to <code>System.Text.Json</code> when migrating to OpenIddict 3.0, as 3.0 no longer includes a built-in JSON.NET <code>JsonConverter</code> for these types.</p></div><h3 id="OpenIddict-now-supports-the-device-authorization-grant"><a href="#OpenIddict-now-supports-the-device-authorization-grant" class="headerlink" title="OpenIddict now supports the device authorization grant"></a>OpenIddict now supports the device authorization grant</h3><p>Standardized in 2019, <a href="https://tools.ietf.org/html/rfc8628" target="_blank" rel="external">the device authorization grant (aka device flow)</a> is now natively supported by OpenIddict 3.0.</p><div class="note warn"><p>Adding device flow support required making changes to the OpenIddict entities. This should be transparent for MongoDB users but Entity Framework 6 and Entity Framework Core users will have to create and apply a migration for the new schema to be reflected in the database.</p></div><h3 id="Enabling-reference-tokens-is-no-longer-required-to-use-immediate-access-token-revocation"><a href="#Enabling-reference-tokens-is-no-longer-required-to-use-immediate-access-token-revocation" class="headerlink" title="Enabling reference tokens is no longer required to use immediate access token revocation"></a>Enabling reference tokens is no longer required to use immediate access token revocation</h3><p>In 1.0/2.0, developers who needed immediate access token revocation support had to use reference tokens. As of 3.0, this is no longer required: <strong>even self-contained JWT or Data Protection access tokens can be revoked, as OpenIddict 3.0 now always creates a database entry for all token types</strong>.</p><p>Like in previous versions, the API projects needing immediate access token revocation need to either have a direct access to OpenIddict&#39;s database or use introspection:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddCore(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// ...</span></div><div class="line">    &#125;)</div><div class="line"></div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// ...</span></div><div class="line">    &#125;)</div><div class="line"></div><div class="line">    .AddValidation(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// Import the configuration from the local OpenIddict server instance.</span></div><div class="line">        options.UseLocalServer();</div><div class="line"></div><div class="line">        <span class="comment">// For applications that need immediate access token or authorization</span></div><div class="line">        <span class="comment">// revocation, the database entry of the received tokens and their</span></div><div class="line">        <span class="comment">// associated authorizations can be validated for each API call.</span></div><div class="line">        <span class="comment">// Enabling these options may have a negative impact on performance.</span></div><div class="line">        options.EnableAuthorizationEntryValidation();</div><div class="line">        options.EnableTokenEntryValidation();</div><div class="line"></div><div class="line">        <span class="comment">// Register the ASP.NET Core host.</span></div><div class="line">        options.UseAspNetCore();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddValidation(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// Note: the validation handler uses OpenID Connect discovery</span></div><div class="line">        <span class="comment">// to retrieve the address of the introspection endpoint.</span></div><div class="line">        options.SetIssuer(<span class="string">"http://localhost:12345/"</span>);</div><div class="line"></div><div class="line">        <span class="comment">// Configure the validation handler to use introspection and register the client</span></div><div class="line">        <span class="comment">// credentials used when communicating with the remote introspection endpoint.</span></div><div class="line">        options.UseIntrospection()</div><div class="line">               .SetClientId(<span class="string">"resource_server_1"</span>)</div><div class="line">               .SetClientSecret(<span class="string">"846B62D0-DEF9-4215-A99D-86E6B8DAB342"</span>);</div><div class="line"></div><div class="line">        <span class="comment">// Register the System.Net.Http integration.</span></div><div class="line">        options.UseSystemNetHttp();</div><div class="line"></div><div class="line">        <span class="comment">// Register the ASP.NET Core host.</span></div><div class="line">        options.UseAspNetCore();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><div class="note tip"><p>Reference tokens can still be used in OpenIddict 3.0 and are now stored either as JWT or Data Protection tokens in the database, but enabling them only affects how tokens are returned to the client: when reference tokens are enabled, the original token payload is added to the database entry and a reference identifier (a 256-bit random value) is returned instead of the token, which greatly reduces the size of the tokens returned to the clients in exchange for more space used in the database.</p></div><div class="note info"><p>Independently of whether <code>options.UseReferenceTokens()</code> is called or not, user codes used in the device flow are always user-readable reference tokens. As such, the device flow cannot be enabled when disabling token storage with <code>options.DisableTokenStorage()</code> and will require custom code when using the degraded mode.</p></div><h3 id="OpenIddict-39-s-managers-and-stores-now-use-IAsyncEnumerable-lt-T-gt"><a href="#OpenIddict-39-s-managers-and-stores-now-use-IAsyncEnumerable-lt-T-gt" class="headerlink" title="OpenIddict&#39;s managers and stores now use IAsyncEnumerable&lt;T&gt;"></a>OpenIddict&#39;s managers and stores now use <code>IAsyncEnumerable&lt;T&gt;</code></h3><p>The application, authorization, scope and token managers (located in <code>OpenIddict.Core</code>) and <strong>all the Entity Framework 6, Entity Framework 6 and MongoDB stores now natively support <code>IAsyncEnumerable&lt;T&gt;</code></strong>. While this is a massive binary/source breaking change, migrating to <code>IAsyncEnumerable&lt;T&gt;</code> allows stores to implement streamed enumerations, which was not possible in OpenIddict 2.0 (that uses <code>Task&lt;ImmutableArray&lt;T&gt;&gt;</code> instead of <code>IAsyncEnumerable&lt;T&gt;</code>).</p><div class="note tip"><p><code>IAsyncEnumerable&lt;T&gt;</code> is fully supported even on .NET Framework &gt;= 4.6.1, thanks to the <a href="https://www.nuget.org/packages/Microsoft.Bcl.AsyncInterfaces" target="_blank" rel="external"><code>Microsoft.Bcl.AsyncInterfaces</code></a> compatibility package that <code>OpenIddict.Abstractions</code> references.</p></div><h3 id="The-Entity-Framework-6-Entity-Framework-Core-and-MongoDB-entities-have-been-renamed"><a href="#The-Entity-Framework-6-Entity-Framework-Core-and-MongoDB-entities-have-been-renamed" class="headerlink" title="The Entity Framework 6, Entity Framework Core and MongoDB entities have been renamed"></a>The Entity Framework 6, Entity Framework Core and MongoDB entities have been renamed</h3><p>To make more obvious the fact the OpenIddict entities are designed for a specific ORM/database, the application, authorization, scope and tokens entities have all been renamed to include the name of their corresponding store (e.g <code>OpenIddictApplication</code> -&gt; <code>OpenIddictMongoDbApplication</code>).</p><h3 id="The-OpenIddict-endpoints-are-all-non-pass-through-by-default"><a href="#The-OpenIddict-endpoints-are-all-non-pass-through-by-default" class="headerlink" title="The OpenIddict endpoints are all non-pass-through by default"></a>The OpenIddict endpoints are all non-pass-through by default</h3><p>In previous versions of OpenIddict, the authorization, logout, token and userinfo endpoints were always pass-through: OpenIddict validated the requests for you, but you needed to add an MVC controller – typically named <code>AuthorizationController</code> – or a custom middleware to handle them later in the ASP.NET Core pipeline.</p><p>Starting in 3.0, <strong>none of the OpenIddict endpoints is pass-through by default</strong>, which helps with debugging as OpenIddict will now throw an exception if the request is not handled using the events model and if the pass-through mode was not enabled, instead of letting ASP.NET Core return a 404 response.</p><p>To enable pass-through for a specific endpoint, use the methods provided by <code>OpenIddictServerAspNetCoreBuilder</code> or <code>OpenIddictServerOwinBuilder</code>:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// When using ASP.NET Core:</span></div><div class="line">        options.UseAspNetCore()</div><div class="line">               .EnableAuthorizationEndpointPassthrough()</div><div class="line">               .EnableLogoutEndpointPassthrough()</div><div class="line">               .EnableTokenEndpointPassthrough()</div><div class="line">               .EnableUserinfoEndpointPassthrough()</div><div class="line">               .EnableVerificationEndpointPassthrough();</div><div class="line"></div><div class="line">        <span class="comment">// When using OWIN/Katana:</span></div><div class="line">        options.UseOwin()</div><div class="line">               .EnableAuthorizationEndpointPassthrough()</div><div class="line">               .EnableLogoutEndpointPassthrough()</div><div class="line">               .EnableTokenEndpointPassthrough()</div><div class="line">               .EnableUserinfoEndpointPassthrough()</div><div class="line">               .EnableVerificationEndpointPassthrough();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><h3 id="Status-code-pages-middleware-integration-is-no-longer-enabled-by-default"><a href="#Status-code-pages-middleware-integration-is-no-longer-enabled-by-default" class="headerlink" title="Status code pages middleware integration is no longer enabled by default"></a>Status code pages middleware integration is no longer enabled by default</h3><p>In 3.0, integration with the ASP.NET Core status code pages middleware is now opt-in:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        options.UseAspNetCore()</div><div class="line">               .EnableStatusCodePagesIntegration();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><h3 id="Returning-custom-parameters-via-AuthenticationProperties-Parameters-is-now-supported"><a href="#Returning-custom-parameters-via-AuthenticationProperties-Parameters-is-now-supported" class="headerlink" title="Returning custom parameters via AuthenticationProperties.Parameters is now supported"></a>Returning custom parameters via <code>AuthenticationProperties.Parameters</code> is now supported</h3><p>Returning custom parameters was already supported in 1.0/2.0, but they had to be stored as strings in <code>AuthenticationProperties.Items</code> with a special suffix. This mechanism is no longer supported, but returning custom <code>bool</code>, <code>long</code>, <code>string</code>, <code>string[]</code> or <code>JsonElement</code> parameters is now much simpler:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> properties = <span class="keyword">new</span> AuthenticationProperties(</div><div class="line">    items: <span class="keyword">new</span> Dictionary&lt;<span class="keyword">string</span>, <span class="keyword">string</span>&gt;(),</div><div class="line">    parameters: <span class="keyword">new</span> Dictionary&lt;<span class="keyword">string</span>, <span class="keyword">object</span>&gt;</div><div class="line">    &#123;</div><div class="line">        [<span class="string">"boolean_parameter"</span>] = <span class="literal">true</span>,</div><div class="line">        [<span class="string">"integer_parameter"</span>] = <span class="number">42</span>,</div><div class="line">        [<span class="string">"string_parameter"</span>] = <span class="string">"Bob l'Eponge"</span>,</div><div class="line">        [<span class="string">"array_parameter"</span>] = JsonSerializer.Deserialize&lt;JsonElement&gt;(<span class="string">@"[""Contoso"",""Fabrikam""]"</span>),</div><div class="line">        [<span class="string">"object_parameter"</span>] = JsonSerializer.Deserialize&lt;JsonElement&gt;(<span class="string">@"&#123;""parameter"":""value""&#125;"</span>)</div><div class="line">    &#125;);</div><div class="line"></div><div class="line"><span class="keyword">return</span> SignIn(principal, properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);</div></pre></td></tr></table></figure><div class="note info"><p><code>AuthenticationProperties.Parameters</code> was introduced in ASP.NET Core 2.1 and thus is not supported on OWIN. To return custom properties on OWIN, you must store them in <code>AuthenticationProperties.Items</code> as strings and create a custom event handler to return them as part of the response. See <a href="https://github.com/openiddict/openiddict-core/issues/357#issuecomment-566235812" target="_blank" rel="external">Add additional values into Authorize endpoint response</a> for an example.</p></div><h2 id="What-39-s-next"><a href="#What-39-s-next" class="headerlink" title="What&#39;s next?"></a>What&#39;s next?</h2><p>There&#39;ll likely be a few other beta releases before RTM to ensure everything is working as intended and to add features like localization support. All the samples contained in the <a href="https://github.com/openiddict/openiddict-samples" target="_blank" rel="external">openiddict-samples</a> have already been updated to target OpenIddict 3.0 beta1 and new samples will be progressively added to cover the newly supported scenarios (e.g device flow).</p><p><strong>Considerable effort has been dedicated</strong> to making sure that <strong>all</strong> users of ASOS, the aspnet-contrib extensions or OpenIddict have a migration path to 3.0 – whether they are using the latest ASP.NET Core version and the latest .NET Core runtime, are stuck with 2.1 on .NET Framework or still use ASP.NET 4.6.1 with OWIN/Katana.</p><p>As such, <strong>I&#39;ll no longer offer free support for OpenIddict 1.0/2.0 or the aspnet-contrib packages</strong> that were merged into OpenIddict as part of this release once the 3.0 RTM packages ship (in a few months). The 2 aspnet-contrib repositories will also be archived.</p><p><strong>The following NuGet packages will be eventually flagged as obsolete to inform users that they are no longer actively developed or supported</strong>:</p><table><thead><tr><th>Package name</th><th>Package version</th></tr></thead><tbody><tr><td>AspNet.Security.OpenIdConnect.Extensions</td><td>All</td></tr><tr><td>AspNet.Security.OpenIdConnect.Primitives</td><td>All</td></tr><tr><td>AspNet.Security.OpenIdConnect.Server</td><td>All</td></tr><tr><td></td><td></td></tr><tr><td>Owin.Security.OpenIdConnect.Extensions</td><td>All</td></tr><tr><td>Owin.Security.OpenIdConnect.Server</td><td>All</td></tr><tr><td></td><td></td></tr><tr><td>AspNet.Security.OAuth.Introspection</td><td>All</td></tr><tr><td>AspNet.Security.OAuth.Validation</td><td>All</td></tr><tr><td></td><td></td></tr><tr><td>Owin.Security.OAuth.Introspection</td><td>All</td></tr><tr><td>Owin.Security.OAuth.Validation</td><td>All</td></tr><tr><td></td><td></td></tr><tr><td>OpenIddict.*</td><td>&lt; 3.0</td></tr></tbody></table><div class="note tip"><p><strong>Users who substantially contributed to OpenIddict, sponsored the project or benefit from a support contract will still get bug fixes for these packages via a private feed</strong> to give them additional time to migrate to OpenIddict 3.0.</p></div><p>With that in mind, I encourage everyone to start migrating to OpenIddict 3.0 beta1 and testing it in a non-production environment.</p><h2 id="What-can-you-do-to-help"><a href="#What-can-you-do-to-help" class="headerlink" title="What can you do to help?"></a>What can you do to help?</h2><p>While super exciting, this release has been way more time-consuming than I had initially anticipated, and there are still many important things to do, like <strong>adding documentation and a migration guide</strong> to help developers update their applications to OpenIddict 3.0.</p><p>For that, <strong>I need your help: please consider contributing to the effort or <a href="https://paypal.me/kevinchalet" target="_blank" rel="external">sponsoring me</a></strong>, so I can spend more time working on OpenIddict. Without external help, I likely will not have enough spare time to work on things that would benefit the community, like documentation.</p><table><thead><tr><th>Contribution area</th></tr></thead><tbody><tr><td><a href="https://github.com/openiddict/openiddict-core/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22" target="_blank" rel="external">Core/server/validation components</a></td></tr><tr><td><a href="https://github.com/openiddict/openiddict-documentation/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22" target="_blank" rel="external">Documentation</a></td></tr><tr><td><a href="https://github.com/openiddict/openiddict-samples/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22" target="_blank" rel="external">Samples</a></td></tr></tbody></table><p>If you&#39;re interested in getting dedicated support or want to discuss sponsoring options, don&#39;t hesitate to reach me at contact@kevinchalet.com.</p>]]></content>
    
    <summary type="html">
    
      In this post, discover the major changes introduced in OpenIddict 3.0.
    
    </summary>
    
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
      <category term="owin" scheme="https://kevinchalet.com/tags/owin/"/>
    
  </entry>
  
  <entry>
    <title>Adding OpenIddict 3.0 to an OWIN application</title>
    <link href="https://kevinchalet.com/2020/03/03/adding-openiddict-3-0-to-an-owin-application/"/>
    <id>https://kevinchalet.com/2020/03/03/adding-openiddict-3-0-to-an-owin-application/</id>
    <published>2020-03-03T18:00:00.000Z</published>
    <updated>2021-12-08T16:32:00.000Z</updated>
    
    <content type="html"><![CDATA[<div class="note tip"><p>Up-to-date samples for ASP.NET 4.x and OWIN can be found in the <a href="https://github.com/openiddict/openiddict-samples" target="_blank" rel="external">openiddict/openiddict-samples</a> repository:</p><ul><li><a href="https://github.com/openiddict/openiddict-samples/tree/dev/samples/Fornax" target="_blank" rel="external">Fornax</a>: authorization code flow demo using ASP.NET Web Forms 4.8 and OWIN/Katana, with a .NET console acting as the client.</li><li><a href="https://github.com/openiddict/openiddict-samples/tree/dev/samples/Kalarba" target="_blank" rel="external">Kalarba</a>: resource owner password credentials demo using OWIN/Katana, ASP.NET Web API and the OpenIddict degraded mode.</li></ul></div><p>Last year, <strong>I announced that OpenIddict would become compatible with OWIN/Katana – and thus usable in any ASP.NET 4.x <em>non-Core</em> application –</strong> as part of the 3.0 release. While using OpenIddict in an OWIN application shouldn&#39;t be much different than using it in an ASP.NET Core application, <strong>there&#39;s actually a part than will slightly differ between the two platforms: dependency injection</strong>.</p><p>Globally introduced in ASP.NET Core 1.0, <strong>dependency injection support was not a thing in OWIN</strong>. Yet, OpenIddict itself depends on dependency injection for all its features: core, server and token validation. To work around the lack of official DI support, <strong>OpenIddict 3.0 will support two approaches: one working with the Autofac DI container and one without</strong>. This blog post briefly describes how to do that.</p><p>To keep things simple, we&#39;ll create a tiny self-hosted .NET 4.7.2 OWIN application, but of course, these 2 approaches also work in any ASP.NET application, no matter what&#39;s the host used: <code>HttpListener</code>, <code>System.Web</code> or even third-party options like <code>Nowin</code>.</p><p>For that, create a new application with the following <code>.csproj</code>, that contains the basic things we&#39;ll need: the Microsoft DI primitives used by OpenIddict, the OWIN self-host and hosting abstractions and the OpenIddict OWIN metapackage (that itself references the core, server and validation packages, plus the OWIN integration assembly):</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">Project</span> <span class="attr">Sdk</span>=<span class="string">"Microsoft.NET.Sdk"</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">PropertyGroup</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">OutputType</span>&gt;</span>Exe<span class="tag">&lt;/<span class="name">OutputType</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">TargetFramework</span>&gt;</span>net472<span class="tag">&lt;/<span class="name">TargetFramework</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">PropertyGroup</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">ItemGroup</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"Microsoft.Extensions.DependencyInjection"</span> <span class="attr">Version</span>=<span class="string">"3.1.4"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"Microsoft.Owin.Host.HttpListener"</span> <span class="attr">Version</span>=<span class="string">"4.1.0"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"Microsoft.Owin.Hosting"</span> <span class="attr">Version</span>=<span class="string">"4.1.0"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"OpenIddict.Owin"</span> <span class="attr">Version</span>=<span class="string">"3.0.0-beta1.20311.67"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">ItemGroup</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;/<span class="name">Project</span>&gt;</span></div></pre></td></tr></table></figure><a id="more"></a><p>Then, add the entry point...</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">using</span> System;</div><div class="line"><span class="keyword">using</span> Microsoft.Owin.Hosting;</div><div class="line"></div><div class="line"><span class="keyword">namespace</span> <span class="title">OpenIddictWebApiDemo</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">Program</span></div><div class="line">    &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">Main</span>(<span class="params"><span class="keyword">string</span>[] args</span>)</span></div><div class="line">        &#123;</div><div class="line">            <span class="keyword">const</span> <span class="keyword">string</span> address = <span class="string">"http://localhost:58779/"</span>;</div><div class="line"></div><div class="line">            <span class="keyword">using</span> (WebApp.Start&lt;Startup&gt;(address))</div><div class="line">            &#123;</div><div class="line">                Console.WriteLine(<span class="string">$"Server is running on <span class="subst">&#123;address&#125;</span>, press CTRL+C to stop."</span>);</div><div class="line">                Console.ReadLine();</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>... and the <code>Startup</code> class:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">using</span> Owin;</div><div class="line"></div><div class="line"><span class="keyword">namespace</span> <span class="title">OpenIddictWebApiDemo</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Startup</span></div><div class="line">    &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configuration</span>(<span class="params">IAppBuilder app</span>)</span></div><div class="line">        &#123;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>At this point, no matter the option you&#39;ll opt for, you&#39;ll need to instantiate a <code>ServiceCollection</code> and register the OpenIddict server and validation services (exactly like how you&#39;d do in a ASP.NET Core application):</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configuration</span>(<span class="params">IAppBuilder app</span>)</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">var</span> services = <span class="keyword">new</span> ServiceCollection();</div><div class="line">    services.AddOpenIddict()</div><div class="line">        .AddServer(options =&gt;</div><div class="line">        &#123;</div><div class="line">            options.AllowPasswordFlow();</div><div class="line">            options.EnableDegradedMode();</div><div class="line">            options.AcceptAnonymousClients();</div><div class="line"></div><div class="line">            options.SetTokenEndpointUris(<span class="string">"/connect/token"</span>);</div><div class="line"></div><div class="line">            options.AddDevelopmentEncryptionCertificate()</div><div class="line">                   .AddDevelopmentSigningCertificate();</div><div class="line"></div><div class="line">            options.UseOwin()</div><div class="line">                   .DisableTransportSecurityRequirement();</div><div class="line"></div><div class="line">            options.AddEventHandler&lt;ValidateTokenRequestContext&gt;(builder =&gt;</div><div class="line">                builder.UseInlineHandler(context =&gt;</div><div class="line">                &#123;</div><div class="line">                    <span class="comment">// Client authentication is used in this sample,</span></div><div class="line">                    <span class="comment">// so there's nothing to validate here.</span></div><div class="line"></div><div class="line">                    <span class="keyword">return</span> <span class="keyword">default</span>;</div><div class="line">                &#125;));</div><div class="line"></div><div class="line">            options.AddEventHandler&lt;HandleTokenRequestContext&gt;(builder =&gt;</div><div class="line">                builder.UseInlineHandler(context =&gt;</div><div class="line">                &#123;</div><div class="line">                    <span class="keyword">if</span> (!context.Request.IsPasswordGrantType())</div><div class="line">                    &#123;</div><div class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> InvalidOperationException(<span class="string">"The specified grant type is not supported."</span>);</div><div class="line">                    &#125;</div><div class="line"></div><div class="line">                    <span class="comment">// Validate the username/password parameters.</span></div><div class="line">                    <span class="comment">// In a real world application, you'd use likely use a key derivation function like PBKDF2 to slow</span></div><div class="line">                    <span class="comment">// the client secret validation process down and a time-constant comparer to prevent timing attacks.</span></div><div class="line">                    <span class="keyword">if</span> (!<span class="keyword">string</span>.Equals(context.Request.Username, <span class="string">"alice@wonderland.com"</span>, StringComparison.Ordinal) ||</div><div class="line">                        !<span class="keyword">string</span>.Equals(context.Request.Password, <span class="string">"P@ssw0rd"</span>, StringComparison.Ordinal))</div><div class="line">                    &#123;</div><div class="line">                        context.Reject(</div><div class="line">                            error: Errors.InvalidGrant,</div><div class="line">                            description: <span class="string">"The username/password couple is invalid."</span>);</div><div class="line"></div><div class="line">                        <span class="keyword">return</span> <span class="keyword">default</span>;</div><div class="line">                    &#125;</div><div class="line"></div><div class="line">                    <span class="keyword">var</span> principal = <span class="keyword">new</span> ClaimsPrincipal(<span class="keyword">new</span> ClaimsIdentity(OpenIddictServerOwinDefaults.AuthenticationType));</div><div class="line">                    principal.SetClaim(Claims.Subject, <span class="string">"Bob"</span>);</div><div class="line"></div><div class="line">                    context.Principal = principal;</div><div class="line"></div><div class="line">                    <span class="keyword">return</span> <span class="keyword">default</span>;</div><div class="line">                &#125;));</div><div class="line">        &#125;)</div><div class="line"></div><div class="line">        .AddValidation(options =&gt;</div><div class="line">        &#123;</div><div class="line">            options.UseLocalServer();</div><div class="line"></div><div class="line">            options.UseOwin();</div><div class="line">        &#125;);</div><div class="line">&#125;</div></pre></td></tr></table></figure><div class="note tip"><p>If you read my <a href="https://kevinchalet.com/2020/02/18/creating-an-openid-connect-server-proxy-with-openiddict-3-0-s-degraded-mode/">previous post</a>, you probably figured out that I used OpenIddict 3.0&#39;s degraded mode in this snippet to implement the OAuth 2.0 password flow, which allows using OpenIddict&#39;s server feature without any backing database.</p></div><p>If you try to run the project as-is, all you&#39;ll get is a 404 error, as there&#39;s still a thing we need to do: integrating OpenIddict into OWIN&#39;s request processing pipeline.</p><h2 id="Using-OpenIddict-3-0-with-Autofac-recommended-option"><a href="#Using-OpenIddict-3-0-with-Autofac-recommended-option" class="headerlink" title="Using OpenIddict 3.0 with Autofac (recommended option)"></a>Using OpenIddict 3.0 with Autofac (recommended option)</h2><p>Unlike most other DI containers, <a href="https://autofac.readthedocs.io/en/latest/integration/owin.html" target="_blank" rel="external">Autofac has an excellent OWIN integration story</a> that keeps things very clean and well-integrated. To use Autofac with OWIN, reference the following packages:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"Autofac.Owin"</span> <span class="attr">Version</span>=<span class="string">"5.0.1"</span> /&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"Autofac.Extensions.DependencyInjection"</span> <span class="attr">Version</span>=<span class="string">"6.0.0"</span> /&gt;</span></div></pre></td></tr></table></figure><p>Then, update your <code>Configuration</code> method to create the <code>IContainer</code>, import the <code>ServiceCollection</code> and register the Autofac OWIN middleware:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configuration</span>(<span class="params">IAppBuilder app</span>)</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">var</span> services = <span class="keyword">new</span> ServiceCollection();</div><div class="line"></div><div class="line">    <span class="comment">// ...</span></div><div class="line"></div><div class="line">    <span class="keyword">var</span> builder = <span class="keyword">new</span> ContainerBuilder();</div><div class="line">    builder.Populate(services);</div><div class="line">    <span class="keyword">var</span> container = builder.Build();</div><div class="line"></div><div class="line">    app.UseAutofacMiddleware(container);</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>This is the beauty of this option: nothing else is required.</p><h2 id="Using-OpenIddict-3-0-without-Autofac"><a href="#Using-OpenIddict-3-0-without-Autofac" class="headerlink" title="Using OpenIddict 3.0 without Autofac"></a>Using OpenIddict 3.0 without Autofac</h2><p>Without Autofac, things will get a bit more complicated, as you&#39;ll need to manage per-request scopes yourself. While we&#39;ll use the Microsoft DI container to achieve that, this approach is also doable with any other DI container that provides an <code>IServiceProvider</code> implementation.</p><p>For that, <strong>we&#39;ll have to add a middleware creating a service scope per request and injecting it in the OWIN environment</strong> with the <code>System.IServiceProvider</code> key (the value that the OpenIddict server and validation OWIN integrations expect). We&#39;ll also need to call <code>app.UseOpenIddictServer()</code> and <code>app.UseOpenIddictValidation()</code>, that register the OpenIddict OWIN authentication handlers.</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configuration</span>(<span class="params">IAppBuilder app</span>)</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">var</span> services = <span class="keyword">new</span> ServiceCollection();</div><div class="line"></div><div class="line">    <span class="comment">// ...</span></div><div class="line"></div><div class="line">    <span class="keyword">var</span> container = services.BuildServiceProvider();</div><div class="line"></div><div class="line">    app.Use(<span class="keyword">async</span> (context, next) =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="keyword">var</span> scope = container.CreateScope();</div><div class="line"></div><div class="line">        <span class="comment">// Store the per-request service provider in the OWIN environment.</span></div><div class="line">        context.Set(<span class="keyword">typeof</span>(IServiceProvider).FullName, scope.ServiceProvider);</div><div class="line"></div><div class="line">        <span class="keyword">try</span></div><div class="line">        &#123;</div><div class="line">            <span class="comment">// Invoke the rest of the pipeline.</span></div><div class="line">            <span class="keyword">await</span> next();</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="keyword">finally</span></div><div class="line">        &#123;</div><div class="line">            <span class="comment">// Remove the scoped service provider from the OWIN environment.</span></div><div class="line">            context.Set&lt;IServiceProvider&gt;(<span class="keyword">typeof</span>(IServiceProvider).FullName, <span class="literal">null</span>);</div><div class="line"></div><div class="line">            <span class="comment">// Dispose of the scoped service provider.</span></div><div class="line">            <span class="keyword">if</span> (scope <span class="keyword">is</span> IAsyncDisposable disposable)</div><div class="line">            &#123;</div><div class="line">                <span class="keyword">await</span> disposable.DisposeAsync();</div><div class="line">            &#125;</div><div class="line"></div><div class="line">            <span class="keyword">else</span></div><div class="line">            &#123;</div><div class="line">                scope.Dispose();</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;);</div><div class="line"></div><div class="line">    app.UseOpenIddictServer();</div><div class="line">    app.UseOpenIddictValidation();</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>To ensure things are working properly, you can use Postman and send a token request:</p><img src="/2020/03/03/adding-openiddict-3-0-to-an-owin-application/postman.png" alt="postman.png"><h2 id="Configure-ASP-NET-Web-API-to-use-token-authentication-optional"><a href="#Configure-ASP-NET-Web-API-to-use-token-authentication-optional" class="headerlink" title="Configure ASP.NET Web API to use token authentication (optional)"></a>Configure ASP.NET Web API to use token authentication (optional)</h2><p>If your application uses ASP.NET Web API, a couple additional things will need to be configured:</p><ul><li>The OWIN host MUST be used to register the ASP.NET Web API integration. Concretely, this means you must ensure your application doesn&#39;t reference the <a href="https://www.nuget.org/packages/Microsoft.AspNet.WebApi.WebHost/" target="_blank" rel="external"><code>Microsoft.AspNet.WebApi.WebHost</code></a> package. Instead, use the <a href="https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Owin/" target="_blank" rel="external"><code>Microsoft.AspNet.WebApi.Owin</code></a> package and call <code>app.UseWebApi()</code> at the end of your <code>Startup.Configuration(IAppBuilder app)</code> method:</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configuration</span>(<span class="params">IAppBuilder app</span>)</span></div><div class="line">&#123;</div><div class="line">    <span class="comment">// ...</span></div><div class="line"></div><div class="line">    <span class="keyword">var</span> configuration = <span class="keyword">new</span> HttpConfiguration();</div><div class="line">    configuration.MapHttpAttributeRoutes();</div><div class="line"></div><div class="line">    app.UseWebApi(configuration);</div><div class="line">&#125;</div></pre></td></tr></table></figure><div class="note tip"><p>When using Autofac and its ASP.NET Web API integration, you&#39;ll also need to call <code>app.UseAutofacWebApi(configuration)</code> before <code>app.UseWebApi(configuration)</code>. For more information, read <a href="https://autofac.readthedocs.io/en/latest/integration/webapi.html" target="_blank" rel="external">the Autofac documentation</a>.</p></div><ul><li>To use token authentication for all Web API actions, the <code>HostAuthenticationFilter</code> can be registered globally to always call the OpenIddict validation handler:</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configuration</span>(<span class="params">IAppBuilder app</span>)</span></div><div class="line">&#123;</div><div class="line">    <span class="comment">// ...</span></div><div class="line"></div><div class="line">    <span class="keyword">var</span> configuration = <span class="keyword">new</span> HttpConfiguration();</div><div class="line">    configuration.MapHttpAttributeRoutes();</div><div class="line"></div><div class="line">    <span class="comment">// Configure ASP.NET Web API to use token authentication.</span></div><div class="line">    configuration.Filters.Add(<span class="keyword">new</span> HostAuthenticationFilter(OpenIddictValidationOwinDefaults.AuthenticationType));</div><div class="line"></div><div class="line">    app.UseWebApi(configuration);</div><div class="line">&#125;</div></pre></td></tr></table></figure><ul><li>Alternatively, <code>HostAuthenticationFilter</code> can be applied per-action or per-controller using <code>HostAuthenticationAttribute</code>:</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">[HostAuthentication(OpenIddictValidationOwinDefaults.AuthenticationType)]</div><div class="line">[RoutePrefix(<span class="string">"api"</span>)]</div><div class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ResourceController</span> : <span class="title">ApiController</span></div><div class="line">&#123;</div><div class="line">    [Authorize, HttpGet]</div><div class="line">    [Route(<span class="string">"message"</span>)]</div><div class="line">    <span class="function"><span class="keyword">public</span> IHttpActionResult <span class="title">GetMessage</span>(<span class="params"></span>)</span></div><div class="line">    &#123;</div><div class="line">        <span class="keyword">var</span> principal = (ClaimsPrincipal) User;</div><div class="line">        <span class="keyword">var</span> name = principal.FindFirst(Claims.Name).Value;</div><div class="line"></div><div class="line">        <span class="keyword">return</span> Content(HttpStatusCode.OK, <span class="string">$"<span class="subst">&#123;name&#125;</span> has been successfully authenticated."</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      In this post, discover how to use OpenIddict 3.0&#39;s new OWIN-based host.
    
    </summary>
    
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
      <category term="owin" scheme="https://kevinchalet.com/tags/owin/"/>
    
  </entry>
  
  <entry>
    <title>Creating an OpenID Connect server proxy with OpenIddict 3.0&#39;s degraded mode</title>
    <link href="https://kevinchalet.com/2020/02/18/creating-an-openid-connect-server-proxy-with-openiddict-3-0-s-degraded-mode/"/>
    <id>https://kevinchalet.com/2020/02/18/creating-an-openid-connect-server-proxy-with-openiddict-3-0-s-degraded-mode/</id>
    <published>2020-02-18T15:30:00.000Z</published>
    <updated>2020-06-11T20:34:12.000Z</updated>
    
    <content type="html"><![CDATA[<p>As some of you may already know, <a href="https://github.com/openiddict/openiddict-core/issues/736" target="_blank" rel="external">I&#39;ve been working on OpenIddict 3.0 for a few months now</a>. <strong>One of the main goals of this major release is to merge <a href="https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server" target="_blank" rel="external">ASOS</a></strong> (a low-level OpenID Connect server middleware for ASP.NET Core) <strong>and OpenIddict</strong> (a higher-level OIDC server library designed for less advanced users) into a unified code base, that would ideally represent the best of both worlds.</p><p><strong>As part of this task, a new feature was added to OpenIddict: the degraded mode</strong> (also known as the <em>ASOS-like</em> or <em>bare</em> mode). Put simply, <strong>this mode allows using OpenIddict&#39;s server without any backing database</strong>. Once enabled, all the features that rely on the OpenIddict application, authorization, scope and token managers (contained in the <code>OpenIddict.Core</code> package) are automatically disabled, which includes things like <code>client_id</code>/<code>client_secret</code> or <code>redirect_uri</code> validation, reference tokens and token revocation support. In other words, this mode allows switching from an &quot;all you can eat&quot; offer to a &quot;pay-to-play&quot; approach.</p><p>A thread, posted on <a href="https://github.com/aspnet-contrib/AspNet.Security.OpenId.Providers/issues/50" target="_blank" rel="external">one of the aspnet-contrib repositories</a> gave me a perfect opportunity to showcase this particular feature. The question asked by the commenters was simple: how can I use an external authentication provider like Steam (that implements the legacy OpenID 2.0 protocol) with my own API endpoints?</p><p><strong>Steam doesn&#39;t issue any access token you could directly use with your API endpoints</strong>. Actually, access tokens are not even a thing in OpenID 2.0, which is a pure authentication protocol that doesn&#39;t offer any authorization capability (unlike OAuth 1.0/2.0 or OpenID Connect).</p><p>So, how do we solve this problem? <strong>The most common approach typically consists in creating your own authorization server</strong> between your frontend application and the remote authentication provider (here, Steam). This way, when the application needs to authenticate a user, the user is redirected to the authorization server, that delegates the actual authentication part to another party. Once authenticated by that party, the user is redirected back to the main authorization server, that issues an access token to the client application.</p><img src="/2020/02/18/creating-an-openid-connect-server-proxy-with-openiddict-3-0-s-degraded-mode/authorization-code-flow.png" alt="authorization-code-flow.png"><p>This a super common scenario, that can be implemented using standard protocols like OpenID Connect and well-known implementations like OpenIddict or IdentityServer. However, <a href="https://github.com/aspnet-contrib/AspNet.Security.OpenId.Providers/issues/50#issuecomment-586734467" target="_blank" rel="external">these options are sometimes considered &quot;overkill&quot; for such simple scenarios</a>. After all, why would you need a fully-fledged OIDC server – with login, registration or consent views – when all you want is to delegate the actual authentication to another server in a totally transparent and almost invisible way?</p><p>Rolling your own protocol is tempting... <a href="https://github.com/aspnet-contrib/AspNet.Security.OpenId.Providers/issues/50#issuecomment-586726240" target="_blank" rel="external">but a very bad idea</a>, as you can&#39;t benefit from all the security measures offered by standard flows like OAuth 2.0/OpenID Connect&#39;s authorization code flow, whose threat model is clearly identified for many years now. <strong>As you may have guessed by now, this is precisely where OpenIddict 3.0&#39;s degraded mode can come in handy</strong>.</p><a id="more"></a><h2 id="Implementing-a-minimalist-OpenID-Connect-server-with-OpenIddict-3-0"><a href="#Implementing-a-minimalist-OpenID-Connect-server-with-OpenIddict-3-0" class="headerlink" title="Implementing a minimalist OpenID Connect server with OpenIddict 3.0"></a>Implementing a minimalist OpenID Connect server with OpenIddict 3.0</h2><h3 id="Add-the-Steam-authentication-integration"><a href="#Add-the-Steam-authentication-integration" class="headerlink" title="Add the Steam authentication integration"></a>Add the Steam authentication integration</h3><p>First, we&#39;ll start by creating an ASP.NET Core 3.1 API application and by adding the aspnet-contrib Steam provider and an instance of the cookies authentication handler (that will be used to store the user identity retrieved from Steam).</p><p>For that, add the following dependency in your .csproj:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">Project</span> <span class="attr">Sdk</span>=<span class="string">"Microsoft.NET.Sdk.Web"</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">PropertyGroup</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">TargetFramework</span>&gt;</span>netcoreapp3.1<span class="tag">&lt;/<span class="name">TargetFramework</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">PropertyGroup</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">ItemGroup</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"AspNet.Security.OpenId.Steam"</span> <span class="attr">Version</span>=<span class="string">"3.0.0"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">ItemGroup</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;/<span class="name">Project</span>&gt;</span></div></pre></td></tr></table></figure><p>Then, update <code>ConfigureServices</code> to register the Steam and cookies authentication handlers:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">ConfigureServices</span>(<span class="params">IServiceCollection services</span>)</span></div><div class="line">&#123;</div><div class="line">    services.AddControllers();</div><div class="line"></div><div class="line">    services.AddAuthentication()</div><div class="line">        .AddCookie()</div><div class="line">        .AddSteam(options =&gt;</div><div class="line">        &#123;</div><div class="line">            options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;</div><div class="line"></div><div class="line">            <span class="comment">// To get additional claims from Steam's authentication APIs,</span></div><div class="line">            <span class="comment">// register your application and set the application key.</span></div><div class="line">            <span class="comment">//</span></div><div class="line">            <span class="comment">// options.ApplicationKey = "application_key";</span></div><div class="line">        &#125;);</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>You&#39;ll also need to update <code>Configure</code> to call <code>app.UseAuthentication()</code>:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configure</span>(<span class="params">IApplicationBuilder app, IWebHostEnvironment env</span>)</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">if</span> (env.IsDevelopment())</div><div class="line">    &#123;</div><div class="line">        app.UseDeveloperExceptionPage();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    app.UseHttpsRedirection();</div><div class="line"></div><div class="line">    app.UseRouting();</div><div class="line"></div><div class="line">    app.UseAuthentication();</div><div class="line"></div><div class="line">    app.UseAuthorization();</div><div class="line"></div><div class="line">    app.UseEndpoints(endpoints =&gt;</div><div class="line">    &#123;</div><div class="line">        endpoints.MapControllers();</div><div class="line">    &#125;);</div><div class="line">&#125;</div></pre></td></tr></table></figure><h3 id="Add-the-OpenIddict-server-and-JWT-validation-components"><a href="#Add-the-OpenIddict-server-and-JWT-validation-components" class="headerlink" title="Add the OpenIddict server and JWT validation components"></a>Add the OpenIddict server and JWT validation components</h3><p>Now, we&#39;ll need to add the OpenID Connect server part. For that, add the following packages:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"OpenIddict.Server.AspNetCore"</span> <span class="attr">Version</span>=<span class="string">"3.0.0-beta1.20311.67"</span> /&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"OpenIddict.Validation.AspNetCore"</span> <span class="attr">Version</span>=<span class="string">"3.0.0-beta1.20311.67"</span> /&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"OpenIddict.Validation.ServerIntegration"</span> <span class="attr">Version</span>=<span class="string">"3.0.0-beta1.20311.67"</span> /&gt;</span></div></pre></td></tr></table></figure><p>Next, tweak <code>ConfigureServices</code> to register the OpenIddict ASP.NET Core server and validation services, with only the options we need: the authorization code flow allowed, the authorization and token endpoints active and the degraded mode enabled:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        options.AddDevelopmentEncryptionCertificate()</div><div class="line">               .AddDevelopmentSigningCertificate();</div><div class="line"></div><div class="line">        options.AllowAuthorizationCodeFlow();</div><div class="line"></div><div class="line">        options.SetAuthorizationEndpointUris(<span class="string">"/connect/authorize"</span>)</div><div class="line">               .SetTokenEndpointUris(<span class="string">"/connect/token"</span>);</div><div class="line"></div><div class="line">        options.EnableDegradedMode();</div><div class="line"></div><div class="line">        options.UseAspNetCore();</div><div class="line">    &#125;)</div><div class="line"></div><div class="line">    .AddValidation(options =&gt;</div><div class="line">    &#123;</div><div class="line">        options.UseLocalServer();</div><div class="line">        options.UseAspNetCore();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><p>At this point, trying to launch the application will result in an exception being thrown:</p><blockquote><p>InvalidOperationException: No custom authorization request validation handler was found. When enabling the degraded mode, a custom <code>IOpenIddictServerHandler&lt;ValidateAuthorizationRequestContext&gt;</code> must be implemented to validate authorization requests (e.g to ensure the client_id and redirect_uri are valid).</p></blockquote><p>This is expected: when using the degraded mode, you must add custom code to validate things that are normally validated for you by OpenIddict, which includes the <code>client_id</code> or <code>redirect_uri</code>, that must be checked to ensure users are not redirected to unsafe/unknown addresses.</p><p>To fix that error, we&#39;ll need to register a handler for the <code>ValidateAuthorizationRequest</code> event. Since we enabled the token endpoint, we&#39;ll also need one to validate token requests.</p><p>There are multiple ways to create and register event handlers in OpenIddict: you can create a dedicated class implementing the generic <code>IOpenIddictServerHandler&lt;TContext&gt;</code> interface – which allows using dependency injection – or you can use inline event handlers.</p><p>To keep things simple, we&#39;ll use inline event handlers (directly defined in <code>ConfigureServices</code>) and static hard-coded checks:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// ...</span></div><div class="line"></div><div class="line">        options.AddEventHandler&lt;ValidateAuthorizationRequestContext&gt;(builder =&gt;</div><div class="line">            builder.UseInlineHandler(context =&gt;</div><div class="line">            &#123;</div><div class="line">                <span class="keyword">if</span> (!<span class="keyword">string</span>.Equals(context.ClientId, <span class="string">"console_app"</span>, StringComparison.Ordinal))</div><div class="line">                &#123;</div><div class="line">                    context.Reject(</div><div class="line">                        error: Errors.InvalidClient,</div><div class="line">                        description: <span class="string">"The specified 'client_id' doesn't match a registered application."</span>);</div><div class="line"></div><div class="line">                    <span class="keyword">return</span> <span class="keyword">default</span>;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="keyword">if</span> (!<span class="keyword">string</span>.Equals(context.RedirectUri, <span class="string">"http://localhost:7890/"</span>, StringComparison.Ordinal))</div><div class="line">                &#123;</div><div class="line">                    context.Reject(</div><div class="line">                        error: Errors.InvalidClient,</div><div class="line">                        description: <span class="string">"The specified 'redirect_uri' is not valid for this client application."</span>);</div><div class="line"></div><div class="line">                    <span class="keyword">return</span> <span class="keyword">default</span>;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="keyword">return</span> <span class="keyword">default</span>;</div><div class="line">            &#125;));</div><div class="line"></div><div class="line">        options.AddEventHandler&lt;ValidateTokenRequestContext&gt;(builder =&gt;</div><div class="line">            builder.UseInlineHandler(context =&gt;</div><div class="line">            &#123;</div><div class="line">                <span class="keyword">if</span> (!<span class="keyword">string</span>.Equals(context.ClientId, <span class="string">"console_app"</span>, StringComparison.Ordinal))</div><div class="line">                &#123;</div><div class="line">                    context.Reject(</div><div class="line">                        error: Errors.InvalidClient,</div><div class="line">                        description: <span class="string">"The specified 'client_id' doesn't match a registered application."</span>);</div><div class="line"></div><div class="line">                    <span class="keyword">return</span> <span class="keyword">default</span>;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="comment">// This demo is used by a single public client application.</span></div><div class="line">                <span class="comment">// As such, no client secret validation is performed.</span></div><div class="line"></div><div class="line">                <span class="keyword">return</span> <span class="keyword">default</span>;</div><div class="line">            &#125;));</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><p><strong>Final and most interesting part: gluing everything together</strong>, so that OpenIddict can redirect users to Steam and generate an authorization response containing an authorization code that the client application will be able to use to redeem an access token. To implement that, we need to use the <code>HandleAuthorizationRequest</code> event:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line">options.AddEventHandler&lt;HandleAuthorizationRequestContext&gt;(builder =&gt;</div><div class="line">    builder.UseInlineHandler(<span class="keyword">async</span> context =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="keyword">var</span> request = context.Transaction.GetHttpRequest() ??</div><div class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> InvalidOperationException(<span class="string">"The ASP.NET Core request cannot be retrieved."</span>);</div><div class="line"></div><div class="line">        <span class="comment">// Retrieve the security principal created by the Steam handler and stored in the authentication cookie.</span></div><div class="line">        <span class="comment">// If the principal cannot be retrieved, this indicates that the user is not logged in. In this case,</span></div><div class="line">        <span class="comment">// an authentication challenge is triggered to redirect the user to Steam's authentication endpoint.</span></div><div class="line">        <span class="keyword">var</span> principal = (<span class="keyword">await</span> request.HttpContext.AuthenticateAsync(SteamAuthenticationDefaults.AuthenticationScheme))?.Principal;</div><div class="line">        <span class="keyword">if</span> (principal == <span class="literal">null</span>)</div><div class="line">        &#123;</div><div class="line">            <span class="keyword">await</span> request.HttpContext.ChallengeAsync(SteamAuthenticationDefaults.AuthenticationScheme);</div><div class="line">            context.HandleRequest();</div><div class="line"></div><div class="line">            <span class="keyword">return</span>;</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="keyword">var</span> identity = <span class="keyword">new</span> ClaimsIdentity(TokenValidationParameters.DefaultAuthenticationType);</div><div class="line"></div><div class="line">        <span class="comment">// Use the "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" claim</span></div><div class="line">        <span class="comment">// (added by the Steam handler to store the user identifier) as the OIDC "sub" claim.</span></div><div class="line">        identity.AddClaim(<span class="keyword">new</span> Claim(Claims.Subject, principal.GetClaim(ClaimTypes.NameIdentifier)));</div><div class="line"></div><div class="line">        <span class="comment">// If needed, you can copy more claims from the cookies principal to the bearer principal.</span></div><div class="line">        <span class="comment">// To get more claims from the Steam handler, you'll need to set the application key.</span></div><div class="line"></div><div class="line">        <span class="comment">// Mark all the added claims as being allowed to be persisted in the access token,</span></div><div class="line">        <span class="comment">// so that the API controllers can retrieve them from the ClaimsPrincipal instance.</span></div><div class="line">        <span class="keyword">foreach</span> (<span class="keyword">var</span> claim <span class="keyword">in</span> identity.Claims)</div><div class="line">        &#123;</div><div class="line">            claim.SetDestinations(Destinations.AccessToken);</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="comment">// Attach the principal to the authorization context, so that an OpenID Connect response</span></div><div class="line">        <span class="comment">// with an authorization code can be generated by the OpenIddict server services.</span></div><div class="line">        context.Principal = <span class="keyword">new</span> ClaimsPrincipal(identity);</div><div class="line">    &#125;));</div></pre></td></tr></table></figure><div class="note tip"><p>Adding a handler for <code>HandleTokenRequestContext</code> is not necessary: in this case, OpenIddict will automatically reuse the user identity extracted from the authorization code to produce an access token returned as part of the token response.</p></div><h2 id="Creating-a-NET-demo-console"><a href="#Creating-a-NET-demo-console" class="headerlink" title="Creating a .NET demo console"></a>Creating a .NET demo console</h2><p>To test our minimalist OpenID Connect proxy, we&#39;ll now create a separate .NET Core 3.1 console referencing the <code>IdentityModel.OidcClient</code> package:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">Project</span> <span class="attr">Sdk</span>=<span class="string">"Microsoft.NET.Sdk"</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">PropertyGroup</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">OutputType</span>&gt;</span>Exe<span class="tag">&lt;/<span class="name">OutputType</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">TargetFramework</span>&gt;</span>netcoreapp3.1<span class="tag">&lt;/<span class="name">TargetFramework</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">LangVersion</span>&gt;</span>8.0<span class="tag">&lt;/<span class="name">LangVersion</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">PropertyGroup</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">ItemGroup</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">PackageReference</span> <span class="attr">Include</span>=<span class="string">"IdentityModel.OidcClient"</span> <span class="attr">Version</span>=<span class="string">"4.0.0-preview.1.3"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">ItemGroup</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;/<span class="name">Project</span>&gt;</span></div></pre></td></tr></table></figure><p>There are typically <strong>2 ways to handle authorization responses in desktop/mobile applications</strong> (i.e applications that don&#39;t run inside a browser):</p><ul><li><p><strong>Running a local HTTP server</strong>: this works well for desktop applications, but might be hard to implement in certain enterprise environments with strict firewall configurations.</p></li><li><p><strong>Registering an application-specific URI scheme</strong> (e.g: <code>myapp://</code>): this is the best approach... and pretty much the only option on most mobile operating systems, where the first option is not always possible, for security reasons.</p></li></ul><p>Since the first option is easier to implement, it&#39;s the one we will choose for this demo client:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">using</span> System;</div><div class="line"><span class="keyword">using</span> System.Diagnostics;</div><div class="line"><span class="keyword">using</span> System.Net;</div><div class="line"><span class="keyword">using</span> System.Threading.Tasks;</div><div class="line"><span class="keyword">using</span> IdentityModel.OidcClient;</div><div class="line"><span class="keyword">using</span> <span class="keyword">static</span> IdentityModel.OidcConstants;</div><div class="line"></div><div class="line"><span class="keyword">namespace</span> <span class="title">OpenIddictClientDemo</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">Program</span></div><div class="line">    &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">async</span> Task <span class="title">Main</span>(<span class="params"><span class="keyword">string</span>[] args</span>)</span></div><div class="line">        &#123;</div><div class="line">            Console.WriteLine(<span class="string">"Press any key to start the authentication process."</span>);</div><div class="line">            Console.ReadKey();</div><div class="line"></div><div class="line">            <span class="comment">// Create a local web server used to receive the authorization response.</span></div><div class="line">            <span class="keyword">using</span> <span class="keyword">var</span> listener = <span class="keyword">new</span> HttpListener();</div><div class="line">            listener.Prefixes.Add(<span class="string">"http://localhost:7890/"</span>);</div><div class="line">            listener.Start();</div><div class="line"></div><div class="line">            <span class="keyword">var</span> options = <span class="keyword">new</span> OidcClientOptions</div><div class="line">            &#123;</div><div class="line">                Authority = <span class="string">"https://localhost:44322/"</span>,</div><div class="line">                ClientId = <span class="string">"console_app"</span>,</div><div class="line">                LoadProfile = <span class="literal">false</span>,</div><div class="line">                RedirectUri = <span class="string">"http://localhost:7890/"</span>,</div><div class="line">                Scope = StandardScopes.OpenId</div><div class="line">            &#125;;</div><div class="line"></div><div class="line">            <span class="keyword">var</span> client = <span class="keyword">new</span> OidcClient(options);</div><div class="line">            <span class="keyword">var</span> state = <span class="keyword">await</span> client.PrepareLoginAsync();</div><div class="line"></div><div class="line">            <span class="comment">// Launch the system browser to initiate the authentication dance.</span></div><div class="line">            Process.Start(<span class="keyword">new</span> ProcessStartInfo</div><div class="line">            &#123;</div><div class="line">                FileName = state.StartUrl,</div><div class="line">                UseShellExecute = <span class="literal">true</span></div><div class="line">            &#125;);</div><div class="line"></div><div class="line">            <span class="comment">// Wait for an authorization response to be posted to the local server.</span></div><div class="line">            <span class="keyword">while</span> (<span class="literal">true</span>)</div><div class="line">            &#123;</div><div class="line">                <span class="keyword">var</span> context = <span class="keyword">await</span> listener.GetContextAsync();</div><div class="line">                context.Response.StatusCode = <span class="number">204</span>;</div><div class="line">                context.Response.Close();</div><div class="line"></div><div class="line">                <span class="keyword">var</span> result = <span class="keyword">await</span> client.ProcessResponseAsync(context.Request.Url.Query, state);</div><div class="line">                <span class="keyword">if</span> (result.IsError)</div><div class="line">                &#123;</div><div class="line">                    Console.WriteLine(<span class="string">"An error occurred: &#123;0&#125;"</span>, result.Error);</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="keyword">else</span></div><div class="line">                &#123;</div><div class="line">                    Console.WriteLine(<span class="string">"\n\nClaims:"</span>);</div><div class="line"></div><div class="line">                    <span class="keyword">foreach</span> (<span class="keyword">var</span> claim <span class="keyword">in</span> result.User.Claims)</div><div class="line">                    &#123;</div><div class="line">                        Console.WriteLine(<span class="string">"&#123;0&#125;: &#123;1&#125;"</span>, claim.Type, claim.Value);</div><div class="line">                    &#125;</div><div class="line"></div><div class="line">                    Console.WriteLine();</div><div class="line">                    Console.WriteLine(<span class="string">"Access token:\n&#123;0&#125;"</span>, result.AccessToken);</div><div class="line"></div><div class="line">                    <span class="keyword">break</span>;</div><div class="line">                &#125;</div><div class="line">            &#125;</div><div class="line"></div><div class="line">            Console.ReadLine();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><h2 id="Testing-the-authentication-process"><a href="#Testing-the-authentication-process" class="headerlink" title="Testing the authentication process"></a>Testing the authentication process</h2><p>For that, start the two applications (server and client). Once the client is started, press a key to start the authentication process. When doing so, the default browser will be launched and you&#39;ll be redirected to the authorization server. If you&#39;re not already logged in, you&#39;ll be immediately invited to authenticate using your Steam account:</p><img src="/2020/02/18/creating-an-openid-connect-server-proxy-with-openiddict-3-0-s-degraded-mode/steam.png" alt="steam.png"><p>After logging in, an authorization response will be returned to the client console, that will automatically send a token request to finish the process:</p><img src="/2020/02/18/creating-an-openid-connect-server-proxy-with-openiddict-3-0-s-degraded-mode/console.png" alt="console.png"><p>And voilà, you&#39;re now ready to create your first APIs! To accept the JWT bearer tokens issued by OpenIddict, don&#39;t forget to decorate your controllers/actions with:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[Authorize(AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme)]</div></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      In this post, discover how to create a minimalist OIDC server proxy between Steam and your own APIs.
    
    </summary>
    
    
      <category term="asp.net core" scheme="https://kevinchalet.com/tags/asp-net-core/"/>
    
      <category term="aspnet-contrib" scheme="https://kevinchalet.com/tags/aspnet-contrib/"/>
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
  </entry>
  
  <entry>
    <title>Forcing an old .NET application to support TLS 1.2 without recompiling it</title>
    <link href="https://kevinchalet.com/2019/04/11/forcing-an-old-net-application-to-support-tls-1-2-without-recompiling-it/"/>
    <id>https://kevinchalet.com/2019/04/11/forcing-an-old-net-application-to-support-tls-1-2-without-recompiling-it/</id>
    <published>2019-04-11T15:30:00.000Z</published>
    <updated>2020-06-02T16:20:33.000Z</updated>
    
    <content type="html"><![CDATA[<p>As most servers are moving toward TLS 1.3 and removing TLS 1.0/1.1 support, examples of legacy .NET applications – compiled with an old version of the .NET Framework, like 4.0 or 4.5 – experiencing connectivity issues with TLS 1.2 servers are becoming more and more common, specially since installing a more recent version of the .NET Framework is not sufficient: <strong>it&#39;s the version used for compiling your project that actually matters</strong> when it comes to selecting the supported TLS versions during the TLS handshake.</p><p>To make migration a bit less painful, <a href="https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls" target="_blank" rel="external">Microsoft published a &quot;transport security best practices&quot; paper</a> that list a few solutions that help avoid handshake errors related to the use of legacy TLS versions that are no longer considered safe.</p><p><strong>One of the proposed solutions is to update your project to target .NET Framework 4.7</strong>: in this case, you&#39;ll have nothing else to do, as .NET 4.7 applications automatically default to whatever the operating system they run on offers and considers safe (which currently includes TLS 1.2 and will later include TLS 1.3).</p><p>Unfortunately, such an option requires re-compiling the application, which is not always feasible. Thankfully, you can also force an existing application to use the system default TLS versions without having to re-compile it (assuming it doesn&#39;t explicitly set the SSL/TLS versions it prefers via <code>ServicePointManager</code>).</p><p>The best practices paper lists a few options, but my favourite one is the one that consists in <strong>simply updating the configuration file associated with the application executable</strong>, as it&#39;s easy to do and doesn&#39;t impact anything else on the machine.</p><p>For that, <strong>locate the configuration file associated to the executable</strong> of the application you want to add TLS 1.2 support to: it&#39;s always named <code>[name of the executable].exe.config</code>. If there&#39;s no such file, create one. Once located or created, <strong>update its content to enable the compatibility switch required to support TLS 1.2</strong>:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">&lt;?xml version="1.0" encoding="utf-8"?&gt;</div><div class="line"><span class="tag">&lt;<span class="name">configuration</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">runtime</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">AppContextSwitchOverrides</span> <span class="attr">value</span>=<span class="string">"Switch.System.Net.DontEnableSystemDefaultTlsVersions=false"</span>/&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">runtime</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></div></pre></td></tr></table></figure><div class="note tip"><p>If you&#39;re still using Windows 7, you&#39;ll also have to tweak the registry to enable TLS 1.2 support, as indicated on <a href="https://docs.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings" target="_blank" rel="external">https://docs.microsoft.com/en-us/windows-server/security/tls/tls-registry-settings</a>.</p></div>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;As most servers are moving toward TLS 1.3 and removing TLS 1.0/1.1 support, examples of legacy .NET applications – compiled with an old v
    
    </summary>
    
    
      <category term=".net" scheme="https://kevinchalet.com/tags/net/"/>
    
      <category term="tls" scheme="https://kevinchalet.com/tags/tls/"/>
    
      <category term="windows" scheme="https://kevinchalet.com/tags/windows/"/>
    
  </entry>
  
  <entry>
    <title>OpenIddict 1.0 and 2.0 general availability</title>
    <link href="https://kevinchalet.com/2018/11/01/openiddict-1-0-and-2-0-general-availability/"/>
    <id>https://kevinchalet.com/2018/11/01/openiddict-1-0-and-2-0-general-availability/</id>
    <published>2018-11-01T20:00:00.000Z</published>
    <updated>2018-11-01T19:36:44.000Z</updated>
    
    <content type="html"><![CDATA[<p>I&#39;m very happy to announce that <strong>OpenIddict 1.0 and 2.0 final packages were pushed earlier today</strong> to <a href="https://www.nuget.org/profiles/openiddict" target="_blank" rel="external">NuGet.org</a> and are now officially available!</p><h2 id="What-changed-since-RC3"><a href="#What-changed-since-RC3" class="headerlink" title="What changed since RC3?"></a>What changed since RC3?</h2><h3 id="A-security-bug-was-fixed"><a href="#A-security-bug-was-fixed" class="headerlink" title="A security bug was fixed"></a>A security bug was fixed</h3><p>A few days ago, <a href="https://github.com/openiddict/openiddict-core/issues/682" target="_blank" rel="external"><strong>a vulnerability impacting application permissions was publicly reported</strong></a> by <a href="https://github.com/nurhat" target="_blank" rel="external">nurhat</a> on GitHub (many thanks to him!).</p><p>In a nutshell, <strong>scope permissions were not correctly enforced for public clients</strong> using the password flow and custom grant types (confidential clients or clients using the code or client credentials flows were not impacted).</p><p><strong>A fix was immediately added to the nightly builds and is present in the RTM release</strong>.</p><h3 id="Built-in-entity-caches-are-now-included"><a href="#Built-in-entity-caches-are-now-included" class="headerlink" title="Built-in entity caches are now included"></a>Built-in entity caches are now included</h3><p><strong>OpenIddict now comes with built-in entity caching</strong> to avoid having to send multiple requests to retrieve the same entities. Concretely, if your <code>AuthorizationController</code> uses APIs like <code>OpenIddictApplicationManager.FindByClientIdAsync(request.ClientId)</code>, the corresponding application will be directly retrieved from the cache and the resulting operation will be extremely cheap.</p><p>To ensure this feature works with non-thread-safe stores and stores that rely on context-affinity (like Entity Framework 6.x or Entity Framework Core), these built-in caches are scoped so that cached entities are not reused across requests.</p><p>While definitely not recommended, this feature can be disabled via the OpenIddict core options:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddCore(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// ...</span></div><div class="line"></div><div class="line">        options.DisableEntityCaching();</div><div class="line">    &#125;)</div></pre></td></tr></table></figure><a id="more"></a><h3 id="The-event-model-was-slightly-reworked"><a href="#The-event-model-was-slightly-reworked" class="headerlink" title="The event model was slightly reworked"></a>The event model was slightly reworked</h3><p>Based on feedback, <strong>the event model used by the server and validation handlers was slightly reworked</strong> so that it&#39;s now more explicit whether next handlers are allowed to be invoked by OpenIddict or not.</p><p>Concretely, both <code>IOpenIddictServerEventHandler.HandleAsync()</code> and <code>IOpenIddictValidationEventHandler.HandleAsync()</code> now return an enum value indicating whether the other handlers can be invoked. Here&#39;s an example of the new syntax:</p><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div></pre></td><td class="code"><pre><div class="line">public class PasswordGrantTypeEventHandler : IOpenIddictServerEventHandler&lt;HandleTokenRequest&gt;</div><div class="line">&#123;</div><div class="line">    public Task&lt;OpenIddictServerEventState&gt; HandleAsync(HandleTokenRequest notification)</div><div class="line">    &#123;</div><div class="line">        var request = notification.Context.Request;</div><div class="line">        if (!request.IsPasswordGrantType())</div><div class="line">        &#123;</div><div class="line">            // Allow other handlers to process the event.</div><div class="line">            return Task.FromResult(OpenIddictServerEventState.Unhandled);</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        // Validate the user credentials.</div><div class="line">        // Note: to mitigate brute force attacks, you SHOULD strongly consider</div><div class="line">        // applying a key derivation function like PBKDF2 to slow down</div><div class="line">        // the password validation process. You SHOULD also consider</div><div class="line">        // using a time-constant comparer to prevent timing attacks.</div><div class="line">        if (request.Username != "alice@wonderland.com" || request.Password != "P@ssw0rd")</div><div class="line">        &#123;</div><div class="line">            notification.Context.Reject(</div><div class="line">                error: OpenIddictConstants.Errors.InvalidGrant,</div><div class="line">                description: "The specified credentials are invalid.");</div><div class="line"></div><div class="line">            // Don't allow other handlers to process the event.</div><div class="line">            return Task.FromResult(OpenIddictServerEventState.Handled);</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        // Create a new ClaimsIdentity holding the user identity.</div><div class="line">        var identity = new ClaimsIdentity(</div><div class="line">            notification.Context.Scheme.Name,</div><div class="line">            OpenIddictConstants.Claims.Name,</div><div class="line">            OpenIddictConstants.Claims.Role);</div><div class="line"></div><div class="line">        // Add a "sub" claim containing the user identifier, and attach</div><div class="line">        // the "access_token" destination to allow OpenIddict to store it</div><div class="line">        // in the access token, so it can be retrieved from your controllers.</div><div class="line">        identity.AddClaim(OpenIddictConstants.Claims.Subject,</div><div class="line">            "71346D62-9BA5-4B6D-9ECA-755574D628D8",</div><div class="line">            OpenIddictConstants.Destinations.AccessToken);</div><div class="line"></div><div class="line">        identity.AddClaim(OpenIddictConstants.Claims.Name, "Alice",</div><div class="line">            OpenIddictConstants.Destinations.AccessToken);</div><div class="line"></div><div class="line">        // ... add other claims, if necessary.</div><div class="line">        var principal = new ClaimsPrincipal(identity);</div><div class="line"></div><div class="line">        var ticket = new AuthenticationTicket(principal, notification.Context.Scheme.Name);</div><div class="line">        ticket.SetScopes(OpenIddictConstants.Scopes.OfflineAccess);</div><div class="line">        notification.Context.Validate(ticket);</div><div class="line"></div><div class="line">        // Don't allow other handlers to process the event.</div><div class="line">        return Task.FromResult(OpenIddictServerEventState.Handled);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line">public class RefreshTokenGrantTypeEventHandler : IOpenIddictServerEventHandler&lt;HandleTokenRequest&gt;</div><div class="line">&#123;</div><div class="line">    public async Task&lt;OpenIddictServerEventState&gt; HandleAsync(HandleTokenRequest notification)</div><div class="line">    &#123;</div><div class="line">        var request = notification.Context.Request;</div><div class="line">        if (!request.IsRefreshTokenGrantType())</div><div class="line">        &#123;</div><div class="line">            // Allow other handlers to process the event.</div><div class="line">            return OpenIddictServerEventState.Unhandled;</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        var scheme = notification.Context.Scheme.Name;</div><div class="line">        var principal = (await notification.Context.HttpContext.AuthenticateAsync(scheme))?.Principal;</div><div class="line"></div><div class="line">        var ticket = new AuthenticationTicket(principal, scheme);</div><div class="line">        notification.Context.Validate(ticket);</div><div class="line"></div><div class="line">        // Don't allow other handlers to process the event.</div><div class="line">        return OpenIddictServerEventState.Handled;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line">    .AddCore(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// ...</span></div><div class="line">    &#125;)</div><div class="line"></div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// ...</span></div><div class="line">        options.AddEventHandler&lt;PasswordGrantTypeEventHandler&gt;()</div><div class="line">               .AddEventHandler&lt;RefreshTokenGrantTypeEventHandler&gt;();</div><div class="line">    &#125;)</div><div class="line"></div><div class="line">    .AddValidation();</div></pre></td></tr></table></figure><h2 id="What-package-s-should-I-reference"><a href="#What-package-s-should-I-reference" class="headerlink" title="What package(s) should I reference?"></a>What package(s) should I reference?</h2><p><strong>OpenIddict supports both ASP.NET Core 1.x and 2.x</strong> so if you&#39;re still on the former version, no need to hurry: both versions basically offer the same feature set, with only a few API differences. For clarity, the OpenIddict packages use the <code>1.x</code> pattern for the ASP.NET Core 1.x-compatible version and <code>2.x</code> for ASP.NET Core 2.x.</p><p>Here&#39;s the complete list of packages published as part of this release:</p><table><thead><tr><th style="text-align:center">ASP.NET Core version</th><th style="text-align:center">Package name</th><th style="text-align:center">Package version</th><th style="text-align:center">Package description</th></tr></thead><tbody><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">References the OpenIddict abstractions, core, server and validation packages.</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.Abstractions</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the common managers/stores interfaces used by OpenIddict.</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.Core</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the default managers implementations used by OpenIddict.</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.EntityFramework</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the Entity Framework 6.x stores (only compatible with .NET Framework 4.5.1).</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.EntityFramework.Models</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the Entity Framework 6.x models.</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.EntityFrameworkCore</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the Entity Framework Core stores.</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.EntityFrameworkCore.Models</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the Entity Framework Core models.</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.MongoDb</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the MongoDB 2.7.0 stores.</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.MongoDb.Models</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the MongoDB 2.7.0 models.</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.Mvc</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the OpenIddict/ASP.NET Core MVC integration components.</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.Server</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the OpenIddict server services.</td></tr><tr><td style="text-align:center"><strong>1.x</strong></td><td style="text-align:center">OpenIddict.Validation</td><td style="text-align:center"><strong>1.0.0</strong></td><td style="text-align:center">Contains the OpenIddict validation services.</td></tr><tr><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">References the OpenIddict abstractions, core, server and validation packages.</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.Abstractions</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the common managers/stores interfaces used by OpenIddict.</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.Core</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the default managers implementations used by OpenIddict.</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.EntityFramework</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the Entity Framework 6.x stores (only compatible with .NET Framework 4.6.1).</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.EntityFramework.Models</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the Entity Framework 6.x models.</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.EntityFrameworkCore</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the Entity Framework Core stores.</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.EntityFrameworkCore.Models</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the Entity Framework Core models.</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.MongoDb</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the MongoDB 2.7.0 stores.</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.MongoDb.Models</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the MongoDB 2.7.0 models.</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.Mvc</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the OpenIddict/ASP.NET Core MVC integration components.</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.Server</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the OpenIddict server services.</td></tr><tr><td style="text-align:center"><strong>2.x</strong></td><td style="text-align:center">OpenIddict.Validation</td><td style="text-align:center"><strong>2.0.0</strong></td><td style="text-align:center">Contains the OpenIddict validation services.</td></tr></tbody></table><h2 id="Support-lifecycle"><a href="#Support-lifecycle" class="headerlink" title="Support lifecycle"></a>Support lifecycle</h2><p>Both OpenIddict 1.0 and 2.0 will be supported for as long as the ASP.NET Core version they are written for gets updates from Microsoft. You can find their <a href="https://www.microsoft.com/net/platform/support-policy" target="_blank" rel="external">support policy on Microsoft.com</a>.</p><h2 id="What-39-s-next"><a href="#What-39-s-next" class="headerlink" title="What&#39;s next?"></a>What&#39;s next?</h2><p>While I&#39;ll probably mostly focus on improving OrchardCore&#39;s OpenID module (which is based on OpenIddict) during the next few weeks, OpenIddict itself will also get updates, including NHibernate 5 stores (that will likely be OpenIddict 2.0-only as NHibernate doesn&#39;t offer a <code>netstandard1.x</code> TFM that would be required to work with .NET Core 1.x).</p><p>Depending on the demand, stores for RavenDB or other databases might also be part of the next update. Don&#39;t hesitate to contact me if you&#39;d like to see a particular database supported in the next version.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;I&amp;#39;m very happy to announce that &lt;strong&gt;OpenIddict 1.0 and 2.0 final packages were pushed earlier today&lt;/strong&gt; to &lt;a href=&quot;https://www.nuget.org/profiles/openiddict&quot;&gt;NuGet.org&lt;/a&gt; and are now officially available!&lt;/p&gt;&lt;h2 id=&quot;What-changed-since-RC3&quot;&gt;&lt;a href=&quot;#What-changed-since-RC3&quot; class=&quot;headerlink&quot; title=&quot;What changed since RC3?&quot;&gt;&lt;/a&gt;What changed since RC3?&lt;/h2&gt;&lt;h3 id=&quot;A-security-bug-was-fixed&quot;&gt;&lt;a href=&quot;#A-security-bug-was-fixed&quot; class=&quot;headerlink&quot; title=&quot;A security bug was fixed&quot;&gt;&lt;/a&gt;A security bug was fixed&lt;/h3&gt;&lt;p&gt;A few days ago, &lt;a href=&quot;https://github.com/openiddict/openiddict-core/issues/682&quot;&gt;&lt;strong&gt;a vulnerability impacting application permissions was publicly reported&lt;/strong&gt;&lt;/a&gt; by &lt;a href=&quot;https://github.com/nurhat&quot;&gt;nurhat&lt;/a&gt; on GitHub (many thanks to him!).&lt;/p&gt;&lt;p&gt;In a nutshell, &lt;strong&gt;scope permissions were not correctly enforced for public clients&lt;/strong&gt; using the password flow and custom grant types (confidential clients or clients using the code or client credentials flows were not impacted).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;A fix was immediately added to the nightly builds and is present in the RTM release&lt;/strong&gt;.&lt;/p&gt;&lt;h3 id=&quot;Built-in-entity-caches-are-now-included&quot;&gt;&lt;a href=&quot;#Built-in-entity-caches-are-now-included&quot; class=&quot;headerlink&quot; title=&quot;Built-in entity caches are now included&quot;&gt;&lt;/a&gt;Built-in entity caches are now included&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;OpenIddict now comes with built-in entity caching&lt;/strong&gt; to avoid having to send multiple requests to retrieve the same entities. Concretely, if your &lt;code&gt;AuthorizationController&lt;/code&gt; uses APIs like &lt;code&gt;OpenIddictApplicationManager.FindByClientIdAsync(request.ClientId)&lt;/code&gt;, the corresponding application will be directly retrieved from the cache and the resulting operation will be extremely cheap.&lt;/p&gt;&lt;p&gt;To ensure this feature works with non-thread-safe stores and stores that rely on context-affinity (like Entity Framework 6.x or Entity Framework Core), these built-in caches are scoped so that cached entities are not reused across requests.&lt;/p&gt;&lt;p&gt;While definitely not recommended, this feature can be disabled via the OpenIddict core options:&lt;/p&gt;&lt;figure class=&quot;highlight csharp&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;5&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;6&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;7&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;services.AddOpenIddict()&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    .AddCore(options =&amp;gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &amp;#123;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        &lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        options.DisableEntityCaching();&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &amp;#125;)&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="Development" scheme="https://kevinchalet.com/categories/development/"/>
    
    
      <category term="asp.net core" scheme="https://kevinchalet.com/tags/asp-net-core/"/>
    
      <category term="aspnet-contrib" scheme="https://kevinchalet.com/tags/aspnet-contrib/"/>
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
  </entry>
  
  <entry>
    <title>Highway to (DLL) Hell</title>
    <link href="https://kevinchalet.com/2018/10/07/highway-to-dll-hell/"/>
    <id>https://kevinchalet.com/2018/10/07/highway-to-dll-hell/</id>
    <published>2018-10-07T13:00:00.000Z</published>
    <updated>2018-10-07T14:00:29.000Z</updated>
    
    <content type="html"><![CDATA[<p>It&#39;s no secret that <strong>I&#39;ve always been a huge fan of <a href="https://en.wikipedia.org/wiki/Windows_Media_Center" target="_blank" rel="external">Windows Media Center</a></strong>, probably the best digital/personal video recorder out there and definitely one of the most impressive Microsoft applications developed using the .NET Framework.</p><p>As an avid WMC user, I have built 3 <a href="https://en.wikipedia.org/wiki/Home_theater_PC" target="_blank" rel="external">HTPC machines</a> and recorded thousands of TV programs (movies, series, documentaries, etc.). As such, when Microsoft announced in May 2015 that Windows Media Center would no longer be developed (almost 6 year after disbanding the development team!) – and thus would not be part of Windows 10 – <strong>I refused to migrate to Windows 10</strong>, despite Microsoft&#39;s offers encouraging users to upgrade their OS for free.</p><p>The fact Windows Media Center was not available on Windows 10 – even as a paid feature like it was on Windows 8.x – <strong>was a major blocker for many users</strong>. To work around this limitation, <a href="https://forums.mydigitallife.net/threads/discussion-patch-wmc-to-run-on-windows-10-final-possible-alternatives.61061/" target="_blank" rel="external">enthusiasts decided to create an unofficial port of Windows Media Center for Windows 10</a>.</p><p>While unofficial, the port worked just fine. <strong>But over time, Microsoft started to update some of the system components Windows Media Center relied on</strong>, causing annoying bugs. For instance, the introduction of breaking changes in Windows 10 1803 <a href="https://forums.mydigitallife.net/threads/discussion-patch-wmc-to-run-on-windows-10-final-possible-alternatives.61061/page-436#post-1424922" target="_blank" rel="external">made watching a <code>.wtv</code> file (WMC&#39;s TV file format) recorded on Windows 7 completely impossible</a>.</p><p>While investigating, I discovered that the issue was caused by a change in <code>MSVidCtl.dll</code>, the system-wide DLL containing the DirectShow components needed by WMC for all its TV-related features. After replacing the faulty DLL by an older version, WMC was able to play old recordings like a charm.</p><p>This phenomenom, that occurs every time API or functional changes are introduced in a DLL a program depends on, has a name: <a href="https://en.wikipedia.org/wiki/DLL_Hell" target="_blank" rel="external"><strong>DLL Hell</strong></a>.</p><a id="more"></a><h2 id="You-shall-not-replace-system-DLLs"><a href="#You-shall-not-replace-system-DLLs" class="headerlink" title="You shall not replace system DLLs"></a>You shall not replace system DLLs</h2><p>The thing is, <strong>replacing a system-owned DLL is far from ideal</strong>: while it solves the initial problem quite easily, it&#39;s not future-proof as any major Windows update will end up overwriting the replaced DLL. The same thing will happen if the user (or the system on his behalf) runs the <code>sfc.exe</code> utility, that will detect the modification and revert it. It may also cause issues in other applications depending on new APIs that are only part of the newest version of the replaced DLL.</p><p>Since replacing a system DLL was not a good option, <strong>I eventually opted for the other approach: forcing Windows Media Center to load its own version of <code>MSVidCtl.dll</code> instead of the global one</strong> stored in <code>System32</code>.</p><p>Typically, you&#39;d achieve that by simply dropping the DLL in the application&#39;s root folder (in our case, <code>C:\Windows\ehome</code>). This usually works because the DLL loader first looks for the DLL <a href="https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order" target="_blank" rel="external">in the directory the application was loaded from</a>.</p><h2 id="Enter-the-marvelous-world-of-COM"><a href="#Enter-the-marvelous-world-of-COM" class="headerlink" title="Enter the marvelous world of COM"></a>Enter the marvelous world of COM</h2><p><strong>In this case, this didn&#39;t work</strong>: Windows Media Center kept loading the <code>MSVidCtl</code> DLL contained in the <code>System32</code> folder. Why? Because <code>MSVidCtl.dll</code> is actually a very special DLL: it&#39;s a <a href="https://en.wikipedia.org/wiki/Component_Object_Model" target="_blank" rel="external">Component Object Model</a> DLL. Unlike other DLLs, <strong>COM DLLs are almost never loaded via <code>LoadLibrary</code></strong> (at least, not directly): instead, they are assigned a GUID that corresponds to a special entry in the registry that contains the absolute path of the COM DLL. When an application wants to instantiate a COM component, it usually imports <code>Ole32.dll</code> and calls <code>CoCreateInstance</code> with the unique GUID: the COM loader locates the entry in the registry and loads the DLL from the associated path.</p><p>Since the path is absolute, trying to add a <code>MSVidCtl.dll</code> in the <code>ehome</code> folder was completely pointless: the loader would never look for it.</p><h2 id="DLL-redirection-and-side-by-side-assemblies"><a href="#DLL-redirection-and-side-by-side-assemblies" class="headerlink" title="DLL redirection and side-by-side assemblies"></a>DLL redirection and side-by-side assemblies</h2><p>To mitigate this issue, Microsoft offers 2 mechanisms that allow overriding the default search order used by Windows&#39; DLL loader even when the specified path is absolute:</p><ul><li><p><a href="https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-redirection" target="_blank" rel="external">DLL redirection</a>: by creating an empty <code>.local</code> file/folder whose name is the same as the executable (in our case, <code>ehshell.exe.local</code>), one can easily force the OS to load DLLs from the application folder: <strong>it&#39;s dead simple and works flawlessly, even for COM DLLs</strong>. <strong>Unfortunately, it has a major caveat: it doesn&#39;t work when the application has an application manifest</strong> (there&#39;s a registry switch to override that but turning it on would impact the entire system, certainly not a good thing to do).</p></li><li><p><a href="https://en.wikipedia.org/wiki/Side-by-side_assembly" target="_blank" rel="external">Side-by-side assemblies</a>: first introduced in Windows 98 Second Edition, it allows an application to define its dependencies in its application manifest (and explicitly opt for a specific DLL version). DLL dependencies can be either stored in the same directory as the application (in this case, they are known as private assemblies) or in a special shared directory called <code>winsxs</code>. <strong>Starting with Windows XP, it also allows defining registration-free COM components</strong>: unlike classical COM components, they don&#39;t have an entry in the registry and thus are not registered globally. Instead, they are defined in the application manifest: when the COM loader finds an entry in the manifest for the specified GUID, it loads the corresponding DLL and doesn&#39;t apply the default registry loading routine.</p></li></ul><div class="note tip"><p>An application manifest is a special XML file that is either embedded in the executable or provided separately and that defines some core aspects of the application (e.g does it need to be executed with administrator rights?).</p></div><p>In our case, the first option is sadly not applicable: all the Windows Media Center executables come with an embedded manifest used to enable <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/mt846517%28v=vs.85%29.aspx" target="_blank" rel="external">the DPI-aware mode</a>. For instance, here&#39;s the manifest embedded in <code>ehshell.exe</code>:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;</div><div class="line"><span class="comment">&lt;!-- Copyright (c) Microsoft Corporation --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">assembly</span> <span class="attr">xmlns</span>=<span class="string">"urn:schemas-microsoft-com:asm.v3"</span> <span class="attr">xmlns:asmv3</span>=<span class="string">"urn:schemas-microsoft-com:asm.v3"</span> <span class="attr">manifestVersion</span>=<span class="string">"1.0"</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">assemblyIdentity</span> <span class="attr">name</span>=<span class="string">"Microsoft.Windows.MultiMedia.EhShell"</span> <span class="attr">processorArchitecture</span>=<span class="string">"amd64"</span> <span class="attr">version</span>=<span class="string">"5.1.0.0"</span> <span class="attr">type</span>=<span class="string">"win32"</span>/&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">description</span>&gt;</span>Windows Media Center Shell<span class="tag">&lt;/<span class="name">description</span>&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">application</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">asmv3:windowsSettings</span> <span class="attr">xmlns</span>=<span class="string">"http://schemas.microsoft.com/SMI/2005/WindowsSettings"</span>&gt;</span> </div><div class="line">            <span class="tag">&lt;<span class="name">dpiAware</span>&gt;</span>true<span class="tag">&lt;/<span class="name">dpiAware</span>&gt;</span> </div><div class="line">    <span class="tag">&lt;/<span class="name">asmv3:windowsSettings</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">application</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="comment">&lt;!-- Identify the application security requirements. --&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">trustInfo</span> <span class="attr">xmlns</span>=<span class="string">"urn:schemas-microsoft-com:asm.v3"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">security</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">requestedPrivileges</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">requestedExecutionLevel</span></span></div><div class="line">          <span class="attr">level</span>=<span class="string">"asInvoker"</span></div><div class="line">          <span class="attr">uiAccess</span>=<span class="string">"false"</span>/&gt;</div><div class="line">        <span class="tag">&lt;/<span class="name">requestedPrivileges</span>&gt;</span></div><div class="line">       <span class="tag">&lt;/<span class="name">security</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">trustInfo</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">assembly</span>&gt;</span></div></pre></td></tr></table></figure><p>Removing the manifest to make the <code>.local</code> approach work would be very easy, but we&#39;d loose all the benefits of the DPI-aware mode: definitely not an option.</p><h2 id="Defining-registration-free-COM-components"><a href="#Defining-registration-free-COM-components" class="headerlink" title="Defining registration-free COM components"></a>Defining registration-free COM components</h2><p>While powerful, defining registration-free COM components is far from being trivial and is rather error-prone.</p><h3 id="Extracting-the-TypeLib-tables"><a href="#Extracting-the-TypeLib-tables" class="headerlink" title="Extracting the TypeLib tables"></a>Extracting the <code>TypeLib</code> tables</h3><p>First, <a href="https://ingeno.io/2016/09/registration-free-com-component-activation/" target="_blank" rel="external">you need to extract the <code>TypeLib</code> table from the COM DLL you want to use</a>. For convenience, it&#39;s almost always embedded as a binary resource in the COM DLLs themselves. To extract these resources, you can use the popular <a href="http://www.angusj.com/resourcehacker/" target="_blank" rel="external">Resource Hacker</a> tool. In some cases, a single COM DLL might expose multiple <code>TypeLib</code> tables: you&#39;ll have to extract them all and merge them later. <code>MSVidCtl.dll</code> is one of them:</p><img src="/2018/10/07/highway-to-dll-hell/resource-hacker.png" alt="resource-hacker.png"><h3 id="Generating-assembly-manifests"><a href="#Generating-assembly-manifests" class="headerlink" title="Generating assembly manifests"></a>Generating assembly manifests</h3><p>Then, you need to use the Manifest Tool coming with Visual Studio to generate the manifest files from the COM definitions.</p><p>For that, open a VS command prompt and run the following commands:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">mt -tlb:TYPELIB1.bin -dll:MSVidCtl.dll -out:Manifest1.manifest</div><div class="line">mt -tlb:TYPELIB2.bin -dll:MSVidCtl.dll -out:Manifest2.manifest</div></pre></td></tr></table></figure><h3 id="Merging-assembly-manifests"><a href="#Merging-assembly-manifests" class="headerlink" title="Merging assembly manifests"></a>Merging assembly manifests</h3><p>In a perfect world, you&#39;d also use <code>mt.exe</code> to merge the 2 manifests into a single one:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mt -manifest:Manifest1.manifest -manifest:Manifest2.manifest -out:Microsoft.Windows.Video.Control.manifest</div></pre></td></tr></table></figure><p>Unfortunately, <strong>the two manifests actually contain multiple elements with the same <code>TypeLib</code> identifier</strong>, making them impossible to merge automatically:</p><blockquote><p>Manifest2.manifest : manifest authoring error c1010001: Values of attribute &quot;tlbid&quot; not equal in different manifest snippets.</p></blockquote><p>To work around this limitation, <strong>I manually merged the two XML files using Notepad++ and wrote a tiny C# script to remove the duplicate entries</strong>:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> path = <span class="string">@"[path pointing to the merged XML manifest file]"</span>;</div><div class="line"><span class="keyword">var</span> document = XDocument.Load(path);</div><div class="line"></div><div class="line"><span class="keyword">foreach</span> (<span class="keyword">var</span> stub <span class="keyword">in</span> document.Root.Elements(XName.Get(<span class="string">"comInterfaceExternalProxyStub"</span>, <span class="string">"urn:schemas-microsoft-com:asm.v1"</span>)).ToList())</div><div class="line">&#123;</div><div class="line">    <span class="keyword">var</span> name = stub.Attribute(<span class="string">"name"</span>);</div><div class="line">    <span class="keyword">if</span> (<span class="keyword">string</span>.IsNullOrEmpty(name?.Value))</div><div class="line">    &#123;</div><div class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> InvalidOperationException();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">var</span> duplicates = document.Root.Elements(XName.Get(<span class="string">"comInterfaceExternalProxyStub"</span>, <span class="string">"urn:schemas-microsoft-com:asm.v1"</span>))</div><div class="line">        .Where(element =&gt; element.Attributes()</div><div class="line">            .Any(attribute =&gt; attribute.Name == <span class="string">"name"</span> &amp;&amp; attribute.Value == name.Value))</div><div class="line">        .ToArray();</div><div class="line"></div><div class="line">    <span class="keyword">if</span> (duplicates.Length &gt; <span class="number">1</span>)</div><div class="line">    &#123;</div><div class="line">        duplicates[<span class="number">0</span>].SetAttributeValue(<span class="string">"tlbid"</span>, <span class="literal">null</span>);</div><div class="line"></div><div class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> index = <span class="number">1</span>; index &lt; duplicates.Length; index++)</div><div class="line">        &#123;</div><div class="line">            duplicates[index].Remove();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line">document.Save(path);</div></pre></td></tr></table></figure><h3 id="Fixing-the-resulting-manifest"><a href="#Fixing-the-resulting-manifest" class="headerlink" title="Fixing the resulting manifest"></a>Fixing the resulting manifest</h3><p>This is not done yet: <strong>if you try to use the resulting manifest as-is, Windows Media Center will simply crash</strong> as the <code>mt</code> utility misses an important part of the COM definitions: <a href="https://msdn.microsoft.com/en-us/library/ms809971.aspx" target="_blank" rel="external">the threading model of the COM classes</a>.</p><p>The good news is that <strong>this information can be retrived from the registry of a Windows 7 machine</strong> (or on Windows 10 pre-1803). To make that process much easier, I wrote a second tiny script updating the manifest from the data found in the registry:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> path = <span class="string">@"[path pointing to the merged XML manifest file]"</span>;</div><div class="line"><span class="keyword">var</span> document = XDocument.Load(path);</div><div class="line"></div><div class="line"><span class="keyword">foreach</span> (<span class="keyword">var</span> file <span class="keyword">in</span> document.Root.Elements(XName.Get(<span class="string">"file"</span>, <span class="string">"urn:schemas-microsoft-com:asm.v1"</span>)))</div><div class="line">&#123;</div><div class="line">    <span class="keyword">foreach</span> (<span class="keyword">var</span> component <span class="keyword">in</span> file.Elements(XName.Get(<span class="string">"comClass"</span>, <span class="string">"urn:schemas-microsoft-com:asm.v1"</span>)))</div><div class="line">    &#123;</div><div class="line">        <span class="keyword">var</span> id = component.Attribute(<span class="string">"clsid"</span>);</div><div class="line"></div><div class="line">        <span class="keyword">using</span> (<span class="keyword">var</span> root = Registry.ClassesRoot.OpenSubKey(<span class="string">"CLSID"</span>))</div><div class="line">        <span class="keyword">using</span> (<span class="keyword">var</span> clsid = root.OpenSubKey(id.Value))</div><div class="line">        <span class="keyword">using</span> (<span class="keyword">var</span> ipc = clsid?.OpenSubKey(<span class="string">"InprocServer32"</span>))</div><div class="line">        &#123;</div><div class="line">            <span class="keyword">var</span> model = ipc?.GetValue(<span class="string">"ThreadingModel"</span>).ToString();</div><div class="line">            <span class="keyword">if</span> (<span class="keyword">string</span>.IsNullOrEmpty(model))</div><div class="line">            &#123;</div><div class="line">                <span class="keyword">continue</span>;</div><div class="line">            &#125;</div><div class="line"></div><div class="line">            component.SetAttributeValue(<span class="string">"threadingModel"</span>, model);</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line">document.Save(path);</div><div class="line"></div></pre></td></tr></table></figure><p>After cleaning the XML manifest a bit, here&#39;s what I ended up with:</p><figure class="highlight xml"><figcaption><span>Microsoft.Windows.Video.Control.manifest</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div><div class="line">125</div><div class="line">126</div><div class="line">127</div><div class="line">128</div><div class="line">129</div><div class="line">130</div><div class="line">131</div><div class="line">132</div><div class="line">133</div><div class="line">134</div><div class="line">135</div><div class="line">136</div><div class="line">137</div><div class="line">138</div><div class="line">139</div><div class="line">140</div><div class="line">141</div><div class="line">142</div><div class="line">143</div><div class="line">144</div><div class="line">145</div><div class="line">146</div><div class="line">147</div><div class="line">148</div><div class="line">149</div><div class="line">150</div><div class="line">151</div><div class="line">152</div><div class="line">153</div><div class="line">154</div><div class="line">155</div><div class="line">156</div><div class="line">157</div><div class="line">158</div><div class="line">159</div><div class="line">160</div><div class="line">161</div><div class="line">162</div><div class="line">163</div><div class="line">164</div><div class="line">165</div><div class="line">166</div><div class="line">167</div><div class="line">168</div><div class="line">169</div><div class="line">170</div><div class="line">171</div><div class="line">172</div><div class="line">173</div><div class="line">174</div><div class="line">175</div><div class="line">176</div><div class="line">177</div><div class="line">178</div><div class="line">179</div><div class="line">180</div><div class="line">181</div><div class="line">182</div><div class="line">183</div><div class="line">184</div><div class="line">185</div><div class="line">186</div><div class="line">187</div><div class="line">188</div><div class="line">189</div><div class="line">190</div><div class="line">191</div><div class="line">192</div><div class="line">193</div><div class="line">194</div><div class="line">195</div><div class="line">196</div><div class="line">197</div><div class="line">198</div><div class="line">199</div><div class="line">200</div><div class="line">201</div><div class="line">202</div><div class="line">203</div><div class="line">204</div><div class="line">205</div><div class="line">206</div><div class="line">207</div><div class="line">208</div><div class="line">209</div><div class="line">210</div><div class="line">211</div><div class="line">212</div><div class="line">213</div><div class="line">214</div><div class="line">215</div><div class="line">216</div><div class="line">217</div><div class="line">218</div><div class="line">219</div><div class="line">220</div><div class="line">221</div><div class="line">222</div><div class="line">223</div><div class="line">224</div><div class="line">225</div><div class="line">226</div><div class="line">227</div><div class="line">228</div><div class="line">229</div><div class="line">230</div><div class="line">231</div><div class="line">232</div><div class="line">233</div><div class="line">234</div><div class="line">235</div><div class="line">236</div><div class="line">237</div><div class="line">238</div><div class="line">239</div><div class="line">240</div><div class="line">241</div><div class="line">242</div><div class="line">243</div><div class="line">244</div><div class="line">245</div><div class="line">246</div><div class="line">247</div><div class="line">248</div><div class="line">249</div><div class="line">250</div><div class="line">251</div><div class="line">252</div><div class="line">253</div><div class="line">254</div><div class="line">255</div><div class="line">256</div><div class="line">257</div><div class="line">258</div><div class="line">259</div><div class="line">260</div><div class="line">261</div><div class="line">262</div><div class="line">263</div><div class="line">264</div><div class="line">265</div><div class="line">266</div><div class="line">267</div><div class="line">268</div><div class="line">269</div><div class="line">270</div><div class="line">271</div><div class="line">272</div><div class="line">273</div><div class="line">274</div><div class="line">275</div><div class="line">276</div><div class="line">277</div><div class="line">278</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">assembly</span> <span class="attr">xmlns</span>=<span class="string">"urn:schemas-microsoft-com:asm.v1"</span> <span class="attr">manifestVersion</span>=<span class="string">"1.0"</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">assemblyIdentity</span> <span class="attr">name</span>=<span class="string">"Microsoft.Windows.Video.Control"</span> <span class="attr">version</span>=<span class="string">"1.0.0.0"</span> /&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">file</span> <span class="attr">name</span>=<span class="string">"MSVidCtl.dll"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;1C15D484-911D-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Analog TV Tuner Device Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;A2E3074E-6C3D-11D3-B653-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control BDA Tuner Device Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;37B0353C-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control File Playback Device Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;011B3619-FE63-4814-8A84-15A194CE9CE3&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MSVidWebDVD Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;FA7C375B-66A7-4280-879D-FD459C84BB02&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MSVidWebDVDAdm Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;37B03543-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Video Renderer(DX7/8) Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;24DC3975-09BF-4231-8655-3EE71F43837D&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Video Renderer 9(DX9) Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C45268A2-FA81-4E19-B1E3-72EDBD60AEDA&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Enhanced Video Renderer(DX10) Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;37B03544-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Audio Renderer Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;4A5869CF-929D-4040-AE03-FCAFC5B9CD42&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Stream Buffer Engine Sink Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;9E77AAC4-35E5-42A1-BDC2-8F3FF399847C&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Stream Buffer Engine Sink Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;AD8E510D-217F-409B-8076-29C5E73B98E8&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Stream Buffer Engine Playback Device Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;FD351EA1-4173-4AF4-821D-80D4AE979048&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Stream Buffer Engine V2 Playback Device Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;BB530C63-D9DF-4B49-9439-63453962E598&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Encoder"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;5740A302-EF0B-45CE-BF3B-4470A14A8980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control iTV Capture"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;9E797ED0-5253-4243-A9B7-BD06C58F8EF3&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control iTV Playback"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;86151827-E47B-45EE-8421-D10E6E690979&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Closed Captions Analysis"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;7F9CB14D-48E4-43B6-9346-1AEBC39C64D3&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Standard Closed Captioning"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;92ED88BF-879E-448F-B6B6-A385BCEB846D&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control CCSI Closed Captioning"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;334125C0-77E5-11D3-B653-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Standard Data Services for Broadcast IP through NDIS stack"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;0149EEDF-D08F-4142-8D73-D23903D21E90&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Encoder"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C5702CD6-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Analog Capture to Data Services"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;38F03426-E83B-4E68-B65B-DCAE73304838&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Data Services to Stream Buffer Sink"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;0429EC6E-1144-4BED-B88B-2FB9899A4A3D&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for DataServices To XDS"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;3540D440-5B1D-49CB-821A-E84B8CF065A7&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for TV Tuner to XDS"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;B0EDF163-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C5702CCC-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Input Device Collection Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C5702CCD-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Output Device Collection Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C5702CCE-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Video Renderer Device Collection Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C5702CCF-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Audio Renderer Device Collection Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C5702CD0-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Feature Collection Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;2764BCE5-CC39-11D2-B639-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Generic Composition Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;E18AF75A-08AF-11D3-B64A-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Analog Capture to Overlay Mixer"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;267DB0B3-55E3-4902-949B-DF8F5CEC0191&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for WebDVD to Overlay Mixer"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;8D04238E-9FD1-41C6-8DE3-9E1EE309E935&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for WebDVD to Audio Renderer"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;6AD28EE1-5002-4E71-AAF7-BD077907B1A4&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Mpeg2 Decoder to Closed Captioning"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;9F50E8B1-9530-4DDC-825E-1AF81D47AED6&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Analog Capture to Stream Buffer Sink"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;ABE40035-27C3-4A2F-8153-6624471608AF&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Digital Capture to Stream Buffer Sink"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;92B94828-1AF7-4E6E-9EBF-770657F77AF5&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for iTV to Stream Buffer Sink"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;3EF76D68-8661-4843-8B8F-C37163D8C9CE&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for CCA to Stream Buffer Sink"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;A0B9B497-AFBC-45AD-A8A6-9B077C40D4F2&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Encoder to Stream Buffer Sink"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;B401C5EB-8457-427F-84EA-A4D2363364B0&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for File Playback to Video Renderer"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;CC23F537-18D4-4ECE-93BD-207A84726979&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for File Playback to Audio Renderer"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;28953661-0231-41DB-8986-21FF4388EE9B&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for TV Tuner to Encoder"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;3C4708DC-B181-46A8-8DA8-4AB0371758CD&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for SBE Source to Video renderer"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;942B7909-A28E-49A1-A207-34EBCBCB4B3B&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for TV Tuner to CCA"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;73D14237-B9DB-4EFA-A6DD-84350421FB2F&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Digital Capture to CCA"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;5D8E73F7-4989-4AC8-8A98-39BA0D325302&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Digital Capture to ITV"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;2291478C-5EE3-4BEF-AB5D-B5FF2CF58352&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Stream Buffer Source to ITV"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;9193A8F9-0CBA-400E-AA97-EB4709164576&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for Stream Buffer Source to CC"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;991DA7E5-953F-435B-BE5E-B92A05EDFC42&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Stream Buffer Source to Generic Sink Composition Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C4BF2784-AE00-41BA-9828-9C953BD3C54A&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for CC to Video Renderer"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;D76334CA-D89E-4BAF-86AB-DDB59372AFC2&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MS Video Control Custom Composition for CC to Audio Renderer"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;577FAA18-4518-445E-8F70-1473F8CF4BA4&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MSEventBinder Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;CAAFDD83-CEFC-4E3D-BA03-175F17A24F91&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"MSVidStreamBufferRecordingControl"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;CB4276E6-7D5F-4CF1-9727-629C5E6DB6AE&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"Automation compliant scalable rectangle Class"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;6E40476F-9C49-4C3E-8BB9-8587958EFF74&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base interface to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;30997F7D-B3B5-4A1C-983A-1FE8098CB77D&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base interface to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;AC1972F2-138A-4CA3-90DA-AE51112EDA28&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base interface to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;95F4820B-BB3A-4E2D-BC64-5B817BC2C30E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base interface to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;1990D634-1A5E-4071-A34A-53AAFFCE9F36&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base interface to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;7748530B-C08A-47EA-B24C-BE8695FF405F&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base interface to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;87EB890D-03AD-4E9D-9866-376E5EC572ED&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base interface to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;D02AAC50-027E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"SystemTuningSpace Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;5FFDC5E6-B83A-4B55-B6E8-C69E765FE9DB&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base tuning space i/f to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;CC829A2F-3365-463F-AF13-81DBB6F3A555&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Channel ID Tuning Space Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;A2E30750-6C3D-11D3-B653-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"ATSC Digital Broadcast Tuning Space Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;D9BB4CEE-B87A-47F1-AC92-B08D9C7813FC&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Digital Cable Tuning Space Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;8A674B4C-1F63-11D3-B64C-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Analog Radio Tuning Space Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;F9769A06-7ACA-4E39-9CFB-97BB35F0E77E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Auxiliary Inputs Tuning Space Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;8A674B4D-1F63-11D3-B64C-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Analog TV Tuning Space Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C6B14B32-76AA-4A86-A7AC-5C79AAF58DA7&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"DVB Tuning Space Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;B64016F3-C9A2-4066-96F0-BD9563314726&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"DVB Satellite Tuning Space Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;A1A2B1C4-0E3A-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Component Types Collection Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;823535A0-0318-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"ComponentType Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;1BE49F30-0E1B-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"LanguageComponentType Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;418008F3-CF67-4668-9628-10DC52BE1D08&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"MPEG2ComponentType Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;A8DCF3D5-0780-4EF4-8A83-2CFFAACB8ACE&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"ATSCComponentType Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;809B6661-94C4-49E6-B6EC-3F0F862215AA&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Components Collection Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;59DC47A8-116C-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Component Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;055CB2D7-2969-45CD-914B-76890722F112&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"MPEG2 Component Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;28AB0005-E845-4FFA-AA9B-F4665236141C&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Analog Audio Component Class"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;B46E0D38-AB35-4A06-A137-70576B01B39F&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base tune request i/f to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;3A9428A7-31A4-45E9-9EFB-E055BF7BB3DB&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Channel Tune Request"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;0369B4E5-45B6-11D3-B650-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Channel Tune Request"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;0369B4E6-45B6-11D3-B650-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"ATSC Channel Tune Request"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;26EC0B63-AA90-458A-8DF4-5659F2C8A18A&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Digital Cable Channel Tune Request"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;0955AC62-BF2E-4CBA-A2B9-A63F772D46CF&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose mpeg2 request i/f to VB"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;2C63E4EB-4CEA-41B8-919C-E947EA19A77C&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Factory for creating IMPEG2TuneRequest"</span> <span class="attr">threadingModel</span>=<span class="string">"Apartment"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;0888C883-AC4F-4943-B516-2C38D9B34562&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base locator i/f to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;6E50CC0D-C19B-4BF6-810B-5BD60761F5CC&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"dummy class to expose base digital locator i/f to VB"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;49638B91-48AB-48B7-A47A-7D0E75A08EDE&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Analog Locator"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;8872FF1B-98FA-4D7A-8D93-C9F1055F85BB&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"ATSC Locator"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;03C06416-D127-407A-AB4C-FDD279ABBE5D&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Digital Cable Locator"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;9CD64701-BDF3-4D14-8E03-F12983D86664&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"DVB-Terrestrial Locator"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;EFE3FA02-45D7-4920-BE96-53FA7F35B0E6&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"DVB-Terrestrial 2 Locator"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;1DF7D126-4050-47F0-A7CF-4C4CA9241333&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"DVB-Satellite Locator"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C531D9FD-9685-4028-8B68-6E1232079F1E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"DVB-Cable Locator"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;6504AFED-A629-455C-A7F1-04964DEA5CC4&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"ISDB-Satellite Locator"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;15D6504A-5494-499C-886C-973C9E53B9F1&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"DVB Tune Request"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;8A674B49-1F63-11D3-B64C-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Create property bag backed by registry"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;0B3FFB92-0919-4934-9D5B-619C719D0202&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"DShow Broadcast Event Service Object"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;6438570B-0C08-4A25-9504-8012BB4D50CF&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"BDA ITuner Marshaling utility object"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;E77026B0-B97F-4CBB-B7FB-F4F03AD69F11&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Utility object for Tuning Model Object Xml deserialization or serialization"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;C20447FC-EC60-475E-813F-D2B0A6DECEFE&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Extensible Eventing Service object"</span> <span class="attr">threadingModel</span>=<span class="string">"Both"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">comClass</span> <span class="attr">clsid</span>=<span class="string">"&#123;8E8A07DA-71F8-40C1-A929-5E3A868AC2C6&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">description</span>=<span class="string">"Eventing Service object(used for unmarshal)"</span> /&gt;</span></div><div class="line"></div><div class="line">    <span class="tag">&lt;<span class="name">typelib</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">version</span>=<span class="string">"1.0"</span> <span class="attr">helpdir</span>=<span class="string">""</span> <span class="attr">flags</span>=<span class="string">"HASDISKIMAGE"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">typelib</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">version</span>=<span class="string">"1.0"</span> <span class="attr">helpdir</span>=<span class="string">""</span> <span class="attr">flags</span>=<span class="string">"HASDISKIMAGE"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">file</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"_IMSVidCtlEvents"</span> <span class="attr">iid</span>=<span class="string">"&#123;B0EDF164-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020420-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidAnalogTuner2"</span> <span class="attr">iid</span>=<span class="string">"&#123;37647BF7-3DDE-4CC8-A4DC-0D534D3D0037&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidAnalogTuner"</span> <span class="attr">iid</span>=<span class="string">"&#123;1C15D47E-911D-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidTuner"</span> <span class="attr">iid</span>=<span class="string">"&#123;1C15D47D-911D-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidVideoInputDevice"</span> <span class="attr">iid</span>=<span class="string">"&#123;1C15D47F-911D-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidInputDevice"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B0353D-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidDevice"</span> <span class="attr">iid</span>=<span class="string">"&#123;1C15D47C-911D-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ITuneRequest"</span> <span class="attr">iid</span>=<span class="string">"&#123;07DDC146-FC3D-11D2-9D8C-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ITuningSpace"</span> <span class="attr">iid</span>=<span class="string">"&#123;061C6E30-E622-11D2-9493-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumGUID"</span> <span class="attr">iid</span>=<span class="string">"&#123;0002E000-0000-0000-C000-000000000046&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;0002E000-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumMoniker"</span> <span class="attr">iid</span>=<span class="string">"&#123;00000102-0000-0000-C000-000000000046&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00000102-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMoniker"</span> <span class="attr">iid</span>=<span class="string">"&#123;0000000F-0000-0000-C000-000000000046&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;0000000F-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IPersistStream"</span> <span class="attr">iid</span>=<span class="string">"&#123;00000109-0000-0000-C000-000000000046&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00000109-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IPersist"</span> <span class="attr">iid</span>=<span class="string">"&#123;0000010C-0000-0000-C000-000000000046&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;0000010C-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IStream"</span> <span class="attr">iid</span>=<span class="string">"&#123;0000000C-0000-0000-C000-000000000046&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;0000000C-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ISequentialStream"</span> <span class="attr">iid</span>=<span class="string">"&#123;0C733A30-2A1C-11CE-ADE5-00AA0044773D&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;0C733A30-2A1C-11CE-ADE5-00AA0044773D&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IBindCtx"</span> <span class="attr">iid</span>=<span class="string">"&#123;0000000E-0000-0000-C000-000000000046&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;0000000E-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IRunningObjectTable"</span> <span class="attr">iid</span>=<span class="string">"&#123;00000010-0000-0000-C000-000000000046&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00000010-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumString"</span> <span class="attr">iid</span>=<span class="string">"&#123;00000101-0000-0000-C000-000000000046&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00000101-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IComponentTypes"</span> <span class="attr">iid</span>=<span class="string">"&#123;0DC13D4A-0313-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumComponentTypes"</span> <span class="attr">iid</span>=<span class="string">"&#123;8A674B4A-1F63-11D3-B64C-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;8A674B4A-1F63-11D3-B64C-00C04F79498E&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IComponentType"</span> <span class="attr">iid</span>=<span class="string">"&#123;6A340DC0-0311-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ILocator"</span> <span class="attr">iid</span>=<span class="string">"&#123;286D7F89-760C-4F89-80C4-66841D2507AA&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IComponents"</span> <span class="attr">iid</span>=<span class="string">"&#123;39A48091-FFFE-4182-A161-3FF802640E26&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumComponents"</span> <span class="attr">iid</span>=<span class="string">"&#123;2A6E2939-2595-11D3-B64C-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;2A6E2939-2595-11D3-B64C-00C04F79498E&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IComponent"</span> <span class="attr">iid</span>=<span class="string">"&#123;1A5576FC-0E19-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidAnalogTunerEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;1C15D486-911D-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidTunerEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;1C15D485-911D-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidInputDeviceEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B0353E-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidGraphSegment"</span> <span class="attr">iid</span>=<span class="string">"&#123;238DEC54-ADEB-4005-A349-F772B9AFEBC4&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;238DEC54-ADEB-4005-A349-F772B9AFEBC4&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumFilters"</span> <span class="attr">iid</span>=<span class="string">"&#123;56A86893-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;56A86893-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IBaseFilter"</span> <span class="attr">iid</span>=<span class="string">"&#123;56A86895-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;56A86895-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMediaFilter"</span> <span class="attr">iid</span>=<span class="string">"&#123;56A86899-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;56A86899-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IReferenceClock"</span> <span class="attr">iid</span>=<span class="string">"&#123;56A86897-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;56A86897-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumPins"</span> <span class="attr">iid</span>=<span class="string">"&#123;56A86892-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;56A86892-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IPin"</span> <span class="attr">iid</span>=<span class="string">"&#123;56A86891-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;56A86891-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumMediaTypes"</span> <span class="attr">iid</span>=<span class="string">"&#123;89C31040-846B-11CE-97D3-00AA0055595A&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;89C31040-846B-11CE-97D3-00AA0055595A&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IFilterGraph"</span> <span class="attr">iid</span>=<span class="string">"&#123;56A8689F-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;56A8689F-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidGraphSegmentContainer"</span> <span class="attr">iid</span>=<span class="string">"&#123;3DD2903D-E0AA-11D2-B63A-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;3DD2903D-E0AA-11D2-B63A-00C04F79498E&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IGraphBuilder"</span> <span class="attr">iid</span>=<span class="string">"&#123;56A868A9-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;56A868A9-0AD4-11CE-B03A-0020AF0BA770&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumMSVidGraphSegment"</span> <span class="attr">iid</span>=<span class="string">"&#123;3DD2903E-E0AA-11D2-B63A-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;3DD2903E-E0AA-11D2-B63A-00C04F79498E&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidFilePlayback2"</span> <span class="attr">iid</span>=<span class="string">"&#123;2F7E44AF-6E52-4660-BC08-D8D542587D72&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidFilePlayback"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B03539-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidPlayback"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B03538-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidFilePlaybackEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B0353A-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidPlaybackEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B0353B-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidWebDVD"</span> <span class="attr">iid</span>=<span class="string">"&#123;CF45F88B-AC56-4EE2-A73A-ED04E2885D3C&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidRect"</span> <span class="attr">iid</span>=<span class="string">"&#123;7F5000A6-A440-47CA-8ACC-C0E75531A2C2&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;7F5000A6-A440-47CA-8ACC-C0E75531A2C2&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidWebDVDEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;B4F7A674-9B83-49CB-A357-C63B871BE958&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidWebDVDAdm"</span> <span class="attr">iid</span>=<span class="string">"&#123;B8BE681A-EB2C-47F0-B415-94D5452F0E05&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidVideoRenderer2"</span> <span class="attr">iid</span>=<span class="string">"&#123;6BDD5C1E-2810-4159-94BC-05511AE8549B&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidVideoRenderer"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B03540-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidOutputDevice"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B03546-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IVMRImageCompositor"</span> <span class="attr">iid</span>=<span class="string">"&#123;7A4FB5AF-479F-4074-BB40-CE6722E43C82&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;7A4FB5AF-479F-4074-BB40-CE6722E43C82&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IVMRMixerBitmap"</span> <span class="attr">iid</span>=<span class="string">"&#123;1E673275-0257-40AA-AF20-7C608D4A0428&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;1E673275-0257-40AA-AF20-7C608D4A0428&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IVMRSurfaceAllocator"</span> <span class="attr">iid</span>=<span class="string">"&#123;31CE832E-4484-458B-8CCA-F4D7E3DB0B52&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;31CE832E-4484-458B-8CCA-F4D7E3DB0B52&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IVMRSurfaceAllocatorNotify"</span> <span class="attr">iid</span>=<span class="string">"&#123;AADA05A8-5A4E-4729-AF0B-CEA27AED51E2&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;AADA05A8-5A4E-4729-AF0B-CEA27AED51E2&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidVideoRendererEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B03545-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidOutputDeviceEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;2E6A14E2-571C-11D3-B652-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidDeviceEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;1C15D480-911D-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidVMR9"</span> <span class="attr">iid</span>=<span class="string">"&#123;D58B0015-EBEF-44BB-BBDD-3F3699D76EA1&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidEVR"</span> <span class="attr">iid</span>=<span class="string">"&#123;15E496AE-82A8-4CF9-A6B6-C561DC60398F&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMFVideoPresenter"</span> <span class="attr">iid</span>=<span class="string">"&#123;29AFF080-182A-4A5D-AF3B-448F3A6346CB&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;29AFF080-182A-4A5D-AF3B-448F3A6346CB&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMFClockStateSink"</span> <span class="attr">iid</span>=<span class="string">"&#123;F6696E82-74F7-4F3D-A178-8A5E09C3659F&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;F6696E82-74F7-4F3D-A178-8A5E09C3659F&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMFVideoMediaType"</span> <span class="attr">iid</span>=<span class="string">"&#123;B99F381F-A8F9-47A2-A5AF-CA3A225A3890&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;B99F381F-A8F9-47A2-A5AF-CA3A225A3890&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMFMediaType"</span> <span class="attr">iid</span>=<span class="string">"&#123;44AE0FA8-EA31-4109-8D2E-4CAE4997C555&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;44AE0FA8-EA31-4109-8D2E-4CAE4997C555&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMFAttributes"</span> <span class="attr">iid</span>=<span class="string">"&#123;2CD2D921-C447-44A7-A13C-4ADABFC247E3&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;2CD2D921-C447-44A7-A13C-4ADABFC247E3&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IStorage"</span> <span class="attr">iid</span>=<span class="string">"&#123;0000000B-0000-0000-C000-000000000046&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;0000000B-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumSTATSTG"</span> <span class="attr">iid</span>=<span class="string">"&#123;0000000D-0000-0000-C000-000000000046&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;0000000D-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IRecordInfo"</span> <span class="attr">iid</span>=<span class="string">"&#123;0000002F-0000-0000-C000-000000000046&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;0000002F-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ITypeInfo"</span> <span class="attr">iid</span>=<span class="string">"&#123;00020401-0000-0000-C000-000000000046&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020401-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ITypeComp"</span> <span class="attr">iid</span>=<span class="string">"&#123;00020403-0000-0000-C000-000000000046&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020403-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ITypeLib"</span> <span class="attr">iid</span>=<span class="string">"&#123;00020402-0000-0000-C000-000000000046&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020402-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidEVREvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;349ABB10-883C-4F22-8714-CECAEEE45D62&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidAudioRenderer"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B0353F-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidAudioRendererEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B03541-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidGenericSink2"</span> <span class="attr">iid</span>=<span class="string">"&#123;6B5A28F3-47F1-4092-B168-60CABEC08F1C&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidGenericSink"</span> <span class="attr">iid</span>=<span class="string">"&#123;6C29B41D-455B-4C33-963A-0D28E5E555EA&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSink3"</span> <span class="attr">iid</span>=<span class="string">"&#123;4F8721D7-7D59-4D8B-99F5-A77775586BD5&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSink2"</span> <span class="attr">iid</span>=<span class="string">"&#123;2CA9FC63-C131-4E5A-955A-544A47C67146&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSink"</span> <span class="attr">iid</span>=<span class="string">"&#123;159DBB45-CD1B-4DAB-83EA-5CB1F4F21D07&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferRecordingControl"</span> <span class="attr">iid</span>=<span class="string">"&#123;160621AA-BBBC-4326-A824-C395AEBC6E74&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSinkEvent4"</span> <span class="attr">iid</span>=<span class="string">"&#123;1B01DCB0-DAF0-412C-A5D1-590C7F62E2B8&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSinkEvent3"</span> <span class="attr">iid</span>=<span class="string">"&#123;735AD8D5-C259-48E9-81E7-D27953665B23&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSinkEvent2"</span> <span class="attr">iid</span>=<span class="string">"&#123;3D7A5166-72D7-484B-A06F-286187B80CA1&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSinkEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;F798A36B-B05B-4BBE-9703-EAEA7D61CD51&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSource"</span> <span class="attr">iid</span>=<span class="string">"&#123;EB0C8CF9-6950-4772-87B1-47D11CF3A02F&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSource2"</span> <span class="attr">iid</span>=<span class="string">"&#123;E4BA9059-B1CE-40D8-B9A0-D4EA4A9989D3&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSourceEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;50CE8A7D-9C28-4DA8-9042-CDFA7116F979&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSourceEvent2"</span> <span class="attr">iid</span>=<span class="string">"&#123;7AEF50CE-8E22-4BA8-BC06-A92A458B4EF2&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferSourceEvent3"</span> <span class="attr">iid</span>=<span class="string">"&#123;CEABD6AB-9B90-4570-ADF1-3CE76E00A763&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidStreamBufferV2SourceEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;49C771F9-41B2-4CF7-9F9A-A313A8F6027E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidEncoder"</span> <span class="attr">iid</span>=<span class="string">"&#123;C0020FD4-BEE7-43D9-A495-9F213117103D&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidFeature"</span> <span class="attr">iid</span>=<span class="string">"&#123;37B03547-A4C8-11D2-B634-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidClosedCaptioning3"</span> <span class="attr">iid</span>=<span class="string">"&#123;C8638E8A-7625-4C51-9366-2F40A9831FC0&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidClosedCaptioning2"</span> <span class="attr">iid</span>=<span class="string">"&#123;E00CB864-A029-4310-9987-A873F5887D97&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidClosedCaptioning"</span> <span class="attr">iid</span>=<span class="string">"&#123;99652EA1-C1F7-414F-BB7B-1C967DE75983&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidDataServices"</span> <span class="attr">iid</span>=<span class="string">"&#123;334125C1-77E5-11D3-B653-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidXDS"</span> <span class="attr">iid</span>=<span class="string">"&#123;11EBC158-E712-4D1F-8BB3-01ED5274C4CE&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidXDSEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;6DB2317D-3B23-41EC-BA4B-701F407EAF3A&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidFeatureEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;3DD2903C-E0AA-11D2-B63A-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidCompositionSegment"</span> <span class="attr">iid</span>=<span class="string">"&#123;1C15D483-911D-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;1C15D483-911D-11D2-B632-00C04F79498E&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IObjectWithSite"</span> <span class="attr">iid</span>=<span class="string">"&#123;FC4801A3-2BA9-11CF-A229-00AA003D7352&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;FC4801A3-2BA9-11CF-A229-00AA003D7352&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidCtl"</span> <span class="attr">iid</span>=<span class="string">"&#123;B0EDF162-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidInputDevices"</span> <span class="attr">iid</span>=<span class="string">"&#123;C5702CD1-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidOutputDevices"</span> <span class="attr">iid</span>=<span class="string">"&#123;C5702CD2-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidVideoRendererDevices"</span> <span class="attr">iid</span>=<span class="string">"&#123;C5702CD3-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidAudioRendererDevices"</span> <span class="attr">iid</span>=<span class="string">"&#123;C5702CD4-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidFeatures"</span> <span class="attr">iid</span>=<span class="string">"&#123;C5702CD5-9B79-11D3-B654-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSEventBinder"</span> <span class="attr">iid</span>=<span class="string">"&#123;C3A9F406-2222-436D-86D5-BA3229279EFB&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMSVidDevice2"</span> <span class="attr">iid</span>=<span class="string">"&#123;87BD2783-EBC0-478C-B4A0-E8E7F43AB78E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;B0EDF154-910A-11D2-B632-00C04F79498E&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ITuningSpaceContainer"</span> <span class="attr">iid</span>=<span class="string">"&#123;5B692E84-E2F1-11D2-9493-00C04F72D980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ITuningSpaces"</span> <span class="attr">iid</span>=<span class="string">"&#123;901284E4-33FE-4B69-8D63-634A596F3756&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IEnumTuningSpaces"</span> <span class="attr">iid</span>=<span class="string">"&#123;8B8EB248-FC2B-11D2-9D8C-00C04F72D980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;8B8EB248-FC2B-11D2-9D8C-00C04F72D980&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IBDAComparable"</span> <span class="attr">iid</span>=<span class="string">"&#123;B34505E0-2F0E-497B-80BC-D43F3B24ED7F&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;B34505E0-2F0E-497B-80BC-D43F3B24ED7F&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IBDACreateTuneRequestEx"</span> <span class="attr">iid</span>=<span class="string">"&#123;C0A4A1D4-2B3C-491A-BA22-499FBADD4D12&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;C0A4A1D4-2B3C-491A-BA22-499FBADD4D12&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IATSCTuningSpace"</span> <span class="attr">iid</span>=<span class="string">"&#123;0369B4E2-45B6-11D3-B650-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IAnalogTVTuningSpace"</span> <span class="attr">iid</span>=<span class="string">"&#123;2A6E293C-2595-11D3-B64C-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDigitalCableTuningSpace"</span> <span class="attr">iid</span>=<span class="string">"&#123;013F9F9C-B449-4EC7-A6D2-9D4F2FC70AE5&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IAnalogRadioTuningSpace2"</span> <span class="attr">iid</span>=<span class="string">"&#123;39DD45DA-2DA8-46BA-8A8A-87E2B73D983A&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IAnalogRadioTuningSpace"</span> <span class="attr">iid</span>=<span class="string">"&#123;2A6E293B-2595-11D3-B64C-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IAuxInTuningSpace"</span> <span class="attr">iid</span>=<span class="string">"&#123;E48244B8-7E17-4F76-A763-5090FF1E2F30&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IAuxInTuningSpace2"</span> <span class="attr">iid</span>=<span class="string">"&#123;B10931ED-8BFE-4AB0-9DCE-E469C29A9729&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDVBTuningSpace2"</span> <span class="attr">iid</span>=<span class="string">"&#123;843188B4-CE62-43DB-966B-8145A094E040&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDVBTuningSpace"</span> <span class="attr">iid</span>=<span class="string">"&#123;ADA0B268-3B19-4E5B-ACC4-49F852BE13BA&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDVBSTuningSpace"</span> <span class="attr">iid</span>=<span class="string">"&#123;CDF7BE60-D954-42FD-A972-78971958E470&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ILanguageComponentType"</span> <span class="attr">iid</span>=<span class="string">"&#123;B874C8BA-0FA2-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMPEG2ComponentType"</span> <span class="attr">iid</span>=<span class="string">"&#123;2C073D84-B51C-48C9-AA9F-68971E1F6E38&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IATSCComponentType"</span> <span class="attr">iid</span>=<span class="string">"&#123;FC189E4D-7BD4-4125-B3B3-3A76A332CC96&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IComponentsOld"</span> <span class="attr">iid</span>=<span class="string">"&#123;FCD01846-0E19-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMPEG2Component"</span> <span class="attr">iid</span>=<span class="string">"&#123;1493E353-1EB6-473C-802D-8E6B8EC9D2A9&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IAnalogAudioComponentType"</span> <span class="attr">iid</span>=<span class="string">"&#123;2CFEB2A8-1787-4A24-A941-C6EAEC39C842&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IChannelIDTuneRequest"</span> <span class="attr">iid</span>=<span class="string">"&#123;156EFF60-86F4-4E28-89FC-109799FD57EE&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IChannelTuneRequest"</span> <span class="attr">iid</span>=<span class="string">"&#123;0369B4E0-45B6-11D3-B650-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IATSCChannelTuneRequest"</span> <span class="attr">iid</span>=<span class="string">"&#123;0369B4E1-45B6-11D3-B650-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDigitalCableTuneRequest"</span> <span class="attr">iid</span>=<span class="string">"&#123;BAD7753B-6B37-4810-AE57-3CE0C4A9E6CB&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMPEG2TuneRequest"</span> <span class="attr">iid</span>=<span class="string">"&#123;EB7D987F-8A01-42AD-B8AE-574DEEE44D1A&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMPEG2TuneRequestFactory"</span> <span class="attr">iid</span>=<span class="string">"&#123;14E11ABD-EE37-4893-9EA1-6964DE933E39&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDigitalLocator"</span> <span class="attr">iid</span>=<span class="string">"&#123;19B595D8-839A-47F0-96DF-4F194F3C768C&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IAnalogLocator"</span> <span class="attr">iid</span>=<span class="string">"&#123;34D1F26B-E339-430D-ABCE-738CB48984DC&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IATSCLocator2"</span> <span class="attr">iid</span>=<span class="string">"&#123;612AA885-66CF-4090-BA0A-566F5312E4CA&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IATSCLocator"</span> <span class="attr">iid</span>=<span class="string">"&#123;BF8D986F-8C2B-4131-94D7-4D3D9FCC21EF&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDigitalCableLocator"</span> <span class="attr">iid</span>=<span class="string">"&#123;48F66A11-171A-419A-9525-BEEECD51584C&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDVBTLocator"</span> <span class="attr">iid</span>=<span class="string">"&#123;8664DA16-DDA2-42AC-926A-C18F9127C302&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDVBTLocator2"</span> <span class="attr">iid</span>=<span class="string">"&#123;448A2EDF-AE95-4B43-A3CC-747843C453D4&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDVBSLocator2"</span> <span class="attr">iid</span>=<span class="string">"&#123;6044634A-1733-4F99-B982-5FB12AFCE4F0&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDVBSLocator"</span> <span class="attr">iid</span>=<span class="string">"&#123;3D7C353C-0D04-45F1-A742-F97CC1188DC8&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDVBCLocator"</span> <span class="attr">iid</span>=<span class="string">"&#123;6E42F36E-1DD2-43C4-9F78-69D25AE39034&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IISDBSLocator"</span> <span class="attr">iid</span>=<span class="string">"&#123;C9897087-E29C-473F-9E4B-7072123DEA14&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IDVBTuneRequest"</span> <span class="attr">iid</span>=<span class="string">"&#123;0D6F567E-A636-42BB-83BA-CE4C1704AFA2&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00020424-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ICreatePropBagOnRegKey"</span> <span class="attr">iid</span>=<span class="string">"&#123;8A674B48-1F63-11D3-B64C-00C04F79498E&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;8A674B48-1F63-11D3-B64C-00C04F79498E&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IBroadcastEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;3B21263F-26E8-489D-AAC4-924F7EFD9511&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;3B21263F-26E8-489D-AAC4-924F7EFD9511&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IRegisterTuner"</span> <span class="attr">iid</span>=<span class="string">"&#123;359B3901-572C-4854-BB49-CDEF66606A25&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;359B3901-572C-4854-BB49-CDEF66606A25&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"ITuner"</span> <span class="attr">iid</span>=<span class="string">"&#123;28C52640-018A-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;28C52640-018A-11D3-9D8E-00C04F72D980&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IPersistTuneXmlUtility"</span> <span class="attr">iid</span>=<span class="string">"&#123;990237AE-AC11-4614-BE8F-DD217A4CB4CB&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;990237AE-AC11-4614-BE8F-DD217A4CB4CB&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IPersistTuneXmlUtility2"</span> <span class="attr">iid</span>=<span class="string">"&#123;992E165F-EA24-4B2F-9A1D-009D92120451&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;992E165F-EA24-4B2F-9A1D-009D92120451&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IESEventService"</span> <span class="attr">iid</span>=<span class="string">"&#123;ED89A619-4C06-4B2F-99EB-C7669B13047C&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;ED89A619-4C06-4B2F-99EB-C7669B13047C&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IESEvent"</span> <span class="attr">iid</span>=<span class="string">"&#123;1F0E5357-AF43-44E6-8547-654C645145D2&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;1F0E5357-AF43-44E6-8547-654C645145D2&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IESEvents"</span> <span class="attr">iid</span>=<span class="string">"&#123;ABD414BF-CFE5-4E5E-AF5B-4B4E49C5BFEB&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;ABD414BF-CFE5-4E5E-AF5B-4B4E49C5BFEB&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IESEventFactory"</span> <span class="attr">iid</span>=<span class="string">"&#123;506A09B8-7F86-4E04-AC05-3303BFE8FC49&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;506A09B8-7F86-4E04-AC05-3303BFE8FC49&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMarshal2"</span> <span class="attr">iid</span>=<span class="string">"&#123;000001CF-0000-0000-C000-000000000046&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;000001CF-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">comInterfaceExternalProxyStub</span> <span class="attr">name</span>=<span class="string">"IMarshal"</span> <span class="attr">iid</span>=<span class="string">"&#123;00000003-0000-0000-C000-000000000046&#125;"</span> <span class="attr">tlbid</span>=<span class="string">"&#123;9B085638-018E-11D3-9D8E-00C04F72D980&#125;"</span> <span class="attr">proxyStubClsid32</span>=<span class="string">"&#123;00000003-0000-0000-C000-000000000046&#125;"</span> /&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">assembly</span>&gt;</span></div></pre></td></tr></table></figure><h3 id="Writing-the-application-manifest"><a href="#Writing-the-application-manifest" class="headerlink" title="Writing the application manifest"></a>Writing the application manifest</h3><p>This is the last step: writing the application manifest and importing the assembly manifest we just created.</p><p>For that, I took the original manifest embedded in <code>ehshell.exe</code>, cleaned it up a bit, added the necessary parts and created <code>ehshell.exe.manifest</code> with the resulting XML:</p><figure class="highlight xml"><figcaption><span>ehshell.exe.manifest</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line">&lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&gt;</div><div class="line"><span class="comment">&lt;!-- Copyright (c) Microsoft Corporation --&gt;</span></div><div class="line"><span class="tag">&lt;<span class="name">assembly</span> <span class="attr">xmlns</span>=<span class="string">"urn:schemas-microsoft-com:asm.v3"</span> <span class="attr">xmlns:asmv3</span>=<span class="string">"urn:schemas-microsoft-com:asm.v3"</span> <span class="attr">manifestVersion</span>=<span class="string">"1.0"</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">assemblyIdentity</span> <span class="attr">name</span>=<span class="string">"Microsoft.Windows.MultiMedia.EhShell"</span> <span class="attr">processorArchitecture</span>=<span class="string">"amd64"</span> <span class="attr">version</span>=<span class="string">"5.1.0.0"</span> <span class="attr">type</span>=<span class="string">"win32"</span> /&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">description</span>&gt;</span>Windows Media Center Shell<span class="tag">&lt;/<span class="name">description</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">application</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">asmv3:windowsSettings</span> <span class="attr">xmlns</span>=<span class="string">"http://schemas.microsoft.com/SMI/2005/WindowsSettings"</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">dpiAware</span>&gt;</span>true<span class="tag">&lt;/<span class="name">dpiAware</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">asmv3:windowsSettings</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">application</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">trustInfo</span> <span class="attr">xmlns</span>=<span class="string">"urn:schemas-microsoft-com:asm.v3"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">security</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">requestedPrivileges</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">requestedExecutionLevel</span> <span class="attr">level</span>=<span class="string">"asInvoker"</span> <span class="attr">uiAccess</span>=<span class="string">"false"</span> /&gt;</span></div><div class="line">      <span class="tag">&lt;/<span class="name">requestedPrivileges</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">security</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">trustInfo</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">dependentAssembly</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">assemblyIdentity</span> <span class="attr">name</span>=<span class="string">"Microsoft.Windows.Video.Control"</span> <span class="attr">version</span>=<span class="string">"1.0.0.0"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">dependentAssembly</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div><div class="line"></div><div class="line">  <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">dependentAssembly</span>&gt;</span></div><div class="line">      <span class="tag">&lt;<span class="name">assemblyIdentity</span> <span class="attr">name</span>=<span class="string">"Microsoft.Windows.Video.Tagging"</span> <span class="attr">version</span>=<span class="string">"1.0.0.0"</span> /&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">dependentAssembly</span>&gt;</span></div><div class="line">  <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">assembly</span>&gt;</span></div></pre></td></tr></table></figure><div class="note tip"><p>The <code>Microsoft.Windows.Video.Tagging</code> dependency was generated using the same exact procedure, but using <code>EncDec.dll</code> instead of <code>MSVidCtl.dll</code>. For more information about this system DLL and why it&#39;s important for Windows Media Center, visit <a href="https://www.windowsmediacenter.fr/2018/06/10/windows-10-april-update-comment-resoudre-le-probleme-de-miniatures-thumbnails-sous-windows-media-center/" target="_blank" rel="external">Windows 10 April Update: comment résoudre le problème de miniatures (Thumbnails) sous Windows Media Center</a> (in French).</p></div><p>At this stage, <strong>it&#39;s important to note that external manifest files are NOT loaded if the executable already contains an embedded manifest</strong>. Since I preferred having an external manifest, I used Resource Hacker to remove the embedded manifest from <code>ehshell.exe</code> and the other WMC executables.</p><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p><strong>Creating registration-free COM manifests to work around a breaking change in a system DLL is definitely a painful process</strong> (after all, it&#39;s called DLL Hell for a reason!). Fortunately, the result is worth it, as it makes an application safe from future DLL changes introduced in minor or major Windows versions.</p><p>Since I don&#39;t expect anyone to reproduce this painful process to fix their Windows Media Center setup, <strong>I decided to team up with <a href="https://twitter.com/lachape_one" target="_blank" rel="external">Rémi Lachapelle</a> – who ows the <a href="https://www.windowsmediacenter.fr/" target="_blank" rel="external">WindowsMediaCenter.fr blog</a> – to offer an updated installer</strong> that includes the precious manifest and the <code>MSVidCtl</code>/<code>EncDec</code> DLLs needed for Windows Media Center to work properly with recent versions of Windows 10: keep an eye on his blog, it should be there shortly!</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;It&amp;#39;s no secret that &lt;strong&gt;I&amp;#39;ve always been a huge fan of &lt;a href=&quot;https://en.wikipedia.org/wiki/Windows_Media_Center&quot;&gt;Windows Media Center&lt;/a&gt;&lt;/strong&gt;, probably the best digital/personal video recorder out there and definitely one of the most impressive Microsoft applications developed using the .NET Framework.&lt;/p&gt;&lt;p&gt;As an avid WMC user, I have built 3 &lt;a href=&quot;https://en.wikipedia.org/wiki/Home_theater_PC&quot;&gt;HTPC machines&lt;/a&gt; and recorded thousands of TV programs (movies, series, documentaries, etc.). As such, when Microsoft announced in May 2015 that Windows Media Center would no longer be developed (almost 6 year after disbanding the development team!) – and thus would not be part of Windows 10 – &lt;strong&gt;I refused to migrate to Windows 10&lt;/strong&gt;, despite Microsoft&amp;#39;s offers encouraging users to upgrade their OS for free.&lt;/p&gt;&lt;p&gt;The fact Windows Media Center was not available on Windows 10 – even as a paid feature like it was on Windows 8.x – &lt;strong&gt;was a major blocker for many users&lt;/strong&gt;. To work around this limitation, &lt;a href=&quot;https://forums.mydigitallife.net/threads/discussion-patch-wmc-to-run-on-windows-10-final-possible-alternatives.61061/&quot;&gt;enthusiasts decided to create an unofficial port of Windows Media Center for Windows 10&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;While unofficial, the port worked just fine. &lt;strong&gt;But over time, Microsoft started to update some of the system components Windows Media Center relied on&lt;/strong&gt;, causing annoying bugs. For instance, the introduction of breaking changes in Windows 10 1803 &lt;a href=&quot;https://forums.mydigitallife.net/threads/discussion-patch-wmc-to-run-on-windows-10-final-possible-alternatives.61061/page-436#post-1424922&quot;&gt;made watching a &lt;code&gt;.wtv&lt;/code&gt; file (WMC&amp;#39;s TV file format) recorded on Windows 7 completely impossible&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;While investigating, I discovered that the issue was caused by a change in &lt;code&gt;MSVidCtl.dll&lt;/code&gt;, the system-wide DLL containing the DirectShow components needed by WMC for all its TV-related features. After replacing the faulty DLL by an older version, WMC was able to play old recordings like a charm.&lt;/p&gt;&lt;p&gt;This phenomenom, that occurs every time API or functional changes are introduced in a DLL a program depends on, has a name: &lt;a href=&quot;https://en.wikipedia.org/wiki/DLL_Hell&quot;&gt;&lt;strong&gt;DLL Hell&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
    
    </summary>
    
      <category term="Development" scheme="https://kevinchalet.com/categories/development/"/>
    
    
      <category term="windows" scheme="https://kevinchalet.com/tags/windows/"/>
    
      <category term="component object model" scheme="https://kevinchalet.com/tags/component-object-model/"/>
    
      <category term="manifest" scheme="https://kevinchalet.com/tags/manifest/"/>
    
      <category term="windows media center" scheme="https://kevinchalet.com/tags/windows-media-center/"/>
    
  </entry>
  
  <entry>
    <title>Session fixation vulnerability in the Auth0 ASP.NET and OWIN SDKs</title>
    <link href="https://kevinchalet.com/2018/08/08/session-fixation-vulnerability-in-the-auth0-asp-net-and-owin-sdks/"/>
    <id>https://kevinchalet.com/2018/08/08/session-fixation-vulnerability-in-the-auth0-asp-net-and-owin-sdks/</id>
    <published>2018-08-08T17:00:00.000Z</published>
    <updated>2018-08-08T17:46:04.000Z</updated>
    
    <content type="html"><![CDATA[<p>Recently, I received a mail from Auth0 asking me if I was interested in joining them. I had used Auth0 many times in the past but I had never taken the time to look at their OSS SDKs. This mail was a good opportunity to change that.</p><p>When doing so, <strong>I discovered that both their <a href="https://github.com/auth0/auth0-aspnet" target="_blank" rel="external">ASP.NET 4.x</a> and <a href="https://github.com/auth0/auth0-aspnet-owin" target="_blank" rel="external">OWIN/Katana</a> SDKs were unfortunately prone to &quot;session fixation&quot;</strong>, which is a form of cross-site request forgery allowing to force a victim to log in under an attacker&#39;s account.</p><p>Since it&#39;s a quite frequent vulnerability, here&#39;s a quick overview of what causes it and how you can concretly exploit it.</p><div class="note warn"><p>Auth0 was already aware of this issue internally and <a href="https://auth0.com/docs/security/bulletins/cve-2018-15121" target="_blank" rel="external">decided to switch to the official OWIN OpenID Connect middleware developed by Microsoft</a>, which is not prone to this class of attack.</p><p><strong>If you need to migrate, Auth0 has prepared a <a href="https://auth0.com/docs/quickstart/webapp/aspnet-owin/04-migrating" target="_blank" rel="external">migration guide</a></strong> listing the steps required to replace <code>Auth0-ASPNET-Owin</code> by Microsoft&#39;s OpenID Connect middleware.</p></div><h2 id="What-do-session-fixation-attacks-consist-in"><a href="#What-do-session-fixation-attacks-consist-in" class="headerlink" title="What do session fixation attacks consist in?"></a>What do session fixation attacks consist in?</h2><p>The <a href="https://tools.ietf.org/html/rfc6819" target="_blank" rel="external">OAuth2 threat model RFC</a> – a must read for anyone dealing with OAuth2 and OpenID Connect – gives <a href="https://tools.ietf.org/html/rfc6819#sehection-4.4.1.8" target="_blank" rel="external">an excellent definition of this threat and its practical implications</a>:</p><blockquote><p>Cross-site request forgery (CSRF) is a web-based attack whereby HTTP requests are transmitted from a user that the web site trusts or has authenticated (e.g., via HTTP redirects or HTML forms). CSRF attacks on OAuth approvals can allow an attacker to obtain authorization to OAuth protected resources without the consent of the user. This attack works against the redirect URI used in the authorization &quot;code&quot; flow.</p><p>An attacker could authorize an authorization &quot;code&quot; to their own protected resources on an authorization server. He then aborts the redirect flow back to the client on his device and tricks the victim into executing the redirect back to the client. The client receives the redirect, fetches the token(s) from the authorization server, and associates the victim&#39;s client session with the resources accessible using the token.</p><p>Impact: the user accesses resources on behalf of the attacker. The effective impact depends on the type of resource accessed. For example, the user may upload private items to an attacker&#39;s resources. Or, when using OAuth in 3rd-party login scenarios, the user may associate his client account with the attacker&#39;s identity at the external Identity Provider. In this way, the attacker could easily access the victim&#39;s data at the client by logging in from another device with his credentials at the external Identity Provider.</p></blockquote><p>Usually, this (generally underestimated) threat is mitigated by correlating the authorization response with the authorization request: <strong>typically, by generating an unguessable value before redirecting the user to the Identity Provider and validating it before sending the token request</strong>.</p><p>Unfortunately, this kind of check is not made by the Auth0 ASP.NET 4.x and OWIN SDKs, making them vulnerable to this class of attack.</p><a id="more"></a><h2 id="How-can-I-reproduce-the-vulnerability"><a href="#How-can-I-reproduce-the-vulnerability" class="headerlink" title="How can I reproduce the vulnerability?"></a>How can I reproduce the vulnerability?</h2><p>Reproducing the vulnerability is quite easy. For that, I wrote a simple self-hosted OWIN/Katana console application using the vulnerable <code>Auth0-ASPNET-Owin</code> package.</p><div class="note tip"><p>The specific version doesn&#39;t matter much, as a quick look at the git history seems to indicate this vulnerability exists since the very first day: all versions are potentially vulnerable.</p></div><h3 id="Create-a-self-hosted-OWIN-application"><a href="#Create-a-self-hosted-OWIN-application" class="headerlink" title="Create a self-hosted OWIN application"></a>Create a self-hosted OWIN application</h3><figure class="highlight xml"><figcaption><span>packages.config</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">&lt;?xml version="1.0" encoding="utf-8"?&gt;</div><div class="line"><span class="tag">&lt;<span class="name">packages</span>&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">package</span> <span class="attr">id</span>=<span class="string">"Auth0-ASPNET-Owin"</span> <span class="attr">version</span>=<span class="string">"2.3.1"</span> <span class="attr">targetFramework</span>=<span class="string">"net47"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">package</span> <span class="attr">id</span>=<span class="string">"Microsoft.Owin"</span> <span class="attr">version</span>=<span class="string">"3.1.0"</span> <span class="attr">targetFramework</span>=<span class="string">"net47"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">package</span> <span class="attr">id</span>=<span class="string">"Microsoft.Owin.Host.HttpListener"</span> <span class="attr">version</span>=<span class="string">"3.1.0"</span> <span class="attr">targetFramework</span>=<span class="string">"net47"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">package</span> <span class="attr">id</span>=<span class="string">"Microsoft.Owin.Hosting"</span> <span class="attr">version</span>=<span class="string">"3.1.0"</span> <span class="attr">targetFramework</span>=<span class="string">"net47"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">package</span> <span class="attr">id</span>=<span class="string">"Microsoft.Owin.Security"</span> <span class="attr">version</span>=<span class="string">"3.1.0"</span> <span class="attr">targetFramework</span>=<span class="string">"net47"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">package</span> <span class="attr">id</span>=<span class="string">"Microsoft.Owin.Security.Cookies"</span> <span class="attr">version</span>=<span class="string">"3.1.0"</span> <span class="attr">targetFramework</span>=<span class="string">"net47"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">package</span> <span class="attr">id</span>=<span class="string">"Newtonsoft.Json"</span> <span class="attr">version</span>=<span class="string">"7.0.1"</span> <span class="attr">targetFramework</span>=<span class="string">"net47"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">package</span> <span class="attr">id</span>=<span class="string">"Owin"</span> <span class="attr">version</span>=<span class="string">"1.0"</span> <span class="attr">targetFramework</span>=<span class="string">"net47"</span> /&gt;</span></div><div class="line">  <span class="tag">&lt;<span class="name">package</span> <span class="attr">id</span>=<span class="string">"System.Text.Encodings.Web"</span> <span class="attr">version</span>=<span class="string">"4.5.0"</span> <span class="attr">targetFramework</span>=<span class="string">"net47"</span> /&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">packages</span>&gt;</span></div></pre></td></tr></table></figure><figure class="highlight csharp"><figcaption><span>Program.cs</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">using</span> System;</div><div class="line"><span class="keyword">using</span> Microsoft.Owin.Hosting;</div><div class="line"></div><div class="line"><span class="keyword">namespace</span> <span class="title">SessionFixationDemo</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">Program</span></div><div class="line">    &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">Main</span>(<span class="params"><span class="keyword">string</span>[] args</span>)</span></div><div class="line">        &#123;</div><div class="line">            <span class="keyword">const</span> <span class="keyword">string</span> address = <span class="string">"http://localhost:[port]/"</span>;</div><div class="line"></div><div class="line">            <span class="keyword">using</span> (WebApp.Start&lt;Startup&gt;(address))</div><div class="line">            &#123;</div><div class="line">                Console.WriteLine(<span class="string">$"Server is running on <span class="subst">&#123;address&#125;</span>, press CTRL+C to stop."</span>);</div><div class="line">                Console.ReadLine();</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><figure class="highlight csharp"><figcaption><span>Startup.cs</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">using</span> System.Text.Encodings.Web;</div><div class="line"><span class="keyword">using</span> Auth0.Owin;</div><div class="line"><span class="keyword">using</span> Microsoft.Owin;</div><div class="line"><span class="keyword">using</span> Microsoft.Owin.Security;</div><div class="line"><span class="keyword">using</span> Microsoft.Owin.Security.Cookies;</div><div class="line"><span class="keyword">using</span> Owin;</div><div class="line"></div><div class="line"><span class="keyword">namespace</span> <span class="title">SessionFixationDemo</span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Startup</span></div><div class="line">    &#123;</div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Configuration</span>(<span class="params">IAppBuilder app</span>)</span></div><div class="line">        &#123;</div><div class="line">            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);</div><div class="line"></div><div class="line">            app.UseCookieAuthentication(<span class="keyword">new</span> CookieAuthenticationOptions());</div><div class="line"></div><div class="line">            app.UseAuth0Authentication(<span class="keyword">new</span> Auth0AuthenticationOptions</div><div class="line">            &#123;</div><div class="line">                Domain = <span class="string">"[domain]"</span>,</div><div class="line">                ClientId = <span class="string">"[client identifier]"</span>,</div><div class="line">                ClientSecret = <span class="string">"[client secret]"</span></div><div class="line">            &#125;);</div><div class="line"></div><div class="line">            app.Run(<span class="keyword">async</span> context =&gt;</div><div class="line">            &#123;</div><div class="line">                <span class="keyword">if</span> (context.Request.Path == <span class="keyword">new</span> PathString(<span class="string">"/challenge"</span>))</div><div class="line">                &#123;</div><div class="line">                    <span class="keyword">var</span> properties = <span class="keyword">new</span> AuthenticationProperties</div><div class="line">                    &#123;</div><div class="line">                        RedirectUri = <span class="string">"/"</span></div><div class="line">                    &#125;;</div><div class="line"></div><div class="line">                    context.Authentication.Challenge(properties, <span class="string">"Auth0"</span>);</div><div class="line">                    <span class="keyword">return</span>;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="keyword">if</span> (context.Request.Path == <span class="keyword">new</span> PathString(<span class="string">"/fix-session"</span>))</div><div class="line">                &#123;</div><div class="line">                    <span class="keyword">var</span> code = context.Request.Query[<span class="string">"attacker_authorization_code"</span>];</div><div class="line">                    <span class="keyword">if</span> (<span class="keyword">string</span>.IsNullOrEmpty(code))</div><div class="line">                    &#123;</div><div class="line">                        context.Response.StatusCode = <span class="number">400</span>;</div><div class="line">                        <span class="keyword">return</span>;</div><div class="line">                    &#125;</div><div class="line"></div><div class="line">                    context.Response.ContentType = <span class="string">"text/html"</span>;</div><div class="line">                    <span class="keyword">await</span> context.Response.WriteAsync(<span class="string">$@"&lt;!doctype html&gt;</span></div><div class="line">&lt;html&gt;</div><div class="line">&lt;head&gt;</div><div class="line">    &lt;title&gt;Session fixation demonstration&lt;/title&gt;</div><div class="line">&lt;/head&gt;</div><div class="line">&lt;body&gt;</div><div class="line">    &lt;iframe src=""/signin-auth0?code=<span class="subst">&#123;HtmlEncoder.Default.Encode(code)&#125;</span>""&gt;&lt;/iframe&gt;</div><div class="line">&lt;/body&gt;</div><div class="line">&lt;/html&gt;");</div><div class="line">                    <span class="keyword">return</span>;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="keyword">if</span> (context.Authentication.User == <span class="literal">null</span> ||</div><div class="line">                   !context.Authentication.User.Identity.IsAuthenticated)</div><div class="line">                &#123;</div><div class="line">                    <span class="keyword">await</span> context.Response.WriteAsync(<span class="string">"You're not logged in. "</span>);</div><div class="line">                    <span class="keyword">await</span> context.Response.WriteAsync(<span class="string">"Visit /challenge to trigger a challenge."</span>);</div><div class="line">                    <span class="keyword">return</span>;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="keyword">var</span> name = HtmlEncoder.Default.Encode(context.Authentication.User.Identity.Name);</div><div class="line">                <span class="keyword">await</span> context.Response.WriteAsync(<span class="string">$"You're logged in as <span class="subst">&#123;name&#125;</span>."</span>);</div><div class="line">            &#125;);</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><h3 id="Register-a-new-Auth0-application"><a href="#Register-a-new-Auth0-application" class="headerlink" title="Register a new Auth0 application"></a>Register a new Auth0 application</h3><p>Thanks to Auth0&#39;s user-friendly portal, registering a new Web server application is quite straightforward. When created, don&#39;t forget to register <code>http://localhost:[port]/signin-auth0</code> as a valid <code>redirect_uri</code>:</p><img src="/2018/08/08/session-fixation-vulnerability-in-the-auth0-asp-net-and-owin-sdks/callback-uris.png" alt="callback-uris.png"><h3 id="Create-a-first-account-playing-the-attacker-role"><a href="#Create-a-first-account-playing-the-attacker-role" class="headerlink" title="Create a first account (playing the attacker role)"></a>Create a first account (playing the attacker role)</h3><p>Once the application is correctly registered, start the OWIN application and visit <code>http://localhost:[port]/challenge</code> to start a new authentication flow. Doing that will redirect you to the Auth0 login page:</p><img src="/2018/08/08/session-fixation-vulnerability-in-the-auth0-asp-net-and-owin-sdks/login-page.png" alt="login-page.png"><p>Then, create a new account that will be used as the attacker account and approve the authorization request. Once approved, you should be redirected to the OWIN application and the following message should appear:</p><blockquote><p>You&#39;re logged in as [email address of your first account].</p></blockquote><h3 id="Create-a-second-account-playing-the-victim-role"><a href="#Create-a-second-account-playing-the-victim-role" class="headerlink" title="Create a second account (playing the victim role)"></a>Create a second account (playing the victim role)</h3><p>For that, visit <code>http://localhost:[port]/challenge</code> a second time, create a new Auth0 account and approve the authorization request exactly like you did during the previous step.</p><p>At this point, you should be logged in as the victim:</p><blockquote><p>You&#39;re logged in as [email address of your second account].</p></blockquote><h3 id="In-a-private-browser-window-start-a-new-authentication-flow-using-the-attacker-account"><a href="#In-a-private-browser-window-start-a-new-authentication-flow-using-the-attacker-account" class="headerlink" title="In a private browser window, start a new authentication flow using the attacker account"></a>In a private browser window, start a new authentication flow using the attacker account</h3><p><strong>At this stage, the objective is to retrieve an authorization code associated with the attacker account</strong>. To do that, my preferred option is to use Fiddler and its breakpoints feature, that will allow you to abort the authentication flow before being redirected back to the OWIN application (and ensure the authorization code is not redeemed):</p><img src="/2018/08/08/session-fixation-vulnerability-in-the-auth0-asp-net-and-owin-sdks/fiddler-breakpoints.png" alt="fiddler-breakpoints.png"><p><strong>In a private browser window</strong>, visit <code>http://localhost:[port]/challenge</code>, go back to Fiddler and allow the challenge request to be processed by clicking on <code>Run to completion</code>.</p><img src="/2018/08/08/session-fixation-vulnerability-in-the-auth0-asp-net-and-owin-sdks/fiddler-breakpoint-management.png" alt="fiddler-breakpoint-management.png"><p>After logging in as the attacker, you should be redirected by Auth0 back to the OWIN application with the precious authorization code:</p><img src="/2018/08/08/session-fixation-vulnerability-in-the-auth0-asp-net-and-owin-sdks/fiddler-authorization-response.png" alt="fiddler-authorization-response.png"><p>Extract the authorization code from the query string and abort the request by choosing a fake response in Fiddler&#39;s list before clicking on <code>Run to completion</code>:</p><img src="/2018/08/08/session-fixation-vulnerability-in-the-auth0-asp-net-and-owin-sdks/fiddler-fake-responses.png" alt="fiddler-fake-responses.png"><p>Copy the attacker&#39;s authorization code and close the window.</p><h3 id="Force-the-victim-to-log-in-as-the-attacker"><a href="#Force-the-victim-to-log-in-as-the-attacker" class="headerlink" title="Force the victim to log in as the attacker"></a>Force the victim to log in as the attacker</h3><p>Now, go back to your main browser window and visit <code>http://localhost:[port]/</code> to confirm you&#39;re still logged in using the victim account.</p><p>Then, visit <code>http://localhost:[port]/fix-session?attacker_authorization_code=[code]</code>. This page contains a malicious iframe that directly points to the <code>redirect_uri</code> endpoint of the OWIN application. When visiting this page, <strong>you&#39;ll be immediately logged out of your main account – i.e the victim – and all requests will now be sent using the attacker&#39;s identity</strong>:</p><img src="/2018/08/08/session-fixation-vulnerability-in-the-auth0-asp-net-and-owin-sdks/malicious-iframe.png" alt="malicious-iframe.png"><div class="note tip"><p>In a real world attack, this HTML page would be hosted by an attacker on a different server and would be embedded in a malicious site or directly reached by a victim after clicking on a link. Here, the <code>/fix-session</code> endpoint is only provided for convenience.</p></div><h3 id="Acknowledgments"><a href="#Acknowledgments" class="headerlink" title="Acknowledgments"></a>Acknowledgments</h3><p><strong>Kudos to Marcin Hoppe and Jerrie Pelser for their promptness in getting back to me</strong>. While Auth0 doesn&#39;t have a real <a href="https://auth0.com/whitehat" target="_blank" rel="external">&quot;bug bounty&quot; program</a> yet – which is frankly unfortunate for a such big player in the Identity sector (specially when vendors like Microsoft or Okta already have proper programs in place) – it&#39;s certainly a good thing to be able to reach them very quickly.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Recently, I received a mail from Auth0 asking me if I was interested in joining them. I had used Auth0 many times in the past but I had never taken the time to look at their OSS SDKs. This mail was a good opportunity to change that.&lt;/p&gt;&lt;p&gt;When doing so, &lt;strong&gt;I discovered that both their &lt;a href=&quot;https://github.com/auth0/auth0-aspnet&quot;&gt;ASP.NET 4.x&lt;/a&gt; and &lt;a href=&quot;https://github.com/auth0/auth0-aspnet-owin&quot;&gt;OWIN/Katana&lt;/a&gt; SDKs were unfortunately prone to &amp;quot;session fixation&amp;quot;&lt;/strong&gt;, which is a form of cross-site request forgery allowing to force a victim to log in under an attacker&amp;#39;s account.&lt;/p&gt;&lt;p&gt;Since it&amp;#39;s a quite frequent vulnerability, here&amp;#39;s a quick overview of what causes it and how you can concretly exploit it.&lt;/p&gt;&lt;div class=&quot;note warn&quot;&gt;&lt;p&gt;Auth0 was already aware of this issue internally and &lt;a href=&quot;https://auth0.com/docs/security/bulletins/cve-2018-15121&quot;&gt;decided to switch to the official OWIN OpenID Connect middleware developed by Microsoft&lt;/a&gt;, which is not prone to this class of attack.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;If you need to migrate, Auth0 has prepared a &lt;a href=&quot;https://auth0.com/docs/quickstart/webapp/aspnet-owin/04-migrating&quot;&gt;migration guide&lt;/a&gt;&lt;/strong&gt; listing the steps required to replace &lt;code&gt;Auth0-ASPNET-Owin&lt;/code&gt; by Microsoft&amp;#39;s OpenID Connect middleware.&lt;/p&gt;&lt;/div&gt;&lt;h2 id=&quot;What-do-session-fixation-attacks-consist-in&quot;&gt;&lt;a href=&quot;#What-do-session-fixation-attacks-consist-in&quot; class=&quot;headerlink&quot; title=&quot;What do session fixation attacks consist in?&quot;&gt;&lt;/a&gt;What do session fixation attacks consist in?&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://tools.ietf.org/html/rfc6819&quot;&gt;OAuth2 threat model RFC&lt;/a&gt; – a must read for anyone dealing with OAuth2 and OpenID Connect – gives &lt;a href=&quot;https://tools.ietf.org/html/rfc6819#sehection-4.4.1.8&quot;&gt;an excellent definition of this threat and its practical implications&lt;/a&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Cross-site request forgery (CSRF) is a web-based attack whereby HTTP requests are transmitted from a user that the web site trusts or has authenticated (e.g., via HTTP redirects or HTML forms). CSRF attacks on OAuth approvals can allow an attacker to obtain authorization to OAuth protected resources without the consent of the user. This attack works against the redirect URI used in the authorization &amp;quot;code&amp;quot; flow.&lt;/p&gt;&lt;p&gt;An attacker could authorize an authorization &amp;quot;code&amp;quot; to their own protected resources on an authorization server. He then aborts the redirect flow back to the client on his device and tricks the victim into executing the redirect back to the client. The client receives the redirect, fetches the token(s) from the authorization server, and associates the victim&amp;#39;s client session with the resources accessible using the token.&lt;/p&gt;&lt;p&gt;Impact: the user accesses resources on behalf of the attacker. The effective impact depends on the type of resource accessed. For example, the user may upload private items to an attacker&amp;#39;s resources. Or, when using OAuth in 3rd-party login scenarios, the user may associate his client account with the attacker&amp;#39;s identity at the external Identity Provider. In this way, the attacker could easily access the victim&amp;#39;s data at the client by logging in from another device with his credentials at the external Identity Provider.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Usually, this (generally underestimated) threat is mitigated by correlating the authorization response with the authorization request: &lt;strong&gt;typically, by generating an unguessable value before redirecting the user to the Identity Provider and validating it before sending the token request&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Unfortunately, this kind of check is not made by the Auth0 ASP.NET 4.x and OWIN SDKs, making them vulnerable to this class of attack.&lt;/p&gt;
    
    </summary>
    
      <category term="Development" scheme="https://kevinchalet.com/categories/development/"/>
    
    
      <category term="owin" scheme="https://kevinchalet.com/tags/owin/"/>
    
      <category term="auth0" scheme="https://kevinchalet.com/tags/auth0/"/>
    
      <category term="vulnerability" scheme="https://kevinchalet.com/tags/vulnerability/"/>
    
      <category term="asp.net" scheme="https://kevinchalet.com/tags/asp-net/"/>
    
  </entry>
  
  <entry>
    <title>Implementing advanced scenarios using the new OpenIddict RC3 events model</title>
    <link href="https://kevinchalet.com/2018/07/02/implementing-advanced-scenarios-using-the-new-openiddict-rc3-events-model/"/>
    <id>https://kevinchalet.com/2018/07/02/implementing-advanced-scenarios-using-the-new-openiddict-rc3-events-model/</id>
    <published>2018-07-02T16:15:00.000Z</published>
    <updated>2018-07-02T17:13:12.000Z</updated>
    
    <content type="html"><![CDATA[<p>Prior to OpenIddict RC3, <a href="https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/blob/dev/src/AspNet.Security.OpenIdConnect.Server/OpenIdConnectServerProvider.cs" target="_blank" rel="external">the events model used by the OpenID Connect server middleware</a> (i.e the OIDC server framework behind OpenIddict) was deliberately not accessible due to the nature of OpenIddict: being initially designed for non-experts, exposing such a powerful API – <strong>that allows altering the way OpenID Connect requests are processed</strong> – didn&#39;t seem like a good idea at first sight.</p><p>With time, the core audience of OpenIddict has evolved a bit to not only include beginners but also developers who were already familiar with OAuth/OpenID Connect or had used <code>OAuthAuthorizationServerMiddleware</code> or ASOS in the past. For them, the fact OpenIddict didn&#39;t allow them to take control of the request processing pipeline was often a blocker. <strong>With the introduction of OpenIddict RC3, we&#39;re changing that.</strong></p><div class="note info"><p>Using these advanced APIs is not recommended if you&#39;re not familiar with the OAuth/OpenID Connect specifications or with <a href="/2016/07/13/creating-your-own-openid-connect-server-with-asos-creating-your-own-authorization-provider/" title="the events model used by the OpenID Connect server middleware">the events model used by the OpenID Connect server middleware</a>. If you&#39;re not sure whether you should use these APIs, don&#39;t hesitate to reach us on Gitter or on GitHub.</p></div><h2 id="Introducing-event-handlers"><a href="#Introducing-event-handlers" class="headerlink" title="Introducing event handlers"></a>Introducing event handlers</h2><p>The events model is structured around <code>IOpenIddictServerEventHandler&lt;TEvent&gt;</code> and <code>IOpenIddictValidationEventHandler&lt;TEvent&gt;</code>. <strong>These 2 interfaces represent handlers that are invoked every time an event of type <code>TEvent</code> is triggered by the OpenIddict server or validation handlers</strong>.</p><p>At the time of writing, <strong>OpenIddict exposes 44 server events</strong> – grouped into the <code>OpenIddictServerEvents</code> static class to make them easier to find – <strong>and 5 validation events</strong>, exposed under <code>OpenIddictValidationEvents</code>.</p><p><strong>Each event represents a specific moment in the request processing pipeline</strong> (e.g the moment the OpenIddict server determines whether the request is an OpenID Connect request it should handle, the moment it extracts it, handles it or returns a response).</p><div class="note tip"><p><strong>Multiple handlers of the same type can be registered</strong>: they will be sequentially invoked in the same order as the one used to register them. As soon as a handler calls a method that indicates the request should no longer be processed (e.g <code>HandleResponse()</code> or <code>SkipHandler()</code>), OpenIddict will stop invoking the handlers and the next ones will be automatically ignored.</p><p>For security reasons, <strong>the custom handlers will be invoked by OpenIddict after its own validation routines</strong>. If a request is rejected by OpenIddict, your own handlers won&#39;t be invoked.</p></div><h2 id="Creating-and-registering-a-custom-event-handler"><a href="#Creating-and-registering-a-custom-event-handler" class="headerlink" title="Creating and registering a custom event handler"></a>Creating and registering a custom event handler</h2><p>Creating an event handler is straightforward: pick the event you need and add a class that implements either <code>IOpenIddictServerEventHandler&lt;TEvent&gt;</code> (for a handler that receives events triggered by the OpenIddict server services) or <code>IOpenIddictValidationEventHandler&lt;TEvent&gt;</code> (for a handler that receives events triggered by the OpenIddict token validation services):</p><p>For instance, to return custom metadata in the discovery document, you&#39;ll need to implement <code>IOpenIddictServerEventHandler&lt;OpenIddictServerEvents.HandleConfigurationRequest&gt;</code>:</p><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">public class MyEventHandler : IOpenIddictServerEventHandler&lt;OpenIddictServerEvents.HandleConfigurationRequest&gt;</div><div class="line">&#123;</div><div class="line">    public Task HandleAsync(OpenIddictServerEvents.HandleConfigurationRequest notification, CancellationToken cancellationToken)</div><div class="line">    &#123;</div><div class="line">        notification.Context.Metadata["company_name"] = "Contoso";</div><div class="line"></div><div class="line">        return Task.CompletedTask;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p>To register it, use <code>options.AddEventHandler&lt;TEvent, THandler&gt;()</code> (by default, the handler is registered as a scoped service):</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">ConfigureServices</span>(<span class="params">IServiceCollection services</span>)</span></div><div class="line">&#123;</div><div class="line">    services.AddOpenIddict()</div><div class="line"></div><div class="line">        <span class="comment">// Register the OpenIddict core services.</span></div><div class="line">        .AddCore(options =&gt;</div><div class="line">        &#123;</div><div class="line">            <span class="comment">// ...</span></div><div class="line">        &#125;)</div><div class="line"></div><div class="line">        <span class="comment">// Register the OpenIddict server handler.</span></div><div class="line">        .AddServer(options =&gt;</div><div class="line">        &#123;</div><div class="line">            <span class="comment">// ...</span></div><div class="line"></div><div class="line">            options.AddEventHandler&lt;OpenIddictServerEvents.HandleConfigurationRequest, MyEventHandler&gt;();</div><div class="line">        &#125;)</div><div class="line"></div><div class="line">        <span class="comment">// Register the OpenIddict validation handler.</span></div><div class="line">        .AddValidation();</div><div class="line">&#125;</div></pre></td></tr></table></figure><a id="more"></a><p>Alternatively, <strong>if your handler can be trivialy implemented and doesn&#39;t use constructor injection, you can register it inline</strong>:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">ConfigureServices</span>(<span class="params">IServiceCollection services</span>)</span></div><div class="line">&#123;</div><div class="line">    services.AddOpenIddict()</div><div class="line"></div><div class="line">        <span class="comment">// Register the OpenIddict core services.</span></div><div class="line">        .AddCore(options =&gt;</div><div class="line">        &#123;</div><div class="line">            <span class="comment">// ...</span></div><div class="line">        &#125;)</div><div class="line"></div><div class="line">        <span class="comment">// Register the OpenIddict server handler.</span></div><div class="line">        .AddServer(options =&gt;</div><div class="line">        &#123;</div><div class="line">            <span class="comment">// ...</span></div><div class="line"></div><div class="line">            options.AddEventHandler&lt;OpenIddictServerEvents.HandleConfigurationRequest&gt;(</div><div class="line">                notification =&gt;</div><div class="line">                &#123;</div><div class="line">                    notification.Context.Metadata[<span class="string">"company_name"</span>] = <span class="string">"Contoso"</span>;</div><div class="line"></div><div class="line">                    <span class="keyword">return</span> Task.CompletedTask;</div><div class="line">                &#125;);</div><div class="line">        &#125;)</div><div class="line"></div><div class="line">        <span class="comment">// Register the OpenIddict validation handler.</span></div><div class="line">        .AddValidation();</div><div class="line">&#125;</div></pre></td></tr></table></figure><h2 id="Concrete-examples"><a href="#Concrete-examples" class="headerlink" title="Concrete examples"></a>Concrete examples</h2><h3 id="Tweaking-the-endpoint-detection-logic"><a href="#Tweaking-the-endpoint-detection-logic" class="headerlink" title="Tweaking the endpoint detection logic"></a>Tweaking the endpoint detection logic</h3><p>By default, <strong>OpenIddict uses a path-based endpoint resolution logic to determine whether the incoming request is an OpenID Connect request</strong> it should handle. This is done by comparing the request path to the endpoint paths registered in the OpenIddict server options. In some cases, you&#39;ll probably want to listen on multiple paths at the same time instead of a single one. For that, you can use the <code>OpenIddictServerEvents.MatchEndpoint</code> event:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">options.AddEventHandler&lt;OpenIddictServerEvents.MatchEndpoint&gt;(notification =&gt;</div><div class="line">&#123;</div><div class="line">    <span class="comment">// By default, only requests sent to /connect/token will be treated as valid</span></div><div class="line">    <span class="comment">// token requests by OpenIddict. This custom logic allows requests pointing</span></div><div class="line">    <span class="comment">// to /connect/second-token-endpoint to be treated the same way so that</span></div><div class="line">    <span class="comment">// requests can be sent to one of the two addresses without any distinction.</span></div><div class="line">    <span class="keyword">var</span> request = notification.Context.HttpContext.Request;</div><div class="line">    <span class="keyword">if</span> (request.Path == <span class="string">"/connect/second-token-endpoint"</span>)</div><div class="line">    &#123;</div><div class="line">        notification.Context.MatchTokenEndpoint();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">return</span> Task.CompletedTask;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><h3 id="Returning-the-list-of-supported-social-providers-as-part-of-the-discovery-document"><a href="#Returning-the-list-of-supported-social-providers-as-part-of-the-discovery-document" class="headerlink" title="Returning the list of supported social providers as part of the discovery document"></a>Returning the list of supported social providers as part of the discovery document</h3><p>If you need to expose the external providers that are supported by your server application, you can use the <code>HandleConfigurationRequest</code> event:</p><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">public class MyEventHandler : IOpenIddictServerEventHandler&lt;OpenIddictServerEvents.HandleConfigurationRequest&gt;</div><div class="line">&#123;</div><div class="line">    private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;</div><div class="line"></div><div class="line">    public MyEventHandler(IAuthenticationSchemeProvider authenticationSchemeProvider)</div><div class="line">        =&gt; _authenticationSchemeProvider = authenticationSchemeProvider;</div><div class="line"></div><div class="line">    public async Task HandleAsync(OpenIddictServerEvents.HandleConfigurationRequest notification, CancellationToken cancellationToken)</div><div class="line">        =&gt; notification.Context.Metadata["external_providers_supported"] = new JArray(</div><div class="line">            from provider in await _authenticationSchemeProvider.GetAllSchemesAsync()</div><div class="line">            where !string.IsNullOrEmpty(provider.DisplayName)</div><div class="line">            select provider.Name);</div><div class="line">&#125;</div></pre></td></tr></table></figure><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line">    <span class="attr">"issuer"</span>: <span class="string">"https://localhost:44344/"</span>,</div><div class="line">    <span class="attr">"token_endpoint"</span>: <span class="string">"https://localhost:44344/connect/token"</span>,</div><div class="line">    <span class="attr">"jwks_uri"</span>: <span class="string">"https://localhost:44344/.well-known/jwks"</span>,</div><div class="line">    <span class="attr">"grant_types_supported"</span>: [</div><div class="line">        <span class="string">"password"</span>,</div><div class="line">        <span class="string">"refresh_token"</span></div><div class="line">    ],</div><div class="line">    <span class="attr">"scopes_supported"</span>: [</div><div class="line">        <span class="string">"openid"</span>,</div><div class="line">        <span class="string">"offline_access"</span></div><div class="line">    ],</div><div class="line">    <span class="attr">"claims_supported"</span>: [</div><div class="line">        <span class="string">"aud"</span>,</div><div class="line">        <span class="string">"exp"</span>,</div><div class="line">        <span class="string">"iat"</span>,</div><div class="line">        <span class="string">"iss"</span>,</div><div class="line">        <span class="string">"jti"</span>,</div><div class="line">        <span class="string">"sub"</span></div><div class="line">    ],</div><div class="line">    <span class="attr">"subject_types_supported"</span>: [</div><div class="line">        <span class="string">"public"</span></div><div class="line">    ],</div><div class="line">    <span class="attr">"token_endpoint_auth_methods_supported"</span>: [</div><div class="line">        <span class="string">"client_secret_basic"</span>,</div><div class="line">        <span class="string">"client_secret_post"</span></div><div class="line">    ],</div><div class="line">    <span class="attr">"claims_parameter_supported"</span>: <span class="literal">false</span>,</div><div class="line">    <span class="attr">"request_parameter_supported"</span>: <span class="literal">false</span>,</div><div class="line">    <span class="attr">"request_uri_parameter_supported"</span>: <span class="literal">false</span>,</div><div class="line">    <span class="attr">"external_providers_supported"</span>: [</div><div class="line">        <span class="string">"Google"</span>,</div><div class="line">        <span class="string">"Microsoft"</span>,</div><div class="line">        <span class="string">"Facebook"</span></div><div class="line">    ]</div><div class="line">&#125;</div></pre></td></tr></table></figure><h3 id="Implementing-the-token-endpoint-at-the-handler-level-without-having-an-authorization-controller"><a href="#Implementing-the-token-endpoint-at-the-handler-level-without-having-an-authorization-controller" class="headerlink" title="Implementing the token endpoint at the handler level without having an authorization controller"></a>Implementing the token endpoint at the handler level without having an authorization controller</h3><p>With OpenIddict, the &quot;standard&quot; way to process authorization or token requests is to have an authorization controller dedicated to handling these requests. Starting with RC3, <strong>this can also be done directly at the OpenIddict server handler level</strong> (e.g for those who don&#39;t need or don&#39;t want to use ASP.NET Core MVC).</p><p>Here&#39;s how it could be done by writing an <code>OpenIddictServerBuilder</code> extension:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">CustomOpenIddictServerExtensions</span></div><div class="line">&#123;</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> OpenIddictServerBuilder <span class="title">UseCustomTokenEndpoint</span>(<span class="params"></span></span></div><div class="line">        <span class="keyword">this</span> OpenIddictServerBuilder builder)</div><div class="line">    &#123;</div><div class="line">        <span class="keyword">if</span> (builder == <span class="literal">null</span>)</div><div class="line">        &#123;</div><div class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> ArgumentNullException(<span class="keyword">nameof</span>(builder));</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="keyword">return</span> builder.AddEventHandler&lt;OpenIddictServerEvents.HandleTokenRequest&gt;(</div><div class="line">            notification =&gt;</div><div class="line">            &#123;</div><div class="line">                <span class="keyword">var</span> request = notification.Context.Request;</div><div class="line">                <span class="keyword">if</span> (!request.IsPasswordGrantType())</div><div class="line">                &#123;</div><div class="line">                    <span class="keyword">return</span> Task.CompletedTask;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="comment">// Validate the user credentials.</span></div><div class="line"></div><div class="line">                <span class="comment">// Note: to mitigate brute force attacks, you SHOULD strongly consider</span></div><div class="line">                <span class="comment">// applying a key derivation function like PBKDF2 to slow down</span></div><div class="line">                <span class="comment">// the password validation process. You SHOULD also consider</span></div><div class="line">                <span class="comment">// using a time-constant comparer to prevent timing attacks.</span></div><div class="line">                <span class="keyword">if</span> (request.Username != <span class="string">"alice@wonderland.com"</span> ||</div><div class="line">                    request.Password != <span class="string">"P@ssw0rd"</span>)</div><div class="line">                &#123;</div><div class="line">                    notification.Context.Reject(</div><div class="line">                        error: OpenIdConnectConstants.Errors.InvalidGrant,</div><div class="line">                        description: <span class="string">"The specified credentials are invalid."</span>);</div><div class="line"></div><div class="line">                    <span class="keyword">return</span> Task.CompletedTask;</div><div class="line">                &#125;</div><div class="line"></div><div class="line">                <span class="comment">// Create a new ClaimsIdentity holding the user identity.</span></div><div class="line">                <span class="keyword">var</span> identity = <span class="keyword">new</span> ClaimsIdentity(</div><div class="line">                    notification.Context.Scheme.Name,</div><div class="line">                    OpenIdConnectConstants.Claims.Name,</div><div class="line">                    OpenIdConnectConstants.Claims.Role);</div><div class="line"></div><div class="line">                <span class="comment">// Add a "sub" claim containing the user identifier, and attach</span></div><div class="line">                <span class="comment">// the "access_token" destination to allow OpenIddict to store it</span></div><div class="line">                <span class="comment">// in the access token, so it can be retrieved from your controllers.</span></div><div class="line">                identity.AddClaim(OpenIdConnectConstants.Claims.Subject,</div><div class="line">                    <span class="string">"71346D62-9BA5-4B6D-9ECA-755574D628D8"</span>,</div><div class="line">                    OpenIdConnectConstants.Destinations.AccessToken);</div><div class="line"></div><div class="line">                identity.AddClaim(OpenIdConnectConstants.Claims.Name, <span class="string">"Alice"</span>,</div><div class="line">                    OpenIdConnectConstants.Destinations.AccessToken);</div><div class="line"></div><div class="line">                <span class="comment">// ... add other claims, if necessary.</span></div><div class="line"></div><div class="line">                <span class="keyword">var</span> principal = <span class="keyword">new</span> ClaimsPrincipal(identity);</div><div class="line">                notification.Context.Validate(principal);</div><div class="line"></div><div class="line">                <span class="keyword">return</span> Task.CompletedTask;</div><div class="line">            &#125;);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">ConfigureServices</span>(<span class="params">IServiceCollection services</span>)</span></div><div class="line">&#123;</div><div class="line">    services.AddOpenIddict()</div><div class="line"></div><div class="line">        <span class="comment">// Register the OpenIddict core services.</span></div><div class="line">        .AddCore(options =&gt;</div><div class="line">        &#123;</div><div class="line">            <span class="comment">// ...</span></div><div class="line">        &#125;)</div><div class="line"></div><div class="line">        <span class="comment">// Register the OpenIddict server handler.</span></div><div class="line">        .AddServer(options =&gt;</div><div class="line">        &#123;</div><div class="line">            <span class="comment">// ...</span></div><div class="line"></div><div class="line">            options.UseCustomTokenEndpoint();</div><div class="line">        &#125;)</div><div class="line"></div><div class="line">        <span class="comment">// Register the OpenIddict validation handler.</span></div><div class="line">        .AddValidation();</div><div class="line">&#125;</div></pre></td></tr></table></figure><h3 id="Extracting-access-tokens-from-the-query-string"><a href="#Extracting-access-tokens-from-the-query-string" class="headerlink" title="Extracting access tokens from the query string"></a>Extracting access tokens from the query string</h3><p>In some cases, flowing the access token in the HTTP request headers is not possible (e.g when using WebSockets with JS clients). To work around these limitations, <strong>you can transfer it as a query string parameter</strong> and configure the OpenIddict validation handler to use your extraction logic by adding an event handler for <code>OpenIddictValidationEvents.RetrieveToken</code>:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">ConfigureServices</span>(<span class="params">IServiceCollection services</span>)</span></div><div class="line">&#123;</div><div class="line">    services.AddOpenIddict()</div><div class="line"></div><div class="line">        <span class="comment">// Register the OpenIddict validation handler.</span></div><div class="line">        .AddValidation(options =&gt;</div><div class="line">        &#123;</div><div class="line">            <span class="comment">// ...</span></div><div class="line"></div><div class="line">            options.AddEventHandler&lt;OpenIddictValidationEvents.RetrieveToken&gt;(</div><div class="line">                notification =&gt;</div><div class="line">                &#123;</div><div class="line">                    notification.Context.Token = notification.Context.Request.Query[<span class="string">"access_token"</span>];</div><div class="line"></div><div class="line">                    <span class="keyword">return</span> Task.CompletedTask;</div><div class="line">                &#125;);</div><div class="line">        &#125;);</div><div class="line">&#125;</div></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      In this post, discover how to leverage the events model introduced in OpenIddict RC3 to implement advanced features.
    
    </summary>
    
      <category term="Development" scheme="https://kevinchalet.com/categories/development/"/>
    
    
      <category term="asp.net core" scheme="https://kevinchalet.com/tags/asp-net-core/"/>
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
  </entry>
  
  <entry>
    <title>OpenIddict RC3 is out</title>
    <link href="https://kevinchalet.com/2018/06/20/openiddict-rc3-is-out/"/>
    <id>https://kevinchalet.com/2018/06/20/openiddict-rc3-is-out/</id>
    <published>2018-06-20T14:00:00.000Z</published>
    <updated>2018-07-04T01:42:43.000Z</updated>
    
    <content type="html"><![CDATA[<p>OpenIddict RC3 is now available on NuGet.org:</p><ul><li><a href="https://www.nuget.org/packages/OpenIddict/1.0.0-rc3-final" target="_blank" rel="external">OpenIddict – 1.0.0-rc3-final</a> (for ASP.NET Core 1.x)</li><li><a href="https://www.nuget.org/packages/OpenIddict/2.0.0-rc3-final" target="_blank" rel="external">OpenIddict – 2.0.0-rc3-final</a> (for ASP.NET Core 2.x)</li></ul><h2 id="What-39-s-new-in-this-release"><a href="#What-39-s-new-in-this-release" class="headerlink" title="What&#39;s new in this release?"></a>What&#39;s new in this release?</h2><h3 id="The-OpenIddict-services-registration-APIs-have-been-revamped"><a href="#The-OpenIddict-services-registration-APIs-have-been-revamped" class="headerlink" title="The OpenIddict services registration APIs have been revamped"></a>The OpenIddict services registration APIs have been revamped</h3><p>In this release, <strong>we focused on reworking the OpenIddict registration APIs to offer a better user experience</strong>.</p><p>As part of this change, <strong>we split the OpenIddict services into three areas</strong> - <code>Core</code>, <code>Server</code> and <code>Validation</code> - and the <code>IServiceCollection</code> APIs have been updated to reflect that:</p><img src="/2018/06/20/openiddict-rc3-is-out/main-builder.png" alt="main-builder.png"><p>Each specialized builder only exposes the options that are relevant to its specific area:</p><img src="/2018/06/20/openiddict-rc3-is-out/specialized-builders.png" alt="specialized-builders.png"><p>Of course, the calls to <code>AddCore()</code>, <code>AddServer()</code> and <code>AddValidation()</code> can be chained:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict core services.</span></div><div class="line">    .AddCore(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// Register the Entity Framework stores and models.</span></div><div class="line">        options.UseEntityFrameworkCore()</div><div class="line">               .UseDbContext&lt;ApplicationDbContext&gt;();</div><div class="line">    &#125;)</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict server handler.</span></div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// Register the ASP.NET Core MVC binder used by OpenIddict.</span></div><div class="line">        <span class="comment">// Note: if you don't call this method, you won't be able to</span></div><div class="line">        <span class="comment">// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.</span></div><div class="line">        options.UseMvc();</div><div class="line"></div><div class="line">        <span class="comment">// Enable the authorization, logout, token and userinfo endpoints.</span></div><div class="line">        options.EnableAuthorizationEndpoint(<span class="string">"/connect/authorize"</span>)</div><div class="line">               .EnableLogoutEndpoint(<span class="string">"/connect/logout"</span>)</div><div class="line">               .EnableTokenEndpoint(<span class="string">"/connect/token"</span>)</div><div class="line">               .EnableUserinfoEndpoint(<span class="string">"/api/userinfo"</span>);</div><div class="line"></div><div class="line">        <span class="comment">// Note: the Mvc.Client sample only uses the code flow and the password flow, but you</span></div><div class="line">        <span class="comment">// can enable the other flows if you need to support implicit or client credentials.</span></div><div class="line">        options.AllowAuthorizationCodeFlow()</div><div class="line">               .AllowPasswordFlow()</div><div class="line">               .AllowRefreshTokenFlow();</div><div class="line"></div><div class="line">        <span class="comment">// During development, you can disable the HTTPS requirement.</span></div><div class="line">        options.DisableHttpsRequirement();</div><div class="line">    &#125;)</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict validation handler.</span></div><div class="line">    <span class="comment">// Note: the OpenIddict validation handler is only compatible with the</span></div><div class="line">    <span class="comment">// default token format or with reference tokens and cannot be used with</span></div><div class="line">    <span class="comment">// JWT tokens. For JWT tokens, use the Microsoft JWT bearer handler.</span></div><div class="line">    .AddValidation();</div></pre></td></tr></table></figure><p><strong>Introducing these specialized builders was also a great opportunity to revisit how the OpenIddict entities are registered</strong>. In the RC2 bits, this is controlled by the <code>services.AddOpenIddict&lt;...&gt;()</code> method, that determines which entities are used depending on the overload.</p><p>In RC3, the generic <code>services.AddOpenIddict&lt;...&gt;()</code> methods have been removed and replaced by a more explicit pattern:</p><img src="/2018/06/20/openiddict-rc3-is-out/core-builder-entities.png" alt="core-builder-entities.png"><a id="more"></a><hr><h3 id="OpenIddict-now-has-its-own-validation-handler-compatible-with-reference-tokens"><a href="#OpenIddict-now-has-its-own-validation-handler-compatible-with-reference-tokens" class="headerlink" title="OpenIddict now has its own validation handler, compatible with reference tokens"></a>OpenIddict now has its own validation handler, compatible with reference tokens</h3><p>Thanks to <a href="https://github.com/openiddict/openiddict-core/pull/589" target="_blank" rel="external">a great contribution from Chino Chang</a>, OpenIddict now has its dedicated validation handler, based on <a href="https://www.nuget.org/packages/AspNet.Security.OAuth.Validation/" target="_blank" rel="external">the aspnet-contrib handler</a>.</p><p><strong>This handler supports both the default token format (opaque) and reference tokens</strong>. Like the aspnet-contrib handler, you can use it as a standalone handler (i.e without having to register the OpenIddict core or server services):</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Register the OpenIddict validation handler.</span></div><div class="line">services.AddOpenIddict()</div><div class="line">    .AddValidation();</div></pre></td></tr></table></figure><p>Resource servers that use reference tokens will have to configure the core services and register the appropriate stores to be able to use it:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Register the OpenIddict services.</span></div><div class="line">services.AddOpenIddict()</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict core services.</span></div><div class="line">    .AddCore(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// Register the Entity Framework entities and stores.</span></div><div class="line">        options.UseEntityFrameworkCore()</div><div class="line">               .UseDbContext&lt;ApplicationDbContext&gt;();</div><div class="line">    &#125;)</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict validation handler.</span></div><div class="line">    .AddValidation(options =&gt; options.UseReferenceTokens());</div></pre></td></tr></table></figure><p>The aspnet-contrib handler will continue to be fully supported and will still be usable with OpenIddict so existing applications can keep using <code>services.AddAuthentication().AddOAuthValidation()</code> instead of <code>services.AddOpenIddict().AddValidation()</code> for opaque token validation.</p><div class="note tip"><p>Note: <code>OpenIddictValidationHandler</code> lives in the <code>OpenIddict.Validation</code> package, which is referenced by the <code>OpenIddict</code> metapackage. <strong>You don&#39;t have to add a new <code>PackageReference</code> to be able to use it</strong>.</p></div><hr><h3 id="MongoDB-is-now-officially-supported"><a href="#MongoDB-is-now-officially-supported" class="headerlink" title="MongoDB is now officially supported"></a>MongoDB is now officially supported</h3><p><strong>OpenIddict now natively supports MongoDB</strong>, one of the most popular NoSQL/document-oriented databases.</p><p>To configure OpenIddict to use MongoDB, reference the <code>OpenIddict.MongoDb</code> package and call the <code>options.UseMongoDb()</code> extension:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict core services.</span></div><div class="line">    .AddCore(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// Configure OpenIddict to use the MongoDB stores and models.</span></div><div class="line">        options.UseMongoDb();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><p>By default, the MongoDB stores will resolve the <code>IMongoDatabase</code> service from the DI container so you&#39;ll have to register it using the usual ASP.NET Core DI extensions. E.g:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">services.AddSingleton(<span class="keyword">new</span> MongoClient().GetDatabase(<span class="string">"main-db"</span>));</div></pre></td></tr></table></figure><p>Alternatively, developers who work with multiple MongoDB databases in the same application will be able to explicitly set the one they want to use in the OpenIddict options:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict core services.</span></div><div class="line">    .AddCore(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// Configure OpenIddict to use the MongoDB stores and models.</span></div><div class="line">        options.UseMongoDb()</div><div class="line">               .UseDatabase(<span class="keyword">new</span> MongoClient().GetDatabase(<span class="string">"openiddict-db"</span>));</div><div class="line">    &#125;)</div></pre></td></tr></table></figure><p>If no database can be resolved, an exception will be automatically thrown at runtime.</p><p>Other helpers are available to allow you to customize the default entities or the collection names:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict core services.</span></div><div class="line">    .AddCore(options =&gt;</div><div class="line">    &#123;</div><div class="line">        <span class="comment">// Configure OpenIddict to use the MongoDB stores and models.</span></div><div class="line">        options.UseMongoDb()</div><div class="line">               .ReplaceDefaultApplicationEntity&lt;MyApp&gt;()</div><div class="line">               .ReplaceDefaultAuthorizationEntity&lt;MyAuth&gt;()</div><div class="line">               .ReplaceDefaultScopeEntity&lt;MyScope&gt;()</div><div class="line">               .ReplaceDefaultTokenEntity&lt;MyToken&gt;()</div><div class="line">               .SetApplicationsCollectionName(<span class="string">"my-apps"</span>)</div><div class="line">               .SetAuthorizationsCollectionName(<span class="string">"my-auths"</span>)</div><div class="line">               .SetScopesCollectionName(<span class="string">"my-scopes"</span>)</div><div class="line">               .SetTokensCollectionName(<span class="string">"my-tokens"</span>);</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><hr><h3 id="OpenIddict-no-longer-comes-with-a-default-set-of-entities-and-stores-base-classes"><a href="#OpenIddict-no-longer-comes-with-a-default-set-of-entities-and-stores-base-classes" class="headerlink" title="OpenIddict no longer comes with a default set of entities and stores base classes"></a>OpenIddict no longer comes with a default set of entities and stores base classes</h3><p><strong>In the previous iterations of OpenIddict, an important effort was made to create a shared set of entities</strong> (contained in the <code>OpenIddict.Models</code> package) that could be used not only by the official Entity Framework 6.x and Entity Framework Core stores, but also by third-party/custom stores.</p><p>This pattern had many pros - like avoiding code duplication or having base classes that simplify the development of custom stores (which is why it was also eventually adopted by the ASP.NET team for ASP.NET Core Identity in 2.0).</p><p>Unfortunately, this approach had also a major issue: we had to design the default entities as &quot;lowest common denominators&quot;, so that they could be used by all/most ORMs or document databases. In practice, this meant that things like <code>OpenIddictApplication.RedirectUris</code> or <code>OpenIddictApplication.PostLogoutRedirectUris</code> had to be represented as JSON-serialized <code>strings</code> for SQL-oriented ORMs like EF 6.x and EF Core to work natively.</p><p><strong>In RC3, each stores package will come with its own models</strong> you&#39;ll be able to use exactly like in the previous iterations.</p><div class="note info"><p><strong>Make sure you don&#39;t reference the obsolete <code>OpenIddict.Models</code> or <code>OpenIddict.Stores</code> packages</strong>. The new models are automatically referenced by the stores packages they belong to and you don&#39;t need to add any reference to use them.</p></div><hr><h3 id="Application-permissions-have-been-reworked-to-be-simpler"><a href="#Application-permissions-have-been-reworked-to-be-simpler" class="headerlink" title="Application permissions have been reworked to be simpler"></a>Application permissions have been reworked to be simpler</h3><p>In RC2, we introduced application permissions. <strong>To make the migration from RC1 to RC2 smoother, application permissions were mostly optional</strong> and OpenIddict had a fallback mechanism called &quot;implicit permissions&quot; it used to determine whether an application could perform the requested action. For instance, if no permission was explicitly attached to the application, it was considered fully trusted and was granted all the permissions.</p><p>Similarly, if you granted the &quot;token endpoint&quot; permission to an application but NO &quot;grant type&quot; permission, it was assumed the client application was allowed to use the password or client credentials grants.</p><p><strong>Retrospectively, this logic was too complex and I decided to remove it in RC3.</strong></p><p><strong>Starting with RC3, permissions are no longer optional nor implicit</strong>: if you don&#39;t explicitly grant an application the necessary permissions, it will be blocked by OpenIddict.</p><p>To attach permissions to an application, use <code>OpenIddictApplicationManager</code>:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> descriptor = <span class="keyword">new</span> OpenIddictApplicationDescriptor</div><div class="line">&#123;</div><div class="line">    ClientId = <span class="string">"mvc"</span>,</div><div class="line">    ClientSecret = <span class="string">"901564A5-E7FE-42CB-B10D-61EF6A8F3654"</span>,</div><div class="line">    DisplayName = <span class="string">"MVC client application"</span>,</div><div class="line">    PostLogoutRedirectUris = &#123; <span class="keyword">new</span> Uri(<span class="string">"http://localhost:53507/signout-callback-oidc"</span>) &#125;,</div><div class="line">    RedirectUris = &#123; <span class="keyword">new</span> Uri(<span class="string">"http://localhost:53507/signin-oidc"</span>) &#125;,</div><div class="line">    Permissions =</div><div class="line">    &#123;</div><div class="line">        OpenIddictConstants.Permissions.Endpoints.Authorization,</div><div class="line">        OpenIddictConstants.Permissions.Endpoints.Logout,</div><div class="line">        OpenIddictConstants.Permissions.Endpoints.Token,</div><div class="line">        OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode,</div><div class="line">        OpenIddictConstants.Permissions.GrantTypes.RefreshToken,</div><div class="line">        OpenIddictConstants.Permissions.Scopes.Email,</div><div class="line">        OpenIddictConstants.Permissions.Scopes.Profile,</div><div class="line">        OpenIddictConstants.Permissions.Scopes.Roles</div><div class="line">    &#125;</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="keyword">await</span> _applicationManager.CreateAsync(descriptor);</div></pre></td></tr></table></figure><p>If you don&#39;t care about permissions (e.g because you don&#39;t have third-party clients), you can instead disable them:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict server handler.</span></div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        options.IgnoreEndpointPermissions()</div><div class="line">               .IgnoreGrantTypePermissions()</div><div class="line">               .IgnoreScopePermissions();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><hr><h3 id="Scope-validation-and-anonymous-clients-rejection-are-now-enabled-by-default"><a href="#Scope-validation-and-anonymous-clients-rejection-are-now-enabled-by-default" class="headerlink" title="Scope validation and anonymous clients rejection are now enabled by default"></a>Scope validation and anonymous clients rejection are now enabled by default</h3><p>Starting with RC3, <strong>OpenIddict will now enforce scope validation and reject token and revocation requests that don&#39;t specify a <code>client_id</code></strong>. In RC2, these checks were opt-in (enabled via <code>options.EnableScopeValidation()</code> and <code>options.RequireClientIdentification()</code>) ; <strong>in RC3, they are now opt-out</strong>.</p><p>If, after migrating to RC3, you see errors similar to these ones:</p><blockquote><p><strong>invalid_scope</strong> : The specified &#39;scope&#39; parameter is not valid.</p></blockquote><p>Simply add the scopes you want to use to the list of registered scopes:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict server handler.</span></div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,</div><div class="line">                               OpenIdConnectConstants.Scopes.Profile,</div><div class="line">                               OpenIddictConstants.Scopes.Roles);</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><blockquote><p><strong>invalid_request</strong> : The mandatory &#39;client_id&#39; parameter is missing.</p></blockquote><p>Add an application entry for the client application and send the corresponding <code>client_id</code> as part of the token request:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> descriptor = <span class="keyword">new</span> OpenIddictApplicationDescriptor</div><div class="line">&#123;</div><div class="line">    ClientId = <span class="string">"postman"</span>,</div><div class="line">    DisplayName = <span class="string">"Postman"</span>,</div><div class="line">    Permissions =</div><div class="line">    &#123;</div><div class="line">        OpenIddictConstants.Permissions.Endpoints.Token,</div><div class="line">        OpenIddictConstants.Permissions.GrantTypes.Password,</div><div class="line">        OpenIddictConstants.Permissions.GrantTypes.RefreshToken,</div><div class="line">        OpenIddictConstants.Permissions.Scopes.Email,</div><div class="line">        OpenIddictConstants.Permissions.Scopes.Profile,</div><div class="line">        OpenIddictConstants.Permissions.Scopes.Roles</div><div class="line">    &#125;</div><div class="line">&#125;;</div><div class="line"></div><div class="line"><span class="keyword">await</span> _applicationManager.CreateAsync(descriptor);</div></pre></td></tr></table></figure><p>If you prefer disabling these checks, you can use <code>options.DisableScopeValidation()</code> and <code>options.AcceptAnonymousClients()</code>:</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict()</div><div class="line"></div><div class="line">    <span class="comment">// Register the OpenIddict server handler.</span></div><div class="line">    .AddServer(options =&gt;</div><div class="line">    &#123;</div><div class="line">        options.AcceptAnonymousClients();</div><div class="line">        options.DisableScopeValidation();</div><div class="line">    &#125;);</div></pre></td></tr></table></figure><div class="note tip"><p>Note: if you already use <code>options.EnableScopeValidation()</code> and/or <code>options.RequireClientIdentification()</code> in your code, you can safely remove these calls.</p></div><hr><h3 id="New-exception-messages-have-been-introduced-to-make-debugging-easier"><a href="#New-exception-messages-have-been-introduced-to-make-debugging-easier" class="headerlink" title="New exception messages have been introduced to make debugging easier"></a>New exception messages have been introduced to make debugging easier</h3><p>In this release, <strong>we also made debugging easier by adding custom exception messages instead of relying on the rather cryptic DI-related messages</strong> thrown by ASP.NET Core.</p><p>If you forget to register stores, you&#39;ll now get a much clearer exception:</p><blockquote><p><strong>System.InvalidOperationException</strong> : No application store has been registered in the dependency injection container.</p><p>To register the Entity Framework Core stores, reference the <code>OpenIddict.EntityFrameworkCore</code> package and call <code>services.AddOpenIddict().AddCore().UseEntityFrameworkCore()</code>.</p><p>To register a custom store, create an implementation of <code>IOpenIddictApplicationStore</code> and use <code>services.AddOpenIddict().AddCore().AddApplicationStore()</code> to add it to the DI container.</p></blockquote><p>If you use an entity that is not compatible with the underlying store, you&#39;ll also get a better exception:</p><blockquote><p><strong>System.InvalidOperationException</strong> : The specified application type is not compatible with the Entity Framework Core stores.</p><p>When enabling the Entity Framework Core stores, make sure you use the built-in <code>OpenIddictApplication</code> entity (from the <code>OpenIddict.EntityFrameworkCore.Models</code> package) or a custom entity that inherits from the generic <code>OpenIddictApplication</code> entity.</p></blockquote><p>Similarly, if you forget to register the core services when enabling the server or validation components, you&#39;ll get an exception:</p><blockquote><p><strong>System.InvalidOperationException</strong> : The core services must be registered when enabling the server handler. To register the OpenIddict core services, use <code>services.AddOpenIddict().AddCore()</code>.</p></blockquote><blockquote><p><strong>System.InvalidOperationException</strong> : The core services must be registered when enabling reference tokens support. To register the OpenIddict core services, use <code>services.AddOpenIddict().AddCore()</code>.</p></blockquote><hr><h2 id="What-39-s-next"><a href="#What-39-s-next" class="headerlink" title="What&#39;s next?"></a>What&#39;s next?</h2><p>OpenIddict RC3 will be the latest release candidate and RTM will be the next step.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;OpenIddict RC3 is now available on NuGet.org:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/OpenIddict/1.0.0-rc3-final&quot;&gt;OpenIddict – 1.0.0-rc3-final&lt;/a&gt; (for ASP.NET Core 1.x)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/OpenIddict/2.0.0-rc3-final&quot;&gt;OpenIddict – 2.0.0-rc3-final&lt;/a&gt; (for ASP.NET Core 2.x)&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;What-39-s-new-in-this-release&quot;&gt;&lt;a href=&quot;#What-39-s-new-in-this-release&quot; class=&quot;headerlink&quot; title=&quot;What&amp;#39;s new in this release?&quot;&gt;&lt;/a&gt;What&amp;#39;s new in this release?&lt;/h2&gt;&lt;h3 id=&quot;The-OpenIddict-services-registration-APIs-have-been-revamped&quot;&gt;&lt;a href=&quot;#The-OpenIddict-services-registration-APIs-have-been-revamped&quot; class=&quot;headerlink&quot; title=&quot;The OpenIddict services registration APIs have been revamped&quot;&gt;&lt;/a&gt;The OpenIddict services registration APIs have been revamped&lt;/h3&gt;&lt;p&gt;In this release, &lt;strong&gt;we focused on reworking the OpenIddict registration APIs to offer a better user experience&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;As part of this change, &lt;strong&gt;we split the OpenIddict services into three areas&lt;/strong&gt; - &lt;code&gt;Core&lt;/code&gt;, &lt;code&gt;Server&lt;/code&gt; and &lt;code&gt;Validation&lt;/code&gt; - and the &lt;code&gt;IServiceCollection&lt;/code&gt; APIs have been updated to reflect that:&lt;/p&gt;&lt;img src=&quot;/2018/06/20/openiddict-rc3-is-out/main-builder.png&quot; alt=&quot;main-builder.png&quot;&gt;&lt;p&gt;Each specialized builder only exposes the options that are relevant to its specific area:&lt;/p&gt;&lt;img src=&quot;/2018/06/20/openiddict-rc3-is-out/specialized-builders.png&quot; alt=&quot;specialized-builders.png&quot;&gt;&lt;p&gt;Of course, the calls to &lt;code&gt;AddCore()&lt;/code&gt;, &lt;code&gt;AddServer()&lt;/code&gt; and &lt;code&gt;AddValidation()&lt;/code&gt; can be chained:&lt;/p&gt;&lt;figure class=&quot;highlight csharp&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;5&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;6&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;7&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;8&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;9&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;10&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;11&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;12&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;13&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;14&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;15&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;16&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;17&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;18&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;19&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;20&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;21&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;22&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;23&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;24&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;25&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;26&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;27&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;28&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;29&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;30&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;31&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;32&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;33&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;34&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;35&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;36&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;37&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;38&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;39&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;services.AddOpenIddict()&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// Register the OpenIddict core services.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    .AddCore(options =&amp;gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &amp;#123;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        &lt;span class=&quot;comment&quot;&gt;// Register the Entity Framework stores and models.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        options.UseEntityFrameworkCore()&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;               .UseDbContext&amp;lt;ApplicationDbContext&amp;gt;();&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &amp;#125;)&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// Register the OpenIddict server handler.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    .AddServer(options =&amp;gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &amp;#123;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        &lt;span class=&quot;comment&quot;&gt;// Register the ASP.NET Core MVC binder used by OpenIddict.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        &lt;span class=&quot;comment&quot;&gt;// Note: if you don&#39;t call this method, you won&#39;t be able to&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        &lt;span class=&quot;comment&quot;&gt;// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        options.UseMvc();&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        &lt;span class=&quot;comment&quot;&gt;// Enable the authorization, logout, token and userinfo endpoints.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        options.EnableAuthorizationEndpoint(&lt;span class=&quot;string&quot;&gt;&quot;/connect/authorize&quot;&lt;/span&gt;)&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;               .EnableLogoutEndpoint(&lt;span class=&quot;string&quot;&gt;&quot;/connect/logout&quot;&lt;/span&gt;)&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;               .EnableTokenEndpoint(&lt;span class=&quot;string&quot;&gt;&quot;/connect/token&quot;&lt;/span&gt;)&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;               .EnableUserinfoEndpoint(&lt;span class=&quot;string&quot;&gt;&quot;/api/userinfo&quot;&lt;/span&gt;);&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        &lt;span class=&quot;comment&quot;&gt;// Note: the Mvc.Client sample only uses the code flow and the password flow, but you&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        &lt;span class=&quot;comment&quot;&gt;// can enable the other flows if you need to support implicit or client credentials.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        options.AllowAuthorizationCodeFlow()&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;               .AllowPasswordFlow()&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;               .AllowRefreshTokenFlow();&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        &lt;span class=&quot;comment&quot;&gt;// During development, you can disable the HTTPS requirement.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        options.DisableHttpsRequirement();&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &amp;#125;)&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// Register the OpenIddict validation handler.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// Note: the OpenIddict validation handler is only compatible with the&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// default token format or with reference tokens and cannot be used with&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// JWT tokens. For JWT tokens, use the Microsoft JWT bearer handler.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    .AddValidation();&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Introducing these specialized builders was also a great opportunity to revisit how the OpenIddict entities are registered&lt;/strong&gt;. In the RC2 bits, this is controlled by the &lt;code&gt;services.AddOpenIddict&amp;lt;...&amp;gt;()&lt;/code&gt; method, that determines which entities are used depending on the overload.&lt;/p&gt;&lt;p&gt;In RC3, the generic &lt;code&gt;services.AddOpenIddict&amp;lt;...&amp;gt;()&lt;/code&gt; methods have been removed and replaced by a more explicit pattern:&lt;/p&gt;&lt;img src=&quot;/2018/06/20/openiddict-rc3-is-out/core-builder-entities.png&quot; alt=&quot;core-builder-entities.png&quot;&gt;
    
    </summary>
    
      <category term="Development" scheme="https://kevinchalet.com/categories/development/"/>
    
    
      <category term="asp.net core" scheme="https://kevinchalet.com/tags/asp-net-core/"/>
    
      <category term="aspnet-contrib" scheme="https://kevinchalet.com/tags/aspnet-contrib/"/>
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
  </entry>
  
  <entry>
    <title>The aspnet-contrib OAuth 2.0/OpenID 2.0 social providers are now RTM</title>
    <link href="https://kevinchalet.com/2018/06/18/the-aspnet-contrib-oauth-2-0-openid-2-0-social-providers-are-now-rtm/"/>
    <id>https://kevinchalet.com/2018/06/18/the-aspnet-contrib-oauth-2-0-openid-2-0-social-providers-are-now-rtm/</id>
    <published>2018-06-18T20:00:00.000Z</published>
    <updated>2018-06-18T21:17:28.000Z</updated>
    
    <content type="html"><![CDATA[<p>Today, I&#39;m really pleased to announce that the aspnet-contrib social providers are now RTM.</p><p>I&#39;d like to thank all the contributors – 47 at the time of writing – who helped make that possible, with a special mention to <a href="https://github.com/kinosang" target="_blank" rel="external">Chino Chang</a>, who did a fantastic job by manually porting most of the social providers to the ASP.NET Core 2.0 authentication stack: sir, you rock!</p><p>Each provider comes with 2 versions: one for ASP.NET Core 1.x and one for ASP.NET Core 2.x. You can find the complete list at the end of this post.</p><div class="note tip"><p>Both versions will receive security updates/bug fixes but new providers will be backported to ASP.NET Core 1.x only if there&#39;s enough demand. If you still run an ASP.NET Core 1.x application, let us know!</p></div><h2 id="OAuth-2-0-social-providers"><a href="#OAuth-2-0-social-providers" class="headerlink" title="OAuth 2.0 social providers"></a>OAuth 2.0 social providers</h2><table><thead><tr><th style="text-align:center">Package name</th><th style="text-align:center">Version for ASP.NET Core 1.x</th><th style="text-align:center">Version for ASP.NET Core 2.x</th></tr></thead><tbody><tr><td style="text-align:center">AspNet.Security.OAuth.Amazon</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.ArcGIS</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Asana</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Autodesk</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Automatic</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.BattleNet</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Beam</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Bitbucket</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Buffer</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.CiscoSpark</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.DeviantArt</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Discord</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Dropbox</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.EVEOnline</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Fitbit</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Foursquare</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.GitHub</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Gitter</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.HealthGraph</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Imgur</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Instagram</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.LinkedIn</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.MailChimp</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Myob</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Onshape</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Patreon</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Paypal</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.QQ</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Reddit</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Salesforce</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Slack</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.SoundCloud</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Spotify</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.StackExchange</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Strava</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Twitch</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Untappd</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Vimeo</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.VisualStudio</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Vkontakte</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Weibo</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Weixin</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.WordPress</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Yahoo</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Yammer</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OAuth.Yandex</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr></tbody></table><h2 id="OpenID-2-0-social-providers"><a href="#OpenID-2-0-social-providers" class="headerlink" title="OpenID 2.0 social providers"></a>OpenID 2.0 social providers</h2><table><thead><tr><th style="text-align:center">Package name</th><th style="text-align:center">Version for ASP.NET Core 1.x</th><th style="text-align:center">Version for ASP.NET Core 2.x</th></tr></thead><tbody><tr><td style="text-align:center">AspNet.Security.OpenId</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr><tr><td style="text-align:center">AspNet.Security.OpenId.Steam</td><td style="text-align:center"><code>1.0.0</code></td><td style="text-align:center"><code>2.0.0</code></td></tr></tbody></table>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Today, I&amp;#39;m really pleased to announce that the aspnet-contrib social providers are now RTM.&lt;/p&gt;&lt;p&gt;I&amp;#39;d like to thank all the contr
    
    </summary>
    
      <category term="Development" scheme="https://kevinchalet.com/categories/development/"/>
    
    
      <category term="asp.net core" scheme="https://kevinchalet.com/tags/asp-net-core/"/>
    
      <category term="aspnet-contrib" scheme="https://kevinchalet.com/tags/aspnet-contrib/"/>
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid" scheme="https://kevinchalet.com/tags/openid/"/>
    
  </entry>
  
  <entry>
    <title>OpenIddict RC2 is now on NuGet.org</title>
    <link href="https://kevinchalet.com/2018/02/19/openiddict-rc2-is-now-on-nuget-org/"/>
    <id>https://kevinchalet.com/2018/02/19/openiddict-rc2-is-now-on-nuget-org/</id>
    <published>2018-02-19T18:00:00.000Z</published>
    <updated>2018-02-19T17:47:40.000Z</updated>
    
    <content type="html"><![CDATA[<p>Earlier today, new OpenIddict packages were pushed to NuGet.org:</p><ul><li><a href="https://www.nuget.org/packages/OpenIddict/1.0.0-rc2-final" target="_blank" rel="external">OpenIddict – 1.0.0-rc2-final</a> (for ASP.NET Core 1.x)</li><li><a href="https://www.nuget.org/packages/OpenIddict/2.0.0-rc2-final" target="_blank" rel="external">OpenIddict – 2.0.0-rc2-final</a> (for ASP.NET Core 2.x)</li></ul><h2 id="What-39-s-new"><a href="#What-39-s-new" class="headerlink" title="What&#39;s new?"></a>What&#39;s new?</h2><p>Starting with RC2, <strong>using OpenIddict with third-party client applications</strong> (i.e applications you don&#39;t own and are managed by someone else) <strong>is officially supported</strong>. For that, new features – that were still work in progress in the previous iterations – have been added:</p><ul><li>A new <strong>application permissions feature was added</strong>, which allows controlling and limiting the features a client application can use. For instance, to allow a client application to use only the authorization code flow and the logout endpoint, the following permissions can be granted:</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">await</span> _applicationManager.CreateAsync(<span class="keyword">new</span> OpenIddictApplicationDescriptor</div><div class="line">&#123;</div><div class="line">    ClientId = <span class="string">"mvc"</span>,</div><div class="line">    ClientSecret = <span class="string">"901564A5-E7FE-42CB-B10D-61EF6A8F3654"</span>,</div><div class="line">    DisplayName = <span class="string">"MVC client application"</span>,</div><div class="line">    PostLogoutRedirectUris = &#123; <span class="keyword">new</span> Uri(<span class="string">"http://localhost:53507/signout-callback-oidc"</span>) &#125;,</div><div class="line">    RedirectUris = &#123; <span class="keyword">new</span> Uri(<span class="string">"http://localhost:53507/signin-oidc"</span>) &#125;,</div><div class="line">    Permissions =</div><div class="line">    &#123;</div><div class="line">        OpenIddictConstants.Permissions.Endpoints.Authorization,</div><div class="line">        OpenIddictConstants.Permissions.Endpoints.Logout,</div><div class="line">        OpenIddictConstants.Permissions.Endpoints.Token,</div><div class="line"></div><div class="line">        OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode</div><div class="line">    &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><p>For more information about this feature, you can read <a href="https://openiddict.github.io/openiddict-documentation/features/application-permissions.html" target="_blank" rel="external">the corresponding documentation</a>.</p><ul><li>A new <strong>opt-in scope validation option was added</strong>. When it is enabled, OpenIddict automatically rejects authorization and token requests that specify unregistered scopes. Scopes can be registered statically using <code>options.RegisterScopes([list of authorized scopes])</code> or dynamically, using <code>OpenIddictScopeManager.CreateAsync()</code>:</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">services.AddOpenIddict(options =&gt;</div><div class="line">&#123;</div><div class="line">    <span class="comment">// ...</span></div><div class="line"></div><div class="line">    <span class="comment">// Mark the "email" and "profile" scopes as valid scopes.</span></div><div class="line">    options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,</div><div class="line">                           OpenIdConnectConstants.Scopes.Profile);</div><div class="line"></div><div class="line">    <span class="comment">// Enable scope validation, so that authorization and token requests</span></div><div class="line">    <span class="comment">// that specify unregistered scopes are automatically rejected.</span></div><div class="line">    options.EnableScopeValidation();</div><div class="line">&#125;);</div></pre></td></tr></table></figure><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">await</span> _scopeManager.CreateAsync(<span class="keyword">new</span> OpenIddictScopeDescriptor</div><div class="line">&#123;</div><div class="line">    Description = <span class="string">"Grants access to the reporting API"</span>,</div><div class="line">    DisplayName = <span class="string">"Reporting API"</span>,</div><div class="line">    Name = <span class="string">"reporting"</span></div><div class="line">&#125;);</div></pre></td></tr></table></figure><ul><li>The <strong>introspection endpoint was updated to reject access tokens that don&#39;t have any audience</strong> (since OpenIddict can no longer assume all the registered applications are fully trusted). This change requires updating your code to explicitly attach a resource to your tokens.</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> ticket = <span class="keyword">new</span> AuthenticationTicket(</div><div class="line">    <span class="keyword">new</span> ClaimsPrincipal(identity),</div><div class="line">    <span class="keyword">new</span> AuthenticationProperties(),</div><div class="line">    OpenIdConnectServerDefaults.AuthenticationScheme);</div><div class="line"></div><div class="line">ticket.SetResources(<span class="string">"reporting"</span>);</div></pre></td></tr></table></figure><a id="more"></a><ul><li>New <code>OpenIddictScopeManager</code> methods have been introduced to allow associating resources (aka API audiences) with specific scopes and retrieving all the resources corresponding to a set of scopes:</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">await</span> _scopeManager.CreateAsync(<span class="keyword">new</span> OpenIddictScopeDescriptor</div><div class="line">&#123;</div><div class="line">    Name = <span class="string">"reporting"</span>,</div><div class="line">    Resources = &#123; <span class="string">"resource-server-1"</span>, <span class="string">"resource-server-2"</span> &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">ticket.SetResources(<span class="keyword">await</span> _scopeManager.ListResourcesAsync(scopes));</div></pre></td></tr></table></figure><ul><li>New <code>OpenIddictAuthorizationManager</code> methods have been added to make authorizations easier to create or retrieve:</li></ul><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Create a new permanent authorization associated with</span></div><div class="line"><span class="comment">// the specified user and containing the granted scopes:</span></div><div class="line"><span class="keyword">var</span> authorization = <span class="keyword">await</span> _authorizationManager.CreateAsync(</div><div class="line">    principal : ticket.Principal,</div><div class="line">    subject   : <span class="keyword">await</span> _userManager.GetUserIdAsync(user),</div><div class="line">    client    : <span class="keyword">await</span> _applicationManager.GetIdAsync(application),</div><div class="line">    type      : OpenIddictConstants.AuthorizationTypes.Permanent,</div><div class="line">    scopes    : ImmutableArray.CreateRange(ticket.GetScopes()),</div><div class="line">    properties: ImmutableDictionary.CreateRange(ticket.Properties.Items));</div></pre></td></tr></table></figure><figure class="highlight csharp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Retrieve all the permanent and valid authorizations associated</span></div><div class="line"><span class="comment">// with the user and containing at least the request scopes:</span></div><div class="line"><span class="keyword">var</span> authorizations = <span class="keyword">await</span> _authorizationManager.FindAsync(</div><div class="line">    subject: _userManager.GetUserId(result.Principal),</div><div class="line">    client : <span class="keyword">await</span> _applicationManager.GetIdAsync(application),</div><div class="line">    status : OpenIddictConstants.Statuses.Valid,</div><div class="line">    type   : OpenIddictConstants.AuthorizationTypes.Permanent,</div><div class="line">    scopes : ImmutableArray.CreateRange(request.GetScopes()));</div></pre></td></tr></table></figure><ul><li><code>OpenIddictApplication.RedirectUris</code>, <code>OpenIddictApplication.PostLogoutRedirectUris</code> and <code>OpenIddictAuthorization.Scopes</code> are now serialized as JSON arrays in the database.</li></ul><h2 id="Migrate-to-OpenIddict-RC2"><a href="#Migrate-to-OpenIddict-RC2" class="headerlink" title="Migrate to OpenIddict RC2"></a>Migrate to OpenIddict RC2</h2><p><strong>Before updating your packages, read the <a href="https://openiddict.github.io/openiddict-documentation/guide/migration.html" target="_blank" rel="external">migration guide</a></strong>. It explains how to add an Entity Framework Core migration to update the OpenIddict tables and includes a migration script to convert the <code>RedirectUris</code>, <code>PostLogoutRedirectUris</code> and <code>Scopes</code> columns to the new JSON format.</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Earlier today, new OpenIddict packages were pushed to NuGet.org:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/OpenIddict/1.0.0-rc2-final&quot;&gt;OpenIddict – 1.0.0-rc2-final&lt;/a&gt; (for ASP.NET Core 1.x)&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://www.nuget.org/packages/OpenIddict/2.0.0-rc2-final&quot;&gt;OpenIddict – 2.0.0-rc2-final&lt;/a&gt; (for ASP.NET Core 2.x)&lt;/li&gt;&lt;/ul&gt;&lt;h2 id=&quot;What-39-s-new&quot;&gt;&lt;a href=&quot;#What-39-s-new&quot; class=&quot;headerlink&quot; title=&quot;What&amp;#39;s new?&quot;&gt;&lt;/a&gt;What&amp;#39;s new?&lt;/h2&gt;&lt;p&gt;Starting with RC2, &lt;strong&gt;using OpenIddict with third-party client applications&lt;/strong&gt; (i.e applications you don&amp;#39;t own and are managed by someone else) &lt;strong&gt;is officially supported&lt;/strong&gt;. For that, new features – that were still work in progress in the previous iterations – have been added:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A new &lt;strong&gt;application permissions feature was added&lt;/strong&gt;, which allows controlling and limiting the features a client application can use. For instance, to allow a client application to use only the authorization code flow and the logout endpoint, the following permissions can be granted:&lt;/li&gt;&lt;/ul&gt;&lt;figure class=&quot;highlight csharp&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;5&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;6&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;7&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;8&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;9&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;10&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;11&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;12&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;13&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;14&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;15&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;16&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;await&lt;/span&gt; _applicationManager.CreateAsync(&lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; OpenIddictApplicationDescriptor&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&amp;#123;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    ClientId = &lt;span class=&quot;string&quot;&gt;&quot;mvc&quot;&lt;/span&gt;,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    ClientSecret = &lt;span class=&quot;string&quot;&gt;&quot;901564A5-E7FE-42CB-B10D-61EF6A8F3654&quot;&lt;/span&gt;,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    DisplayName = &lt;span class=&quot;string&quot;&gt;&quot;MVC client application&quot;&lt;/span&gt;,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    PostLogoutRedirectUris = &amp;#123; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; Uri(&lt;span class=&quot;string&quot;&gt;&quot;http://localhost:53507/signout-callback-oidc&quot;&lt;/span&gt;) &amp;#125;,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    RedirectUris = &amp;#123; &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; Uri(&lt;span class=&quot;string&quot;&gt;&quot;http://localhost:53507/signin-oidc&quot;&lt;/span&gt;) &amp;#125;,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    Permissions =&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &amp;#123;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        OpenIddictConstants.Permissions.Endpoints.Authorization,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        OpenIddictConstants.Permissions.Endpoints.Logout,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        OpenIddictConstants.Permissions.Endpoints.Token,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;        OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &amp;#125;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&amp;#125;);&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;p&gt;For more information about this feature, you can read &lt;a href=&quot;https://openiddict.github.io/openiddict-documentation/features/application-permissions.html&quot;&gt;the corresponding documentation&lt;/a&gt;.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A new &lt;strong&gt;opt-in scope validation option was added&lt;/strong&gt;. When it is enabled, OpenIddict automatically rejects authorization and token requests that specify unregistered scopes. Scopes can be registered statically using &lt;code&gt;options.RegisterScopes([list of authorized scopes])&lt;/code&gt; or dynamically, using &lt;code&gt;OpenIddictScopeManager.CreateAsync()&lt;/code&gt;:&lt;/li&gt;&lt;/ul&gt;&lt;figure class=&quot;highlight csharp&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;5&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;6&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;7&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;8&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;9&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;10&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;11&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;12&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;services.AddOpenIddict(options =&amp;gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&amp;#123;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// Mark the &quot;email&quot; and &quot;profile&quot; scopes as valid scopes.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    options.RegisterScopes(OpenIdConnectConstants.Scopes.Email,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;                           OpenIdConnectConstants.Scopes.Profile);&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// Enable scope validation, so that authorization and token requests&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// that specify unregistered scopes are automatically rejected.&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    options.EnableScopeValidation();&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&amp;#125;);&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;figure class=&quot;highlight csharp&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;5&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;6&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;await&lt;/span&gt; _scopeManager.CreateAsync(&lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; OpenIddictScopeDescriptor&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&amp;#123;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    Description = &lt;span class=&quot;string&quot;&gt;&quot;Grants access to the reporting API&quot;&lt;/span&gt;,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    DisplayName = &lt;span class=&quot;string&quot;&gt;&quot;Reporting API&quot;&lt;/span&gt;,&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    Name = &lt;span class=&quot;string&quot;&gt;&quot;reporting&quot;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&amp;#125;);&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;ul&gt;&lt;li&gt;The &lt;strong&gt;introspection endpoint was updated to reject access tokens that don&amp;#39;t have any audience&lt;/strong&gt; (since OpenIddict can no longer assume all the registered applications are fully trusted). This change requires updating your code to explicitly attach a resource to your tokens.&lt;/li&gt;&lt;/ul&gt;&lt;figure class=&quot;highlight csharp&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;4&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;5&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;6&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;var&lt;/span&gt; ticket = &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; AuthenticationTicket(&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; ClaimsPrincipal(identity),&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;keyword&quot;&gt;new&lt;/span&gt; AuthenticationProperties(),&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    OpenIdConnectServerDefaults.AuthenticationScheme);&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;ticket.SetResources(&lt;span class=&quot;string&quot;&gt;&quot;reporting&quot;&lt;/span&gt;);&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;
    
    </summary>
    
      <category term="Development" scheme="https://kevinchalet.com/categories/development/"/>
    
    
      <category term="asp.net core" scheme="https://kevinchalet.com/tags/asp-net-core/"/>
    
      <category term="aspnet-contrib" scheme="https://kevinchalet.com/tags/aspnet-contrib/"/>
    
      <category term="authentication" scheme="https://kevinchalet.com/tags/authentication/"/>
    
      <category term="jwt" scheme="https://kevinchalet.com/tags/jwt/"/>
    
      <category term="oauth" scheme="https://kevinchalet.com/tags/oauth/"/>
    
      <category term="openid connect" scheme="https://kevinchalet.com/tags/openid-connect/"/>
    
      <category term="openiddict" scheme="https://kevinchalet.com/tags/openiddict/"/>
    
  </entry>
  
</feed>
