<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Steinars blog on Digital Identity, BBQ and other things he finds meaningful]]></title><description><![CDATA[I am Steinar. I work as a consultant for Udelt AS.
I specialize in digital identity, and I feel a genuine love for the standardized authentication protocols OAu]]></description><link>https://noem.blog</link><generator>RSS for Node</generator><lastBuildDate>Mon, 27 Apr 2026 16:26:15 GMT</lastBuildDate><atom:link href="https://noem.blog/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Why is it so difficult to share health information?]]></title><description><![CDATA[I am a consultant, and I mainly do work for the public sector in Norway. For six years my work has primarily been focused on digital identity, authenticating health personell in particular. But, during this time I have also been involved in solving a...]]></description><link>https://noem.blog/sharing-health-information</link><guid isPermaLink="true">https://noem.blog/sharing-health-information</guid><dc:creator><![CDATA[Steinar Noem]]></dc:creator><pubDate>Tue, 14 Sep 2021 09:32:13 GMT</pubDate><content:encoded><![CDATA[<p>I am a consultant, and I mainly do work for the public sector in Norway. For six years my work has primarily been focused on digital identity, authenticating health personell in particular. But, during this time I have also been involved in solving an interestingly tricky problem: </p>
<blockquote>
<p><strong>How can we effectively share health information between different health personell in different organizations?</strong></p>
</blockquote>
<p>There are obviously several technical answers to this question. Some of the answers are seemingly both feasible, effective and a good idea - until the next big thing comes along. Perhaps some day we will save everything in an unstructured blob and have an AI sort things out. Can't wait for that one.. </p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=ARJ8cAGm6JE">https://www.youtube.com/watch?v=ARJ8cAGm6JE</a></div>
<p>On the other hand, the underlying complexity of health information adds to the problem. Knowing which information is relevant to share at different times is not easy. Some types of health information are snapshots in time and totally irrelevant in three weeks, while other types of information are important pieces of the puzzle that is needed to give the patient the correct treatment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1631600150431/RTOvrJKAvQ.jpeg" alt="hasan-almasi.jpg" /></p>
<h4 id="the-complexity-of-complex-diseases">The complexity of complex diseases</h4>
<p>Statistics show that patients with <a target="_blank" href="https://en.wikipedia.org/wiki/Comorbidity">comorbidity</a> or <a target="_blank" href="https://en.wikipedia.org/wiki/Multimorbidity">multimorbidity</a> accounts for a disproportionately large part of the total amount of care given by the health care system in Norway.</p>
<blockquote>
<p>Some estimates show that roughly 80% of all the resources are spent on about 20% of the patients, who are typically co- or multimorbid.</p>
</blockquote>
<p>These co- or mulitimorbid patients often use several health care providers. This greatly increases the need for enabling the sharing of health information.
It also adds a load to the allready stated problem, because some of these patients have <em>large</em> amounts of data distributed across the different organizations. In some cases more than 30.000 documents. Searching and choosing the relevant pieces of information in those cases is tricky - at best.</p>
<h3 id="my-semi-humble-opinion">My semi-humble opinion</h3>
<p>The fundamental problem doesn't lie within the ones and zeros, nor the complexity of health information. </p>
<p><strong>It lies in the murky world of <em>trust</em></strong> : The governing laws and juridical system, the responsibilities, the agreements, the knowledge about how processes and routines are carried out in the organizations that communicate with each other. The security measures and mechanisms that are in play, and how securely the systems and interfaces are designed, built and deployed etc.</p>
<p>Without trust between the different actors we won't be able to share a single thing. Regardless of the technical solutions or our capability to acheive semantical interoperability.</p>
<p>In other words: the answer to the question is about as <em>clear as mud</em>. So for this blog post I'll explain how I perceive <em>trust</em>, and why I believe trust to be the most fundamental issue that needs to be solved before we are able to effectively share health information between different actors in Norway. </p>
<p><em>How</em> trust can be modeled and built is material for a later post...</p>
<h1 id="trust-you-say">Trust you say?</h1>
<h2 id="what-is-trust-anyway">What is trust anyway?</h2>
<p>This is one of my favorite quotes - allegedly from E. Hemingway:</p>
<blockquote>
<p>The best way to find out if you can trust somebody is to trust them</p>
</blockquote>
<p>I interpret this quote to mean that <em>trust is not a binary thing</em>, it is a <strong>decision</strong>.</p>
<p>And where there is need for a decision, there is <strong>always underlying <em>uncertainty</em> </strong>.</p>
<p>Because, when there is no uncertainty you don't really need to make a decision. In other words: when all the facts are known, you don't need to decide anything. The decision is given.</p>
<p>The caveat is that you won't know wether the decision was good or bad until you gain some experience with the party you've decided to trust. It's possible that you'll make the wrong decision, but you won't know until you've tried.</p>
<p>So, to trust something or someone is a decision that is based on facts you have about the party you need to trust, in addition to previous experiences you or others have had dealing with the same party.</p>
<h1 id="the-health-sector-in-norway-a-primer">The health sector in Norway - a primer</h1>
<p>Since the title of this blog post contains the words <em>health information</em>, I think it is time to give some context. </p>
<p>But first a true story from my life.</p>
<h3 id="shut-up-and-give-me-the-morphine">Shut up and give me the morphine!</h3>
<p>I experienced how health care systems don't talk to each other when I suffered an acute and intense back-pain last year. A suprisingly effective lack of systems interoperability forced me to tell my story over and over again - which is rarely a good thing unless the story is really funny or interesting. In my case the story could be summed up in these three words: "give me morphine".</p>
<h2 id="my-close-encounter">My close encounter</h2>
<p>Here is a concise recount of what happened after about half an hour of intense pain and nearly shitting my pants:</p>
<ol>
<li>Call 911 (113 in Norway)</li>
<li>Ambulance arrives with health personel</li>
<li>Tell ambulance personnel what's wrong</li>
<li>Get morphine</li>
<li>Transported to emergency</li>
<li>Repeat what's wrong to the emergency doctor</li>
<li>Transferred to hospital (in the same building)</li>
<li>Repeat what's wrong to the hospital doctor</li>
<li>Repeat what's wrong to other hospital doctor</li>
<li>Get more morphine until able to stand and walk</li>
</ol>
<p>Interestingly, there were three different EHR systems into play (sic!) and none of them were interested in speaking with each other. </p>
<p>I would have preferred that they had trusted each other more...</p>
<h3 id="health-sector-vs-the-law">Health sector vs. <em>the law</em></h3>
<p>Since health information is personal data and is considered sensitive, all health care providers with EHR systems are responsible for processing health information.</p>
<h4 id="counteracting-legislation-or-a-perfect-mix">Counteracting legislation or a perfect mix?</h4>
<p>The health care providers in Norway actually have three layers of law to understand and to comply with:</p>
<ul>
<li>EU (GDPR)</li>
<li>Public management legislation</li>
<li>Health care legislation</li>
</ul>
<p>With GDPR in play, the organizations have two good reasons to keep the health information safe and secured: </p>
<ul>
<li>Loosing it or giving access to the wrong people or organizations might be very expensive.</li>
<li>They might loose the trust of their patients if it becomes known that their most sensitive information has been accessed by someone that might use it for other purposes than treatment (e.g. an employer, an insurance company etc).</li>
</ul>
<p>On the other side of the coin, the health care legislation actually require the organizations to give health personnel access to patient health records when they are needed for treatment, regardless of wether they work within the same organization or for a different organization.</p>
<p>The organizations are obligated to share health information, but they better not share it with someone who doesn't need it for treatment of a patient.</p>
<h3 id="what-is-mine-is-yours-or-was-it-the-other-way-around">What is mine is yours - or was it the other way around?</h3>
<p>In Norway all health care services are <strong>free of charge</strong>, which is damned phantastic. Come join us if you want great health services, enjoy paying high taxes, crave lousy weather and good looking swedish waitors. <a target="_blank" href="https://www.udi.no/en/want-to-apply/">Here is the link</a> to where you apply for recidency. And <a target="_blank" href="https://www.finn.no/job/browse.html">this is where to find a job</a>. You will be greeted with lapskaus and boller og brus. I am certain that you will love it here!</p>
<p>Ok, so <em>free of charge</em> is perhaps not entirely correct.. Most health services are publicly funded, which actually means that we pay for it through taxes. I still think the concept is pretty awesome though, and I do pay my taxes with pride - knowing that one day I will probably be on the receiving end.</p>
<h3 id="where-would-you-like-to-have-your-vasectomy-done-sir">Where would you like to have your vasectomy done, sir?</h3>
<p>All inhabitants of Norway have several rights that grant them freedom to choose certain aspects of their use of the health care sector:</p>
<ul>
<li><p>The general practitioner scheme (fastlegeordningen):
Every inhabitant has the right to have a GP, and the municipalities are responsible for providing their inhabitants with their own personal doctor-person. I highly reccommend mine.</p>
</li>
<li><p>The right to choose the place of treatment (fritt behandlingsvalg):
If you have heard about a hospital somewhere far from where you live that has prettier nurses and better doctors than your local hospital, and serve cold beers and bbq for lunch you can choose to be treated there.</p>
</li>
</ul>
<p>These are great features, but these rights also require health information to flow between different businesses, which again requires the systems to interoperate.</p>
<h3 id="the-health-regions-and-categories">The health regions and categories</h3>
<p>The public health sector in Norway is split into four health regions: </p>
<ul>
<li>the northern region (aka Mordor)</li>
<li>the middle-part-of-norway region (aka "the best region in the country")</li>
<li>the western-part-of-norway region</li>
<li>the southern-and-eastern-part-of-norway region.</li>
</ul>
<p>Since almost no people (with their wits intact) choose to live outside of Oslo in Norway, the southern-and-eastern-part-of-norway region is larger than the other regions combined. </p>
<p>These four regions provide services that are divided into two main categories:</p>
<ul>
<li>The specialist health care services - basically the hospitals</li>
<li>The primary health care services - where you'll find the GPs and nursing and care, etc.</li>
</ul>
<p>If you are both healthy and lucky you'll never get any value back from the taxes you've paid. If you are like most people though, you get to enjoy the services from two or more of these providers several times during your life.</p>
<h3 id="the-business-model">The business model</h3>
<p>All of these providers are organized as individual organizations/companys. Some of them are subsidiaries of the regional organization, others are subsidiaries of municipalities, and others again are independent and privately held.</p>
<p>Most/all of their funding comes sizzling down in steady streams through the fat governmental money pipeline, singing "do-wah-diddy-diddy-dum-diddy-do".</p>
<p>This is the same money pipeline where most of my salary comes from. Which is why I almost feel religious talking about it. It's beauty is absolutely mezmerising.</p>
<h3 id="the-system-landscape">The system landscape</h3>
<h4 id="national-systems-and-infrastructure">National systems and infrastructure</h4>
<p>We have some national systems in different flavours. We also have other national stuff that is useful, like common infrastructural components, a dedicated separate membership-based network called "Helsenettet", a dedicated CERT for the sector and a common national <em>code of conduct</em>. </p>
<h4 id="local-and-regional-ehr-systems">Local and regional EHR systems</h4>
<p>The health information is largely produced and stored in the local or regional EHR systems, and is seldom shared across organizational boundarys.</p>
<p>Every health care provider is responsible for providing their health personnel with suitable <a target="_blank" href="https://en.wikipedia.org/wiki/Electronic_health_record">EHR</a> systems, where they are required to register relevant and neccessary information about the patients they treat and details of the treatment they are given.
Every organization choose their own system. And, these systems can be quite different from each other depending on the type of health services they provide.</p>
<h4 id="do-systems-have-personality-traits">Do systems have personality traits?</h4>
<p>The local systems are quite introvert. They just really like to keep to themselves, and rarely talk to others. There are several reasons for this, many of which are historical: most of these systems were built at a time where it actually was illegal to share health information outside of the business boundaries - unless there were very good reasons for doing so. </p>
<h4 id="manual-vs-automatic">Manual vs Automatic</h4>
<p>Todays practice of sharing health information is mainly done through messaging or by some physical medium. This practice has an advantage that at the same time is a great disadvantage: it requires manual processing - someone has to do something and/or make a decicion.
This obviously doesn't scale..</p>
<p>Instead, we want to be able to search and request information across organizational boundaries automatically, e.g. by having the EHR systems consume APIs.</p>
<p>I know.. This isn't exactly rocket science. We've had web based APIs for decades, but when it comes to GDPR and the health legislation there isn't a whole lot of room for error. And the rules and laws are not easy to understand when you put them into the perspective of sharing health information.</p>
<h1 id="deciding-to-mistrust">Deciding to mistrust</h1>
<p>To sum up why I think trust is the most fundamental issue to solve I have hand crafted a numbered list containing a few of the issues that lead to mistrust.</p>
<ol>
<li>Who bears the responsibility if health information is stolen? The delivering part or the consuming part? Here the law is as unclear and interpretable as it probably should be.</li>
<li>The organizations choose to not trust each other. Why? Don't the other organizations know what they are doing?</li>
<li>With what precision should we authenticate the health personnel?</li>
<li>With what precision should we authenticate the organizations?</li>
<li>Some health personnel might be up to no good, how can we stop them before they snoop around in their neighbors health records?</li>
<li>Some health personnel might be up to no good, how can we detect that they have snooped around in their neighbors health records?</li>
<li>Which doubious security mechanisms are in play? Perhaps the guys who consume  services are using nOAuth or nOIDC? noTLS? Poorly implemented authentication? Home grown crypto algorithm/crypto libraries? Home grown PKI?</li>
<li>Are the health personnel using poorly implemented or really old EHR systems leaking like a tea strainer?</li>
</ol>
<p>There are individual answers to all of these questions. And yes, some of the answers can be combined to form what is referred to as a common "trust framework". But it often turns out that the obvious answers aren't as obvoius as one might assume.</p>
<p>An example is eID for physical and legal entities. What if the requirement is some ridiculously high LoA, but the user is authenticated through one or several federation gateways. What happes with the LoA after five hops through five different gateways? Does the identity still comply with the LoA? Do all these gateways take their security equally serious?</p>
<h1 id="goodbye">Goodbye</h1>
<p>Well, it was good to finally get this off my chest. I plan to write about how trust can be modeled and realized in a future post. </p>
<p>In the meantime, have a great day and thank you for reading!</p>
]]></content:encoded></item><item><title><![CDATA[DPoP #3: Implementing DPoP client side with javascript]]></title><description><![CDATA[Part 3: A mediocre programmer tries something new
In the two previous posts in my DPoP blog series i looked a little closer at the DPoP specification, and implemented the Authorization Server side of the spec using Duende IdentityServer.
In this post...]]></description><link>https://noem.blog/3-implementing-dpop-client-side-with-javascript</link><guid isPermaLink="true">https://noem.blog/3-implementing-dpop-client-side-with-javascript</guid><category><![CDATA[oauth]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Steinar Noem]]></dc:creator><pubDate>Mon, 16 Aug 2021 20:31:27 GMT</pubDate><content:encoded><![CDATA[<h1 id="part-3-a-mediocre-programmer-tries-something-new">Part 3: A mediocre programmer tries something new</h1>
<p>In the two previous posts in my DPoP blog series i <a target="_blank" href="https://noem.blog/1-sender-constraining-oauth-access-tokens-with-dpop">looked a little closer</a> at the <a target="_blank" href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop-03">DPoP specification</a>, and <a target="_blank" href="https://noem.blog/2-implementing-dpop-in-duende-identityserver">implemented the Authorization Server side of the spec</a> using <a target="_blank" href="https://duendesoftware.com/products/identityserver">Duende IdentityServer</a>.</p>
<p>In this post I will describe how things went when I implemented a javascript client that uses the DPoP mechanism.</p>
<p>How did it go, you ask? </p>
<p>Well, to quote Colonel Kurtz in Apocolypse Now: <em>"The horror.. The horror.."</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629106123550/jOOuw6b5U.jpeg" alt="alex-iby-HeFtpMLUscE-unsplash.jpg" /></p>
<h2 id="source-code-and-a-demo">Source code and a demo</h2>
<p>I'm not really sure that sharing my code for this blog post is a good idea for my future career.. But I'll take my chances. Before you proceed, just a quick word of advice: <em>Don't use my code as a reference for any production system :)</em>. I know I wouldn't.</p>
<p>With that said, you'll find the source code for my client implementation here: https://github.com/udelt/dpop_js_test</p>
<p>And you'll find a demo of the client here: https://dpoptest.z1.web.core.windows.net/index.htm.</p>
<h4 id="patience-is-a-virtue">Patience is a virtue</h4>
<p>The Authorization Server and the API for the demo runs on "cold" Azure instances, and will take a moment to load if it hasn't been accessed in a while. So, you might need to be patient while waiting for a response from the Duende IdentityServer and the API in the DPoP client demo.</p>
<h2 id="developing-a-dpop-enabled-client">Developing a DPoP enabled client</h2>
<p>I haven't coded any javascript in many years, so I decided that this was a great opportunity to catch up. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629105834051/EyiGEqaEm.jpeg" alt="fabrizio-conti-EJQlF0GIvq8-unsplash.jpg" /></p>
<h3 id="initial-attempts">Initial attempts</h3>
<ul>
<li>I first looked at Filip Skokan's POC implementation of DPoP - aaaaand.. I didn't understand a thing...</li>
<li>I then looked at OIDC/OAuth client libraries - aaaaaand... I didn't understand a thing </li>
</ul>
<p>Bummer.. I quickly realized that I needed to focus on learning some javascript, so I started reading the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide">Mozilla documentation pages</a> to understand some of the new concepts in javascript.</p>
<h3 id="i-never-ride-on-the-same-day-that-i-saddle">I never ride on the same day that I saddle....</h3>
<p>To make a long story short, I ended up spending a lot more time on learning javascript than I anticipated. I'm not convinced that this was a great investment since I really don't do any work developing javascript applications. Well - I'm sure I'll have no trouble quickly unlearning what I've just learned. </p>
<h3 id="keeping-it-simple">Keeping it <em>simple</em></h3>
<p>After my intense javascript crash-course I was running extremely low on self-esteem, and decided on the following:</p>
<ul>
<li>Steal what I can from Filip Skokan</li>
<li>No frameworks - just pure javascript (ES6)</li>
<li>No existing OAuth/OIDC client side library</li>
<li>Only use a simple OAuth flow: client_credentials</li>
<li>There will be no client secret</li>
<li>There will be no PKCE</li>
<li>I'll spend no time on responsive html</li>
<li>Using Node as development server</li>
<li>Deploy the client to Azure Storage (Static Web Site)</li>
<li>Using Visual Studio Code as IDE</li>
<li>Only test on chromium based browsers</li>
<li>All crypto mechanisms using the Web Crypto API in the browser</li>
</ul>
<h2 id="the-demo">The demo</h2>
<p>Welcome to the demo!</p>
<p>Herein lies the most central pieces of the client demo code.</p>
<h3 id="generating-the-key-material">Generating the key material</h3>
<p>The first step in the DPoP flow is to generate the neccessary key material.</p>
<p>The key is used to sign the DPoP proof, and the public key will be transferred to the AS in the jose header in a <em>jwk</em> structure.</p>
<p>A friendly expert pointed out to me that there is no need to export the private key. I've now set the <em>extractable</em> parameter to <em>false</em>. Now, in case of an attack, the key material is kept safe. This tip will be rewarded with a cold beer the next time we meet :)</p>
<p>This job was easily acheived with the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto">SubtleCrypto interface</a>.</p>
<p>In this example the algorithm and curve is hard coded, but it is easy to extend with support for other algorithms.</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateKey</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">var</span> key = <span class="hljs-keyword">await</span> crypto.subtle.generateKey({
            <span class="hljs-attr">name</span>: <span class="hljs-string">"ECDSA"</span>,
            <span class="hljs-attr">namedCurve</span>: <span class="hljs-string">"P-384"</span>
        }, <span class="hljs-literal">false</span>, [<span class="hljs-string">"sign"</span>, <span class="hljs-string">"verify"</span>])
        .then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">eckey</span>) </span>{           
            <span class="hljs-keyword">return</span> eckey;
        })
        .catch(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err</span>) </span>{
            <span class="hljs-built_in">console</span>.error(err);
        });
    <span class="hljs-keyword">return</span> key;
}
</code></pre><h3 id="creating-the-dpop-proof">Creating the DPoP proof</h3>
<p>When the key is generated we can use it to create the DPoP proof that the client presents to the Authorization Server.</p>
<p>This code example basically shows the final part of creating the DPoP proof, and I've more or less copied it directly from Filip Skokans DPoP POC.</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createDpopProof</span>(<span class="hljs-params">privateKey, header, payload</span>) </span>{
    <span class="hljs-keyword">const</span> p = <span class="hljs-built_in">JSON</span>.stringify(payload);
    <span class="hljs-keyword">const</span> h = <span class="hljs-built_in">JSON</span>.stringify(header);

    <span class="hljs-keyword">const</span> partialToken = [
        Base64Url.ToBase64Url(Base64Url.utf8ToUint8Array(h)),
        Base64Url.ToBase64Url(Base64Url.utf8ToUint8Array(p)),
    ].join(<span class="hljs-string">"."</span>);

    <span class="hljs-keyword">const</span> messageAsUint8Array = Base64Url.utf8ToUint8Array(partialToken);

    <span class="hljs-keyword">var</span> signatureAsBase64 = <span class="hljs-keyword">await</span> Crypto.Sign(privateKey, messageAsUint8Array);

    <span class="hljs-keyword">var</span> token = <span class="hljs-string">`<span class="hljs-subst">${partialToken}</span>.<span class="hljs-subst">${signatureAsBase64}</span>`</span>;

    <span class="hljs-keyword">return</span> token;        
}
</code></pre><p>The signature is generated by using the SubtleCrypto interface, which felt quite intuitive. The part that felt a bit weird was the Base64Url formatting, and the lack of conversion support in the browser.</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Sign</span>(<span class="hljs-params">privateKey, messageAsUint8Array</span>)</span>{
    <span class="hljs-keyword">var</span> signature = <span class="hljs-keyword">await</span> crypto.subtle.sign({
            <span class="hljs-attr">name</span>: <span class="hljs-string">"ECDSA"</span>,
            <span class="hljs-attr">hash</span>: { <span class="hljs-attr">name</span>: <span class="hljs-string">"SHA-256"</span> },
            },
            privateKey,
            messageAsUint8Array)
        .then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">signature</span>) </span>{
            <span class="hljs-keyword">const</span> signatureAsBase64 = Base64.ToBase64Url(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Uint8Array</span>(signature));
            <span class="hljs-keyword">return</span> signatureAsBase64;
        })
        .catch(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err</span>)</span>{
            <span class="hljs-built_in">console</span>.log(err);
            <span class="hljs-keyword">throw</span>(err);
        });
    <span class="hljs-keyword">return</span> signature;
};
</code></pre><h3 id="requesting-the-access-token-with-the-dpop-proof">Requesting the Access Token with the DPoP proof</h3>
<p>The token request code is so simplified that it isn't really useful for other things than showing how the DPoP proof is transferred via the http header. </p>
<p>Oh, almost forgot: since you're an avid CSP champion, you'll be able to imagine that I added the url of the AS in the CSP meta tag.</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAccessToken</span>(<span class="hljs-params">url, dpopProof</span>) </span>{

    <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> fetch(url, {
            <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
            <span class="hljs-attr">headers</span>: {
                <span class="hljs-string">'DPOP'</span>: dpopProof,
                <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/x-www-form-urlencoded'</span>
            },
            <span class="hljs-attr">body</span>: <span class="hljs-string">'client_id=client&amp;grant_type=client_credentials'</span>
        })
        .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
            <span class="hljs-keyword">return</span> response.text();
        })
        .catch(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">error</span>) </span>{
            <span class="hljs-built_in">console</span>.log(error);
        });

        <span class="hljs-keyword">var</span> jsonResult = <span class="hljs-built_in">JSON</span>.parse(result);
        <span class="hljs-keyword">var</span> token = jsonResult.access_token;        
        <span class="hljs-keyword">return</span> token;
}
</code></pre><h3 id="generating-the-the-access-token-hash-for-the-new-dpop-proof">Generating the the Access Token hash for the  new DPoP proof</h3>
<p>When the client receives the AccessToken we can calculate it's hash value, and include that in the new DPoP proof that the client will present to the API.</p>
<p>Still using the SubtleCrypto interface. Also, still quite easy..</p>
<p>And the spec says no padding, so i removed the padding.</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createHash</span>(<span class="hljs-params">accessToken, noPadding = false</span>)</span>{
    <span class="hljs-keyword">var</span> encodedAT = <span class="hljs-keyword">new</span> TextEncoder().encode(accessToken);
    <span class="hljs-keyword">var</span> atHash = <span class="hljs-keyword">await</span> crypto.subtle.digest(<span class="hljs-string">'SHA-256'</span>, encodedAT)
    .then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">hash</span>) </span>{        
        <span class="hljs-keyword">var</span> base = Base64.ToBase64Url(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Uint8Array</span>(hash));
        <span class="hljs-keyword">if</span> (noPadding){
            base = base.replace(<span class="hljs-regexp">/\=+$/</span>, <span class="hljs-string">''</span>);
        }    
        <span class="hljs-keyword">return</span> base;
    })
    .catch(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err</span>)</span>{
        <span class="hljs-built_in">console</span>.log(err);
        <span class="hljs-keyword">throw</span> err;
    });
    <span class="hljs-keyword">return</span> atHash;
}
</code></pre><h3 id="generating-the-dpop-proof-for-the-api">Generating the DPoP proof for the API</h3>
<p>The next step is to generate the DPoP proof that the client will present to the API. 
Also quite easily acheived with javascript. Actually, I think that this is one of the times during coding where I felt that javascript had a certain <em>shine</em> to it..</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createDpopProof</span>(<span class="hljs-params">atHash, jwk, key, resourceUrl</span>) </span>{
    <span class="hljs-keyword">var</span> dpopProof = {
        <span class="hljs-attr">key</span>: <span class="hljs-literal">undefined</span>,
        <span class="hljs-attr">jwk</span>: <span class="hljs-literal">undefined</span>,
        <span class="hljs-attr">thumbprint</span>: <span class="hljs-literal">undefined</span>
    };

    <span class="hljs-keyword">var</span> dpop_proof_payload = {
        <span class="hljs-attr">jti</span>: <span class="hljs-keyword">await</span> uuid.generate,
        <span class="hljs-attr">htm</span>: <span class="hljs-string">"POST"</span>,
        <span class="hljs-attr">htu</span>: resourceUrl,
        <span class="hljs-attr">iat</span>: <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Date</span>.now() / <span class="hljs-number">1000</span>)
    };

    <span class="hljs-keyword">if</span> (atHash)
        dpop_proof_payload[<span class="hljs-string">"ath"</span>] = atHash;

    <span class="hljs-keyword">var</span> header = {
        <span class="hljs-attr">typ</span>: <span class="hljs-string">"dpop+jwt"</span>,
        <span class="hljs-attr">alg</span>: <span class="hljs-string">"ES256"</span>,
        <span class="hljs-attr">jwk</span>: <span class="hljs-literal">undefined</span>
    };

    <span class="hljs-keyword">if</span> (!key){
        key = <span class="hljs-keyword">await</span> cryptoModule.generateKey();
    }

    <span class="hljs-keyword">if</span> (!jwk){
        jwk = <span class="hljs-keyword">await</span> cryptoModule.exportJwk(key.publicKey);
        <span class="hljs-keyword">delete</span> jwk.ext;
        <span class="hljs-keyword">delete</span> jwk.key_ops;
    }        

    header.jwk = jwk;

    <span class="hljs-keyword">var</span> dpopProof = <span class="hljs-keyword">await</span> Jwt.create(key.privateKey, header, dpop_proof_payload);

    <span class="hljs-keyword">return</span> { <span class="hljs-attr">dpopProof</span>: dpopProof, <span class="hljs-attr">key</span>: key, <span class="hljs-attr">jwk</span>: jwk};

}
</code></pre><h3 id="requesting-the-api-resource-with-the-new-dpop-proof-and-access-token">Requesting the API resource with the new DPoP proof and Access Token</h3>
<p>The final part is where it's all brought together =&gt; requesting the resource at the API.</p>
<p>This code example really only shows how easily the tokens are added to the http header. You don't really have to read it if you think it's boring.</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">callAPI</span>(<span class="hljs-params">accessToken, dpopProof</span>) </span>{
    <span class="hljs-keyword">var</span> url = <span class="hljs-string">"https://dpoptestapi.azurewebsites.net/DPoP"</span>;
    <span class="hljs-keyword">var</span> response = <span class="hljs-keyword">await</span> fetch(url, {
            <span class="hljs-attr">method</span>: <span class="hljs-string">'GET'</span>,
            <span class="hljs-attr">headers</span>: {
                <span class="hljs-string">'DPOP'</span>: dpopProof,
                <span class="hljs-string">'Authorization'</span>: <span class="hljs-string">`DPOP <span class="hljs-subst">${accessToken}</span>`</span>,
                <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>
            },            
        })
        .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
            <span class="hljs-keyword">var</span> json = response.json();            
            <span class="hljs-keyword">return</span> json;
        })
        .catch(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err</span>)</span>{
            <span class="hljs-built_in">console</span>.error(err);
        });
        <span class="hljs-keyword">return</span> response;
}
</code></pre><h2 id="my-conclusion">My conclusion</h2>
<p>Regardless of my lacking experience and competence in Javascript I was able to implement the DPoP mechanism, although perhaps not without effort.. But, for a skilled javascript programmer I would assume that it is actually quite easy.</p>
<h4 id="not-only-for-browser-apps-public-apps">Not only for browser apps (public apps)?</h4>
<p>As mentioned in my <a target="_blank" href="https://noem.blog/1-sender-constraining-oauth-access-tokens-with-dpop">first blog post in this series</a>, DPoP is an alternative to the existing mTLS mechanism.</p>
<p>Choosing between sender constraining token with mTLS or on the application  layer by using DPoP (or similar) boils down to the ecosystems, environments and infrastructures that the apps run in. As mentioned in part 1 of this series, there are some pitfalls that could make mTLS challenging to use, and for these scenarios DPoP definitely is a viable option.</p>
<p>With that in mind, I really think that the DPoP mechanism is promising for other client types than browser based apps, e.g. mobile and desktop apps.</p>
<p>I would love to see <em>sender constraining</em> of tokens become a default behaviour in OAuth flows, where the choice of using mTLS or DPoP would be more or less transparent for the developer? (E.g: <em>if no TLS client cert is available, then run with DPoP</em>)</p>
<h4 id="securing-the-security-mechanism">Securing the security mechanism</h4>
<p>While sender constraining tokens with DPoP is a mechanism that protects the protocol flow itself, the threats for the client (browser) are unchanged. In other words: if the client is compromised, you are still in big trouble.</p>
<p>So, the difficult questions remain:</p>
<ul>
<li>How do you keep secrets safe in the browser?</li>
<li>And, how do you protect your browser app against attacks?</li>
</ul>
<p>There are, however, some obvious advantages of DPoP: </p>
<ul>
<li>The secrets (key material) can have a short lifespan</li>
<li>DPoP proofs can have a short life span</li>
<li>The spec is relatively simple to implement</li>
</ul>
<h4 id="wrapping-it-up">Wrapping it up</h4>
<p>While previous attempts (like the <a target="_blank" href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-token-binding-08">token-binding specification</a>) came to a halt, there is some reason to hope that the DPoP spec receives enough attention and that the IETF is able to finalize the specification.</p>
<p>I really hope that the IETF is able to keep the simplicity of the mechanism intact, and that they agree that it complements mTLS in a way that is beneficial to apps that aren't easily able to use client certificates.</p>
<p>Since you might be too lazy to scroll all the way up to the top of the page, I've added a link to the source code and the demo page here at the bottom as well:</p>
<p>You'll find the source code for my client implementation here: https://github.com/udelt/dpop_js_test</p>
<p>And you'll find a demo of the client here: https://dpoptest.z1.web.core.windows.net/index.htm</p>
]]></content:encoded></item><item><title><![CDATA[DPoP #2: Implementing DPoP in Duende IdentityServer]]></title><description><![CDATA[In this second post of the series of three posts I will write about my endeavors to implement the DPoP draft from the IETF OAuth WG. 
In my first post i tried to give a high level description of how the mechanism works.
Possible  abstract for this po...]]></description><link>https://noem.blog/2-implementing-dpop-in-duende-identityserver</link><guid isPermaLink="true">https://noem.blog/2-implementing-dpop-in-duende-identityserver</guid><category><![CDATA[oauth]]></category><dc:creator><![CDATA[Steinar Noem]]></dc:creator><pubDate>Tue, 10 Aug 2021 13:43:02 GMT</pubDate><content:encoded><![CDATA[<p>In this second post of the series of three posts I will write about my endeavors to implement the <a target="_blank" href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop">DPoP draft from the IETF OAuth WG</a>. </p>
<p>In my <a target="_blank" href="https://noem.blog/1-sender-constraining-oauth-access-tokens-with-dpop">first post</a> i tried to give a high level description of how the mechanism works.</p>
<p>Possible  abstract for this post: Not so super programming skills vs IETF OAuth draft, part 1.</p>
<h2 id="duende-identityserver">Duende IdentityServer</h2>
<p>I wanted to test the mechanism. So, my first task was to implement DPoP in an Authorization Server. </p>
<p>Building an AS from scratch was definitely out of scope, so I decided to use the Duende IdentityServer as a starting point. 
<a target="_blank" href="https://duendesoftware.com/">Duende IdentityServer</a> is an implementation of OpenID Connect. It should be considered as more of a core framework than a complete product.
Now, I might not be totally unpartial, but believe me when I say that the framework is easy to work with - just plainly damned well written - and it has an appetite for extensions.</p>
<h3 id="keeping-it-simple">Keeping it simple</h3>
<p>The specification describes the following</p>
<blockquote>
<p>To request an access token that is bound to a public key using DPoP,
   the client MUST provide a valid DPoP proof JWT in a "DPoP" header
   when making an access token request to the authorization server's
   token endpoint.  This is applicable for all access token requests
   regardless of grant type (including, for example, the common
   "authorization_code" and "refresh_token" grant types but also
   extension grants such as the JWT authorization grant [RFC7523]).</p>
</blockquote>
<p>I interpreted this part quite literally, and aimed for the simplest possible solution:</p>
<ul>
<li>only testing client_credentials grant/flow</li>
<li>no refresh token support</li>
<li>not caring if I break existing flows (e.g. mTLS)</li>
</ul>
<h2 id="the-token-endpoint">The token endpoint</h2>
<p>I started by extending Duende IdentityServer with a DPoP validator class, and added it to the token endpoint. </p>
<pre><code><span class="hljs-keyword">var</span> dpopResult = <span class="hljs-keyword">await</span> _dpopValidator.ValidateAsync(context);

<span class="hljs-keyword">if</span> (dpopResult.IsError)
{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid_dpop_proof"</span>); <span class="hljs-comment">//<span class="hljs-doctag">TODO:</span> conform to spec</span>
}
</code></pre><h3 id="the-thumbprint-of-the-public-key-in-the-dpop-proof">The thumbprint of the public key in the DPoP proof</h3>
<p>I want Duende IdentityServer to include the thumbprint of the public key in the DPoP proof in accordance with the specification. The goal is to enable the API to verify that the jwk in the DPoP proof that it receives from its client is the same that was used when the client requested the Access Token.</p>
<pre><code><span class="hljs-string">if</span> <span class="hljs-string">(dpopResult</span> <span class="hljs-type">!=</span> <span class="hljs-literal">null</span><span class="hljs-string">)</span>
{
    <span class="hljs-string">if</span> <span class="hljs-string">(dpopResult.ValidatedDpopProof.ThumbprintBase64Url</span> <span class="hljs-type">!=</span> <span class="hljs-literal">null</span><span class="hljs-string">)</span>
    {
         <span class="hljs-string">requestResult.ValidatedRequest.DPoPThumbprint</span> <span class="hljs-string">=</span> <span class="hljs-string">dpopResult.ValidatedDpopProof.ThumbprintBase64Url;</span>
     }
}
</code></pre><h3 id="the-dpop-validator">The DPoP validator</h3>
<p>My DPoP validator interface has one method that needs to be implemented. This stuff is a job for people that actually know what they're doing (e.g. <a target="_blank" href="https://twitter.com/leastprivilege">Dominick</a> and <a target="_blank" href="https://twitter.com/brocklallen">Brock</a>) - in other words:  my validator is not in any way complete..</p>
<pre><code><span class="hljs-built_in">public</span> async Task&lt;DpopValidationResult&gt; ValidateAsync(HttpContext context)
{
      _proof = <span class="hljs-built_in">new</span> ValidatedDpopProof(context);           

      <span class="hljs-keyword">if</span> (_proof.DpopHeader == <span class="hljs-keyword">null</span>)
          <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;

      //<span class="hljs-number">2.</span> <span class="hljs-keyword">check</span> "typ"
      <span class="hljs-keyword">if</span> (!_proof.JoseHeader.ContainsKey("typ"))
      {
          <span class="hljs-keyword">return</span> Invalid("DPoP proof does not contain \"typ\" claim", <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>);              
      }

      <span class="hljs-keyword">if</span> (_proof.JoseHeader["typ"].GetString() != "dpop+jwt")
      {
          <span class="hljs-keyword">return</span> Invalid("DPoP proof is not correct type");
      }

      //<span class="hljs-number">3.</span> <span class="hljs-keyword">check</span> algorithm
     <span class="hljs-keyword">if</span> (!_proof.JoseHeader.ContainsKey("alg"))
     {
         <span class="hljs-keyword">return</span> Invalid("DPoP proof is not well formed", "JOSE header does not contain \"alg\" claim", <span class="hljs-keyword">null</span>);
      }

      var algorithms = <span class="hljs-built_in">new</span> string[] { "RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "PS256", "PS384", "PS512" };
      var joseAlg = _proof.JoseHeader["alg"].GetString();

      <span class="hljs-keyword">if</span> (joseAlg == "none")
         <span class="hljs-keyword">return</span> Invalid("DPoP jose header cannot contain \"<span class="hljs-keyword">none</span>\" as value");

      <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">Array</span>.<span class="hljs-keyword">Exists</span>(algorithms, alg =&gt; alg == joseAlg))
      {
          <span class="hljs-keyword">return</span> Invalid("DPoP jose header contains invalid algorithm", <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>);
      }

      <span class="hljs-keyword">if</span> (!_proof.Payload.ContainsKey("htu"))
      {
            <span class="hljs-keyword">return</span> Invalid("DPoP proof is not well formed", "The \"htu\" claim is not present", <span class="hljs-keyword">null</span>);
      }

      <span class="hljs-keyword">if</span> (_proof.Payload["htu"].GetString() != "https://dpopidentityserver.azurewebsites.net/connect/token")
      {
            <span class="hljs-keyword">return</span> Invalid("Invalid htu value");
      }

      <span class="hljs-keyword">if</span> (!_proof.Payload.ContainsKey("htm"))
      {
            <span class="hljs-keyword">return</span> Invalid("DPoP proof is not well formed", "The \"htm\" claim is not present", <span class="hljs-keyword">null</span>);
      }

      <span class="hljs-keyword">if</span> (_proof.Payload["htm"].GetString().ToLower() != context.Request.<span class="hljs-keyword">Method</span>.ToLower())
      {
            <span class="hljs-keyword">return</span> Invalid("Invalid htm value");
      }


      //<span class="hljs-number">4.</span> <span class="hljs-keyword">Validate</span> jwt signature <span class="hljs-keyword">using</span> key <span class="hljs-keyword">in</span> jose <span class="hljs-keyword">header</span>
      var jwtHandler = <span class="hljs-built_in">new</span> JwtSecurityTokenHandler();
      var validationParameters = <span class="hljs-built_in">new</span> TokenValidationParameters
      {
          IssuerSigningKey = _proof.Jwk,
          ValidateIssuerSigningKey = <span class="hljs-keyword">true</span>,
         RequireAudience = <span class="hljs-keyword">false</span>,
          ValidateIssuer = <span class="hljs-keyword">false</span>,
          ValidateAudience = <span class="hljs-keyword">false</span>,
          RequireExpirationTime = <span class="hljs-keyword">false</span>
      };

      jwtHandler.ValidateToken(_proof.DpopHeader, validationParameters, <span class="hljs-keyword">out</span> var validatedToken);

      <span class="hljs-keyword">if</span> (validatedToken == <span class="hljs-keyword">null</span>)
          <span class="hljs-keyword">return</span> Invalid("DPoP Error", "DPoP validation failed", <span class="hljs-keyword">null</span>);

      <span class="hljs-keyword">return</span> <span class="hljs-built_in">new</span> DpopValidationResult(_proof, <span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>);
}
</code></pre><h2 id="binding-the-dpop-proof-to-the-access-token">Binding the DPoP proof to the access token</h2>
<p>The DPoP spec describes how the access token should be bound to the key material contained in the DPoP proof.
I felt lucky when I discovered that Microsoft has added some convenience methods that makes the job of creating the thumbprint in accordance with the spec really easy:</p>
<pre><code><span class="hljs-keyword">var</span> jwk = <span class="hljs-keyword">new</span> Microsoft.IdentityModel.Tokens.JsonWebKey(JoseHeader[<span class="hljs-string">"jwk"</span>].GetRawText());
Thumbprint = jwk.ComputeJwkThumbprint();
</code></pre><p>Now that the token validation result contains the DPoP proof thumbprint, the last step is to include the claim in the Access Token.
I added the following line to the TokenResponseGenerator class:</p>
<pre><code><span class="hljs-number">8</span>&lt;<span class="hljs-keyword">...</span>
<span class="hljs-keyword">if</span> (!request.DPoPThumbprint.IsNullOrEmpty())
{
      tokenRequest.Jkt = request.DPoPThumbprint;
}
<span class="hljs-keyword">...</span>&gt;<span class="hljs-number">8</span>
</code></pre><p>And the following code to the DefaultTokenService implementation:</p>
<pre><code><span class="hljs-number">8</span>&lt;<span class="hljs-keyword">...</span>
 <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (request.Jkt.IsPresent())
{
      token.Confirmation = <span class="hljs-string">"{\"jkt\" : \""</span> + request.Jkt + <span class="hljs-string">"\"}"</span>;
}
<span class="hljs-keyword">...</span>&gt;<span class="hljs-number">8</span>
</code></pre><p>The last steps involved adding the <code>"token_type": "dpop"</code> to the token response, but this was such an ugly hack that I am too embarrassed to show anyone..</p>
<h2 id="conclusion">Conclusion</h2>
<p>I think I spent around a total of 6-7 hours in Duende IdentityServer to implement the features that I needed for my POC. Most of this time was spent on plumbing, frenetically trying to remember .net, c# and VS. The DPoP specific details were actually really easy to implement.</p>
<p>I think the specification is quite easy to follow, and Duende IdentityServer was easy to extend.
At the same time I think that writing a production ready implementation will take a lot more skills than I possess, and more time - which is why I'll wait for the professionals to implement it correctly.</p>
<p>In my next post I'll show you how my attempts at writing a client that uses my DPoP implementation in Duende IdentityServer went.. </p>
<p>Spoiler alert! It turns out that javascript does not love me..</p>
]]></content:encoded></item><item><title><![CDATA[DPoP #1: Sender constraining OAuth Access Tokens with DPoP]]></title><description><![CDATA[Nope, DPop is not a new sexy music genre from Deutschland, Denmark or Djibouti. Personally, I do find DPoP quite exciting. So, you might want to read on - even if you came here looking for the next big thing in music.
In this series of three posts I ...]]></description><link>https://noem.blog/dpop-1-sender-constraining-oauth-access-tokens-with-dpop</link><guid isPermaLink="true">https://noem.blog/dpop-1-sender-constraining-oauth-access-tokens-with-dpop</guid><category><![CDATA[oauth]]></category><dc:creator><![CDATA[Steinar Noem]]></dc:creator><pubDate>Tue, 10 Aug 2021 12:26:06 GMT</pubDate><content:encoded><![CDATA[<p>Nope, DPop is not a new sexy <a target="_blank" href="https://en.wikipedia.org/wiki/K-pop">music genre</a> from Deutschland, Denmark or Djibouti. Personally, I do find DPoP quite exciting. So, you might want to read on - even if you came here looking for the next big thing in music.</p>
<p>In this series of three posts I will take a look at the <a target="_blank" href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop">DPoP draft</a> from the <a target="_blank" href="https://datatracker.ietf.org/wg/oauth/about/">IETF OAuth WG</a>, and see if I am able to implement the specification using my not so super programming skills..</p>
<h1 id="the-problem-of-token-theft">The problem of token theft</h1>
<p>With OAuth 2.0 the API outsources the job of establishing trust to its clients to the Authorization Server. The API relies on the Authorization Server to ensure that the Access Token is issued only to clients that have been granted the right to act on behalf of the user, and that the client is authorized to access the resource.<br />With the awesome power that the holder of an Access Token posesses, it is clearly a weak link that Access Tokens by specification are <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc6750">Bearer tokens</a>.</p>
<blockquote>
<p>[A bearer token is] a security token with the property that any party in possession of
      the token (a "bearer") can use the token in any way that any other
      party in possession of it can.  Using a bearer token does not
      require a bearer to prove possession of cryptographic key material
      (proof-of-possession).</p>
</blockquote>
<p>Bearer tokens have been used in OAuth 2.0 flows since the framework was published, so whats the problem? Well, one of the more obvious shortcomings of bearer tokens is the risk of tokens being stolen and misused. And basically, if the consequences of stolen tokens are high, the use of bearer token just doesn't cut it. In high risk scenarios the party which is responsible for the data that is exposed through the API would probably <em>require a bearer to prove the possession of cryptographic key material</em>. Or, <em>sender constraining</em> tokens, as some people call it - meaning that use of the token is restricted to the party who was granted access.</p>
<p>You see, a bearer token is comparable to cash: if someone steals your hard earned 100$ bill they can spend it all on cheap vodka at the liqour shop without the cashier ever knowing that it's actually your money, nor does he know the fact that you'd never spend money on cheap liqour. Only the fine stuff, right?</p>
<h2 id="the-token-is-mine-and-only-mine">The token is mine, and only mine!</h2>
<p>So how can we mitigate the risk of token theft?  Well, the API should ideally be able to somehow verify that the access token was issued to the same client that sends the request to the API.
There are several proposed solutions for how this could be accomplished, and most of them describe a way of cryptographically binding the access token to some secret that only the client knows. And then to have the client use the same secret when calling the API. Thus making the API capable of verifying that the same secret was used both at the AS and the API.</p>
<h3 id="oauth-20-mutual-tls-client-authentication-and-certificate-bound-access-tokens">OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens</h3>
<p>The IETF WG has described a way of binding Access Token to clients by leveraging existing TLS implementations. Not only does it present an elegant solution for the need to bind the Access Token to the client, it also provides a way to strongly authenticate the client by using the underlying TLS connection.  </p>
<p>The  <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8705">mTLS</a> spec is now a  <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc6410.html#section-2">Proposed Standard</a> which means that the mechanism will be available in many Authorization Server products.
The mTLS approach really makes a lot of sense, and seems to be the obvious choice to mitigate the problem.<br />...Well, this is true as long as the client can keep the private key that belongs to the client certificate a secret.</p>
<p>Oh... AND, if you are able to give the user a decent user experience - which does not involve the user selecting the correct certificate from a obscure list that pops up on his screen..  </p>
<p>Oh... AND, when using ephemeral certificates (created on the fly) - as long as the web server permits self-signed certificates.  </p>
<p>Oh... AND, if you want to use mTLS for client authentication - as long as it's <em>practically feasible</em> to <strong>create, deploy and manage client certificates</strong> for potentially <strong>thousands of clients</strong>.</p>
<p>Oh... AND, as long as <em>TLS isn't prematurely terminated by some proxy</em> you aren't in control of.  </p>
<p>Sadly, this isn't allways the case.<br />Oh.. Let me rephrase: this is quite often <strong>not the case..</strong></p>
<h3 id="a-compelling-alternative">A compelling alternative</h3>
<h3 id="oauth-20-demonstrating-proof-of-possession-at-the-application-layer-dpop">OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP)</h3>
<p>Some very clever people at IETF realized that the mTLS mechanism is a bad fit for some application types like SPAs and mobile apps. So they came up with an alternative solution, and named it DPoP (I know, sounds like a new break dance style, innit?).  </p>
<p>The name was apparently conceived by strictly following the naming conventions of the IETF, as you can see from <a target="_blank" href="https://twitter.com/__b_c/status/1108974390052638720">Brian Campbells Twitter thread</a>. So, the introduction to my post was not so far fetched after all?</p>
<h4 id="the-dpop-proof">The DPoP proof</h4>
<p>The specification describes how a client can prove that it is in possession of a private key (DPoP Proof) that is used  when requesting a token from an Authorization Server. </p>
<p>The DPoP proof looks like this (pay extra special notice to the "typ" claim, and some newbies in the payload):</p>
<pre><code>{
     <span class="hljs-string">"typ"</span>:<span class="hljs-string">"dpop+jwt"</span>,
     <span class="hljs-string">"alg"</span>:<span class="hljs-string">"ES256"</span>,
     <span class="hljs-string">"jwk"</span>: {
       <span class="hljs-string">"kty"</span>:<span class="hljs-string">"EC"</span>,
       <span class="hljs-string">"x"</span>:<span class="hljs-string">"l8tFrhx-34tV3hRICRDY9zCkDlpBhF42UQUfWVAWBFs"</span>,
       <span class="hljs-string">"y"</span>:<span class="hljs-string">"9VE4jf_Ok_o64zbTTlcuNJajHmt6v9TDVrU0CdvGRDA"</span>,
       <span class="hljs-string">"crv"</span>:<span class="hljs-string">"P-256"</span>
     }
   }
   .
   {
     <span class="hljs-string">"jti"</span>:<span class="hljs-string">"-BwC3ESc6acc2lTc"</span>,
     <span class="hljs-string">"htm"</span>:<span class="hljs-string">"POST"</span>,
     <span class="hljs-string">"htu"</span>:<span class="hljs-string">"https://server.example.com/token"</span>,
     <span class="hljs-string">"iat"</span>:<span class="hljs-number">1562262616</span>
   }
</code></pre><h4 id="the-dpop-http-header-and-the-authorization-server">The DPoP HTTP Header and the Authorization Server</h4>
<p>The DPoP proof is transferred to the Authorization Server by adding a new HTTP Header called <code>DPoP</code>, and it goes a little something like this:</p>
<pre><code>POST /token HTTP/<span class="hljs-number">1.1</span>
   Host: server.example.com
   Content-Type: application/<span class="hljs-keyword">x</span>-www-form-urlencoded;charset=UTF-<span class="hljs-number">8</span>
   DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik
    VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR
    nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE
    QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj
    oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia
    WF0IjoxNTYyMjYyNjE2fQ.<span class="hljs-number">2</span>-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg
    <span class="hljs-number">4</span>PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg

   grant_type=authorization_code
   &amp;code=SplxlOBeZQQYbYS6WxSbIA
   &amp;redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
   &amp;code_verifier=bEaL42izcC-o-xBk0K2vuJ6U-y1p9r_wW2dFWIWgjz-
</code></pre><p>After receiving the token request with the DPoP proof the Authorization Server can validate and bind the proof to the Access Token that the client requested - the Access Token now includes the new claim "jkt" which holds a value that uniquely identifies the proof that was presented. </p>
<pre><code>{
     <span class="hljs-attr">"sub"</span>: <span class="hljs-string">"someone@example.com"</span>,
     <span class="hljs-attr">"iss"</span>: <span class="hljs-string">"https://server.example.com"</span>,
     <span class="hljs-attr">"nbf"</span>: <span class="hljs-number">1562262611</span>,
     <span class="hljs-attr">"exp"</span>: <span class="hljs-number">1562266216</span>,
     <span class="hljs-attr">"cnf"</span>: { <span class="hljs-attr">"jkt"</span> : <span class="hljs-string">"0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"</span> }
   }
</code></pre><p>After the token is created it is returned to the client just like in the normal flow.</p>
<h4 id="requesting-the-api-with-dpop">Requesting the API with DPoP</h4>
<p>When the client makes a request to the API it includes the Access Token in the Authorization Header like usual, but in addition it adds a new DPoP proof which is constructed using the same key material as the DPoP proof for the token request made to the Authorization Server. The new DPoP proof includes a hash of the Access Token that the client received from the Authorization Server, binding the access token to the DPoP proof in order to make sure that the DPoP proof is tighly bound to this particular grant.</p>
<pre><code><span class="hljs-attribute">GET</span> /protectedresource HTTP/<span class="hljs-number">1</span>.<span class="hljs-number">1</span>
   <span class="hljs-attribute">Host</span>: resource.example.org
   <span class="hljs-attribute">Authorization</span>: DPoP Kz~<span class="hljs-number">8</span>mXK<span class="hljs-number">1</span>EalYznwH-LC-<span class="hljs-number">1</span>fBAo.<span class="hljs-number">4</span>Ljp~zsPE_NeO.gxU
   <span class="hljs-attribute">DPoP</span>: eyJ<span class="hljs-number">0</span>eXAiOiJkcG<span class="hljs-number">9</span>wK<span class="hljs-number">2</span>p<span class="hljs-number">3</span>dCIsImFsZyI<span class="hljs-number">6</span>IkVTMjU<span class="hljs-number">2</span>IiwiandrIjp<span class="hljs-number">7</span>Imt<span class="hljs-number">0</span>eSI<span class="hljs-number">6</span>Ik
    <span class="hljs-attribute">VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR</span>
    <span class="hljs-attribute">nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE</span>
    <span class="hljs-attribute">QSIsImNydiI6IlAtMjU2In19</span>.eyJqdGkiOiJlMWozVl<span class="hljs-number">9</span>iS<span class="hljs-number">2</span>ljOC<span class="hljs-number">1</span>MQUVCIiwiaHRtIj
    <span class="hljs-attribute">oiR0VUIiwiaHR1IjoiaHR0cHM6Ly9yZXNvdXJjZS5leGFtcGxlLm9yZy9wcm90ZWN0Z</span>
    <span class="hljs-attribute">WRyZXNvdXJjZSIsImlhdCI6MTU2MjI2MjYxOCwiYXRoIjoiZlVIeU8ycjJaM0RaNTNF</span>
    <span class="hljs-attribute">c05yV0JiMHhXWG9hTnk1OUlpS0NBcWtzbVFFbyJ9</span>.<span class="hljs-number">2</span>oW<span class="hljs-number">9</span>RP<span class="hljs-number">35</span>yRqzhrtNP<span class="hljs-number">86</span>L-Ey<span class="hljs-number">71</span>E
    <span class="hljs-attribute">OptxRimPPToA1plemAgR6pxHF8y6</span>-yqyVnmcw<span class="hljs-number">6</span>Fy<span class="hljs-number">1</span>dqd-jfxSYoMxhAJpLjA
</code></pre><p>When the API receives both the DPoP proof and the Access Token it is now able to verify that the client that requested the Access Token is in possession of the same key material as the client that called the API.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I really like the approach this specification takes. The concept is easy to understand, and seems to be relatively simple to implement on the client and API with the capabilities found in modern browsers and programming frameworks.</p>
<p>In the next blog-post in this series I will describe how I implemented a proof-of-concept for the Authorization Server side using Duende IdentityServer.</p>
<p>WARNING: I don't do a lot of programming, so the code examples are not for the faint of heart :)</p>
]]></content:encoded></item></channel></rss>