<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Fortify on Tangible Bytes</title><link>https://tangiblebytes.co.uk/tags/fortify/</link><description>Recent content in Fortify on Tangible Bytes</description><generator>Hugo</generator><language>en-gb</language><copyright>&amp;copy; Sean Burlington</copyright><lastBuildDate>Tue, 01 Sep 2026 16:00:00 +0100</lastBuildDate><atom:link href="https://tangiblebytes.co.uk/tags/fortify/index.xml" rel="self" type="application/rss+xml"/><item><title>Laravel Fortify - making 2fa required</title><link>https://tangiblebytes.co.uk/2026/laravel-fortify-require-2fa/</link><pubDate>Tue, 01 Sep 2026 16:00:00 +0100</pubDate><guid>https://tangiblebytes.co.uk/2026/laravel-fortify-require-2fa/</guid><description>&lt;p&gt;By default Fortify makes 2FA optional&lt;/p&gt;&#10;&lt;p&gt;This article details how to make it required - using email by default with TOTP optional.&lt;/p&gt;&#10;&lt;p&gt;This assumes the &lt;a href="./laravel-migrate-from-breeze-to-fortify"&gt;Breeze → Fortify migration&lt;/a&gt; is done: feature flag on, trait on User, two-factor columns migrated.&lt;/p&gt;&#10;&lt;h2 id="0--make-sure-domains-match"&gt;0 . Make sure domains match&lt;/h2&gt;&#10;&lt;p&gt;I&amp;rsquo;m using a multisite setup and need to ensure the public and auth route handlers dont overlap&lt;/p&gt;&#10;&lt;p&gt;in &lt;code&gt;routes/web.php&lt;/code&gt; I added&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$adminDomain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;preg_quote&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;app.admin_domain&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$publicSiteDomain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;(?!&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$adminDomain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;$)[a-z0-9.-]+&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{site:domain}&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;site&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$publicSiteDomain&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and in &lt;code&gt;phpunit.xml&lt;/code&gt; I make sure the default domain is defined&lt;/p&gt;</description></item></channel></rss>