PageSourceSearch

https://andrewlock.net/exploring-the-dotnet-11-preview-8-experimen…ce-bound-session-credentials-in-aspnetcore/

html andrewlock.net collected 2026-10-02 02:44:24 UTC 100,857 bytes, 145 lines download raw bytes

1<!DOCTYPE html><html lang="en"><head><link rel="preload" as="style" href="/assets/css/site.css?v=V/cbZfrVuJUfnKQcP4YQMw=="><link rel="preload" as="image" href="/content/images/2026/dbsc_aspnetcore_banner.webp" fetchpriority="high"><title>Experimental support for Device Bound Session Credentials (DBSC) in ASP.NET Core</title><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="HandheldFriendly" content="True"><meta name="MobileOptimized" content="320"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content="In this post I look at the new experimental support for DBSC provided by the Microsoft.AspNetCore.Authentication.DeviceBoundSessions package"><meta name="robots" content="noarchive, nocache"><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png?v=QEMBRv9w7P"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png?v=QEMBRv9w7P"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png?v=QEMBRv9w7P"><link rel="manifest" href="/site.webmanifest?v=QEMBRv9w7P"><link rel="mask-icon" href="/safari-pinned-tab.svg?v=QEMBRv9w7P" color="#206da8"><link rel="shortcut icon" href="/favicon.ico?v=QEMBRv9w7P"><meta name="msapplication-TileColor" content="#206da8"><meta name="theme-color" content="#206da8"><link rel="canonical" href="https://andrewlock.net/exploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore/"><meta name="referrer" content="no-referrer-when-downgrade"><link rel="alternate" type="application/rss+xml" title="Andrew Lock | .NET Escapades" href="https://andrewlock.net/rss.xml"><meta property="og:site_name" content="Andrew Lock | .NET Escapades"><meta property="og:type" content="article"><meta property="og:title" content="Experimental support for Device Bound Session Credentials (DBSC) in ASP.NET Core: Exploring the .NET 11 preview - Part 8"><meta property="og:description" content="In this post I look at the new experimental support for DBSC provided by the Microsoft.AspNetCore.Authentication.DeviceBoundSessions package"><meta property="og:url" content="https://andrewlock.net/exploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore/"><meta property="og:image" content="https://andrewlock.net/content/images/2026/dbsc_aspnetcore_banner.webp"><meta property="article:published_time" content="2026-09-22T10:00:00.0000000"><meta property="article:tag" content=".NET 11"><meta property="article:tag" content="C#"><meta property="article:tag" content="ASP.NET Core"><meta property="article:tag" content="Security"><meta property="article:publisher" content="https://www.facebook.com/NetEscapades"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="Experimental support for Device Bound Session Credentials (DBSC) in ASP.NET Core: Exploring the .NET 11 preview - Part 8"><meta name="twitter:description" content="In this post I look at the new experimental support for DBSC provided by the Microsoft.AspNetCore.Authentication.DeviceBoundSessions package"><meta name="twitter:url" content="https://andrewlock.net/exploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore/"><meta name="twitter:image" content="https://andrewlock.net/content/images/2026/dbsc_aspnetcore_banner.webp"><meta name="twitter:label1" content="Written by"><meta name="twitter:data1" content="Andrew Lock"><meta name="twitter:label2" content="Filed under"><meta name="twitter:data2" content=".NET 11, C#, ASP.NET Core, Security"><meta name="twitter:site" content="@andrewlocknet"><meta property="og:image:width" content="960"><meta property="og:image:height" content="640">
1<script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","publisher":{"@type":"Organization","name":"Andrew Lock | .NET Escapades","logo":{"@type":"ImageObject","url":"https://andrewlock.net/content/images/publisher.png","width":520,"height":60}},"author":{"@type":"Person","name":"Andrew Lock","image":{"@type":"ImageObject","url":"https://andrewlock.net/content/images/logo.jpg","width":731,"height":725},"url":"https://andrewlock.net/author/sock/","sameAs":[]},"headline":"Experimental support for Device Bound Session Credentials (DBSC) in ASP.NET Core: Exploring the .NET 11 preview - Part 8","url":"https://andrewlock.net/exploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore/","datePublished":"2026-09-22T10:00:00.0000000","image":{"@type":"ImageObject","url":"https://andrewlock.net/content/images/2026/dbsc_aspnetcore_banner.webp","width":1800,"height":1200},"mainEntityOfPage":{"@type":"WebPage","@id":"https://andrewlock.net"},"keywords":".NET 11, C#, ASP.NET Core, Security","description":"Hi, my name is Andrew, or \u2018Sock\u2019 to most people. This blog is where I share my experiences as I journey into ASP.NET Core."}</script>
1<link rel="dns-prefetch" href="//cdn.carbonads.com/"><link rel="dns-prefetch" href="//www.google-analytics.com/"><link rel="preconnect" href="//cdn.carbonads.com/"><link rel="preconnect" href="//www.google-analytics.com/">
1<script type="text/javascript">function setDarkTheme(e){e?document.documentElement.classList.add("dark"):document.documentElement.classList.remove("dark")}if("serviceWorker"in navigator)try{navigator.serviceWorker.getRegistrations().then(function(r){for(let x of r)x.unregister().then(function(success){success&&location.reload()})})}catch(f){}try{var s=window.localStorage,enabled=s.theme==="dark"||!("theme"in s)&&window.matchMedia("(prefers-color-scheme: dark)").matches;setDarkTheme(enabled)}catch(h){}</script>
1<script async defer src="/assets/js/site.min.js?v=t9YhD8xWLSzuqP6PCEH6VQ==" type="text/javascript" charset="utf-8"></script>
1<link rel="stylesheet" href="/assets/css/site.css?v=V/cbZfrVuJUfnKQcP4YQMw==">
vendor: 63 bytes, line 1
1<script async src="https://www.googletagmanager.com/gtag/js?id=
1G-LRDSG8GTT8
vendor: 11 bytes, line 1
1"></script>
1<script>
vendor: 125 bytes, line 1
1window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);} gtag('js', new Date());gtag('config', '
1G-LRDSG8GTT8
vendor: 3 bytes, line 1
1');
1</script>
1</head><body class="bg-zinc-200 dark:bg-zinc-500"><header class="relative text-center"><div class="h-full w-full absolute after:absolute after:bg-black/60 after:block after:h-full after:left-0 after:shadow-inner-xl after:top-0 after:w-full"><img src="/content/images/2026/dbsc_aspnetcore_banner.webp" alt="blog post image" class="absolute h-full object-cover w-full" fetchpriority="high"></div> <a href="/about/" class="absolute xs:fixed left-2 xs:left-4 top-2 xs:top-4 z-40 drop-shadow-lg"><img height="45" width="45" src="/content/images/logo-small.jpg" alt="Andrew Lock avatar" class="border-2 box-content rounded-full border-primary hover:scale-110 transition-transform duration-300"></a> <button class="absolute xs:fixed right-2 xs:right-4 top-2 xs:top-4 p-3 z-40 border-2 box-content rounded-full bg-primary fill-zinc-100 border-zinc-100 cursor-pointer hover:scale-110 transition-transform duration-300 drop-shadow-lg"><svg width="20px" height="20px" viewBox="0 -150 1000 1000" version="1.1" aria-labelledby="icons-search" role="img" xmlns="http://www.w3.org/2000/svg"><title id="icons-search">Search</title><path glyph-name="search" d="m 393.04199,493.07229 q -103,0 -176,-73 -73,-73 -74,-177 -1,-104 74,-177 75,-73 176,-73 101,0 177,73 76,73 73,177 -3,104 -73,177 -70,73 -177,73 z m 465,286 q 29,0 50,-22 21,-22 21,-50 0,-30 -21,-50 l -191,-191 q 69,-100 69,-223 0,-80 -31,-153 -31,-73 -84,-125 -53,-52 -125,-84 -72,-32 -153,-31 -81,1 -152,31 -71,30 -126,84 -55,54 -84,125 Q 2.0419913,161.07229 0.04199134,243.07229 q -2,82 31,153 33,71 84,125 51,54 126,84 75,30 152,31 123,0 223,-69 l 191,191 q 21,21 51,21 z"></path></svg></button> <button class="hidden absolute xs:fixed right-2 xs:right-4 top-2 xs:top-4 p p-3 z-40 border-2 box-content rounded-full bg-primary fill-zinc-100 border-zinc-100 cursor-pointer hover:scale-110 transition-transform duration-300 drop-shadow-lg"><svg width="20px" height="20px" viewBox="40 -40 700 700" version="1.1" aria-labelledby="icons-close" role="img" xmlns="http://www.w3.org/2000/svg"><title id="icons-close">Close</title><path glyph-name="close" d="M724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z"></path></svg></button> <div class="h-10 xs:h-20"></div> <h1 class="pb-2 relative font-thin text-5xl text-zinc-100 tracking-tight hover:text-darkprimary text-shadow shadow-zinc-800 transition-colors duration-300"><a href="/" class="hidden sm:inline-block">Andrew Lock | .NET Escapades</a> <a href="/" class="sm:hidden">Andrew Lock</a></h1> <nav class="mt-2 pb-24 relative"><ul class="font-light text-xl text-zinc-100 tracking-tight"><li class="inline-block hover:text-darkprimary text-shadow shadow-zinc-800 transition-colors duration-300"><a class="px-3 py-2" href="/">Home</a></li> <li class="inline-block border-l border-zinc-100 text-shadow shadow-zinc-800 hover:text-darkprimary transition-colors duration-300"><a class="px-3 py-2" href="/about/">About</a></li><li class="inline-block border-l border-zinc-100 text-shadow shadow-zinc-800 hover:text-darkprimary transition-colors duration-300"><a class="px-3 py-2" id="subscribe-link" href="#">Subscribe</a></li><li class="inline-block border-l border-zinc-100 text-shadow shadow-zinc-800 hover:text-darkprimary transition-colors duration-300"><a class="before:content-['\263E'] px-3 py-2 dark:hidden" data-theme="dark" href="#" title="Switch to dark theme"> <span class="hidden xs:inline">Dark</span></a> <a class="before:content-['\2600'] px-3 py-2 hidden dark:inline" data-theme="light" href="#" title="Switch to light theme"> <span class="hidden xs:inline">Light</span></a></li></ul> <ul class="mt-2 fill-zinc-100"><li class="inline-block p-2 hover:rotate-6 hover:scale-110 transition-transform duration-300"><a rel="me noopener" target="_blank" href="https://www.facebook.com/NETescapades" title="Like .NET Escapades on Facebook" class="track fill-zinc-100 hover:fill-primary"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 810,-79 q 19,0 33,14 14,14 14,34 v 762 q 0,20 -14,34 -14,14 -33,14 H 592 V 446 H 703 L 719,317 H 592 v -83 q 0,-31 13,-46 13,-15 51,-16 l 68,-1 V 56 q -35,-5 -100,-5 -75,0 -121,44 -46,44 -45,127 v 95 H 346 V 446 H 458 V 779 H 47 Q 28,779 14,765 0,751 0,731 V -31 q 0,-20 14,-34 14,-14 33,-14 z"></path></svg></a></li> <li class="inline-block p-2 hover:rotate-6 hover:scale-110 transition-transform duration-300"><a rel="me noopener" target="_blank" href="https://twitter.com/andrewlocknet" title="Follow @andrewlocknet on Twitter" class="track fill-zinc-100 hover:fill-primary"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-label="Twitter" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 904,77.882353 q -37,54 -90,93 0,8 0,23 0,73 -21,145 -21,72 -64,139 -43,67 -103,117 -60,50 -144,82 -84,32 -181,30 -151,0 -276,-81 19,2 43,2 126,0 224,-77 -59,-1 -105,-36 -46,-35 -64,-89 19,3 34,3 24,0 48,-6 -63,-13 -104,-62 -41,-49 -41,-115 v -2 q 38,21 82,23 -37,-25 -59,-64 -22,-39 -22,-86 0,-49 25,-91 68,83 164,133 96,50 208,55 -5,-21 -5,-41 0,-75 53,-127 53,-52 127,-53 79,0 132,57 61,-12 115,-44 -21,64 -80,100 52,-6 104,-28 z"></path></svg></a></li> <li class="inline-block p-2 hover:rotate-6 hover:scale-110 transition-transform duration-300"><a rel="me noopener" target="_blank" href="https://bsky.app/profile/andrewlock.bsky.social" title="Follow @andrewlock.bsky.social on Bluesky" class="track fill-zinc-100 hover:fill-primary"><svg width="24px" height="24px" viewBox="0 0 568 501" version="1.1" aria-label="Bluesky" role="img" xmlns="http://www.w3.org/2000/svg"><path d="M123.121 33.6637C188.241 82.5526 258.281 181.681 284 234.873C309.719 181.681 379.759 82.5526 444.879 33.6637C491.866 -1.61183 568 -28.9064 568 57.9464C568 75.2916 558.055 203.659 552.222 224.501C531.947 296.954 458.067 315.434 392.347 304.249C507.222 323.8 536.444 388.56 473.333 453.32C353.473 576.312 301.061 422.461 287.631 383.039C285.169 375.812 284.017 372.431 284 375.306C283.983 372.431 282.831 375.812 280.369 383.039C266.939 422.461 214.527 576.312 94.6667 453.32C31.5556 388.56 60.7778 323.8 175.653 304.249C109.933 315.434 36.0535 296.954 15.7778 224.501C9.94525 203.659 0 75.2916 0 57.9464C0 -28.9064 76.1345 -1.61183 123.121 33.6637Z"></path></svg></a></li> <li class="inline-block p-2 hover:rotate-6 hover:scale-110 transition-transform duration-300"><a rel="me noopener" target="_blank" href="https://hachyderm.io/@andrewlock" title="Follow @[email protected] on Mastadon" class="track fill-zinc-100 hover:fill-primary"><svg width="24px" height="24px" viewBox="0 0 417.8 512" version="1.1" aria-label="Mastadon" role="img" xmlns="http://www.w3.org/2000/svg"><path d="M417.8 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.5-28.4-290.4 0 0 0-63.7 28.5-63.7 125.7 0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.
11c-40.4-1.4-83-4.4-89.6-54-.6-4.4-.9-9-.9-13.9 85.6 20.9 158.6 9.1 178.7 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6V190.1c0-49.7-64-51.6-64 6.9v62.5h-46.3V197c0-58.5-64-56.6-64-6.9v114.2H75.1c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"></path></svg></a></li> <li class="inline-block p-2 hover:rotate-6 hover:scale-110 transition-transform duration-300"><a rel="me noopener" target="_blank" href="https://uk.linkedin.com/in/andrewdlock" title="Andrew Lock on LinkedIn" class="track fill-zinc-100 hover:fill-primary"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-label="LinkedIn" role="img" xmlns="http://www.w3.org/2000/svg"><path d="M 132,639 H 261 V 252 H 132 Z M 270,132 q -1,-29 -21,-48 -20,-19 -51,-19 -31,0 -53,19 -22,19 -21,48 0,29 20,48 20,19 52,19 v 0 q 33,0 53,-19 20,-19 21,-48 z M 596,639 H 725 V 417 q 0,-86 -41,-130 -41,-44 -107,-44 -76,0 -117,65 h 1 V 252 H 332 q 2,37 0,387 H 461 V 422 q 0,-21 4,-31 8,-19 25,-33 17,-14 41,-14 65,0 65,88 z M 857,82 v 536 q 0,66 -47,113 -47,47 -114,48 H 161 Q 94,779 47,731 0,683 0,618 V 82 Q 0,16 47,-31 94,-78 161,-79 h 535 q 67,0 114,48 47,48 47,113 z"></path></svg></a></li> <li class="inline-block p-2 hover:rotate-6 hover:scale-110 transition-transform duration-300"><a rel="me noopener" target="_blank" href="https://github.com/andrewlock" title="Andrew lock on Github" class="track fill-zinc-100 hover:fill-primary"><svg width="24px" height="24px" viewBox="-10 -100 900 900" version="1.1" aria-label="Github" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 429,-57.9 q 116,0 215,58 99,58 156,156 57,98 57,215 0,140 -82,252 -82,112 -211,155 -15,3 -22,-4 -7,-7 -7,-17 0,-1 0,-43 0,-42 0,-75 0,-54 -29,-79 32,-3 57,-10 25,-7 53,-22 28,-15 45,-37 17,-22 30,-58 13,-36 11,-84 0,-67 -44,-115 21,-51 -4,-114 -16,-5 -46,6 -30,11 -51,25 l -21,13 q -52,-15 -107,-15 -55,0 -108,15 -8,-6 -23,-15 -15,-9 -47,-22 -32,-13 -47,-7 -25,63 -5,114 -44,48 -44,115 0,47 12,83 12,36 29,59 17,23 45,37 28,14 52,22 24,8 57,10 -21,20 -27,58 -12,5 -25,8 -13,3 -32,3 -19,0 -36,-12 -17,-12 -31,-35 -11,-18 -27,-29 -16,-11 -28,-14 l -11,-1 q -12,0 -16,2 -4,2 -3,7 1,5 5,8 4,3 7,6 l 4,3 q 12,6 24,21 12,15 18,29 l 6,13 q 7,21 24,34 17,13 37,17 20,4 39,3 19,-1 31,-1 l 13,-3 q 0,22 0,50 0,28 1,30 0,10 -8,17 -8,7 -22,4 -129,-43 -211,-155 -82,-112 -82,-252 0,-117 58,-215 58,-98 155,-156 97,-58 216,-58 z m -267,616 q 2,-4 -3,-7 -6,-1 -8,1 -1,4 4,7 5,3 7,-1 z m 18,19 q 4,-3 -1,-9 -6,-5 -9,-2 -4,3 1,9 5,6 9,2 z m 16,25 q 6,-4 0,-11 -4,-7 -9,-3 -5,3 0,10 5,7 9,4 z m 24,23 q 4,-4 -2,-10 -7,-7 -11,-2 -5,5 2,11 6,6 11,1 z m 32,14 q 1,-6 -8,-9 -8,-2 -10,4 -2,6 7,9 8,3 11,-4 z m 35,3 q 0,-7 -10,-6 -9,0 -9,6 0,7 10,6 9,0 9,-6 z m 32,-5 q -1,-7 -10,-5 -9,1 -8,8 1,7 10,4 9,-3 8,-7 z"></path></svg></a></li> <li class="inline-block p-2 hover:rotate-6 hover:scale-110 transition-transform duration-300"><a href="/rss.xml" title="Subscribe to RSS" class="track fill-zinc-100 hover:fill-primary"><svg width="24px" height="24px" viewBox="-10 -10 800 800" version="1.1" aria-label="Rss" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 107.04167,565.04167 q -45.000003,0 -76.000003,31 -31.00000033,31 -31.00000033,76 0,45 31.00000033,76 31,31 76.000003,31 45,0 76,-31 31,-31 31,-76 0,-45 -31,-76 -31,-31 -76,-31 z m -69.000003,-286 q -15,-1 -26,9 -12.00000033,10 -12.00000033,27 v 75 q 0,14 9.00000003,24 9.0000003,10 23.0000003,11 128.000003,12 219.000003,103 91,91 103,219 1,14 11,23 10,9 24,9 h 75 q 16,0 26,-12 10,-9 10,-24 v -3 q -7,-89 -45,-170 -38,-81 -101,-145 -63,-63 -145,-101 -82,-38 -171.000003,-45 z m -1,-286.0000033 q -15,-1 -26,10 Q 0.04166667,13.041667 0.04166667,29.041667 v 80.000003 q 0,14 10.00000033,25 10,11 23,10 120.000003,7 228.000003,57 108,50 188,129 80,79 129,188 49,109 57,227 1,14 11,24 10,10 24,10 h 80 q 16,0 26,-11 10,-10 10,-25 v -1 q -8,-147 -67,-280 -59,-133 -164,-238 -104,-104 -238,-164.000003 -134,-60.00000033 -280.000003,-67.0000003 z"></path></svg></a></li></ul></nav></header> <main class="-top-20 xs:mx-4 sm:max-w-xl sm:mx-auto md:max-w-2xl lg:max-w-4xl xl:max-w-6xl 2xl:max-w-7xl relative block xl:flex justify-between"><section class="xl:w-[50rem] 2xl:w-[54rem] text-zinc-600 dark:text-zinc-300"><aside class="relative p-2 sm:px-4 md:px-6 bg-tan dark:bg-darktan dark:text-zinc-200 shadow-lg"><strong>Sponsored by</strong>
1 <a href="https://dometrain.com/dometrain-pro/?ref=andrew-lock&amp;promo=banner&amp;coupon_code=ANDREW30" target="_blank" rel="noopener" class="track text-primary hover:text-primary2 dark:text-darkprimary dark:hover:text-primary2 transition-colors duration-300"><strong>Dometrain Courses</strong></a>—Get 30% off <a href="https://dometrain.com/dometrain-pro/?ref=andrew-lock&amp;promo=banner&amp;coupon_code=ANDREW30" target="_blank" rel="noopener" class="track text-primary hover:text-primary2 dark:text-darkprimary dark:hover:text-primary2 transition-colors duration-300"><strong>Dometrain Pro</strong></a> with code <a href="https://dometrain.com/dometrain-pro/?ref=andrew-lock&amp;promo=banner&amp;coupon_code=ANDREW30" target="_blank" rel="noopener" class="track text-primary hover:text-primary2 dark:text-darkprimary dark:hover:text-primary2 transition-colors duration-300"><strong>ANDREW30</strong></a> and access the best courses for .NET Developers</aside> <article class="p-2 sm:p-4 md:p-6 lg:px-10 bg-white dark:bg-zinc-800 shadow-lg"><header class="mb-10"><div class="text-center text-xs"><time datetime="September 22, 2026">September 22, 2026</time> <span> ~14 min read</span></div> <ul class="p-2 text-xs flex flex-wrap items-center justify-center gap-4 md:gap-y-1 md:gap-x-2"><li><a href="/tag/net-11/" class="bg-primary px-2 rounded-md text-zinc-100 uppercase hover:bg-primary2 transition-colors duration-300">.NET 11</a></li><li><a href="/tag/c/" class="bg-primary px-2 rounded-md text-zinc-100 uppercase hover:bg-primary2 transition-colors duration-300">C#</a></li><li><a href="/tag/aspnet-core/" class="bg-primary px-2 rounded-md text-zinc-100 uppercase hover:bg-primary2 transition-colors duration-300">ASP.NET Core</a></li><li><a href="/tag/security/" class="bg-primary px-2 rounded-md text-zinc-100 uppercase hover:bg-primary2 transition-colors duration-300">Security</a></li></ul><hgroup class="mt-0 xs:mt-4 mb-4 break-words dark:text-zinc-300 font-light leading-tight text-center text-zinc-700 tracking-tight text-pretty"><h1 class="mb-4 text-4xl">Experimental support for Device Bound Session Credentials (DBSC) in ASP.NET Core</h1> <p class="decoration-from-font hover:underline text-2xl text-primary dark:text-darkprimary"><a class="series-link" href="/series/exploring-the-dotnet-11-preview/">Exploring the .NET 11 preview - Part 8</a></p></hgroup><div class="text-sm align-middle text-center"> Share on: <ul class="align-middle inline-block"><li class="pl-0.5 inline-block hover:rotate-6 hover:scale-110 transition-transform duration-300"><a class="track fill-primary" href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fandrewlock.net%2Fexploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore%2F&amp;t=Experimental+support+for+Device+Bound+Session+Credentials+(DBSC)+in+ASP.NET+Core" target="_blank" rel="noopener" title="Share on Facebook"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-labelledby="icons-facebook" role="img" xmlns="http://www.w3.org/2000/svg"><title id="icons-facebook">Facebook</title><path d="m 696,-79 q 67,0 114,48 47,48 47,113 v 536 q 0,66 -47,113 -47,47 -114,48 H 592 V 446 H 703 L 719,317 H 592 v -83 q 0,-31 13,-46 13,-15 51,-16 l 68,-1 V 56 q -35,-5 -100,-5 -75,0 -121,44 -46,44 -45,127 v 95 H 346 V 446 H 458 V 779 H 161 Q 94,779 47,731 0,683 0,618 V 82 Q 0,16 47,-31 94,-78 161,-79 Z"></path></svg></a></li> <li class="pl-0.5 inline-block hover:rotate-6 hover:scale-110 transition-transform duration-300"><a class="track fill-primary" href="https://twitter.com/intent/tweet?source=https%3A%2F%2Fandrewlock.net%2Fexploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore%2F&amp;text=Experimental+support+for+Device+Bound+Session+Credentials+(DBSC)+in+ASP.NET+Core:https%3A%2F%2Fandrewlock.net%2Fexploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore%2F" target="_blank" rel="noopener" title="Tweet"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 714,190 q -31,14 -67,19 38,-22 52,-65 -37,21 -75,28 -34,-36 -85,-36 -49,0 -83,34 -34,34 -35,83 0,16 3,27 -72,-4 -135,-37 -63,-33 -107,-86 -16,28 -16,59 0,64 51,98 -27,-1 -56,-15 v 1 q 0,42 28,75 28,33 68,40 -16,5 -28,5 -7,0 -22,-3 12,36 42,59 30,23 67,23 -64,50 -145,50 -15,0 -28,-2 82,53 180,53 62,0 117,-20 55,-20 94,-53 39,-33 67,-76 28,-43 42,-91 14,-48 13,-94 0,-10 0,-15 35,-25 58,-61 z M 857,82 v 536 q 0,66 -47,113 -47,47 -114,48 H 161 Q 94,779 47,731 0,683 0,618 V 82 Q 0,16 47,-31 94,-78 161,-79 h 535 q 67,0 114,48 47,48 47,113 z"></path></svg></a></li> <li class="pl-0.5 inline-block hover:rotate-6 hover:scale-110 transition-transform duration-300"><a class="track fill-primary" href="https://twitter.com/intent/tweet?source=https%3A%2F%2Fandrewlock.net%2Fexploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore%2F&amp;text=Experimental+support+for+Device+Bound+Session+Credentials+(DBSC)+in+ASP.NET+Core:https%3A%2F%2Fandrewlock.net%2Fexploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore%2F" target="_blank" rel="noopener" title="Share on Bluesky"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 714,190 q -31,14 -67,19 38,-22 52,-65 -37,21 -75,28 -34,-36 -85,-36 -49,0 -83,34 -34,34 -35,83 0,16 3,27 -72,-4 -135,-37 -63,-33 -107,-86 -16,28 -16,59 0,64 51,98 -27,-1 -56,-15 v 1 q 0,42 28,75 28,33 68,40 -16,5 -28,5 -7,0 -22,-3 12,36 42,59 30,23 67,23 -64,50 -145,50 -15,0 -28,-2 82,53 180,53 62,0 117,-20 55,-20 94,-53 39,-33 67,-76 28,-43 42,-91 14,-48 13,-94 0,-10 0,-15 35,-25 58,-61 z M 857,82 v 536 q 0,66 -47,113 -47,47 -114,48 H 161 Q 94,779 47,731 0,683 0,618 V 82 Q 0,16 47,-31 94,-78 161,-79 h 535 q 67,0 114,48 47,48 47,113 z"></path></svg></a></li> <li class="pl-0.5 inline-block hover:rotate-6 hover:scale-110 transition-transform duration-300"><a class="track fill-primary" href="http://www.reddit.com/submit?url=https%3A%2F%2Fandrewlock.net%2Fexploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore%2F&amp;title=Experimental+support+for+Device+Bound+Session+Credentials+(DBSC)+in+ASP.NET+Core" target="_blank" rel="noopener" title="Submit to Reddit"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 524,480 q 7,7 0,14 -30,30 -95,30 -65,0 -96,-30 -7,-7 0,-14 3,-3 7,-3 4,0 8,3 23,23 81,23 58,0 81,-23 2,-3 7,-3 5,0 7,3 z M 377,393 q 0,17 -13,30 -13,13 -30,13 -17,0 -30,-13 -13,-13 -13,-30 0,-18 13,-30 13,-12 30,-13 17,-1 31,13 14,14 12,30 z m 189,0 q 0,17 -13,30 -13,13 -30,13 -17,0 -30,-13 -13,-13 -13,-30 0,-18 13,-30 13,-12 30,-13 17,-1 30,13 13,14 13,30 z m 120,-58 q 0,-23 -17,-40 
1-17,-17 -41,-16 -23,0 -40,17 -63,-44 -149,-46 l 30,-136 95,22 q 1,18 13,30 12,12 30,13 18,0 31,-13 13,-13 12,-30 -1,-17 -12,-31 -11,-14 -31,-12 -27,0 -38,24 L 463,93 q -9,-3 -11,8 l -34,149 q -86,3 -148,46 -17,-17 -41,-17 -24,0 -41,16 -17,16 -17,40 0,17 9,31 9,14 24,21 -3,14 -3,27 0,68 67,116 67,48 161,49 95,0 162,-49 67,-49 67,-116 0,-14 -3,-27 14,-7 22,-21 8,-14 9,-31 z M 857,82 v 536 q 0,66 -47,113 -47,47 -114,48 H 161 Q 94,779 47,731 0,683 0,618 V 82 Q 0,16 47,-31 94,-78 161,-79 h 535 q 67,0 114,48 47,48 47,113 z"></path></svg></a></li> <li class="pl-0.5 inline-block hover:rotate-6 hover:scale-110 transition-transform duration-300"><a class="track fill-primary" href="http://www.linkedin.com/shareArticle?mini=true&amp;url=https%3A%2F%2Fandrewlock.net%2Fexploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore%2F&amp;title=Experimental+support+for+Device+Bound+Session+Credentials+(DBSC)+in+ASP.NET+Core&amp;source=https%3A%2F%2Fandrewlock.net%2Fexploring-the-dotnet-11-preview-8-experimental-support-for-device-bound-session-credentials-in-aspnetcore%2F" target="_blank" rel="noopener" title="Share on LinkedIn"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-label="LinkedIn" role="img" xmlns="http://www.w3.org/2000/svg"><path d="M 132,639 H 261 V 252 H 132 Z M 270,132 q -1,-29 -21,-48 -20,-19 -51,-19 -31,0 -53,19 -22,19 -21,48 0,29 20,48 20,19 52,19 v 0 q 33,0 53,-19 20,-19 21,-48 z M 596,639 H 725 V 417 q 0,-86 -41,-130 -41,-44 -107,-44 -76,0 -117,65 h 1 V 252 H 332 q 2,37 0,387 H 461 V 422 q 0,-21 4,-31 8,-19 25,-33 17,-14 41,-14 65,0 65,88 z M 857,82 v 536 q 0,66 -47,113 -47,47 -114,48 H 161 Q 94,779 47,731 0,683 0,618 V 82 Q 0,16 47,-31 94,-78 161,-79 h 535 q 67,0 114,48 47,48 47,113 z"></path></svg></a></li></ul></div></header> <section class="post-body break-words prose prose-zinc prose-img:mx-auto prose-img:mx-auto prose-table:block prose-table:overflow-auto dark:prose-invert max-w-none"><nav><p>This is the eighth post in the series: <a href="/series/exploring-the-dotnet-11-preview/">Exploring the .NET 11 preview</a>. </p> <ol class="list-none"><li><a href="/exploring-the-dotnet-11-preview-1-running-background-tasks-in-blazor-with-web-workers/">Part 1 - Running background tasks in Blazor with Web Workers</a></li><li><a href="/exploring-the-dotnet-11-preview-2-dotnet-gets-union-types/">Part 2 - .NET (OK, C#) finally gets union types🎉</a></li><li><a href="/exploring-the-dotnet-11-preview-3-avoiding-tostring-allocations-with-stringbuilder-movechunks/">Part 3 - Avoiding ToString() allocations with StringBuilder.MoveChunks</a></li><li><a href="/exploring-the-dotnet-11-preview-4-closed-class-hierarchies/">Part 4 - Closed class hierarchies</a></li><li><a href="/exploring-the-dotnet-11-preview-5-improvments-to-process-apis/">Part 5 - Improvements to reading Process outputs</a></li><li><a href="/exploring-the-dotnet-11-preview-6-automatic-csrf-protection-based-on-fetch-metadata-http-headers/">Part 6 - Automatic CSRF protection based on Fetch Metadata headers</a></li><li><a href="/exploring-the-dotnet-11-preview-7-the-pain-of-serializing-unions-and-closed-class-hierarchies-with-system-text-json/">Part 7 - The pain of serializing unions and closed class hierarchies with System.Text.Json</a></li><li>Part 8 - Experimental support for Device Bound Session Credentials (DBSC) in ASP.NET Core (this post) </li></ol></nav><p>In this post I look at the new experimental support for Device Bound Session Credentials (DBSC) released for .NET 11 in the <a href="https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.DeviceBoundSessions"><em>Microsoft.AspNetCore.Authentication.DeviceBoundSessions</em> package</a>. In this post I show how to use the package, how to add it to an ASP.NET Core Identity application, the effect it has on the requests between the server and the browser, as well as some of the implementation details behind the package.</p> <h2 id="a-brief-introduction-onto-device-bound-session-credentials-dbsc-" class="heading-with-anchor"><a href="#a-brief-introduction-onto-device-bound-session-credentials-dbsc-" class="relative text-zinc-800 dark:text-white no-underline hover:underline">A brief introduction onto Device Bound Session Credentials (DBSC)</a></h2> <p>A well known risk with cookie authentication is "cookie theft" or "session hijacking", where long-lived authentication cookies are exfiltrated from a victim machine and can then be used from an attacker's machine. As the cookies are "bearer" tokens, there's no way for the server to know that the tokens have been stolen (outside of using vague heuristics like <a href="https://techcommunity.microsoft.com/blog/microsoftthreatprotectionblog/detecting-and-remediating-impossible-travel/3366017">impossible travel</a> etc.)</p> <p><a href="https://w3c.github.io/webappsec-dbsc/">Device Bound Session Credentials (DBSC)</a> aim to provide a defence against this weakness, by exchanging long-live credentials for short-lived credentials, and using a refresh token that requires a server to verify it is being used on the same machine it was issued to. This makes session hijacking harder, as you can no longer simply extract credentials and use them elsewhere;
1 the short-lived cookies will shortly expire, and the refresh token would not be valid.</p> <blockquote> <p>This verification works by having the browser sign requests using a private key that is stored in a Trusted Platform Module (TPM). The key is never exposed outside of the TPM, so you can be sure that if a request is signed with the same key, it's coming from the same machine.</p> </blockquote> <p>The overall flow of DBSC consists of a "registration" step and a "refresh" step, as shown in the following diagram. In summary:</p> <ul><li>The server returns a <code>Secure-Session-Registration</code> header that tells the browser that DBSC is supported.</li> <li>The browser creates a private-public key pair and registers it with the server.</li> <li>The server replaces the long-lived authentication cookies with short-lived cookies instead.</li> <li>When required, the browser sends a request to the server's refresh endpoint to get updated credentials. The server sends a challenge, the browser signs and sends it back, and the server updates the short-lived cookies.</li></ul> <p><img src="/content/images/2026/dbsc.webp" alt="The dbsc flow"></p> <p>For a more detailed exploration of the protocol, see <a href="/understanding-device-bound-session-credentials/">my previous post</a>, in which I walk through all of the steps at the HTTP level. The final section in this post assumes you have a decent overview of how the protocol works, so I recommend reading that post first (or one of <a href="/understanding-device-bound-session-credentials/#resources">the other posts linked to</a>)</p> <h2 id="asp-net-core-s-support-for-dbsc" class="heading-with-anchor"><a href="#asp-net-core-s-support-for-dbsc" class="relative text-zinc-800 dark:text-white no-underline hover:underline">ASP.NET Core's support for DBSC</a></h2> <p>In .NET 11, ASP.NET Core <a href="https://github.com/dotnet/aspnetcore/pull/67388">has added</a> experimental support for Device Bound Session Credentials (DBSC). This is being shipped as a standalone package, <a href="https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.DeviceBoundSessions"><em>Microsoft.AspNetCore.Authentication.DeviceBoundSessions</em></a>, and as described in the original PR:</p> <blockquote> <p>It is designed as a drop-in hardening layer over an existing cookie auth scheme: you point it at a "source" sign-in scheme and it manages the registration handshake, a path-scoped refresh cookie, and a short-lived session cookie.</p> </blockquote> <p>There's a fair amount of <a href="https://github.com/dotnet/aspnetcore/issues/68117">public API surface</a>, but for the simplest use cases, enabling DBSC for an application simply involves calling <code>AddDeviceBoundSession()</code> on the <code>AuthenticationHandler</code>:</p> <div class="pre-code-wrapper"><pre class="language-csharp"><code class="language-csharp"><span class="token comment">// Normal authentication code</span>
2builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddAuthentication</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
3    <span class="token punctuation">.</span><span class="token function">AddCookie</span><span class="token punctuation">(</span><span class="token string">"Application"</span><span class="token punctuation">,</span> o <span class="token operator">=&gt;</span> <span class="token punctuation">{</span> <span class="token comment">/* normal sign-in cookie */</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
4    <span class="token comment">// 👇 Add DBSC support for the `"Application"` cookie</span>
5    <span class="token punctuation">.</span><span class="token function">AddDeviceBoundSession</span><span class="token punctuation">(</span><span class="token string">"Application"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
6</code></pre></div> <p>The <code>"Application"</code> argument is key there, as it defines <em>which</em> of the cookies gets the DBSC "wrapping" and protection. You can apply it to more than one cookie authentication scheme if you wish by calling <code>AddDeviceBoundSession()</code> multiple times, and passing in a different scheme.</p> <p>Of course, if you're looking into adding DBSC to your ASP.NET Core application, you might well be using ASP.NET Core Identity, and that's the example I'm going to explore in the rest of this post as an obvious case study.</p> <h2 id="using-dbsc-with-asp-net-core-identity" class="heading-with-anchor"><a href="#using-dbsc-with-asp-net-core-identity" class="relative text-zinc-800 dark:text-white no-underline hover:underline">Using DBSC with ASP.NET Core Identity</a></h2> <p>In this section we'll create a sample ASP.NET Core app with ASP.NET Core Identity. We'll then add the <em>Microsoft.AspNetCore.Authentication.DeviceBoundSessions</em> package to the app, and configure the default authentication cookie to use DBSC. Finally we'll explore how this looks when the browser is making the requests.</p> <h3 id="creating-the-sample-app" class="heading-with-anchor"><a href="#creating-the-sample-app" class="relative text-zinc-800 dark:text-white no-underline hover:underline">Creating the sample app</a></h3> <p>We start with a simple ASP.NET Core MVC app with ASP.NET Core Identity, created using <code>dotnet new</code>:</p> <div class="pre-code-wrapper"><pre class="language-bash"><code class="language-bash">dotnet new mvc <span class="token parameter variable">
6-au</span> Individual
7</code></pre></div> <p>This creates an app that uses ASP.NET Core Identity for storing users in a SQLite database with EF Core. The service configuration in the <em>Program.cs</em> file looks something like this:</p> <div class="pre-code-wrapper"><pre class="language-csharp"><code class="language-csharp"><span class="token class-name"><span class="token keyword">var</span></span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
8
9<span class="token comment">// Add services to the container.</span>
10<span class="token class-name"><span class="token keyword">var</span></span> connectionString <span class="token operator">=</span> builder<span class="token punctuation">.</span>Configuration<span class="token punctuation">.</span><span class="token function">GetConnectionString</span><span class="token punctuation">(</span><span class="token string">"DefaultConnection"</span><span class="token punctuation">)</span> <span class="token operator">??</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token constructor-invocation class-name">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Connection string 'DefaultConnection' not found."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
11builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddDbContext</span><span class="token generic class-name"><span class="token punctuation">&lt;</span>ApplicationDbContext<span class="token punctuation">&gt;</span></span></span><span class="token punctuation">(</span>options <span class="token operator">=&gt;</span>
12    options<span class="token punctuation">.</span><span class="token function">UseSqlite</span><span class="token punctuation">(</span>connectionString<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
13builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddDatabaseDeveloperPageExceptionFilter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
14
15builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddDefaultIdentity</span><span class="token generic class-name"><span class="token punctuation">&lt;</span>IdentityUser<span class="token punctuation">&gt;</span></span></span><span class="token punctuation">(</span>options <span class="token operator">=&gt;</span> options<span class="token punctuation">.</span>SignIn<span class="token punctuation">.</span>RequireConfirmedAccount <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">)</span>
16    <span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddEntityFrameworkStores</span><span class="token generic class-name"><span class="token punctuation">&lt;</span>ApplicationDbContext<span class="token punctuation">&gt;</span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
17builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddControllersWithViews</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
18
19<span class="token class-name"><span class="token keyword">var</span></span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
20
21<span class="token comment">// ... middleware pipeline config elided for brevity</span>
22
23app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
24</code></pre></div> <p>The interesting part for us here is the <code>AddDefaultIdentity()</code> method, as that sets up the authentication for the app as follows:</p> <div class="pre-code-wrapper"><pre class="language-csharp"><code class="language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token return-type class-name">IdentityBuilder</span> <span class="token generic-method"><span class="token function">AddDefaultIdentity</span><span class="token generic class-name"><span class="token punctuation">&lt;</span>TUser<span class="token punctuation">&gt;</span></span></span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token class-name">IServiceCollection</span> services<span class="token punctuation">,</span> <span class="token class-name">Action<span class="token punctuation">&lt;</span>IdentityOptions<span class="token punctuation">&gt;</span></span> configureOptions<span class="token punctuation">)</span> <span class="token keyword">where</span> <span class="token class-name">
24TUser</span> <span class="token punctuation">:</span> <span class="token type-list"><span class="token keyword">class</span></span>
25<span class="token punctuation">{</span>
26    services<span class="token punctuation">.</span><span class="token function">AddAuthentication</span><span class="token punctuation">(</span>o <span class="token operator">=&gt;</span>
27    <span class="token punctuation">{</span>
28        o<span class="token punctuation">.</span>DefaultScheme <span class="token operator">=</span> IdentityConstants<span class="token punctuation">.</span>ApplicationScheme<span class="token punctuation">;</span>
29        o<span class="token punctuation">.</span>DefaultSignInScheme <span class="token operator">=</span> IdentityConstants<span class="token punctuation">.</span>ExternalScheme<span class="token punctuation">;</span>
30    <span class="token punctuation">}</span><span class="token punctuation">)</span>
31    <span class="token punctuation">.</span><span class="token function">AddIdentityCookies</span><span class="token punctuation">(</span>o <span class="token operator">=&gt;</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
32
33    <span class="token keyword">return</span> services<span class="token punctuation">.</span><span class="token generic-method"><span class="token function">AddIdentityCore</span><span class="token generic class-name"><span class="token punctuation">&lt;</span>TUser<span class="token punctuation">&gt;</span></span></span><span class="token punctuation">(</span>o <span class="token operator">=&gt;</span>
34    <span class="token punctuation">{</span>
35        o<span class="token punctuation">.</span>Stores<span class="token punctuation">.</span>MaxLengthForKeys <span class="token operator">=</span> <span class="token number">128</span><span class="token punctuation">;</span>
36        configureOptions<span class="token punctuation">?.</span><span class="token function">Invoke</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">;</span>
37    <span class="token punctuation">}</span><span class="token punctuation">)</span>
38        <span class="token punctuation">.</span><span class="token function">AddDefaultUI</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
39        <span class="token punctuation">.</span><span class="token function">AddDefaultTokenProviders</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
40<span class="token punctuation">}</span>
41</code></pre></div> <p><code>AddIdentityCookies()</code> configures the authentication cookies, but the important thing here is seeing the default scheme used is <code>IdentityConstants.ApplicationScheme</code>. That's the scheme we're going to use with DBSC.</p> <h3 id="adding-the-dbsc-package" class="heading-with-anchor"><a href="#adding-the-dbsc-package" class="relative text-zinc-800 dark:text-white no-underline hover:underline">Adding the DBSC package</a></h3> <p>First we need to add the <a href="https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.DeviceBoundSessions"><em>Microsoft.AspNetCore.Authentication.DeviceBoundSessions</em></a> package to our project. This package is staying in preview even when .NET 11 goes GA, so we need to use the <code>--prerelease</code> flag with the .NET CLI:</p> <div class="pre-code-wrapper"><pre class="language-bash"><code class="language-bash">dotnet <span class="token function">add</span> package Microsoft.AspNetCore.Authentication.DeviceBoundSessions <span class="token parameter variable">--prerelease</span>
42</code></pre></div> <p>This adds the package to the project file, something like the following:</p> <div class="pre-code-wrapper"><pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Sdk</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Microsoft.NET.Sdk.Web<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
43
44  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PropertyGroup</span><span class="token punctuation">&gt;</span></span>
45    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TargetFramework</span><span class="token punctuation">&gt;</span></span>net11.0<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TargetFramework</span><span class="token punctuation">&gt;</span></span>
46    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Nullable</span><span class="token punctuation">&gt;</span></span>enable<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Nullable</span><span class="token punctuation">&gt;</span></span>
47    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ImplicitUsings</span><span class="token punctuation">&gt;</span></span>enable<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ImplicitUsings</span><span class="token punctuation">&gt;</span></span>
48    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>UserSecretsId</span><span class="token punctuation">&gt;</span></span>aspnet-temp103-b48f7b3d-0bde-4298-9a9c-205fd5fb92c3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>UserSecretsId</span><span class="token punctuation">&gt;</span></span>
49  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>PropertyGroup</span><span class="token punctuation">&gt;</span></span>
50
51  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ItemGroup</span><span class="token punctuation">&gt;</span></span>
52    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>None</span> <span class="token attr-name">Update</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>app.db<span class="token punctuation">"</span></span> <span class="token attr-name">CopyToOutputDirectory</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>PreserveNewest<span class="token punctuation">"</span></span> <span class="token attr-name">ExcludeFromSingleFile</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
53  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ItemGroup</span><span class="token punctuation">&gt;</span></span>
54
55  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ItemGroup</span><span class="token punctuation">&gt;</span></span>
56    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PackageReference</span> <span class="token attr-name">Include</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>
56Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore<span class="token punctuation">"</span></span> <span class="token attr-name">Version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>11.0.0-rc.1.26425.128<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
57    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PackageReference</span> <span class="token attr-name">Include</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Microsoft.AspNetCore.Identity.EntityFrameworkCore<span class="token punctuation">"</span></span> <span class="token attr-name">Version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>11.0.0-rc.1.26425.128<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
58    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PackageReference</span> <span class="token attr-name">Include</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Microsoft.AspNetCore.Identity.UI<span class="token punctuation">"</span></span> <span class="token attr-name">Version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>11.0.0-rc.1.26425.128<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
59    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PackageReference</span> <span class="token attr-name">Include</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Microsoft.EntityFrameworkCore.Sqlite<span class="token punctuation">"</span></span> <span class="token attr-name">Version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>11.0.0-rc.1.26425.128<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
60    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PackageReference</span> <span class="token attr-name">Include</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Microsoft.EntityFrameworkCore.Design<span class="token punctuation">"</span></span> <span class="token attr-name">Version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>11.0.0-rc.1.26425.128<span class="token punctuation">"</span></span> <span class="token attr-name">PrivateAssets</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>all<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
61    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PackageReference</span> <span class="token attr-name">Include</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Microsoft.EntityFrameworkCore.Tools<span class="token punctuation">"</span></span> <span class="token attr-name">Version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>11.0.0-rc.1.26425.128<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
62    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PackageReference</span> <span class="token attr-name">Include</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Microsoft.IdentityModel.JsonWebTokens<span class="token punctuation">"</span></span> <span class="token attr-name">Version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>8.22.0<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
63
64    <span class="token comment">&lt;!-- 👇 Adds this package --&gt;</span>
65    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PackageReference</span> <span class="token attr-name">Include</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Microsoft.AspNetCore.Authentication.DeviceBoundSessions<span class="token punctuation">"</span></span> <span class="token attr-name">Version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0.11.0-rc.1.26425.128<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
66  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ItemGroup</span><span class="token punctuation">&gt;</span></span>
67
68<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Project</span><span class="token punctuation">&gt;</span></span>
69</code></pre></div> <p>With the restore complete, we now need to hook up the package in the <em>Program.cs</em>.</p> <h3 id="enabling-the-dbsc-flow-for-identity-cookies" class="heading-with-anchor"><a href="#enabling-the-dbsc-flow-for-identity-cookies" class="relative text-zinc-800 dark:text-white no-underline hover:underline">Enabling the DBSC flow for Identity cookies</a></h3> <p>The main integration point of the <em>Microsoft.AspNetCore.Authentication.DeviceBoundSessions</em> package is an extension method on <code>AuthenticationBuilder</code> called <code>AddDeviceBoundSession()</code>, with the expectation that you call it something like this (as I showed previously):</p> <div class="pre-code-wrapper"><pre class="language-csharp"><code class="language-csharp">builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddAuthentication</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
70    <span class="token punctuation">.</span><span class="token function">AddCookie</span><span class="token punctuation">(</span><span class="token string">"Application"</span><span class="token punctuation">)</span>
71    <span class="token punctuation">.</span><span class="token function">AddDeviceBoundSession</span><span class="token punctuation">(</span><span class="token string">"Application"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
72</code></pre></div> <p>However, when you're using ASP.NET Core Identity, the <code>AddAuthentication()</code> and <code>AddCookie()</code> calls are hidden <em>inside</em> the <code>AddDefaultIdentity()</code> and <code>
72AddIdentityCookies()</code> helper methods respectively. That means the "expected" chaining approach above isn't possible.</p> <p>Luckily, the <code>AuthenticationBuilder</code> needed for the <code>AddDeviceBoundSession()</code> method is just a thin wrapper around an <code>IServiceCollection</code>, which means we can do something like this:</p> <div class="pre-code-wrapper"><pre class="language-csharp"><code class="language-csharp"><span class="token keyword">new</span> <span class="token constructor-invocation class-name">AuthenticationBuilder</span><span class="token punctuation">(</span>builder<span class="token punctuation">.</span>Services<span class="token punctuation">)</span>
73    <span class="token punctuation">.</span><span class="token function">AddDeviceBoundSession</span><span class="token punctuation">(</span><span class="token named-parameter punctuation">sourceScheme</span><span class="token punctuation">:</span> IdentityConstants<span class="token punctuation">.</span>ApplicationScheme<span class="token punctuation">)</span><span class="token punctuation">;</span>
74</code></pre></div> <p>The important thing here is that we specify the sourceScheme to be the same as the Identity authentication cookie, to ensure the standard Identity authentication cookie is wrapped by DBSC.</p> <blockquote> <p>I wouldn't be surprised if they add another extension method directly on <code>IServiceCollection</code> to avoid needing to create an <code>AuthenticationBuilder</code>.</p> </blockquote> <p>With that single addition, DBSC is now in place, so let's take a look at the results!</p> <h2 id="exploring-the-dbsc-behaviour-in-asp-net-core" class="heading-with-anchor"><a href="#exploring-the-dbsc-behaviour-in-asp-net-core" class="relative text-zinc-800 dark:text-white no-underline hover:underline">Exploring the DBSC behaviour in ASP.NET Core</a></h2> <p>Now that we have our sample app, we'll inspect the DBSC interactions between the browser and the server. Unfortunately, this is <em>somewhat</em> harder than you might think. First, it's important to know that the browser will only consider opting-in to DBSC if the app is server over https, so we have to make sure that we run the app with the https project.</p> <blockquote> <p>I also found that I couldn't get DBSC to work on a site unless I disabled my ad blocker. I tried it on multiple different sites, and neither of them worked, so that's something to bear in mind when exploring DBSC.</p> </blockquote> <p>Making things even trickier to validate, Chrome <em>also</em> doesn't show the registration/refresh requests in the "normal" network tab, so if your DBSC requests aren't working, you won't necessarily know it from looking at the Chrome side. To counteract that, I updated the <em>appsettings.json</em> file to make sure ASP.NET Core infrastructure logs of <code>Information</code> and above were logged, so that I could easily see the server-side of any DBSC requests made by the browser.</p> <p>As already mentioned, it's important that you run the application with an HTTPS endpoint. There are several ways you can do that; I took the easy option of swapping the order of profiles in the <em>launchSettings.json</em>. <code>dotnet run</code> chooses the first profiler by default, so putting <code>https</code> first means it will be chosen (and then trusting the dev certificate with <code>dotnet dev-certs https --trust</code>).</p> <h3 id="1-behind-the-scenes-registering-multiple-authentication-handlers" class="heading-with-anchor"><a href="#1-behind-the-scenes-registering-multiple-authentication-handlers" class="relative text-zinc-800 dark:text-white no-underline hover:underline">1. Behind the scenes: registering multiple authentication handlers</a></h3> <p>Before we get to the actual exchanges, we'll take a little look at the infrastructure ASP.NET Core uses to implement DBSC. This involves registering a number of services, handlers and options as part of the implementation.</p> <p>At the heart of the implementation, we have two cookies, and their associated schemes:</p> <div class="pre-code-wrapper"><pre class="language-csharp"><code class="language-csharp"><span class="token class-name"><span class="token keyword">var</span></span> refreshScheme <span class="token operator">=</span> <span class="token interpolation-string"><span class="token string">$"</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">sourceScheme</span><span class="token punctuation">}</span></span><span class="token string">.Dbsc.Refresh"</span></span><span class="token punctuation">;</span>
75<span class="token class-name"><span class="token keyword">var</span></span> sessionScheme <span class="token operator">=</span> <span class="token interpolation-string"><span class="token string">$"</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">sourceScheme</span><span class="token punctuation">}</span></span><span class="token string">.Dbsc.Session"</span></span><span class="token punctuation">;</span>
76
77<span class="token comment">// This cookie is the "main" short-lived credential cookie</span>
78builder<span class="token punctuation">.</span><span class="token function">AddCookie</span><span class="token punctuation">(</span>sessionScheme<span class="token punctuation">,</span> o <span class="token operator">=&gt;</span>
79<span class="token punctuation">{</span>
80    o<span class="token punctuation">.</span>Cookie<span class="token punctuation">.</span>Name <span class="token operator">=</span> <span class="token interpolation-string"><span class="token string">$".AspNetCore.</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">sourceScheme</span><span class="token punctuation">}</span></span><span class="token string">.Dbsc.Session"</span></span><span class="token punctuation">;</span>
81<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
82
83<span class="token comment">// This cookie is only applied to the /.well-known/dbsc path</span>
84<span class="token comment">// and is used as a way to manage the refreshing of the credentials</span>
85builder<span class="token punctuation">.</span><span class="token function">AddCookie</span><span class="token punctuation">(</span>refreshScheme<span class="token punctuation">,</span> o <span class="token operator">=&gt;</span>
86<span class="token punctuation">{</span>
87    o<span class="token punctuation">.</span>Cookie<span class="token punctuation">.</span>Name <span class="token operator">=</span> <span class="token interpolation-string"><span class="token string">$".AspNetCore.</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">sourceScheme</span><span class="token punctuation">}</span></span><span class="token string">.Dbsc.Refresh"</span></span><span class="token punctuation">;</span>
88    o<span class="token punctuation">.</span>Cookie<span class="token punctuation">.</span>Path <span class="token operator">=</span> <span class="token string">"/.well-known/dbsc"</span><span class="token punctuation">;</span>
89<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
90</code></pre></div> <p>In my standard ASP.NET Core Identity setup, I used the <code>IdentityConstants.ApplicationScheme</code> source scheme, which gives:</p> <ul><li>The refresh scheme is <code>"Identity.Application.Dbsc.Refresh"</code></li> <li>The session scheme is <code>"Identity.Application.Dbsc.Session"</code></li></ul> <p>Another important component is registering <a href="https://learn.microsoft.com/en-us
90/aspnet/core/security/authentication/policyschemes?view=aspnetcore-10.0">a <code>PolicySchemeHandler</code></a>. This is how the short-lived cookie can easily "replace" the default authentication after the DBSC session has been established, and the long-lived cookie has been removed.</p> <div class="pre-code-wrapper"><pre class="language-csharp"><code class="language-csharp"><span class="token comment">// Add a policy scheme that tries the session cookie first, then falls back to the source scheme</span>
91<span class="token class-name"><span class="token keyword">var</span></span> policyScheme <span class="token operator">=</span> <span class="token interpolation-string"><span class="token string">$"</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">sourceScheme</span><span class="token punctuation">}</span></span><span class="token string">.Dbsc"</span></span><span class="token punctuation">;</span>
92builder<span class="token punctuation">.</span><span class="token function">AddPolicyScheme</span><span class="token punctuation">(</span>policyScheme<span class="token punctuation">,</span> policyScheme<span class="token punctuation">,</span> o <span class="token operator">=&gt;</span>
93<span class="token punctuation">{</span>
94    o<span class="token punctuation">.</span>ForwardDefaultSelector <span class="token operator">=</span> context <span class="token operator">=&gt;</span>
95    <span class="token punctuation">{</span>
96        <span class="token comment">// Resolve the session scheme's configured cookie name at request time so an app that</span>
97        <span class="token comment">// customizes CookieAuthenticationOptions.Cookie.Name is still matched. Otherwise the</span>
98        <span class="token comment">// selector would miss the cookie and fall back to a source scheme that DBSC registration</span>
99        <span class="token comment">// may have deleted, effectively logging the user out.</span>
100        <span class="token class-name"><span class="token keyword">var</span></span> sessionCookieName <span class="token operator">=</span> context<span class="token punctuation">.</span>RequestServices
101            <span class="token punctuation">.</span><span class="token generic-method"><span class="token function">GetRequiredService</span><span class="token generic class-name"><span class="token punctuation">&lt;</span>IOptionsMonitor<span class="token punctuation">&lt;</span>CookieAuthenticationOptions<span class="token punctuation">&gt;</span><span class="token punctuation">&gt;</span></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
102            <span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span>sessionScheme<span class="token punctuation">)</span><span class="token punctuation">.</span>Cookie<span class="token punctuation">.</span>Name <span class="token operator">??</span> <span class="token interpolation-string"><span class="token string">$".AspNetCore.</span><span class="token interpolation"><span class="token punctuation">{</span><span class="token expression language-csharp">sessionScheme</span><span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">;</span>
103        <span class="token keyword">if</span> <span class="token punctuation">(</span>context<span class="token punctuation">.</span>Request<span class="token punctuation">.</span>Cookies<span class="token punctuation">.</span><span class="token function">ContainsKey</span><span class="token punctuation">(</span>sessionCookieName<span class="token punctuation">)</span><span class="token punctuation">)</span>
104        <span class="token punctuation">{</span>
105            <span class="token keyword">return</span> sessionScheme<span class="token punctuation">;</span>
106        <span class="token punctuation">}</span>
107        <span class="token keyword">return</span> sourceScheme<span class="token punctuation">;</span>
108    <span class="token punctuation">}</span><span class="token punctuation">;</span>
109<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
110</code></pre></div> <p>Next up, a number <a href="/delaying-strongly-typed-options-configuration-using-postconfigure-in-asp-net-core/">of <code>IPostConfigureOptions</code> implementations</a> are registered. in summary:</p> <ul><li><code>PostConfigureDeviceBoundSessionCookieOptions</code> chains an <code>CookieAuthenticationOptions.Events.OnSigningIn</code> event to ensure the <code>Secure-Session-Registration</code> header is added on sign in, and the DBSC cookies are all removed on sign out.</li> <li><code>PostConfigureDeviceBoundSessionDerivedCookieOptions</code> copies settings from the "main" authentication cookie to the DBSC cookies, like <code>HttpOnly</code>, <code>Secure</code>, <code>SameSite</code>, <code>Domain</code>, and lifetime settings.</li> <li><code>PostConfigureDeviceBoundSessionAuthenticationOptions</code> upgrades the default authentication scheme to point to the DBSC policy scheme.</li></ul> <p>Finally, a <code>DeviceBoundSessionChallengeProtector</code> services is added (more on that shortly) and the <code>DeviceBoundSessionHandler</code> authentication handler, which handles the registration and refresh endpoints and the overall protocol. We'll be looking at that one a lot!</p> <p>With all that said, let's look at the app behaviour when we run it with <code>dotnet run</code>, and explore how DBSC works in ASP.NET Core!</p> <h3 id="2-returning-the-secure-session-registration-header" class="heading-with-anchor"><a href="#2-returning-the-secure-session-registration-header" class="relative text-zinc-800 dark:text-white no-underline hover:underline">2. Returning the <code>Secure-Session-Registration</code> header</a></h3> <p>The DBSC protocol really starts when the server first returns the <code>Secure-Session-Registration</code> header, which ASP.NET Core does after you sign in to a DBSC-wrapped scheme. <a href="https://github.com/dotnet/aspnetcore/blob/0d79c284a2b740a6a2d72791e2f5177f36778fd7/src/Security/Authentication/DeviceBoundSessions/src/PostConfigureDeviceBoundSessionCookieOptions.cs#L39">The <code>OnSigningIn</code> cookie event</a>
110 fires, and <a href="https://github.com/dotnet/aspnetcore/blob/0d79c284a2b740a6a2d72791e2f5177f36778fd7/src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionRegistrationHeader.cs#L30">calls <code>DeviceBoundSessionRegistrationHeader.Emit()</code></a>, which is responsible for adding the <code>Secure-Session-Registration</code> header to the response.</p> <p>The bulk of this implementation is about <a href="/understanding-device-bound-session-credentials/#1-the-login-response-includes-a-secure-session-registration-header">generating the challenge</a>. ASP.NET Core bases the challenge on the current <code>ClaimsPrincipal</code> (the just-logged in user), by choosing a key claim (such as the <code>sub</code>, or other name claim if possible) and then using the <a href="/an-introduction-to-the-data-protection-system-in-asp-net-core/">Data Protection System</a> to create an encrypted value. This is then base64 encoded and included into the header.</p> <blockquote> <p>By using the data protection system, and encrypting values available in the <code>ClaimsPrincipal</code>, ASP.NET Core can easily verify that the <em>signed</em> challenge the browser sends matches, without needing to store any additional data anywhere.</p> </blockquote> <p>The net result is that if you check the response headers login request's response, you can see the <code>Secure-Session-Registration</code> is returned:</p> <p><img src="/content/images/2026/dbsc_aspnetcore.png" alt="The login response showing the Secure-Session-Registration header and the Identity cookie"></p> <p>As you can see, the <code>Secure-Session-Registration</code> cookie is in the response, along with the ASP.NET Core Identity cookie. The browser will spot this header, and initiate the DBSC call to the registration path listed in the header: <code>/.well-known/dbsc/registration</code>.</p> <h3 id="3-registration-of-a-dbsc-session" class="heading-with-anchor"><a href="#3-registration-of-a-dbsc-session" class="relative text-zinc-800 dark:text-white no-underline hover:underline">3. Registration of a DBSC session</a></h3> <p>The next step is where it gets harder to track things from the browser side, as the DBSC registration request isn't visible in the browser tools, but we <em>can</em> see it if we check our ASP.NET Core server logs:</p> <div class="pre-code-wrapper"><pre><code>info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
111      Request starting HTTP/2 POST https://localhost:7029/.well-known/dbsc/registration - - 0
112info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[10]
113      AuthenticationScheme: Identity.Application.Dbsc.Refresh signed in.
114info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[10]
115      AuthenticationScheme: Identity.Application.Dbsc.Session signed in.
116info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[11]
117      AuthenticationScheme: Identity.Application signed out.
118info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
119      Request finished HTTP/2 POST https://localhost:7029/.well-known/dbsc/registration - 200 - application/json 7.1578ms
120</code></pre></div> <p>The logs here give an idea about what's happening, but we'll work through it in more detail now. First of all, <a href="https://github.com/dotnet/aspnetcore/blob/0d79c284a2b740a6a2d72791e2f5177f36778fd7/src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionHandler.cs">the <code>DeviceBoundSessionHandler</code></a> handles the incoming request to <code>/.well-known/dbsc/registration</code>, and calls <code>HandleRegistrationAsync</code> which does the following:</p> <ol><li>Read the <code>Secure-Session-Response</code> header (which contains a JWT).</li> <li>Verify the JWT signature and extract the public key and the challenge.</li> <li>Authenticate against the long-lived ASP.NET Core Identity cookie.</li> <li>Decode the provided challenge, and verify that it matches the original (based on the claims).</li> <li>If everything is valid, create a random session ID property and use it (along with the the public key details) to 
120sign-in to the <em>refresh</em> cookie.</li> <li>Sign-in to the short-lived <em>session</em> cookie.</li> <li>Sign-<em>out</em> of the long-lived authentication cookie.</li> <li>Finally, build <a href="https://github.com/dotnet/aspnetcore/blob/0d79c284a2b740a6a2d72791e2f5177f36778fd7/src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionHandler.cs#L278">the JSON response body instructions</a> that describes <a href="/understanding-device-bound-session-credentials/#3-the-server-validates-the-request-and-returns-short-lived-credentials">how the credentials should be used</a>, and return with a <code>200 OK</code></li></ol> <p>Once the browser receives that response, it's now registered in a DBSC session, and will use the short-lived credentials for subsequent requests. If we make a standard navigation in the browser, we can see the short-lived session cookie in the request headers, and there's no sign of the long-lived cookie any more:</p> <p><img src="/content/images/2026/dbsc_aspnetcore_02.png" alt="The .AspNetCore.Identity.Application.Dbsc.Session cookie in the request"></p> <p>You'll note that the Refresh cookie is <em>not</em> sent in this request, because that cookie is scoped <em>only</em> to the refresh path, <code>/.well-known/dbsc/refresh</code>. However, you can see in the above screenshot there's a <code>Device bound sessions</code> tab, which shows that a session is active:</p> <p><img src="/content/images/2026/dbsc_aspnetcore_03.png" alt="The Device Bound sessions tab, showing the session ID">.</p> <p>The deferral decision refers to whether the browser needed to pause the request to refresh the short-lived session, which in this case it didn't. However, eventually, the browser <em>will</em> need to call the refresh endpoint.</p> <h3 id="4-refreshing-the-credentials" class="heading-with-anchor"><a href="#4-refreshing-the-credentials" class="relative text-zinc-800 dark:text-white no-underline hover:underline">4. Refreshing the credentials</a></h3> <p>Eventually, the browser <em>will</em> need to refresh the credentials, and again, we need to check the ASP.NET Core server logs to see this happening:</p> <div class="pre-code-wrapper"><pre><code>info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
121      Request starting HTTP/2 POST https://localhost:7029/.well-known/dbsc/refresh - - 0
122info: Microsoft.EntityFrameworkCore.Database.Command[20101]
123      Executed DbCommand (7ms) [Parameters=[@p='?' (Size = 36)], CommandType='Text', CommandTimeout='30']
124      SELECT "a"."Id", "a"."AccessFailedCount", "a"."ConcurrencyStamp", "a"."Email", "a"."EmailConfirmed", "a"."LockoutEnabled", "a"."LockoutEnd", "a"."NormalizedEmail", "a"."NormalizedUserName", "a"."PasswordHash", "a"."PhoneNumber", "a"."PhoneNumberConfirmed", "a"."SecurityStamp", "a"."TwoFactorEnabled", "a"."UserName"
125      FROM "AspNetUsers" AS "a"
126      WHERE "a"."Id" = @p
127      LIMIT 1
128info: Microsoft.EntityFrameworkCore.Database.Command[20101]
129      Executed DbCommand (0ms) [Parameters=[@user_Id='?' (Size = 36)], CommandType='Text', CommandTimeout='30']
130      SELECT "a"."Id", "a"."ClaimType", "a"."ClaimValue", "a"."UserId"
131      FROM "AspNetUserClaims" AS "a"
132      WHERE "a"."UserId" = @user_Id
133info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
134      Request finished HTTP/2 POST https://localhost:7029/.well-known/dbsc/refresh - 403 0 - 16.5363ms
135info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
136      Request starting HTTP/2 POST https://localhost:7029/.well-known/dbsc/refresh - - 0
137info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[10]
138      AuthenticationScheme: Identity.Application.Dbsc.Session signed in.
139info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
140      Request finished HTTP/2 POST https://localhost:7029/.well-known/dbsc/refresh - 200 - application/json 8.6243ms
141</code></pre></div> <p>There's a lot more happening here; we have two separate requests and some EF Core requests, so let's dig in.</p> <p>It again starts with the authentication handler <code>DeviceBoundSessionHandler</code> and the <a href="https://github.com/dotnet/aspnetcore/blob/0d79c284a2b740a6a2d72791e2f5177f36778fd7/src/Security/Authentication/DeviceBoundSessions/src/DeviceBoundSessionHandler.cs#L174">call to <code>HandleRefreshAsync()</code></a>. This does the following:</p> <ol><li>Read the DBSC session ID from the <code>Sec-Secure-Session-Id</code> header.</li> <li>Authenticate the refresh scheme (using the refresh cookie).</li> <li>Grab the session ID from the cookie, and confirm it matches the header.</li> <li>Generate a new challenge value based on the claims principal.</li> <li>Return a <code>403</code> response, with the challenge value in the <code>Secure-Session-Challenge</code> header.</li></ol> <p>The browser then signs the challenge, and sends a JWT in the <code>Secure-Session-Response</code> header to the same endpoint. The <code>DeviceBoundSessionHandler</code> again calls <code>HandleRefreshAsync()</code> to handle the response, with the first 3 steps being the same:</p> <ol><li>Read the DBSC session ID from the <code>Sec-Secure-Session-Id</code> header.</li> <li>Authenticate the refresh scheme (using the refresh cookie).</li> <li>Grab the session ID from the cookie, and confirm it matches the header.</li> <li>Grab the expected public key from the cookie.</li> <li>Validate the JWT, and ensure the public key matches.</li> <li>Decode the provided challenge, and verify that it matches the original (based on the claims).</li> <li>
141Do a fresh sign-in to the short-lived <em>session</em> cookie.</li> <li>Build the session instructions again, and send them in the response body along with a <code>200 OK</code> response</li></ol> <p>And with that, the refresh is complete! As already stated, the browser will make this exchange <em>before</em> sending a main request if necessary, which will show that the request was deferred in that case:</p> <p><img src="/content/images/2026/dbsc_aspnetcore_04.png" alt="The deferred refresh"></p> <p>I've obviously skipped over a lot of the details here, but if you want to learn more, you can find it all <a href="https://github.com/dotnet/aspnetcore/tree/0d79c284a2b740a6a2d72791e2f5177f36778fd7/src/Security/Authentication/DeviceBoundSessions/src">in the ASP.NET Core repo</a>. As described <a href="https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-11?view=aspnetcore-10.0&amp;tabs=minimal-apis#experimental-device-bound-session-credentials-support">in the docs</a>, the implementation is only experimental, and will remain as such throughout .NET 11 apparently. That said, given it's very low-effort to give it a try, I recommend taking a look!</p> <h2 id="summary" class="heading-with-anchor"><a href="#summary" class="relative text-zinc-800 dark:text-white no-underline hover:underline">Summary</a></h2> <p>In this post I gave a brief introduction to Device Bound Session Credentials (DBSC), and why it's a useful extra defence against cookie theft and session hijacking. I then introduced the new <a href="https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.DeviceBoundSessions">experimental <em>Microsoft.AspNetCore.Authentication.DeviceBoundSessions</em> package</a> and show how you can use it in your applications. I then show the feature working, and provide an overview of how the various components work behind the scenes. Ultimately, there's very little you <em>need</em> to understand to use the feature, it just requires a single API call, so I recommend checking it out if you're interested.</p> </section></article><footer class="p-2 sm:p-4 md:p-6 border-zinc-200 border-t text-center lg:text-left bg-white dark:bg-zinc-800 shadow-lg"><label class="w-40 block lg:inline-block mb-2 lg:mb-0 mx-auto uppercase">This Series</label> <a href="/series/exploring-the-dotnet-11-preview/" class="hover:text-primary2 text-primary dark:text-darkprimary hover:underline">Exploring the .NET 11 preview</a></footer><footer class="p-2 sm:p-4 md:p-6 border-zinc-200 border-t text-center lg:text-left bg-white dark:bg-zinc-800 shadow-lg"><label class="w-40 block lg:inline-block mb-2 lg:mb-0 mx-auto uppercase align-middle">Follow me</label> <ul class="block md:inline-block align-middle"><li class="inline-block px-2 duration-300 hover:rotate-6 hover:scale-110 transition-transform"><a rel="me noopener" target="_blank" href="https://www.facebook.com/NETescapades" title="Like .NET Escapades on Facebook" class="fill-primary dark:fill-darkprimary track"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 810,-79 q 19,0 33,14 14,14 14,34 v 762 q 0,20 -14,34 -14,14 -33,14 H 592 V 446 H 703 L 719,317 H 592 v -83 q 0,-31 13,-46 13,-15 51,-16 l 68,-1 V 56 q -35,-5 -100,-5 -75,0 -121,44 -46,44 -45,127 v 95 H 346 V 446 H 458 V 779 H 47 Q 28,779 14,765 0,751 0,731 V -31 q 0,-20 14,-34 14,-14 33,-14 z"></path></svg></a></li> <li class="inline-block px-2 duration-300 hover:rotate-6 hover:scale-110 transition-transform"><a rel="me noopener" target="_blank" href="https://twitter.com/andrewlocknet" title="Follow @andrewlocknet on Twitter" class="fill-primary dark:fill-darkprimary track"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-label="Twitter" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 904,77.882353 q -37,54 -90,93 0,8 0,23 0,73 -21,145 -21,72 -64,139 -43,67 -103,117 -60,50 -144,82 -84,32 -181,30 -151,0 -276,-81 19,2 43,2 126,0 224,-77 -59,-1 -105,-36 -46,-35 -64,-89 19,3 34,3 24,0 48,-6 -63,-13 -104,-62 -41,-49 -41,-115 v -2 q 38,21 82,23 -37,-25 -59,-64 -22,-39 -22,-86 0,-49 25,-91 68,83 164,133 96,50 208,55 -5,-21 -5,-41 0,-75 53,-127 53,-52 127,-53 79,0 132,57 61,-12 115,-44 -21,64 -80,100 52,-6 104,-28 z"></path></svg></a></li> <li class="inline-block px-2 duration-300 hover:rotate-6 hover:scale-110 transition-transform"><a rel="me noopener" target="_blank" href="https://hachyderm.io/@andrewlock" title="Follow @[email protected] on Mastadon" class="fill-primary dark:fill-darkprimary track"><svg width="24px" height="24px" viewBox="0 0 417.8 512" version="1.1" aria-label="Mastadon" role="img" xmlns="http://www.w3.org/2000/svg"><path d="M417.8 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.5-28.4-290.4 0 0 0-63.7 28.5-63.7 125.7 0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.
1411c-40.4-1.4-83-4.4-89.6-54-.6-4.4-.9-9-.9-13.9 85.6 20.9 158.6 9.1 178.7 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6V190.1c0-49.7-64-51.6-64 6.9v62.5h-46.3V197c0-58.5-64-56.6-64-6.9v114.2H75.1c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"></path></svg></a></li> <li class="inline-block px-2 duration-300 hover:rotate-6 hover:scale-110 transition-transform"><a rel="me noopener" target="_blank" href="https://uk.linkedin.com/in/andrewdlock" title="Andrew Lock on LinkedIn" class="fill-primary dark:fill-darkprimary track"><svg width="24px" height="24px" viewBox="0 -100 900 900" version="1.1" aria-label="LinkedIn" role="img" xmlns="http://www.w3.org/2000/svg"><path d="M 132,639 H 261 V 252 H 132 Z M 270,132 q -1,-29 -21,-48 -20,-19 -51,-19 -31,0 -53,19 -22,19 -21,48 0,29 20,48 20,19 52,19 v 0 q 33,0 53,-19 20,-19 21,-48 z M 596,639 H 725 V 417 q 0,-86 -41,-130 -41,-44 -107,-44 -76,0 -117,65 h 1 V 252 H 332 q 2,37 0,387 H 461 V 422 q 0,-21 4,-31 8,-19 25,-33 17,-14 41,-14 65,0 65,88 z M 857,82 v 536 q 0,66 -47,113 -47,47 -114,48 H 161 Q 94,779 47,731 0,683 0,618 V 82 Q 0,16 47,-31 94,-78 161,-79 h 535 q 67,0 114,48 47,48 47,113 z"></path></svg></a></li> <li class="inline-block px-2 duration-300 hover:rotate-6 hover:scale-110 transition-transform"><a rel="me noopener" target="_blank" href="https://github.com/andrewlock" title="Andrew lock on Github" class="fill-primary dark:fill-darkprimary track"><svg width="24px" height="24px" viewBox="-10 -100 900 900" version="1.1" aria-label="Github" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 429,-57.9 q 116,0 215,58 99,58 156,156 57,98 57,215 0,140 -82,252 -82,112 -211,155 -15,3 -22,-4 -7,-7 -7,-17 0,-1 0,-43 0,-42 0,-75 0,-54 -29,-79 32,-3 57,-10 25,-7 53,-22 28,-15 45,-37 17,-22 30,-58 13,-36 11,-84 0,-67 -44,-115 21,-51 -4,-114 -16,-5 -46,6 -30,11 -51,25 l -21,13 q -52,-15 -107,-15 -55,0 -108,15 -8,-6 -23,-15 -15,-9 -47,-22 -32,-13 -47,-7 -25,63 -5,114 -44,48 -44,115 0,47 12,83 12,36 29,59 17,23 45,37 28,14 52,22 24,8 57,10 -21,20 -27,58 -12,5 -25,8 -13,3 -32,3 -19,0 -36,-12 -17,-12 -31,-35 -11,-18 -27,-29 -16,-11 -28,-14 l -11,-1 q -12,0 -16,2 -4,2 -3,7 1,5 5,8 4,3 7,6 l 4,3 q 12,6 24,21 12,15 18,29 l 6,13 q 7,21 24,34 17,13 37,17 20,4 39,3 19,-1 31,-1 l 13,-3 q 0,22 0,50 0,28 1,30 0,10 -8,17 -8,7 -22,4 -129,-43 -211,-155 -82,-112 -82,-252 0,-117 58,-215 58,-98 155,-156 97,-58 216,-58 z m -267,616 q 2,-4 -3,-7 -6,-1 -8,1 -1,4 4,7 5,3 7,-1 z m 18,19 q 4,-3 -1,-9 -6,-5 -9,-2 -4,3 1,9 5,6 9,2 z m 16,25 q 6,-4 0,-11 -4,-7 -9,-3 -5,3 0,10 5,7 9,4 z m 24,23 q 4,-4 -2,-10 -7,-7 -11,-2 -5,5 2,11 6,6 11,1 z m 32,14 q 1,-6 -8,-9 -8,-2 -10,4 -2,6 7,9 8,3 11,-4 z m 35,3 q 0,-7 -10,-6 -9,0 -9,6 0,7 10,6 9,0 9,-6 z m 32,-5 q -1,-7 -10,-5 -9,1 -8,8 1,7 10,4 9,-3 8,-7 z"></path></svg></a></li> <li class="inline-block px-2 duration-300 hover:rotate-6 hover:scale-110 transition-transform"><a href="/rss.xml" title="Subscribe to RSS" class="fill-primary dark:fill-darkprimary track"><svg width="24px" height="24px" viewBox="-10 -10 800 800" version="1.1" aria-label="Rss" role="img" xmlns="http://www.w3.org/2000/svg"><path d="m 107.04167,565.04167 q -45.000003,0 -76.000003,31 -31.00000033,31 -31.00000033,76 0,45 31.00000033,76 31,31 76.000003,31 45,0 76,-31 31,-31 31,-76 0,-45 -31,-76 -31,-31 -76,-31 z m -69.000003,-286 q -15,-1 -26,9 -12.00000033,10 -12.00000033,27 v 75 q 0,14 9.00000003,24 9.0000003,10 23.0000003,11 128.000003,12 219.000003,103 91,91 103,219 1,14 11,23 10,9 24,9 h 75 q 16,0 26,-12 10,-9 10,-24 v -3 q -7,-89 -45,-170 -38,-81 -101,-145 -63,-63 -145,-101 -82,-38 -171.000003,-45 z m -1,-286.0000033 q -15,-1 -26,10 Q 0.04166667,13.041667 0.04166667,29.041667 v 80.000003 q 0,14 10.00000033,25 10,11 23,10 120.000003,7 228.000003,57 108,50 188,129 80,79 129,188 49,109 57,227 1,14 11,24 10,10 24,10 h 80 q 16,0 26,-11 10,-10 10,-25 v -1 q -8,-147 -67,-280 -59,-133 -164,-238 -104,-104 -238,-164.000003 -134,-60.00000033 -280.000003,-67.0000003 z"></path></svg></a></li></ul></footer> <footer class="p-2 sm:p-4 md:p-6 border-zinc-200 border-t text-center lg:text-left bg-white dark:bg-zinc-800 shadow-lg"><label class="w-40 block lg:inline-block mb-2 lg:mb-0 mx-auto uppercase align-middle">Enjoy this blog?</label> <ul class="align-middle block md:inline-block"><li class="inline-block ml-4"><a href="https://www.buymeacoffee.com/andrewlock" target="_blank" rel="noopener" class="track p-0.5 bg-[#E38546] inline-block shadow-md track duration-300 hover:scale-110 rounded-md transition-transform"><img height="38" width="200" src="/assets/img/coffee.png" alt="Buy Me A Coffee" loading="lazy"></a></li> <li class="inline-block ml-4"><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=M5VJREL5PTWNC&amp;currency_code=GBP&amp;source=url" target="_blank" rel="noopener" class="track py-1 px-5 bg-[#5999db] inline-block shadow-md track duration-300 hover:scale-110 rounded-md transition-transform"><img src="/assets/img/paypal.png" width="125" height="33" alt="Donate with PayPal" loading="lazy"></a></li></ul></footer> <nav class="mt-4 lg:grid grid-cols-2 gap-x-4 text-zinc-600 dark:text-zinc-300"><a rel="next" href="/understanding-device-bound-session-credentials/" class="flex mb-4 bg-white dark:bg-zinc-800 duration-300 hover:scale-105 shadow-lg transition-transform"><span class="after:absolute after:block after:h-full after:left-0 after:shadow-inner-xl after:top-0 after:w-full flex-none relative"><img src="/content/images/2026/dbsc_banner.webp" alt="Understanding Device Bound Session Credentials (DBSC)" class="h-[150px] sm:h-[100px] lg:h-[150px] w-[150px] object-cover"></span> <span class="p-2 pl-4"><label class="block text-lg uppercase">Previous</label> <span>Understanding Device Bound Session Credentials (DBSC)</span></span></a></nav> <aside class="giscus p-2 sm:p-4 md:p-6 bg-white dark:bg-zinc-800 shadow-lg dark:text-zinc-300"><noscript>Please enable JavaScript to view the <a href="https://giscus.app/">comments powered by Giscus.</a></noscript>
141<script>(function() {
142var a = { "src": "https://giscus.app/client.js","data-theme": document.documentElement.classList.contains('dark') ? 'dark' : 'light',"data-repo": "andrewlock/blog-comments","data-repo-id": "R_kgDOHvS21g","data-category": "Announcements","data-category-id": "DIC_kwDOHvS21s4CQg24","data-mapping": "pathname","data-strict": "1","data-reactions-enabled": "1","data-emit-metadata": "0","data-input-position": "top","data-lang": "en","data-loading": "lazy","crossorigin": "anonymous","async": "" };
143var g = document.createElement("script");
144Object.entries(a).forEach(function ([key,value]) { g.setAttribute(key,value) });
145document.head.appendChild(g)}) ();</script>
145</aside></section> <aside class="hidden xl:block xl:sticky xl:w-80 2xl:w-96 h-fit"><div class="mb-4 text-xs xl:text-sm text-zinc-600 bg-white dark:text-zinc-300 dark:bg-zinc-800 shadow-lg transition hover:shadow-2xl hover:-translate-y-2"><div class="h-[100px]">
145<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CE7I523N&amp;placement=andrewlocknet" id="_carbonads_js"></script>
145<div role="status" class="animate-pulse flex h-full w-full"><div class="bg-zinc-200 dark:bg-zinc-700 flex-none h-full w-[130px]"></div> <div class="grow p-4"><div class="h-2 bg-zinc-200 rounded-full dark:bg-gray-700 w-5/6 mb-2.5"></div> <div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 w-3/4 mb-2.5"></div> <div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 w-4/5"></div> <span class="sr-only">Loading...</span></div></div></div></div><div class="mb-4 shadow-lg transition hover:shadow-2xl hover:-translate-y-2"><a href="https://dometrain.com/dometrain-pro/?ref=andrew-lock&amp;promo=banner&amp;coupon_code=ANDREW30" target="_blank" class="track" rel="noopener"><img src="/content/images/a/nickchapsas2025.jpg" alt="30% off with code ANDREW30 on Dometrain Pro"></a></div><div class="mb-4 text-zinc-600 bg-white dark:text-zinc-300 dark:bg-zinc-800 shadow-lg transition hover:shadow-2xl hover:-translate-y-2"><a href="http://mng.bz/5mRz" target="_blank" class="track" rel="noopener"><div class="h-48 relative after:absolute after:block after:h-full after:left-0 after:shadow-inner-xl after:top-0 after:w-full"><img src="/content/images/aspnetcoreinaction3e.png" alt="ASP.NET Core in Action, Third Edition" class="h-full w-full object-center object-cover"></div> <p class="p-4"> My new book <em>ASP.NET Core in Action, Third Edition</em> is available now! It supports .NET 7.0, and is available as an eBook or paperback. </p></a></div> <div class="mb-4 p-4 text-zinc-600 bg-white dark:text-zinc-300 dark:bg-zinc-800 shadow-lg"><label class="block mb-2 uppercase">Enjoy this blog?</label> <ul><li class="mb-2"><a href="https://www.buymeacoffee.com/andrewlock" target="_blank" rel="noopener" class="track p-0.5 bg-[#E38546] inline-block shadow-md track duration-300 hover:scale-110 rounded-md transition-transform"><img height="38" width="200" src="/assets/img/coffee.png" alt="Buy Me A Coffee" loading="lazy"></a></li> <li class="mb-2"><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=M5VJREL5PTWNC&amp;currency_code=GBP&amp;source=url" target="_blank" rel="noopener" class="track py-1 px-5 bg-[#5999db] inline-block shadow-md track duration-300 hover:scale-110 rounded-md transition-transform"><img src="/assets/img/paypal.png" width="125" height="33" alt="Donate with PayPal" loading="lazy"></a></li></ul></div><div class="mb-4 p-4 text-zinc-600 bg-white dark:text-zinc-300 dark:bg-zinc-800 shadow-lg prose"><label class="block mb-2 uppercase">Table of Contents</label> <ul class="break-words"><li class="my-0"><a href="#a-brief-introduction-onto-device-bound-session-credentials-dbsc-" class="no-underline font-normal dark:text-darkprimary hover:underline">A brief introduction onto Device Bound Session Credentials (DBSC)</a></li><li class="my-0"><a href="#asp-net-core-s-support-for-dbsc" class="no-underline font-normal dark:text-darkprimary hover:underline">ASP.NET Core's support for DBSC</a></li><li class="my-0"><a href="#using-dbsc-with-asp-net-core-identity" class="no-underline font-normal dark:text-darkprimary hover:underline">Using DBSC with ASP.NET Core Identity</a><ul><li class="my-0"><a href="#creating-the-sample-app" class="no-underline font-normal dark:text-darkprimary hover:underline">Creating the sample app</a></li><li class="my-0"><a href="#adding-the-dbsc-package" class="no-underline font-normal dark:text-darkprimary hover:underline">Adding the DBSC package</a></li><li class="my-0"><a href="#enabling-the-dbsc-flow-for-identity-cookies" class="no-underline font-normal dark:text-darkprimary hover:underline">Enabling the DBSC flow for Identity cookies</a></li></ul></li><li class="my-0"><a href="#exploring-the-dbsc-behaviour-in-asp-net-core" class="no-underline font-normal dark:text-darkprimary hover:underline">Exploring the DBSC behaviour in ASP.NET Core</a><ul><li class="my-0"><a href="#1-behind-the-scenes-registering-multiple-authentication-handlers" class="no-underline font-normal dark:text-darkprimary hover:underline">1. Behind the scenes: registering multiple authentication handlers</a></li><li class="my-0"><a href="#2-returning-the-secure-session-registration-header" class="no-underline font-normal dark:text-darkprimary hover:underline">2. Returning the Secure-Session-Registration header</a></li><li class="my-0"><a href="#3-registration-of-a-dbsc-session" class="no-underline font-normal dark:text-darkprimary hover:underline">3. Registration of a DBSC session</a></li><li class="my-0"><a href="#4-refreshing-the-credentials" class="no-underline font-normal dark:text-darkprimary hover:underline">4. Refreshing the credentials</a></li></ul></li><li class="my-0"><a href="#summary" class="no-underline font-normal dark:text-darkprimary hover:underline">Summary</a></li></ul></div><div class="mb-4 p-4 text-zinc-600 bg-white dark:text-zinc-300 dark:bg-zinc-800 shadow-lg"><label class="block mb-2 uppercase">
145This series</label> <a href="/series/exploring-the-dotnet-11-preview/" class="text-primary hover:text-primary-2 dark:text-darkprimary hover:underline"><span>Exploring the .NET 11 preview</span></a></div></aside></main> <footer class="mb-1 text-center text-xs text-zinc-700 dark:text-zinc-300"> © 2026 Andrew Lock | .NET Escapades. All Rights Reserved. | <a class="underline" href="/credits/">Image credits</a></footer><dialog id="tags-overlay" class="hidden z-20 text-zinc-100 bg-zinc-800 fixed h-full left-0 overflow-y-auto pt-10 text-center top-0 w-full"><form class="mb-8 text-zinc-800"><input id="search-field" class="p-4 rounded-full" type="text" name="search" placeholder="Enter search term..." aria-label="Search blog"></form> <section id="popular-tags" class="hidden max-w-2xl mx-auto" data-searchjs="qsUT9QTLOh40yYfIf4Z3oQ=="><h2 class="leading-tight mb-2 text-xl text-center tracking-tight">Tags</h2> <div class="flex flex-wrap gap-2 mb-4 justify-center tags text-sm uppercase" id="search-tags"></div></section> <section id="search-results" class="text-xl max-w-2xl mx-auto"></section></dialog><div id="subbscribe" class="fixed inline-block right-4 bottom-4 border bg-white dark:bg-zinc-800 dark:text-zinc-300 rounded-md text-zinc-600 transition-transform translate-y-96"><div class="border-b flex justify-between p-2 subb-title"> Andrew Lock | .Net Escapades <button class="close-x ml-4" aria-label="Close"><img src="/assets/img/icons-close.svg" height="14" width="14" class="inline-block" alt="close"></button></div> <div class="flex items-center p-2 subb-body"><img src="/assets/img/icons/apple/apple-touch-icon-180x180.png" class="inline-block rounded-full" width="40" height="40" alt=""> <span class="px-4 py-2">Want an email when<br>there's new posts?</span> <button class="bg-primary flex-none hover:bg-primary2 inline-block px-2 py-1 rounded-md show-form subb-button text-zinc-100">Subscribe</button></div> <div class="p-2 hidden subb-form"><p class="mx-2">Stay up to the date with the latest posts!</p> <form id="subbscribe-form" method="post" action="https://email.andrewlock.net/subscription/form"><div class="hidden mx-2 font-medium text-red-700 subbscribe-error">Oops! Check your details and try again.</div> <div class="hidden mx-2 font-medium text-lime-700 subbscribe-success">Thanks! Check your email for confirmation.</div> <input type="hidden" name="nonce"> <input type="hidden" name="l" value="2811c5dc-505f-418a-90a2-6fec384ee0d9"> <input type="email" name="email" autocomplete="email" id="subb-EMAIL" required placeholder="Email Address" class="bg-zinc-200 dark:bg-zinc-900 rounded-md block m-2 px-2"> <input type="text" name="name" autocomplete="name" id="subb-NAME" maxlength="250" placeholder="Name (Optional)" class="bg-zinc-200 dark:bg-zinc-900 rounded-md block m-2 px-2"> <div id="hcaptchatarget" class="h-captcha" data-sitekey="972cb060-e84e-476e-a4a6-770e02e69dbc"></div> <button id="subb-SUBMIT" class="bg-primary hover:bg-primary2 m-2 px-2 py-1 rounded-md subb-button submit-form text-zinc-100">Subscribe</button></form></div></div><!-- Cloudflare Pages Analytics -->
vendor: 99 bytes, line 145
145<script defer src='https://static.cloudflareinsights.com/beacon.min.js' data-cf-beacon='{"token": "
145bfc06bebadf94336ba070f88c1539b74
vendor: 13 bytes, line 145
145"}'></script>
145<!-- Cloudflare Pages Analytics --></body></html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.