<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Leo</title>
    <description>The latest articles on DEV Community by Leo (@leobaniak).</description>
    <link>https://dev.to/leobaniak</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3997121%2F061e76e1-0a08-4d9d-827c-24f1e8b9e15b.png</url>
      <title>DEV Community: Leo</title>
      <link>https://dev.to/leobaniak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/leobaniak"/>
    <language>en</language>
    <item>
      <title>Azure DevOps issuer in WIF service connections heads for July 2027 retirement</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Mon, 22 Jun 2026 16:24:27 +0000</pubDate>
      <link>https://dev.to/leobaniak/azure-devops-issuer-in-wif-service-connections-heads-for-july-2027-retirement-4al5</link>
      <guid>https://dev.to/leobaniak/azure-devops-issuer-in-wif-service-connections-heads-for-july-2027-retirement-4al5</guid>
      <description>&lt;p&gt;About a year of lead time, then a forced move. Microsoft announced on June 22 that the Azure DevOps issuer used in workload identity federation (WIF) service connections will be retired on July 1, 2027, per the Microsoft DevOps Blog. Azure Pipelines that already abandoned long-lived secrets in favour of WIF now have a follow-up migration on the calendar: switching the federated credentials on their service connections from the Azure DevOps issuer (the &lt;code&gt;https://vstoken.dev.azure.com&lt;/code&gt; prefix) to the Microsoft Entra issuer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The issuer that's being retired
&lt;/h2&gt;

&lt;p&gt;WIF lets a workload exchange a signed OIDC token from a trusted issuer for an Azure access token, no client secret on disk. Today an Azure DevOps service connection can wire that trust up under either of two issuer URLs in the federated credential on the Entra side: the Azure DevOps issuer, with the &lt;code&gt;https://vstoken.dev.azure.com&lt;/code&gt; prefix, or the Microsoft Entra issuer. After July 1, 2027, only the Entra issuer will be accepted. The change is part of a wider Microsoft initiative to standardize on the Entra issuer across Azure services that implement workload identity federation, the blog says.&lt;/p&gt;

&lt;p&gt;The blog frames this as a deprecation, not a behaviour change. The protocol stays the same, the trust relationship stays the same, only the issuer URL on the federated credential moves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which connections are actually in scope
&lt;/h2&gt;

&lt;p&gt;The deprecation is narrower than the headline reads. It applies only to service connections in Azure public cloud that target single-tenant Microsoft Entra applications or managed identities. Microsoft explicitly excludes three categories from this announcement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service connections to Azure Government, Azure China, or Azure Stack.&lt;/li&gt;
&lt;li&gt;Service connections backed by multi-tenant Entra applications, the ones registered with &lt;code&gt;signInAudience: AzureADMultipleOrgs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The default federated identity flow on those excluded clouds and multi-tenant apps remains as it was.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If every WIF connection in your estate is in one of those bands, this announcement does not touch you yet. If you run a mixed estate, you will be holding two issuer policies in parallel until 2027 and possibly beyond.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the migration shape looks
&lt;/h2&gt;

&lt;p&gt;For everyone in scope, this is a federated-credential rewrite, not a service-connection rebuild. The federated credential on the target Entra application or managed identity needs an issuer URL pointing at the Entra issuer instead of the &lt;code&gt;vstoken.dev.azure.com&lt;/code&gt; prefix, and a subject that matches what the Azure Pipelines side sends under the Entra issuer. The Azure DevOps service connection itself stays in place.&lt;/p&gt;

&lt;p&gt;The blog's framing is that platform teams have roughly a year to do this on their own schedule rather than under a force-cutover. The practical sequence is the same one any federated-credential migration follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Inventory every service connection in the affected clouds, by Entra-application or managed-identity target.&lt;/li&gt;
&lt;li&gt;Add a second federated credential on each target, pointed at the Entra issuer, alongside the existing Azure DevOps issuer credential.&lt;/li&gt;
&lt;li&gt;Cut Azure Pipelines over to the Entra-issued credential in a low-stakes pipeline first, watching for auth failures.&lt;/li&gt;
&lt;li&gt;Remove the old &lt;code&gt;vstoken.dev.azure.com&lt;/code&gt; credential once nothing is using it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A code example with placeholders for the credential body, because every tenant will produce different real values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Federated credential on the target Entra app / managed identity&lt;/span&gt;
&lt;span class="na"&gt;issuer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://&amp;lt;entra-issuer-url&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;subject-from-azure-pipelines-side&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;audiences&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api://AzureADTokenExchange&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not copy a subject value from another tenant. Read it from a test pipeline against your own tenant first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this catches you
&lt;/h2&gt;

&lt;p&gt;This is a year of lead time, but the catch is upstream of the change-window math. Federated credentials live in two places at once: the Entra side (on the application or managed identity) and the Azure DevOps side (on the service connection). Platform teams discover that asymmetry the first time they try to migrate one pipeline and find the team that owns the Entra application sits in a different tenant, or a different change-management process. Inventory both ends before scheduling the cutover, otherwise the change-freeze you thought you had only covers one side of the trust.&lt;/p&gt;

&lt;p&gt;The exclusion list is the second snag. A platform team that standardized on multi-tenant Entra apps for cross-tenant pipelines, or that runs CI in Azure Government, gets a partial migration: the public-cloud single-tenant connections move, the rest sit on the Azure DevOps issuer indefinitely. That is two WIF configurations to keep documented, two break-glass procedures, two on-call run-books.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continuity caveat
&lt;/h2&gt;

&lt;p&gt;Microsoft is keeping multi-tenant applications and sovereign clouds out of scope for this announcement, which means the &lt;code&gt;vstoken.dev.azure.com&lt;/code&gt; issuer is not going away in absolute terms on July 1, 2027, only on the most common configuration. Teams running mixed estates need to track the same WIF policy in two flavours through the deadline and out the other side, until or unless Microsoft brings the excluded categories into the same standardization.&lt;/p&gt;

</description>
      <category>azuredevops</category>
      <category>azurepipelines</category>
      <category>workloadidentityfederation</category>
      <category>oidc</category>
    </item>
    <item>
      <title>`headingoffset` is not the outline algorithm coming back</title>
      <dc:creator>Leo</dc:creator>
      <pubDate>Mon, 22 Jun 2026 16:09:15 +0000</pubDate>
      <link>https://dev.to/leobaniak/headingoffset-is-not-the-outline-algorithm-coming-back-1nhh</link>
      <guid>https://dev.to/leobaniak/headingoffset-is-not-the-outline-algorithm-coming-back-1nhh</guid>
      <description>&lt;p&gt;The way most screen-reader users navigate a long page is by jumping heading to heading. Get the hierarchy right and the page becomes a table of contents you can step through with a single key; get it wrong, and the page collapses into one undifferentiated wall.&lt;/p&gt;

&lt;p&gt;Reusable components have always made that hierarchy harder. Drop a card component into a sidebar and an &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; inside it suddenly outranks its parent. Drop the same card under an &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; somewhere else and the levels need to shift again. The pragmatic answer for years has been: pick a level and live with the result.&lt;/p&gt;

&lt;p&gt;Firefox has now shipped (behind a flag, per Adrian Roselli's write-up) a small HTML attribute that takes some of that pain away — and a much larger conversation about what it does not do.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the attribute actually does
&lt;/h2&gt;

&lt;p&gt;Per the source, &lt;code&gt;headingoffset&lt;/code&gt; lives on a container and shifts the heading levels of its descendants in the accessibility tree. Put &lt;code&gt;headingoffset="2"&lt;/code&gt; on a wrapper and an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; inside that wrapper is exposed as a level-3 heading; an &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; becomes a level-4 heading; and so on. The DOM stays as you wrote it, but the level the assistive tech announces is the offset version.&lt;/p&gt;

&lt;p&gt;The values are constrained. Per the source, &lt;code&gt;headingoffset&lt;/code&gt; accepts non-negative integers from 0 to 8, and any value greater than 8 is specified to max out at heading level 9.&lt;/p&gt;

&lt;p&gt;The reusable-card case is the obvious win. You write the card once with its own internal heading order (an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; title, an &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; subhead) and the page that drops the card in declares where that internal order should sit. No more shipping a component that is wrong everywhere except its first home.&lt;/p&gt;

&lt;h2&gt;
  
  
  The escape valve: &lt;code&gt;headingreset&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;There is a partner attribute. Per the source, &lt;code&gt;headingreset&lt;/code&gt; is a boolean attribute that tells the browser to ignore any inherited &lt;code&gt;headingoffset&lt;/code&gt; for the node it sits on and that node's descendants. If a card lands inside a region that has already been offset and you do not want a second offset stacking on top, &lt;code&gt;headingreset&lt;/code&gt; puts that subtree back to its raw DOM levels.&lt;/p&gt;

&lt;p&gt;That is a small piece of API surface, but it matters. Without a reset, nested offsets compound, and the meaning of "level 2" on the page becomes a function of every ancestor a component happens to land under.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is not the outline algorithm coming back
&lt;/h2&gt;

&lt;p&gt;Here is the part worth saying out loud, because the temptation to read it that way is real.&lt;/p&gt;

&lt;p&gt;Per the source, the proposed Document Outline Algorithm, the idea that sectioning elements would shoulder the heading hierarchy for authors automatically, was never part of a finalised HTML specification, and has been abandoned. A brief implementation in JAWS exists in the historical record; the source's verdict is that the implementation demonstrated the algorithm was unworkable.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;headingoffset&lt;/code&gt; is not that. It does not look at structure. It does not infer. It is an explicit, author-set value that says: shift the levels under this node by this many. If you do not set it, nothing happens. The responsibility for the page's overall heading structure stays with you, exactly where it was last week.&lt;/p&gt;

&lt;p&gt;In other words: an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; inside a &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; is still an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; unless you tell the browser otherwise.&lt;/p&gt;

&lt;h2&gt;
  
  
  A caveat the source flags hard
&lt;/h2&gt;

&lt;p&gt;One detail not to skip. Per the source, computed heading levels above 6 still trip bugs in JAWS and TalkBack at the time of writing, and the author's advice is to avoid producing computed levels above 6 until those bugs are fixed. That is a hard constraint on how you use the attribute, not a footnote.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;headingoffset="3"&lt;/code&gt; on a wrapper whose internal headings start at &lt;code&gt;&amp;lt;h4&amp;gt;&lt;/code&gt; produces a computed level of 7, and that is exactly the range the source is telling you to avoid for now. Walk that math through every component before you ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves you
&lt;/h2&gt;

&lt;p&gt;The judgement from the source is that &lt;code&gt;headingoffset&lt;/code&gt; is a useful primitive for component reuse and nothing more. It does not turn &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; into a structural tool. It does not absolve the author of getting the document's heading order right.&lt;/p&gt;

&lt;p&gt;For your next PR with this enabled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treat &lt;code&gt;headingoffset&lt;/code&gt; as a property of where the component lands, not of the component itself.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;headingreset&lt;/code&gt; whenever you carry a subtree across boundaries and want its inner levels restored.&lt;/li&gt;
&lt;li&gt;Keep computed levels at 6 or below until the screen-reader bugs the source flags are fixed.&lt;/li&gt;
&lt;li&gt;Leave the document's spine, the page's own &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; and section headings, as ordinary hand-authored HTML. The new attribute complements that work; it does not replace it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The platform has given you a small, honest tool. Use it for the job it actually does.&lt;/p&gt;

</description>
      <category>html</category>
      <category>a11y</category>
      <category>headings</category>
      <category>semantics</category>
    </item>
  </channel>
</rss>
