<?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[Matt Lins]]></title><description><![CDATA[Engineering Leader. Ruby, Rails, Stimulus, Hotwire, Server Deployment and Management. Healthcare, PropTech, and iGaming.]]></description><link>https://www.mattlins.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 03 Jun 2026 23:38:52 GMT</lastBuildDate><atom:link href="https://www.mattlins.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Adding 'Sign in with Apple' to your Ruby on Rails 7.1 App: A Step-by-Step Guide]]></title><description><![CDATA[I added omniauth-apple to one of my side projects to get 'Sign in with Apple' authentication. I hit a couple of issues that took me a while to fix, so I want to share how I solved them, hoping it saves others some time.
Getting Started
I'm working wi...]]></description><link>https://www.mattlins.com/adding-sign-in-with-apple-to-your-ruby-on-rails-71-app-a-step-by-step-guide</link><guid isPermaLink="true">https://www.mattlins.com/adding-sign-in-with-apple-to-your-ruby-on-rails-71-app-a-step-by-step-guide</guid><category><![CDATA[Ruby on Rails]]></category><category><![CDATA[Ruby]]></category><dc:creator><![CDATA[Matt Lins]]></dc:creator><pubDate>Fri, 10 Nov 2023 04:47:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1699588678250/233d845c-f29d-4462-9027-1e2418b2628e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I added <a target="_blank" href="https://github.com/nhosoya/omniauth-apple">omniauth-apple</a> to one of my side projects to get '<a target="_blank" href="https://support.apple.com/en-us/102609">Sign in with Apple</a>' authentication. I hit a couple of issues that took me a while to fix, so I want to share how I solved them, hoping it saves others some time.</p>
<h3 id="heading-getting-started">Getting Started</h3>
<p>I'm working with <a target="_blank" href="https://edgeguides.rubyonrails.org/7_1_release_notes.html">Rails 7.1</a> and <a target="_blank" href="https://github.com/lazaronixon/authentication-zero">authentication-zero</a>. My <code>Gemfile</code> already has <a target="_blank" href="https://github.com/omniauth/omniauth">omniauth</a> and <a target="_blank" href="https://github.com/cookpad/omniauth-rails_csrf_protection">omniauth-rails_csrf_protection</a> because I passed the <code>--omniauthable</code> flag to the <code>authentication-zero</code> generator:</p>
<pre><code class="lang-bash">rails generate authentication --omniauthable
</code></pre>
<p>Next, I added <code>omniauth-apple</code> to my <code>Gemfile</code>:</p>
<pre><code class="lang-ruby">gem <span class="hljs-string">"omniauth-apple"</span>
</code></pre>
<p>Configuring 'Sign in with Apple' requires several steps on the Apple side of the house that I won't go into. However, the short version is you will need an <a target="_blank" href="https://developer.apple.com/programs/">Apple Developer</a> account, and you'll need to add some configuration to it. The <code>omniauth-apple</code> gem outlines how to do that in its <a target="_blank" href="https://github.com/nhosoya/omniauth-apple#configuring-sign-in-with-apple">README</a>.</p>
<p>Next, I added the Apple configuration to my <a target="_blank" href="https://guides.rubyonrails.org/security.html#custom-credentials">Custom Credentials</a>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apple:</span>
  <span class="hljs-attr">bundle_id:</span> <span class="hljs-string">com.example</span>
  <span class="hljs-attr">app_id_prefix:</span> <span class="hljs-string">&lt;redacted&gt;</span>
  <span class="hljs-attr">key_id:</span> <span class="hljs-string">&lt;redacted&gt;</span>
  <span class="hljs-attr">private_key:</span> <span class="hljs-string">|
    -----BEGIN PRIVATE KEY-----
    &lt;redacted&gt;
    -----END PRIVATE KEY-----</span>
</code></pre>
<p>Then, I added the apple provider to <code>config/initializers/omniauth.rb</code>:</p>
<pre><code class="lang-ruby">Rails.application.config.middleware.use OmniAuth::Builder <span class="hljs-keyword">do</span>
  provider <span class="hljs-symbol">:apple</span>,
    Rails.application.credentials.apple[<span class="hljs-symbol">:bundle_id</span>],
    <span class="hljs-string">""</span>,
    {
      <span class="hljs-symbol">scope:</span> <span class="hljs-string">"email name"</span>,
      <span class="hljs-symbol">team_id:</span> Rails.application.credentials.apple[<span class="hljs-symbol">:app_id_prefix</span>],
      <span class="hljs-symbol">key_id:</span> Rails.application.credentials.apple[<span class="hljs-symbol">:key_id</span>],
      <span class="hljs-symbol">pem:</span> Rails.application.credentials.apple[<span class="hljs-symbol">:private_key</span>]
    }
<span class="hljs-keyword">end</span>
</code></pre>
<p>Finally, you must add a button to 'Sign in with Apple' on your login page. I'm using <a target="_blank" href="https://tailwindcss.com/">tailwindcss</a> via <a target="_blank" href="https://github.com/rails/tailwindcss-rails">tailwindcss-rails</a> to style the button:</p>
<pre><code class="lang-erb"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">%=</span></span></span><span class="ruby"> button_to <span class="hljs-string">"/auth/apple"</span>, <span class="hljs-string">"data-turbo"</span> =&gt; <span class="hljs-literal">false</span>, <span class="hljs-class"><span class="hljs-keyword">class</span>: "<span class="hljs-title">text</span>-<span class="hljs-title">white</span> <span class="hljs-title">bg</span>-[<span class="hljs-comment">#050708] hover:bg-[#050708]/90 focus:ring-4 focus:ring-[#050708]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-[#050708]/50 dark:hover:bg-[#050708]/30 mr-2 mb-2" do </span></span></span><span class="xml"><span class="hljs-tag">%&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">svg</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mr-2 -ml-1 w-5 h-5"</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">focusable</span>=<span class="hljs-string">"false"</span> <span class="hljs-attr">data-prefix</span>=<span class="hljs-string">"fab"</span> <span class="hljs-attr">data-icon</span>=<span class="hljs-string">"apple"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"img"</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">"http://www.w3.org/2000/svg"</span> <span class="hljs-attr">viewBox</span>=<span class="hljs-string">"0 0 384 512"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">path</span> <span class="hljs-attr">fill</span>=<span class="hljs-string">"currentColor"</span> <span class="hljs-attr">d</span>=<span class="hljs-string">"M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">path</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">svg</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-sm font-semibold leading-6"</span>&gt;</span>Sign in with Apple<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">%</span></span></span><span class="ruby"> <span class="hljs-keyword">end</span> </span><span class="xml"><span class="hljs-tag">%&gt;</span></span>
</code></pre>
<p>If you want to test this locally, you'll need to use <code>https</code>. There are multiple options for that, but I use <a target="_blank" href="https://ngrok.com/">ngrok</a>. Once you have an <code>https</code> URL pointing at your local Rails server, you'll need to add a couple of things to your <code>config/environments/development.rb</code>:</p>
<pre><code class="lang-ruby">config.force_ssl = <span class="hljs-literal">true</span>
config.hosts &lt;&lt; <span class="hljs-string">"yourexternalurl.com"</span> <span class="hljs-comment"># If you're using something like ngrok</span>
</code></pre>
<p>While we're at it, you might want to turn on <code>force_ssl</code> in your <code>config/environments/production.rb</code> for your deployed app:</p>
<pre><code class="lang-ruby">config.assume_ssl = <span class="hljs-literal">true</span> <span class="hljs-comment"># You may need this depending on your prodution deployment</span>
config.force_ssl = <span class="hljs-literal">true</span>
</code></pre>
<p><strong>Note: Don't forget to update your domains and redirect_uris in your 'Sign in with Apple' configuration within your Apple Developer account (see the</strong> <a target="_blank" href="https://github.com/nhosoya/omniauth-apple#steps"><strong>README</strong></a><strong>)</strong></p>
<p>The <code>force_ssl</code> configuration is crucial because it ensures that your Rails session cookie is set with the <code>Secure</code> attribute. We're going to set <code>SameSite</code> to <code>None</code> later to fix an error, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#none">which only works with a secure cookie</a>.</p>
<p>That's a fair bit of work! You are probably excited to try this out, but you'll likely encounter an error.</p>
<h3 id="heading-csrf-detected"><strong>CSRF detected 😱</strong></h3>
<p>If you click on your 'Sign in with Apple' button, everything should work fine until you're redirected back. If you're testing locally, you'll probably see this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699580951011/682e8f1e-3484-44e1-871c-142353347e16.png" alt class="image--center mx-auto" /></p>
<p>This took me quite a while to wrap my head around, and I'll explain what's going on, but if you want the fix and don't care about the explanation, add this to your <code>config/application.rb</code> (credit to <a target="_blank" href="https://github.com/nhosoya/omniauth-apple/issues/54#issuecomment-1334754066">jmonteiro</a>)</p>
<pre><code class="lang-ruby">config.action_dispatch.cookies_same_site_protection =
  lambda <span class="hljs-keyword">do</span> <span class="hljs-params">|request|</span>
    request.path.starts_with?(<span class="hljs-string">"/auth/apple"</span>) ? <span class="hljs-symbol">:none</span> : <span class="hljs-symbol">:lax</span>
  <span class="hljs-keyword">end</span>
</code></pre>
<h3 id="heading-why-does-this-happen">Why does this happen?</h3>
<p>There are <a target="_blank" href="https://www.bscotch.net/post/sign-in-with-apple-implementation-hurdles">multiple</a> <a target="_blank" href="https://github.com/nhosoya/omniauth-apple/issues/54">discussions</a> <a target="_blank" href="https://github.com/nhosoya/omniauth-apple/issues/64">about</a> what's happening here and even a <a target="_blank" href="https://github.com/nhosoya/omniauth-apple/pull/95">couple</a> of <a target="_blank" href="https://github.com/nhosoya/omniauth-apple/pull/107">PRs</a> against <code>omniauth-apple</code> that have been closed by the maintainer. A lot is going on, but I'll try to summarize it in a Rails context and provide links for more information along the way:</p>
<ul>
<li><p><a target="_blank" href="https://api.rubyonrails.org/classes/ActionDispatch/Session/CookieStore.html">Rails uses a cookie to store sessions by default.</a></p>
</li>
<li><p>Modern versions of Rails set the <code>SameSite</code> attribute to <code>Lax</code> on the session cookie (see the <a target="_blank" href="https://github.com/rails/rails/pull/28297">PR</a> here for more explanation). This means the cookie is sent along to cross-site requests (like our 'Sign in with Apple' link), but it only works with "safe" HTTP methods like <code>GET</code>.</p>
</li>
<li><p>Apple <a target="_blank" href="https://www.bscotch.net/post/sign-in-with-apple-implementation-hurdles">somewhat breaks OAuth protocol</a> by redirecting back with a <code>POST</code> instead of a <code>GET</code>.</p>
</li>
<li><p>Part of a secure OAuth handshake is generating a <code>state</code>, storing it, and then sending it along as a parameter to the identity provider (in this case, Apple). When the user is redirected back, the identity provider sends that <code>state</code> back as a parameter for the server to compare to its stored <code>state</code>. If it doesn't match, we have a <a target="_blank" href="https://owasp.org/www-community/attacks/csrf">CSRF</a> problem (see more <a target="_blank" href="https://auth0.com/docs/secure/attack-protection/state-parameters">here</a>).</p>
</li>
<li><p><a target="_blank" href="https://github.com/omniauth/omniauth-oauth2/blob/master/lib/omniauth/strategies/oauth2.rb#L75">omniauth-oauth2 stores the state</a> in the <a target="_blank" href="https://github.com/rack/rack">rack</a> session, which, in Rails, means storing it in a cookie (see the first bullet).</p>
</li>
<li><p>Because of the <code>POST</code> redirect back from Apple and the <code>Lax</code> <code>SameSite</code> cookie setting, the <code>state</code> parameter never gets back to our Rails app, and <code>omniauth-oauth2</code> correctly raises <code>csrf_detected</code>.</p>
</li>
</ul>
<p>Now, back to the fix (see above). In our case, we just set <code>SameSite</code> to <code>None</code>, but only in the case where we're hitting the omniauth <code>/auth/apple</code> route.</p>
<p>Is this secure? Well, you'll have to make that determination for yourself, but let me explain why I'm ok with it:</p>
<ul>
<li><p>We're selectively setting <code>SameSite</code> to <code>None</code> in the case where we're making a request to 'Sign in with Apple' (via <code>omniauth</code>). That means, on all our other routes, the session cookie is set with <code>SameSite</code> to <code>Lax</code>.</p>
</li>
<li><p>We already have another form of CSRF protection built into OAuth with the state parameter.</p>
</li>
<li><p>Rails <a target="_blank" href="https://guides.rubyonrails.org/v7.1.0/security.html#session-storage">cookie sessions are encrypted</a>, meaning if there was a man-in-the-middle attack, the attacker couldn't read the <code>state</code>, which means they couldn't send it back in the redirect and <code>omniauth-oauth2</code> would raise <code>csrf_detected</code>.</p>
</li>
</ul>
<p>I hope that helps! If you feel like I got something wrong or you have a question, please comment or shoot me an email at <a target="_blank" href="mailto:mattlins@hey.com"><strong>mattlins@hey.com</strong></a></p>
]]></content:encoded></item><item><title><![CDATA[Missing item markers and indentation with tailwindcss + Trix?]]></title><description><![CDATA[I started a new Ruby on Rails 7.1 project the other day, and I added both tailwindcss (via the tailwindcss-rails gem) and the trix editor. Everything worked great, except I was missing item markers and indentation on my bulleted lists (both ordered a...]]></description><link>https://www.mattlins.com/missing-item-markers-and-indentation-with-tailwindcss-trix</link><guid isPermaLink="true">https://www.mattlins.com/missing-item-markers-and-indentation-with-tailwindcss-trix</guid><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[Ruby on Rails]]></category><dc:creator><![CDATA[Matt Lins]]></dc:creator><pubDate>Fri, 03 Nov 2023 00:53:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1699591530699/2d86cb25-4d5e-44c4-a9a0-e9da702622bf.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I started a new <a target="_blank" href="https://rubyonrails.org/">Ruby on Rails</a> <a target="_blank" href="https://guides.rubyonrails.org/7_1_release_notes.html">7.1</a> project the other day, and I added both <a target="_blank" href="https://tailwindcss.com/">tailwindcss</a> (via the <a target="_blank" href="https://github.com/rails/tailwindcss-rails">tailwindcss-rails</a> gem) and the <a target="_blank" href="https://trix-editor.org/">trix editor</a>. Everything worked great, except I was missing item markers and indentation on my bulleted lists (both ordered and unordered).</p>
<p>After some research, I discovered that tailwindcss <a target="_blank" href="https://tailwindcss.com/docs/preflight#lists-are-unstyled">doesn't style lists by default</a>. My lists looked like this in trix:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698975297187/4137eed5-ab22-4051-8d79-e0e5552b23e4.png" alt class="image--center mx-auto" /></p>
<p>To fix it, I first added this to my <strong>app/assets/application.tailwind.css:</strong></p>
<pre><code class="lang-css"><span class="hljs-selector-class">.trix-content</span> <span class="hljs-selector-tag">ul</span> {
  @apply list-disc;
}

<span class="hljs-selector-class">.trix-content</span> <span class="hljs-selector-tag">ol</span> {
  @apply list-decimal;
}
</code></pre>
<p>That was a little better, but I lost indentation:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698978341517/4c69cbf6-02af-4748-8145-008dafeb8bd4.png" alt class="image--center mx-auto" /></p>
<p>I added some padding:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.trix-content</span> <span class="hljs-selector-tag">ul</span> {
  @apply list-disc pl-5;
}

<span class="hljs-selector-class">.trix-content</span> <span class="hljs-selector-tag">ol</span> {
  @apply list-decimal pl-5;
}
</code></pre>
<p>That did it:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698975326604/87a051e8-2d29-4926-b864-559d2c18ffa6.png" alt class="image--center mx-auto" /></p>
<p>I hope that helps! If you feel like I got something wrong or you have a question, please comment or shoot me an email at <a target="_blank" href="mailto:mattlins@hey.com"><strong>mattlins@hey.com</strong></a></p>
]]></content:encoded></item></channel></rss>