<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Tips on I_AM Fabio</title>
    <link>https://iam.fabiograsso.net/tips/</link>
    <description>Recent content in Tips on I_AM Fabio</description>
    <generator>Hugo</generator>
    <language>en</language>
    <atom:link href="https://iam.fabiograsso.net/tips/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Federating Microsoft 365 / EntraID Guest Accounts with Okta</title>
      <link>https://iam.fabiograsso.net/tips/guest-accounts-microsoft-office-365-entraid/</link>
      <pubDate>Sun, 23 Mar 2025 00:00:00 +0000</pubDate>
      <guid>https://iam.fabiograsso.net/tips/guest-accounts-microsoft-office-365-entraid/</guid>
      <description>How to handle EntraID B2B guest accounts in Okta: tenant-specific OIDC endpoints, username mapping, and the common pitfalls that block sign-in.</description>
      <content:encoded>&lt;![CDATA[<p>If you&rsquo;ve ever tried to let <strong>EntraID (Azure AD) guest users</strong> sign in to an Okta-protected app via the built-in Microsoft IdP, you&rsquo;ve probably hit a wall. The login flow either drops the user on a personal Microsoft account screen or fails outright with a confusing error.</p>
<p>The root cause is simple: <strong>guest accounts (B2B) live in a specific tenant</strong>, but Okta&rsquo;s default Microsoft IdP talks to the <code>common</code> endpoint, which assumes the user can be discovered globally. Here are the notes I keep handy when wiring this up.</p>

<h2 class="relative group">Why the default Microsoft IdP fails for guests
    <div id="why-the-default-microsoft-idp-fails-for-guests" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#why-the-default-microsoft-idp-fails-for-guests" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>Okta&rsquo;s out-of-the-box <strong>Microsoft IdP</strong> is pre-configured against the multi-tenant endpoint:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">https://login.microsoftonline.com/common/oauth2/v2.0/authorize</span></span></code></pre></div></div>
<p>The <code>/common</code> endpoint relies on Microsoft&rsquo;s <strong>home realm discovery</strong>: it inspects the email entered by the user and routes the authentication to the tenant where that identity lives. For a guest, the email (<code>alice@example.com</code>) resolves to a <strong>personal Microsoft account</strong> namespace, not the inviting tenant. The result is the classic error:</p>
<blockquote><p>You can&rsquo;t sign in here with a personal account. Use your work or school account instead.</p>
</blockquote><p>…unless the user happens to also own a personal Microsoft Account with that email — in which case they&rsquo;ll authenticate against the <em>wrong</em> identity and Okta will receive a token that has no relationship with your tenant.</p>

<h3 class="relative group">Fix: use a tenant-specific Custom OIDC IdP
    <div id="fix-use-a-tenant-specific-custom-oidc-idp" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#fix-use-a-tenant-specific-custom-oidc-idp" aria-label="Anchor">#</a>
    </span>
    
</h3>
<p>The solution is to bypass <code>/common</code> and pin the authentication to <strong>your</strong> tenant by using its tenant ID (a GUID, found in EntraID → Overview). This forces Microsoft to authenticate the user against your directory, where the guest object actually exists.</p>
<p>In Okta, <strong>don&rsquo;t edit the built-in Microsoft IdP</strong> — create a new <strong>OIDC Identity Provider</strong> (Security → Identity Providers → Add → OpenID Connect IdP) with the following endpoints, replacing <code>[MS_TENANT_ID]</code> with your tenant GUID:</p>
<table>
  <thead>
      <tr>
          <th>Setting</th>
          <th>Value</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Issuer</td>
          <td><code>https://login.microsoftonline.com/[MS_TENANT_ID]/v2.0</code></td>
      </tr>
      <tr>
          <td>Authorization endpoint</td>
          <td><code>https://login.microsoftonline.com/[MS_TENANT_ID]/oauth2/v2.0/authorize</code></td>
      </tr>
      <tr>
          <td>Token endpoint</td>
          <td><code>https://login.microsoftonline.com/[MS_TENANT_ID]/oauth2/v2.0/token</code></td>
      </tr>
      <tr>
          <td>JWKS endpoint</td>
          <td><code>https://login.microsoftonline.com/[MS_TENANT_ID]/discovery/v2.0/keys</code></td>
      </tr>
      <tr>
          <td>Userinfo endpoint</td>
          <td><code>https://graph.microsoft.com/oidc/userinfo</code></td>
      </tr>
      <tr>
          <td>Scopes</td>
          <td><code>openid profile email https://graph.microsoft.com/User.Read</code></td>
      </tr>
  </tbody>
</table>
<p>You&rsquo;ll also need an <strong>App Registration</strong> in EntraID (Application type: Web) with:</p>
<ul>
<li>The Okta redirect URI (<code>https://&lt;your-okta-domain&gt;/oauth2/v1/authorize/callback</code>) added under <em>Authentication → Redirect URIs</em>.</li>
<li>A client secret generated under <em>Certificates &amp; secrets</em>.</li>
<li>The delegated permissions <code>openid</code>, <code>profile</code>, <code>email</code>, and <code>User.Read</code> granted under <em>API permissions</em>.</li>
</ul>

<h2 class="relative group">Understand the guest username format
    <div id="understand-the-guest-username-format" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#understand-the-guest-username-format" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>When an external user (e.g. <code>alice@example.com</code>) is invited as a guest into an EntraID tenant, Microsoft does <strong>not</strong> store their email as the UPN. Instead it generates an internal identifier:</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">alice_example.com#EXT#@contoso.onmicrosoft.com</span></span></code></pre></div></div>
<p>This matters because:</p>
<ul>
<li>Microsoft&rsquo;s <code>preferred_username</code> / <code>upn</code> claim will return that mangled string, not <code>alice@example.com</code>.</li>
<li>If you want Okta to match the user by <strong>email</strong>, you must map the IdP&rsquo;s <code>email</code> claim (not <code>sub</code> or <code>upn</code>) to Okta&rsquo;s username/email attribute in the IdP profile mapping.</li>
<li>JIT provisioning rules, group rules, and policy conditions should be written against the <code>email</code> claim too, otherwise you&rsquo;ll end up with users whose Okta username looks like <code>alice_example.com#ext#@contoso.onmicrosoft.com</code>.</li>
</ul>
<div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="tip">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M112.1 454.3c0 6.297 1.816 12.44 5.284 17.69l17.14 25.69c5.25 7.875 17.17 14.28 26.64 14.28h61.67c9.438 0 21.36-6.401 26.61-14.28l17.08-25.68c2.938-4.438 5.348-12.37 5.348-17.7L272 415.1h-160L112.1 454.3zM191.4 .0132C89.44 .3257 16 82.97 16 175.1c0 44.38 16.44 84.84 43.56 115.8c16.53 18.84 42.34 58.23 52.22 91.45c.0313 .25 .0938 .5166 .125 .7823h160.2c.0313-.2656 .0938-.5166 .125-.7823c9.875-33.22 35.69-72.61 52.22-91.45C351.6 260.8 368 220.4 368 175.1C368 78.61 288.9-.2837 191.4 .0132zM192 96.01c-44.13 0-80 35.89-80 79.1C112 184.8 104.8 192 96 192S80 184.8 80 176c0-61.76 50.25-111.1 112-111.1c8.844 0 16 7.159 16 16S200.8 96.01 192 96.01z"/></svg>
</span></div>
        <div class="grow">
          Tip
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>Always inspect the ID token returned by Microsoft (e.g. via the Okta System Log → &ldquo;Authentication of user via IDP&rdquo; event) before finalizing the mapping. Different tenants and consent scopes return slightly different claim sets.</p></div></div><div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note">
      <div class="flex items-center gap-2 font-semibold text-inherit">
        <div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"><span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z"/></svg>
</span></div>
        <div class="grow">
          Note
        </div>
      </div><div class="admonition-content mt-3 text-base leading-relaxed text-inherit"><p>The <code>User.Read</code> scope is what allows Okta to call <code>https://graph.microsoft.com/oidc/userinfo</code> and retrieve the guest&rsquo;s actual email address rather than the <code>#EXT#</code> UPN.</p></div></div>]]></content:encoded>
      <category>Microsoft</category>
      <category>Azure</category>
      <category>GuestAccount</category>
      <category>AzureGuest</category>
      <category>Office365</category>
      <category>Okta</category>
      <category>OIDC</category>
      <category>EntraID</category>
      <category>B2B</category>
    </item>
    <item>
      <title>Mac OS - Resize Window to a specific size</title>
      <link>https://iam.fabiograsso.net/tips/mac-os-resize-window-to-a-specific-size/</link>
      <pubDate>Fri, 16 Feb 2024 00:00:00 +0000</pubDate>
      <guid>https://iam.fabiograsso.net/tips/mac-os-resize-window-to-a-specific-size/</guid>
      <description>Automate Mac OS window resizing with AppleScript to set precise dimensions and position for applications, useful for video recording or screen sharing.</description>
      <content:encoded>&lt;![CDATA[<p>Use case: when you need to record a video or share an application via Zoom, and you need the right aspect ratio (i.e. 16:9)</p>
<p>Sample Apple Script for Arc (you can change the app name for other applications):</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-applescript" data-lang="applescript"><span class="line"><span class="cl"><span class="k">set</span> <span class="nv">appName</span> <span class="k">to</span> <span class="s2">&#34;Arc&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&#34;System Events&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">set</span> <span class="nv">appProcess</span> <span class="k">to</span> <span class="nb">first</span> <span class="nv">process</span> <span class="nb">whose</span> <span class="na">name</span> <span class="ow">is</span> <span class="nv">appName</span>
</span></span><span class="line"><span class="cl">    <span class="k">tell</span> <span class="nv">appProcess</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="nv">frontWindow</span> <span class="k">to</span> <span class="nb">first</span> <span class="na">window</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="nv">newWindowSize</span> <span class="k">to</span> <span class="p">{</span><span class="mi">1920</span><span class="p">,</span> <span class="mi">1080</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="na">position</span> <span class="k">of</span> <span class="nv">frontWindow</span> <span class="k">to</span> <span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="na">size</span> <span class="k">of</span> <span class="nv">frontWindow</span> <span class="k">to</span> <span class="nv">newWindowSize</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span> <span class="k">tell</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span> <span class="k">tell</span></span></span></code></pre></div></div>
<p>If you use a dual (or more) monitor setup, you have to play a bit with set position of frontWindow in order to place the window in the right position. The first value can be the resolution of the 1st monitor (i.e. 1513 for a 14” MacBook Pro), and the second value depends on the resolution of the 2nd monitor. Try some value until you find the right one winking face</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-applescript" data-lang="applescript"><span class="line"><span class="cl"><span class="k">set</span> <span class="nv">appName</span> <span class="k">to</span> <span class="s2">&#34;Arc&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">tell</span> <span class="nb">application</span> <span class="s2">&#34;System Events&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">set</span> <span class="nv">appProcess</span> <span class="k">to</span> <span class="nb">first</span> <span class="nv">process</span> <span class="nb">whose</span> <span class="na">name</span> <span class="ow">is</span> <span class="nv">appName</span>
</span></span><span class="line"><span class="cl">    <span class="k">tell</span> <span class="nv">appProcess</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="nv">frontWindow</span> <span class="k">to</span> <span class="nb">first</span> <span class="na">window</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="nv">newWindowSize</span> <span class="k">to</span> <span class="p">{</span><span class="mi">1920</span><span class="p">,</span> <span class="mi">1080</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="na">position</span> <span class="k">of</span> <span class="nv">frontWindow</span> <span class="k">to</span> <span class="p">{</span><span class="mi">1513</span><span class="p">,</span> <span class="o">-</span><span class="mi">1000</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">set</span> <span class="na">size</span> <span class="k">of</span> <span class="nv">frontWindow</span> <span class="k">to</span> <span class="nv">newWindowSize</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span> <span class="k">tell</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span> <span class="k">tell</span></span></span></code></pre></div></div>

<h3 class="relative group">Notes:
    <div id="notes" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#notes" aria-label="Anchor">#</a>
    </span>
    
</h3>
<ol>
<li>
<p>In order to work, the Script Editor must be on the same virtual monitor as the application.</p>
</li>
<li>
<p>if you want, you can export the script in .app format and run it without opening the Script Editor</p>
</li>
<li>
<p>I’m not an Apple Script expert. Maybe there is a better way… this just runs fine for my needs slightly smiling face</p>
</li>
</ol>
]]></content:encoded>
      <category>Apple</category>
      <category>MacOS</category>
      <category>Automation</category>
      <category>AppleScript</category>
      <category>WindowSize</category>
      <category>Arc</category>
    </item>
    <item>
      <title>WinAuth - MFA software for Windows and shared PC use case</title>
      <link>https://iam.fabiograsso.net/tips/winauth/</link>
      <pubDate>Wed, 14 Feb 2024 00:00:00 +0000</pubDate>
      <guid>https://iam.fabiograsso.net/tips/winauth/</guid>
      <description>WinAuth, an open-source Windows MFA solution for environments with shared PCs and restrictive requirements. WinAuth provides TOTP authentication with password-protected authenticators, addressing scenarios where traditional MFA methods aren&amp;rsquo;t viable.</description>
      <content:encoded>&lt;![CDATA[<p>I had a customer with very specific requirements for MFA:</p>
<ul>
<li>No app to be installed on employees&rsquo; devices</li>
<li>No SMS (they cannot use the employees&rsquo; personal number)</li>
<li>Protect with MFA access to a corporate app</li>
<li>Hardware keys are to be avoided due to the cost and risk of losing them</li>
<li>Shared PC - included Windows user (more employees use the same session on Windows and share the same password)</li>
</ul>
<p>Though is not the best in terms of security, it&rsquo;s very hard for them to change this behaviour.</p>
<p>So, I was looking for a solution to propose. We cannot rely on FastPass because the Windows user is shared. We cannot use SMS, FIDO2 Keys, or other system.</p>
<p>There are a lot of MFA software compatible with TOTP or Google Authenticator, but most of them run without a password or with a master password, making it impossible to use them securely in a shared Windows session.</p>

<h2 class="relative group">WinAuth
    <div id="winauth" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#winauth" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>WinAuth is a portable, open-source Authenticator for Windows that provides <strong>counter or time-based <a href="https://datatracker.ietf.org/doc/html/rfc6238"  target="_blank" rel="noreferrer">RFC 6238</a> authenticators</strong> and common implementations, such as the <strong>Google Authenticator</strong>.</p>
<p>One of the feature is the possibility to protect each Authenticator with a different password. This permit to run the app on a shared PC.</p>
<p>You can <a href="https://winauth.github.io/winauth/download.html"  target="_blank" rel="noreferrer">download it from Github</a>
GitHub source code: (<a href="https://github.com/winauth/winauth"  target="_blank" rel="noreferrer">https://github.com/winauth/winauth</a>)</p>

<h2 class="relative group">Demo
    <div id="demo" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#demo" aria-label="Anchor">#</a>
    </span>
    
</h2>
<video controls preload="metadata" style="max-width:100%">
<source src="./winauth.mp4" type="video/mp4">
</video>

<h2 class="relative group">Advantages of WinAuth
    <div id="advantages-of-winauth" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#advantages-of-winauth" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ul>
<li>Light (~5Mb)</li>
<li>Don’t require installation and run without admin permissions</li>
<li>Support multiple MFA and permit to protect each one with a separate code</li>
<li>It’s open source. A customer can potentially customize it, remove unnecessary features, add custom branding, etc.</li>
</ul>

<h2 class="relative group">Warning
    <div id="warning" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#warning" aria-label="Anchor">#</a>
    </span>
    
</h2>
<p>All that glitters is not gold :winking-face: there are some caveats to consider:</p>
<ul>
<li>The app is not maintained anymore. Last release was in 2017, that means that it can have issues with newer version of Windows, unfixed security bugs, etc</li>
<li>Though it is possible to have separate passwords, every user can delete every Authenticator</li>
<li>You have to educate users on how to add a new Authenticator and protect it with a password</li>
<li>It’s a good starting point, but to use it in an Enterprise context, the application must be customized by the customer.</li>
</ul>

<h2 class="relative group">Alternatives to be evaluated
    <div id="alternatives-to-be-evaluated" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#alternatives-to-be-evaluated" aria-label="Anchor">#</a>
    </span>
    
</h2>
<ul>
<li><a href="https://github.com/Bubka/2FAuth"  target="_blank" rel="noreferrer">https://github.com/Bubka/2FAuth</a></li>
<li><a href="https://saaspass.com/"  target="_blank" rel="noreferrer">https://saaspass.com/</a></li>
<li><a href="https://github.com/2fast-team/2fast"  target="_blank" rel="noreferrer">https://github.com/2fast-team/2fast</a></li>
<li><a href="https://github.com/multiOTP/multiotp"  target="_blank" rel="noreferrer">https://github.com/multiOTP/multiotp</a></li>
<li><a href="https://github.com/VladimirAkopyan/Authenticator"  target="_blank" rel="noreferrer">https://github.com/VladimirAkopyan/Authenticator</a></li>
</ul>
]]></content:encoded>
      <category>Okta</category>
      <category>MFA</category>
      <category>Windows</category>
      <category>TOTP</category>
      <category>WinAuth</category>
      <category>Shared PC</category>
      <category>Authentication</category>
      <category>Security</category>
      <category>RFC 6238</category>
    </item>
  </channel>
</rss>