<rss xmlns:source="http://source.scripting.com/" version="2.0">
  <channel>
    <title>Hey Loura!</title>
    <link>https://heyloura.com/</link>
    <description></description>
    
    <language>en</language>
    
    <lastBuildDate>Tue, 21 Apr 2026 12:44:23 -0400</lastBuildDate>
    <item>
      <title></title>
      <link>https://heyloura.com/2026/04/13/sdam-strikes-again-i-was.html</link>
      <pubDate>Mon, 13 Apr 2026 15:34:59 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/04/13/sdam-strikes-again-i-was.html</guid>
      <description>&lt;p&gt;My poor memory strikes again. I was talking with my sister that I would love, love, love to go see Les Misérables some day on stage when my husband speaks up and tells me he took me to see it already 🤔😆 Woops.&lt;/p&gt;
</description>
      <source:markdown>My poor memory strikes again. I was talking with my sister that I would love, love, love to go see Les Misérables some day on stage when my husband speaks up and tells me he took me to see it already 🤔😆 Woops.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/04/08/stylebody-margin-fontfamily-georgia-serif.html</link>
      <pubDate>Wed, 08 Apr 2026 10:27:04 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/04/08/stylebody-margin-fontfamily-georgia-serif.html</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://heyloura.com/2024/04/03/my-son-cracks.html&#34; rel=&#34;noopener noreferrer nofollow&#34;&gt;Two years&lt;/a&gt; later and my other kid needs cookies. At least this time my child checked in the morning well ahead of the lab 😆&lt;/p&gt;
</description>
      <source:markdown>&lt;p&gt;&lt;a href=&#34;https://heyloura.com/2024/04/03/my-son-cracks.html&#34; rel=&#34;noopener noreferrer nofollow&#34;&gt;Two years&lt;/a&gt; later and my other kid needs cookies. At least this time my child checked in the morning well ahead of the lab 😆&lt;/p&gt;
</source:markdown>
    </item>
    
    <item>
      <title>March 8th - April 4th Practice Log</title>
      <link>https://heyloura.com/2026/04/07/march-th-april-th-practice.html</link>
      <pubDate>Tue, 07 Apr 2026 09:06:23 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/04/07/march-th-april-th-practice.html</guid>
      <description>&lt;h1 id=&#34;tai-chi&#34;&gt;&lt;a href=&#34;https://heyloura.com/tai-chi&#34;&gt;Tai Chi&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&#34;tai-chi-and-kung-fu-practice-log&#34;&gt;&lt;em&gt;Tai Chi and Kung Fu Practice Log&lt;/em&gt;&lt;/h2&gt;
&lt;h3 id=&#34;march-8th---april-4th&#34;&gt;March 8th - April 4th&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Stats&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;28 days&lt;/strong&gt; logged&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;approx. 42 hours&lt;/strong&gt; practiced (class + personal)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;approx. 4.1 hours&lt;/strong&gt; standing meditation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;20 forms&lt;/strong&gt; tai chi &amp;amp; kung fu&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&amp;ldquo;When the opponent does not move, you will not move; when the opponent slightly moves, you move first.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;— Yáng Bān-Hóu · on Pushing Hands&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;personal-practice-focus&#34;&gt;Personal Practice Focus&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;01: Opening the Hip Joint&lt;/strong&gt;
I included daily kua opening exercises, researched the anatomy of the hip, and explored releasing into the hip socket during move transitions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;02: Standing Meditation&lt;/strong&gt;
I worked up from 6-8 minute sessions to 20 min sessions over the month. By April I needed to do less posture corrections.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;03: Explosive Power&lt;/strong&gt;
Most moves in Taijiquan have an element of explosive power to them. I drilled silk reeling and fajin expression in my weekly rotation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;04: Sensitivity and Structure&lt;/strong&gt;
Regular weekly pushing hands practice in class and deep form corrections in 64 Guan Ping Yang Form.&lt;/p&gt;
</description>
      <source:markdown># [Tai Chi](https://heyloura.com/tai-chi)

## *Tai Chi and Kung Fu Practice Log*

### March 8th - April 4th

**Stats**

- **28 days** logged
- **approx. 42 hours** practiced (class + personal)
- **approx. 4.1 hours** standing meditation
- **20 forms** tai chi &amp; kung fu


&gt; *&#34;When the opponent does not move, you will not move; when the opponent slightly moves, you move first.&#34;*
&gt;
&gt; — Yáng Bān-Hóu · on Pushing Hands

---

## Personal Practice Focus

**01: Opening the Hip Joint**
I included daily kua opening exercises, researched the anatomy of the hip, and explored releasing into the hip socket during move transitions.

**02: Standing Meditation**
I worked up from 6-8 minute sessions to 20 min sessions over the month. By April I needed to do less posture corrections.

**03: Explosive Power**
Most moves in Taijiquan have an element of explosive power to them. I drilled silk reeling and fajin expression in my weekly rotation.

**04: Sensitivity and Structure**
Regular weekly pushing hands practice in class and deep form corrections in 64 Guan Ping Yang Form.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/03/27/now-that-i-got-my.html</link>
      <pubDate>Fri, 27 Mar 2026 09:53:30 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/03/27/now-that-i-got-my.html</guid>
      <description>&lt;p&gt;Now that I got my pkm tool to where I want it, my next &lt;a href=&#34;https://heyloura.com/projects&#34; rel=&#34;noopener noreferrer nofollow&#34;&gt;project&lt;/a&gt; is going to be a CMS that lets me build a website like a sticker book.&lt;/p&gt;
</description>
      <source:markdown>&lt;p&gt;Now that I got my pkm tool to where I want it, my next &lt;a href=&#34;https://heyloura.com/projects&#34; rel=&#34;noopener noreferrer nofollow&#34;&gt;project&lt;/a&gt; is going to be a CMS that lets me build a website like a sticker book.&lt;/p&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/03/25/i-feeling-the-digital-mess.html</link>
      <pubDate>Wed, 25 Mar 2026 10:42:54 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/03/25/i-feeling-the-digital-mess.html</guid>
      <description>&lt;p&gt;I feeling the digital mess of my files across devices pretty strongly today. The urge to toss everything in an archive folder and zip it… is strong. But I&#39;ll still know there is a mess of files within it. Don&#39;t get me started on digital photos. It&#39;s my blog too. It feels messy and disordered right now, not theme-wise (though I kinda want to switch back to my rpg one) but the micro posts and the long posts all together. I&#39;ve outgrown some categories. I struggle because I both want everything in one place but also organized and consistent. Arg… I guess it&#39;s just one of those days.&lt;/p&gt;
</description>
      <source:markdown>&lt;p&gt;I feeling the digital mess of my files across devices pretty strongly today. The urge to toss everything in an archive folder and zip it… is strong. But I&#39;ll still know there is a mess of files within it. Don&#39;t get me started on digital photos. It&#39;s my blog too. It feels messy and disordered right now, not theme-wise (though I kinda want to switch back to my rpg one) but the micro posts and the long posts all together. I&#39;ve outgrown some categories. I struggle because I both want everything in one place but also organized and consistent. Arg… I guess it&#39;s just one of those days.&lt;/p&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/03/22/im-required-to-use-claude.html</link>
      <pubDate>Sun, 22 Mar 2026 13:18:10 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/03/22/im-required-to-use-claude.html</guid>
      <description>&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/b88df3e31a.png&#34; width=&#34;600&#34; height=&#34;568&#34; alt=&#34;Auto-generated description: A digital journal interface displays an agenda for March 22, 2026, featuring martial arts and fitness activities, personal notes, and a video link.&#34;&gt;
&lt;p&gt;I&amp;rsquo;m required to use Claude at work so I&amp;rsquo;ve been testing it out. My favorite so far: A PWA quine PKM system. Took what I liked from Logseq (outline - simplified) and single file architecture (Tiddlywiki). I also added in sync to Micro.blog&amp;rsquo;s private notes with chunking. Came together in a day.&lt;/p&gt;
</description>
      <source:markdown>&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/b88df3e31a.png&#34; width=&#34;600&#34; height=&#34;568&#34; alt=&#34;Auto-generated description: A digital journal interface displays an agenda for March 22, 2026, featuring martial arts and fitness activities, personal notes, and a video link.&#34;&gt;

I&#39;m required to use Claude at work so I&#39;ve been testing it out. My favorite so far: A PWA quine PKM system. Took what I liked from Logseq (outline - simplified) and single file architecture (Tiddlywiki). I also added in sync to Micro.blog&#39;s private notes with chunking. Came together in a day.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/03/05/ive-had-kids-for-a.html</link>
      <pubDate>Thu, 05 Mar 2026 14:29:31 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/03/05/ive-had-kids-for-a.html</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve had kid(s) for a decade and a half now, and I&amp;rsquo;m still surprised how early registration opens up for things&amp;hellip; like summer camp. Guess I know what I&amp;rsquo;m researching this evening.&lt;/p&gt;
</description>
      <source:markdown>I&#39;ve had kid(s) for a decade and a half now, and I&#39;m still surprised how early registration opens up for things... like summer camp. Guess I know what I&#39;m researching this evening. 
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/03/05/read-as-text-using-ai.html</link>
      <pubDate>Thu, 05 Mar 2026 12:18:37 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/03/05/read-as-text-using-ai.html</guid>
      <description>&lt;map name=&#34;usingaiatworkmap&#34;&gt;
&lt;area shape=&#34;rect&#34; coords=&#34;155,95,700,185&#34; href=&#34;https://heyloura.com/2026/03/05/read-as-text-using-ai.html&#34; alt=&#34;Using AI at work&#34;&gt;&lt;/map&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img usemap=&#34;#usingaiatworkmap&#34; src=&#34;https://cdn.uploads.micro.blog/88328/2026/formatconvert-20260305-104938-8641.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. Doodles: Two grey squiggles adorn the sides and along their bottom, sets of small green plants with pink buds. At the end of the post is a simple drawing of a stick figure shrugging with a thought bubble. Inside the thought bubble is a question mark. Under the stick figure is the word &#39;shrug&#39;&#34;&gt;
&lt;/div&gt;
&lt;details&gt;
  &lt;summary&gt;Read as text&lt;/summary&gt;
&lt;p&gt;Using AI at Work&lt;/p&gt;
&lt;p&gt;Well, the time finally came. My boss wants me to start using AI in my workflow. In anticipation I&#39;ve been playing around with Claude Code the last couple of weeks. It&#39;s improved since the last time I tried it out.&lt;/p&gt;
&lt;p&gt;Maybe that&#39;s why I&#39;m feeling drawn to this handwriting mood with my blog. Something a bit more me that isn&#39;t a quick prompt into existence.&lt;/p&gt;
&lt;p&gt;Shrug&lt;/p&gt;
&lt;/details&gt;
</description>
      <source:markdown>&lt;map name=&#34;usingaiatworkmap&#34;&gt;
&lt;area shape=&#34;rect&#34; coords=&#34;155,95,700,185&#34; href=&#34;https://heyloura.com/2026/03/05/read-as-text-using-ai.html&#34; alt=&#34;Using AI at work&#34;&gt;&lt;/map&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img usemap=&#34;#usingaiatworkmap&#34; src=&#34;https://cdn.uploads.micro.blog/88328/2026/formatconvert-20260305-104938-8641.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. Doodles: Two grey squiggles adorn the sides and along their bottom, sets of small green plants with pink buds. At the end of the post is a simple drawing of a stick figure shrugging with a thought bubble. Inside the thought bubble is a question mark. Under the stick figure is the word &#39;shrug&#39;&#34;&gt;
&lt;/div&gt;
&lt;details&gt;
  &lt;summary&gt;Read as text&lt;/summary&gt;
&lt;p&gt;Using AI at Work&lt;/p&gt;
&lt;p&gt;Well, the time finally came. My boss wants me to start using AI in my workflow. In anticipation I&#39;ve been playing around with Claude Code the last couple of weeks. It&#39;s improved since the last time I tried it out.&lt;/p&gt;
&lt;p&gt;Maybe that&#39;s why I&#39;m feeling drawn to this handwriting mood with my blog. Something a bit more me that isn&#39;t a quick prompt into existence.&lt;/p&gt;
&lt;p&gt;Shrug&lt;/p&gt;
&lt;/details&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/03/03/read-as-text-i-got.html</link>
      <pubDate>Tue, 03 Mar 2026 11:29:02 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/03/03/read-as-text-i-got.html</guid>
      <description>&lt;map name=&#34;bloodmooneclipsemap&#34;&gt;
&lt;area shape=&#34;rect&#34; coords=&#34;155,95,700,185&#34; href=&#34;https://heyloura.com/2026/03/03/read-as-text-i-got.html&#34; alt=&#34;Blood Moon Eclipse&#34;&gt;&lt;/map&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img usemap=&#34;#bloodmooneclipsemap&#34; src=&#34;https://cdn.uploads.micro.blog/88328/2026/formatconvert-20260303-085825-5017.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. Doodles: Three grey cloud shapes and two grey spiral/swirl shapes are scattered around the margins; on the left side is a dark grey cloud with a red/coral half-circle peeking behind it, suggesting a blood moon obscured by clouds.&#34;&gt;
&lt;/div&gt;
&lt;details&gt;
  &lt;summary&gt;Read as text&lt;/summary&gt;
&lt;p&gt;I got up this morning around 3am because… my brain sucks sometimes. Anyway, it was cloudy then and its still cloudy now. Oh well…&lt;/p&gt;
&lt;p&gt;♥ Loura&lt;/p&gt;&lt;/details&gt;
</description>
      <source:markdown>&lt;map name=&#34;bloodmooneclipsemap&#34;&gt;
&lt;area shape=&#34;rect&#34; coords=&#34;155,95,700,185&#34; href=&#34;https://heyloura.com/2026/03/03/read-as-text-i-got.html&#34; alt=&#34;Blood Moon Eclipse&#34;&gt;&lt;/map&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img usemap=&#34;#bloodmooneclipsemap&#34; src=&#34;https://cdn.uploads.micro.blog/88328/2026/formatconvert-20260303-085825-5017.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. Doodles: Three grey cloud shapes and two grey spiral/swirl shapes are scattered around the margins; on the left side is a dark grey cloud with a red/coral half-circle peeking behind it, suggesting a blood moon obscured by clouds.&#34;&gt;
&lt;/div&gt;
&lt;details&gt;
  &lt;summary&gt;Read as text&lt;/summary&gt;
&lt;p&gt;I got up this morning around 3am because… my brain sucks sometimes. Anyway, it was cloudy then and its still cloudy now. Oh well…&lt;/p&gt;
&lt;p&gt;♥ Loura&lt;/p&gt;&lt;/details&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/03/02/read-as-text-my-kid.html</link>
      <pubDate>Mon, 02 Mar 2026 22:15:00 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/03/02/read-as-text-my-kid.html</guid>
      <description>&lt;map name=&#34;thewaitinggamemap&#34;&gt;
&lt;area shape=&#34;rect&#34; coords=&#34;155,95,700,185&#34; href=&#34;https://heyloura.com/2026/03/02/read-as-text-my-kid.html&#34; alt=&#34;The Waiting Game&#34;&gt;&lt;/map&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img usemap=&#34;#thewaitinggamemap&#34; src=&#34;https://cdn.uploads.micro.blog/88328/2026/formatconvert-20260303-085746-6314.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. A decorative border of two curving rose vines with green leaves runs down the left and right sides, each topped with a pink/red spiral rosebud; a small red broken heart appears inline before the second paragraph.&#34;&gt;
&lt;/div&gt;
&lt;details&gt;
  &lt;summary&gt;Read as text&lt;/summary&gt;
&lt;p&gt;My kid fell in love with the local agricultural school during a summer camp session. This year our state switched all technical schools to be lottery based. We filled out the application and today was the day. We waited…&lt;/p&gt;
&lt;p&gt;No luck. I have a very sad kid.&lt;/p&gt;&lt;/details&gt;
</description>
      <source:markdown>&lt;map name=&#34;thewaitinggamemap&#34;&gt;
&lt;area shape=&#34;rect&#34; coords=&#34;155,95,700,185&#34; href=&#34;https://heyloura.com/2026/03/02/read-as-text-my-kid.html&#34; alt=&#34;The Waiting Game&#34;&gt;&lt;/map&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img usemap=&#34;#thewaitinggamemap&#34; src=&#34;https://cdn.uploads.micro.blog/88328/2026/formatconvert-20260303-085746-6314.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. A decorative border of two curving rose vines with green leaves runs down the left and right sides, each topped with a pink/red spiral rosebud; a small red broken heart appears inline before the second paragraph.&#34;&gt;
&lt;/div&gt;

&lt;details&gt;
  &lt;summary&gt;Read as text&lt;/summary&gt;
&lt;p&gt;My kid fell in love with the local agricultural school during a summer camp session. This year our state switched all technical schools to be lottery based. We filled out the application and today was the day. We waited…&lt;/p&gt;
&lt;p&gt;No luck. I have a very sad kid.&lt;/p&gt;&lt;/details&gt;
</source:markdown>
    </item>
    
    <item>
      <title>Handwriting a blog</title>
      <link>https://heyloura.com/2026/03/01/handwriting-a-blog.html</link>
      <pubDate>Sun, 01 Mar 2026 22:15:00 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/03/01/handwriting-a-blog.html</guid>
      <description>&lt;map name=&#34;map1&#34;&gt;
  &lt;area shape=&#34;rect&#34; coords=&#34;200,30,900,175&#34; href=&#34;https://heyloura.com/2026/03/01/handwriting-a-blog.html&#34; alt=&#34;Handwriting a blog – March 1st 2026&#34;&gt;
  &lt;area shape=&#34;rect&#34; coords=&#34;175,190,880,880&#34; href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34; alt=&#34;Link reference [1]: handwritten.blog on the Internet Archive&#34;&gt;
  &lt;area shape=&#34;rect&#34; coords=&#34;940,180,990,880&#34; href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34; alt=&#34;Link reference [1]: handwritten.blog on the Internet Archive&#34;&gt;
&lt;/map&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img usemap=&#34;#map1&#34; src=&#34;https://cdn.uploads.micro.blog/88328/2026/4ec2861eb2.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. Decorative doodles: Left margin: a vertical trail of blue teardrop/raindrop shapes, and a blue swirl/spiral shape Inline after mind wander.: a small drawing of a swimmer with a red cap and goggles, with blue splashes Bottom of page: a row of colorful triangular bunting/pennant flags in red, yellow, and blue&#34;&gt;
&lt;/div&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/1a95339342.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. Decorative doodles: Upper left: a blue flower with petals splayed in two directions, like a snowflake or asterisk shape. Between paragraphs: a horizontal divider made of small blue leaf/teardrop shapes in a row. Left of second paragraph: a stick figure mermaid with a yellow hair and a curvy green tail&#34;&gt;
&lt;/div&gt;
&lt;details&gt;
  &lt;summary&gt;Read as text&lt;/summary&gt;
  &lt;p&gt;One of the nice things about going to my kids swim meet is that I have time to let my mind wander.&lt;/p&gt;
&lt;p&gt;A few years ago, I came across a website called &#34;Handwritten. blog.&#34; Sadly, it is no longer hosted but you can still see some of it on the internet archive [1].&lt;/p&gt;
&lt;p&gt;Maybe it was because I was reading on my e-ink tablet. Or perhaps I&#39;m just tired of my current blog design again. But why not give it a shot?&lt;/p&gt;
&lt;p&gt;One thing is obvious, editing posts will be a nightmare. Usually I take my time and will rewrite a blog post a few times. Not gonna happen if I&#39;m writing these out long hand. So yeah… a little unpolished it is.&lt;/p&gt;
&lt;p&gt;♥ Loura&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;br&gt;
[1] &lt;a href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34;&gt;Internet Archive&lt;/a&gt;
&lt;/p&gt;
&lt;/details&gt;
</description>
      <source:markdown>&lt;map name=&#34;map1&#34;&gt;
  &lt;area shape=&#34;rect&#34; coords=&#34;200,30,900,175&#34; href=&#34;https://heyloura.com/2026/03/01/handwriting-a-blog.html&#34; alt=&#34;Handwriting a blog – March 1st 2026&#34;&gt;
  &lt;area shape=&#34;rect&#34; coords=&#34;175,190,880,880&#34; href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34; alt=&#34;Link reference [1]: handwritten.blog on the Internet Archive&#34;&gt;
  &lt;area shape=&#34;rect&#34; coords=&#34;940,180,990,880&#34; href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34; alt=&#34;Link reference [1]: handwritten.blog on the Internet Archive&#34;&gt;
&lt;/map&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img usemap=&#34;#map1&#34; src=&#34;https://cdn.uploads.micro.blog/88328/2026/4ec2861eb2.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. Decorative doodles: Left margin: a vertical trail of blue teardrop/raindrop shapes, and a blue swirl/spiral shape Inline after mind wander.: a small drawing of a swimmer with a red cap and goggles, with blue splashes Bottom of page: a row of colorful triangular bunting/pennant flags in red, yellow, and blue&#34;&gt;
&lt;/div&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/1a95339342.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. Decorative doodles: Upper left: a blue flower with petals splayed in two directions, like a snowflake or asterisk shape. Between paragraphs: a horizontal divider made of small blue leaf/teardrop shapes in a row. Left of second paragraph: a stick figure mermaid with a yellow hair and a curvy green tail&#34;&gt;
&lt;/div&gt;

&lt;details&gt;
  &lt;summary&gt;Read as text&lt;/summary&gt;
  &lt;p&gt;One of the nice things about going to my kids swim meet is that I have time to let my mind wander.&lt;/p&gt;
&lt;p&gt;A few years ago, I came across a website called &#34;Handwritten. blog.&#34; Sadly, it is no longer hosted but you can still see some of it on the internet archive [1].&lt;/p&gt;
&lt;p&gt;Maybe it was because I was reading on my e-ink tablet. Or perhaps I&#39;m just tired of my current blog design again. But why not give it a shot?&lt;/p&gt;
&lt;p&gt;One thing is obvious, editing posts will be a nightmare. Usually I take my time and will rewrite a blog post a few times. Not gonna happen if I&#39;m writing these out long hand. So yeah… a little unpolished it is.&lt;/p&gt;
&lt;p&gt;♥ Loura&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;br&gt;
[1] &lt;a href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34;&gt;Internet Archive&lt;/a&gt;
&lt;/p&gt;
&lt;/details&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/02/23/our-town-reported-in-of.html</link>
      <pubDate>Mon, 23 Feb 2026 19:31:12 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/02/23/our-town-reported-in-of.html</guid>
      <description>&lt;p&gt;Our town reported 25in of snow for today&amp;rsquo;s blizzard. Though I feel like we got more. Not the best day for a shear pin to break on the snow blower. So we got to hand shovel the way too long driveway.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/5f81ff7be9.jpg&#34; width=&#34;450&#34; height=&#34;600&#34; alt=&#34;Women in snow gear standing in front of large snow pile and bamboo.&#34;&gt;
</description>
      <source:markdown>Our town reported 25in of snow for today&#39;s blizzard. Though I feel like we got more. Not the best day for a shear pin to break on the snow blower. So we got to hand shovel the way too long driveway.

&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/5f81ff7be9.jpg&#34; width=&#34;450&#34; height=&#34;600&#34; alt=&#34;Women in snow gear standing in front of large snow pile and bamboo.&#34;&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/01/31/i-purchased-a-variety-of.html</link>
      <pubDate>Sat, 31 Jan 2026 17:28:44 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/01/31/i-purchased-a-variety-of.html</guid>
      <description>&lt;p&gt;I purchased a variety of green teas to try for the new year. As a way to take quiet moments and enjoy some health benefits. Today&amp;rsquo;s selection: premium dragon well long jing. It has a nice smell and its quite mellow.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/jpeg-20260131-162731-3582694327120505276.jpg&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;&#34;&gt;
</description>
      <source:markdown>I purchased a variety of green teas to try for the new year. As a way to take quiet moments and enjoy some health benefits. Today&#39;s selection: premium dragon well long jing. It has a nice smell and its quite mellow. 

&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/jpeg-20260131-162731-3582694327120505276.jpg&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;&#34;&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/01/30/i-downloaded-the-hobonichi-app.html</link>
      <pubDate>Fri, 30 Jan 2026 19:12:26 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/01/30/i-downloaded-the-hobonichi-app.html</guid>
      <description>&lt;p&gt;I downloaded the hobonichi app to see if I liked it more than day one. It is cuter which may help me stick, but I like the encrypted option of day one and with day one I can use the android share sheet and get thoughts like these into Micro.blog. I&amp;rsquo;m going to give each a month and then decide.&lt;/p&gt;
</description>
      <source:markdown>I downloaded the hobonichi app to see if I liked it more than day one. It is cuter which may help me stick, but I like the encrypted option of day one and with day one I can use the android share sheet and get thoughts like these into Micro.blog. I&#39;m going to give each a month and then decide.
</source:markdown>
    </item>
    
    <item>
      <title>App Defaults 2026</title>
      <link>https://heyloura.com/2026/01/30/app-defaults.html</link>
      <pubDate>Fri, 30 Jan 2026 09:46:42 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/01/30/app-defaults.html</guid>
      <description>&lt;p&gt;Quite a few changes from my last update in &lt;a href=&#34;https://heyloura.com/2023/12/07/my-app-defaults.html&#34;&gt;2023&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;📧 Email Client (Web): Fastmail&lt;/p&gt;
&lt;p&gt;📧 Email Client (Android): e/os default mail app (K-9 Mail fork)&lt;/p&gt;
&lt;p&gt;🕸️ Website: Micro.blog &amp; Namecheap&lt;/p&gt;
&lt;p&gt;📝✅ Notes + Todos: Index cards, Micro.blog private notes (both notes and todos), and Logseq (for work notes)&lt;/p&gt;
&lt;p&gt;📔 Journal: Hobonichi 5 year, trying out Day one&lt;/p&gt;
&lt;p&gt;📸 Photo Management: Ente&lt;/p&gt;
&lt;p&gt;🗓️ Calendar (Web): Fastmail&lt;/p&gt;
&lt;p&gt;🗓️ Calendar (Android): e/os default calendar app&lt;/p&gt;
&lt;p&gt;🎁 Cloud file storage (unencrypted): Fastmail&lt;/p&gt;
&lt;p&gt;🎁 Cloud file storage (encrypted): 1password&lt;/p&gt;
&lt;p&gt;👽 Contacts: e/os default Android app&lt;/p&gt;
&lt;p&gt;🌐 Browser: Brave (hoping for Kagi browser soon)&lt;/p&gt;
&lt;p&gt;🔎 Search Engine: Kagi&lt;/p&gt;
&lt;p&gt;💬 Chat: e/os default + Signal&lt;/p&gt;
&lt;p&gt;🔖 Bookmarks: Micro.blog&lt;/p&gt;
&lt;p&gt;💵 Budgeting: self-hosted dumb budget app&lt;/p&gt;
&lt;p&gt;💵 Personal Finance: Micro.blog private notes (self aggregated data)&lt;/p&gt;
&lt;p&gt;📰 News: Allsides&lt;/p&gt;
&lt;p&gt;🔐 Password Management: 1password&lt;/p&gt;
&lt;p&gt;🎨 Photo Editing: Paint.Net&lt;/p&gt;
&lt;p&gt;👩‍💻 Code Editor: Visual Studio Code or Visual Studio&lt;/p&gt;
&lt;p&gt;📚 Books: Self-hosted - audiobookshelf + physical copies&lt;/p&gt;
</description>
      <source:markdown>Quite a few changes from my last update in [2023](https://heyloura.com/2023/12/07/my-app-defaults.html): 


&lt;p&gt;📧 Email Client (Web): Fastmail&lt;/p&gt;
&lt;p&gt;📧 Email Client (Android): e/os default mail app (K-9 Mail fork)&lt;/p&gt;
&lt;p&gt;🕸️ Website: Micro.blog &amp; Namecheap&lt;/p&gt;
&lt;p&gt;📝✅ Notes + Todos: Index cards, Micro.blog private notes (both notes and todos), and Logseq (for work notes)&lt;/p&gt;
&lt;p&gt;📔 Journal: Hobonichi 5 year, trying out Day one&lt;/p&gt;
&lt;p&gt;📸 Photo Management: Ente&lt;/p&gt;
&lt;p&gt;🗓️ Calendar (Web): Fastmail&lt;/p&gt;
&lt;p&gt;🗓️ Calendar (Android): e/os default calendar app&lt;/p&gt;
&lt;p&gt;🎁 Cloud file storage (unencrypted): Fastmail&lt;/p&gt;
&lt;p&gt;🎁 Cloud file storage (encrypted): 1password&lt;/p&gt;
&lt;p&gt;👽 Contacts: e/os default Android app&lt;/p&gt;
&lt;p&gt;🌐 Browser: Brave (hoping for Kagi browser soon)&lt;/p&gt;
&lt;p&gt;🔎 Search Engine: Kagi&lt;/p&gt;
&lt;p&gt;💬 Chat: e/os default + Signal&lt;/p&gt;
&lt;p&gt;🔖 Bookmarks: Micro.blog&lt;/p&gt;
&lt;p&gt;💵 Budgeting: self-hosted dumb budget app&lt;/p&gt;
&lt;p&gt;💵 Personal Finance: Micro.blog private notes (self aggregated data)&lt;/p&gt;
&lt;p&gt;📰 News: Allsides&lt;/p&gt;
&lt;p&gt;🔐 Password Management: 1password&lt;/p&gt;
&lt;p&gt;🎨 Photo Editing: Paint.Net&lt;/p&gt;
&lt;p&gt;👩‍💻 Code Editor: Visual Studio Code or Visual Studio&lt;/p&gt;
&lt;p&gt;📚 Books: Self-hosted - audiobookshelf + physical copies&lt;/p&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/01/28/i-had-a-good-time.html</link>
      <pubDate>Wed, 28 Jan 2026 22:36:49 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/01/28/i-had-a-good-time.html</guid>
      <description>&lt;p&gt;I had a good time attending a virtual Homebrew Website Club meeting this evening while I worked on my blog redesign. I was inspired by a codepen I came across for a journal with flipping pages done with CSS. The design only shows for desktop and its still a work in progress, but I&amp;rsquo;m having a blast.&lt;/p&gt;
</description>
      <source:markdown>I had a good time attending a virtual Homebrew Website Club meeting this evening while I worked on my blog redesign. I was inspired by a codepen I came across for a journal with flipping pages done with CSS. The design only shows for desktop and its still a work in progress, but I&#39;m having a blast.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/01/27/i-didnt-realize-day-one.html</link>
      <pubDate>Tue, 27 Jan 2026 21:37:52 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/01/27/i-didnt-realize-day-one.html</guid>
      <description>&lt;p&gt;I didn&amp;rsquo;t realize day one now had a web app and an android app. I&amp;rsquo;m going to have to check it out.&lt;/p&gt;
</description>
      <source:markdown>I didn&#39;t realize day one now had a web app and an android app. I&#39;m going to have to check it out.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/01/23/planting-bamboo-along-a-driveway.html</link>
      <pubDate>Fri, 23 Jan 2026 13:35:24 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/01/23/planting-bamboo-along-a-driveway.html</guid>
      <description>&lt;p&gt;Planting bamboo along a driveway is fun! Ignore that it&amp;rsquo;s impassable mess when covered in snow. Every winter storm we need to keep shaking the snow off to keep it from freezing to the ground. Maybe this is why the previous owner left 🤣 (they planted it).&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/img-20260119-091519.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;Snow-covered bamboo blocks a driveway with a backdrop of winter forest scene.&#34;&gt;
</description>
      <source:markdown>Planting bamboo along a driveway is fun! Ignore that it&#39;s impassable mess when covered in snow. Every winter storm we need to keep shaking the snow off to keep it from freezing to the ground. Maybe this is why the previous owner left 🤣 (they planted it).

&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/img-20260119-091519.jpg&#34; width=&#34;600&#34; height=&#34;450&#34; alt=&#34;Snow-covered bamboo blocks a driveway with a backdrop of winter forest scene.&#34;&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/01/11/i-forgot-how-loud-an.html</link>
      <pubDate>Sun, 11 Jan 2026 13:19:06 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/01/11/i-forgot-how-loud-an.html</guid>
      <description>&lt;p&gt;I forgot how loud an indoor water park can get. The kids are having fun though 😄&lt;/p&gt;
</description>
      <source:markdown>I forgot how loud an indoor water park can get. The kids are having fun though 😄
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2026/01/01/weve-been-sick-with-the.html</link>
      <pubDate>Thu, 01 Jan 2026 13:40:31 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2026/01/01/weve-been-sick-with-the.html</guid>
      <description>&lt;p&gt;We&amp;rsquo;ve been sick with the flu the last two weeks so I&amp;rsquo;ve done zero prep for the new year. But waking up to a fresh dusting of snow was nice.&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/387f21556c.jpg&#34; width=&#34;450&#34; height=&#34;600&#34; alt=&#34;A snow-covered landscape with bare trees is visible through a rectangular window.&#34;&gt;
</description>
      <source:markdown>We&#39;ve been sick with the flu the last two weeks so I&#39;ve done zero prep for the new year. But waking up to a fresh dusting of snow was nice.

&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2026/387f21556c.jpg&#34; width=&#34;450&#34; height=&#34;600&#34; alt=&#34;A snow-covered landscape with bare trees is visible through a rectangular window.&#34;&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2025/12/26/i-had-a-lovely-holiday.html</link>
      <pubDate>Fri, 26 Dec 2025 13:33:04 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2025/12/26/i-had-a-lovely-holiday.html</guid>
      <description>&lt;p&gt;I had a lovely holiday and I&amp;rsquo;m on vacation until the new year. I&amp;rsquo;m going to spend time catching up on my five year journal and planning out the next five years. There are college plans for one kid, figuring out high school plans for another and finishing up my &amp;ldquo;homeschooling the kids&amp;rdquo; journey.&lt;/p&gt;
</description>
      <source:markdown>I had a lovely holiday and I&#39;m on vacation until the new year. I&#39;m going to spend time catching up on my five year journal and planning out the next five years. There are college plans for one kid, figuring out high school plans for another and finishing up my &#34;homeschooling the kids&#34; journey.
</source:markdown>
    </item>
    
    <item>
      <title>Supabase, Deno and Server Side Rendering</title>
      <link>https://heyloura.com/2025/12/22/supabase-deno-and-server-side.html</link>
      <pubDate>Mon, 22 Dec 2025 11:16:37 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2025/12/22/supabase-deno-and-server-side.html</guid>
      <description>&lt;p&gt;A project I&amp;rsquo;m currently working on is using &lt;a href=&#34;https://supabase.com/&#34;&gt;Supabase&lt;/a&gt; for its database and authentication layer. And so far so good. That is until I wanted to move all the calls to the Supabase API server side for server side rendering (SSR). Now, they do have lots of documentation and tutorials out there and a bunch of third party tutorials all over the web. But nothing quite fit what I was trying to do. I&amp;rsquo;m using &lt;a href=&#34;https://deno.com/&#34;&gt;Deno&lt;/a&gt;, for server side JavaScript, and I figured with Supabase&amp;rsquo;s SSR package it would be a pretty easy to implement. You know what they say about assumptions 😆&lt;/p&gt;
&lt;p&gt;Since I&amp;rsquo;m not using any additional framework, their tutorial documentation wasn&amp;rsquo;t all that helpful. But it did give me enough of a sense of what I needed to do. Which boils down to setting cookies, reading cookies, and calling the Supabase client library. Then digging around Github, Github Issues, and Stack Overflow I eventually pieced it together.&lt;/p&gt;
&lt;p&gt;So, to maybe save someone else (or an AI scraper) some time in the future. Here is I tied up Supabase SSR with Deno and no additional framework:&lt;/p&gt;
&lt;h1 id=&#34;putting-it-all-together&#34;&gt;Putting it all together&lt;/h1&gt;
&lt;h2 id=&#34;logging-in-a-user&#34;&gt;Logging in a user&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;I&amp;rsquo;ve saved some key info in enviromental variables, namely &lt;code&gt;Deno.env.get(&amp;quot;SUPABASE_URL&amp;quot;)&lt;/code&gt; and &lt;code&gt;Deno.env.get(&amp;quot;SUPABASE_ANON_KEY&amp;quot;)&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I&amp;rsquo;m importing the supabase client with the following statement at the top of my main.js file: &lt;code&gt;import { createClient } from &amp;quot;jsr:@supabase/supabase-js@2&amp;quot;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I have a login page with a simple HTML form that does a &lt;code&gt;POST&lt;/code&gt; request to an endpoint and then calls the &lt;code&gt;createClient&lt;/code&gt; to determine if the credentials are correct and we have a valid user logging in. This method also creates three cookies with the request. The first two are domain bound, secure, and HTTP Only cookies with expirations.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;auth-token&lt;/code&gt; this is returned from the successful createClient call with an accompyining expiration which is 1 hour after issue.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;refresh-token&lt;/code&gt; is needed to refresh the session without having the user log back into the application with credentials. If you need it to stick around longer, adjust the &lt;code&gt;MaxAge&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;auth-issued-at&lt;/code&gt; is needed for the client to calculate when to prompt the user if they want to continue with their session (for my use case, 5 minutes before the auth-token expires). A lot of modern apps skip this and would just grab the refresh-token for a more seamless experience, but I&amp;rsquo;m going to need it. Also this cookie is not HTTP only, since I need to access the value on the client.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is the code so far (simplified)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;createClient&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;jsr:@supabase/supabase-js@2&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_domain&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;my-awesome-app.com&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Deno&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;serve&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;URLPattern&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/sign-in&amp;#34;&lt;/span&gt; }).&lt;span style=&#34;color:#a6e22e&#34;&gt;exec&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;formData&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;email&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;email&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;password&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;supabase&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createClient&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Deno&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SUPABASE_URL&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;Deno&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SUPABASE_ANON_KEY&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;supabase&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;auth&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;signInWithPassword&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;email&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;password&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;session&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;user&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;We could not log you into our application&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;403&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/plain&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`&amp;lt;h1&amp;gt;Yay! Go to your &amp;lt;a href=&amp;#34;/dashboard&amp;#34;&amp;gt;dashboard&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;`&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/html&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;set-cookie&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;`auth-token=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;session&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;access_token&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;domain:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_domain&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;SameSite=Lax;Path=/;Secure;HttpOnly;MaxAge=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;session&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;expires_in&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;set-cookie&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;`refresh-token=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;session&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;refresh_token&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;domain:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_domain&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;SameSite=Lax;Path=/;Secure;HttpOnly;MaxAge=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;session&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;expires_in&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;set-cookie&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;`auth-issued-at=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;Date.&lt;span style=&#34;color:#a6e22e&#34;&gt;now&lt;/span&gt;()&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;domain:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_domain&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;SameSite=Lax;Path=/;Secure;MaxAge=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;180&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;GET&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;signInTemplate&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;TextDecoder&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;decode&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Deno&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;readFile&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;layouts/sign-in.html&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;signInTemplate&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/html&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; is not supported`&lt;/span&gt;, { &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;405&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;reading-the-saved-cookies-on-the-server&#34;&gt;Reading the saved cookies on the server&lt;/h2&gt;
&lt;p&gt;Cookies are passed along with each request from the browser. I use a simple function that takes in the request, finds the cookie I ask for and then passes back the value.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// req is the request object from Deno.serve
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// name is the name of the cookie to find
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getCookieValue&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cookies&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cookie&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;cookie&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;; &amp;#39;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cookieValue&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cookies&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;filter&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;cookie&lt;/span&gt; =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cookie&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;includes&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;=`&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;cookieValue&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cookieValue&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;length&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cookieValue&lt;/span&gt;[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;].&lt;span style=&#34;color:#a6e22e&#34;&gt;split&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;=&amp;#39;&lt;/span&gt;)[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cookieValue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;protecting-endpoints&#34;&gt;Protecting endpoints&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Create a user-aware Supabase client. The one we&amp;rsquo;ve been using up until now has just been using the anonymous keys which aren&amp;rsquo;t tied to a particular user. However, the database has been set up with a bunch of Row-Level Security (RLS) Policies that do care who the user is. From this &lt;a href=&#34;&#34;&gt;Github issue&lt;/a&gt; I put together a function that takes in the user&amp;rsquo;s auth token from supabase and then creates a user aware client. I don&amp;rsquo;t have &lt;code&gt;persistSession&lt;/code&gt; or &lt;code&gt;autoResfreshSession&lt;/code&gt; set, since I will be handling those myself.&lt;/li&gt;
&lt;li&gt;Every protected route calls my verify user function that takes a users auth token, verifys the token with a call to the unauthenticated Supabase client and if it looks good, creates and sets the user-aware Supabase client that the rest of the code for that endpoint will use.&lt;/li&gt;
&lt;li&gt;If the auth token is expired or bad, send that information back with the request so my webpage can redirect the user to a login page. (NOTE, some may just grab the refresh token now and use it, depends on business requirements)&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_supabase&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//https://github.com/supabase/supabase/issues/8490#issuecomment-1219766620
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createServerDbClient&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;accessToken&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createClient&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Deno&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SUPABASE_URL&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;Deno&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SUPABASE_ANON_KEY&amp;#34;&lt;/span&gt;), {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;db&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;schema&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;public&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;auth&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;persistSession&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;autoRefreshToken&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;global&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;accessToken&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;Authorization&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`Bearer &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;accessToken&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            } &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// called before any protected endpoint and result checked.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// req is the request object from Deno.serve
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;VerifyUser&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;token&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getCookieValue&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;auth-token&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;token&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;verified&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`No token found`&lt;/span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// unauthenticated supabase client
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;supabaseClient&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createClient&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Deno&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SUPABASE_URL&amp;#34;&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;Deno&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SUPABASE_ANON_KEY&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// check what supabase says,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// getClaims throws exception with expired token
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;supabaseClient&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;auth&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getClaims&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;token&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;verified&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;verified&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Supabase client exception thrown&amp;#39;&lt;/span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// looks good, let the user through and set the supabase client for use
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;_supabase&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createServerDbClient&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;token&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;verified&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;this-will-work-for-an-hour&#34;&gt;This will work&amp;hellip; for an hour&lt;/h2&gt;
&lt;p&gt;Calls to my protected endpoint will work for an hour before that auth token expires. So, I needed to check on the webpage when the token is going to expire so I can alert the user. That&amp;rsquo;s why I set the &lt;code&gt;auth-issued-at&lt;/code&gt; cookie to be readable by JavaScript on the webpage. The basic plan is to figure out the current time, the time the auth code was issued and how long it has left. For my purposes, I do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Longer that 5 minutes left? Create a call to &lt;code&gt;setTimeout&lt;/code&gt; that will alert the user when they have five minutes left.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Less than 5 minutes? Alert the user.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Expired? Send the user to the login page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The alert is a modal that lets the user choose to continue working (i.e. refresh the session) or to log them out. Assuming they want to continue then I call the endpoint to refresh the session. But there is the case to consider that the alert was up and received no input for longer than the time to expire. In that case I show another alert on any interaction that the session expired and kick them back to the login page.&lt;/p&gt;
&lt;h2 id=&#34;using-the-refresh-token&#34;&gt;Using the refresh token&lt;/h2&gt;
&lt;p&gt;I protect the refresh endpoint just like I do other protected endpoints, I make sure the user has a valid and unexpired token before letting a call go through. This is a business requirement I have, others for a more seamless workflow may allow a refresh token to be used after a session has already expired.&lt;/p&gt;
&lt;p&gt;The trickest part was figuring out what endpoint to call. There doesn&amp;rsquo;t seem to be any good documentation around it so I needed to dig around a bunch to find what other libraries were doing in their code. But once the sleuthing was done it was pretty straight forward.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// verify the user can access restricted content
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;verify&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;VerifyUser&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;verify&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;verified&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`Please log back in: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;verify&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;404&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/html&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// user requested to refresh the session
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;URLPattern&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/refresh_session&amp;#34;&lt;/span&gt; }).&lt;span style=&#34;color:#a6e22e&#34;&gt;exec&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refreshToken&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getCookieValue&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;refresh-token&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;// send the token to supabase
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetching&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Deno&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SUPABASE_URL&amp;#34;&lt;/span&gt;)&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/auth/v1/token?grant_type=refresh_token`&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;apikey&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Deno&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;SUPABASE_ANON_KEY&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;JSON&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stringify&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;refresh_token&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;refreshToken&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        })
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetching&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// if we don&amp;#39;t get a new token, then the refresh failed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;access_token&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Refresh failed&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;403&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/html&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// everything worked, refresh the cookies
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;Processed&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text/html&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }); 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;set-cookie&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;`auth-token=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;access_token&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;domain:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_domain&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;SameSite=Lax;Path=/;Secure;HttpOnly;MaxAge=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;expires_in&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;set-cookie&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;`refresh-token=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;refresh_token&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;domain:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_domain&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;SameSite=Lax;Path=/;Secure;HttpOnly;MaxAge=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;expires_in&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;set-cookie&amp;#34;&lt;/span&gt;,&lt;span style=&#34;color:#e6db74&#34;&gt;`auth-issued-at=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;Date.&lt;span style=&#34;color:#a6e22e&#34;&gt;now&lt;/span&gt;()&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;domain:&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;_domain&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;;SameSite=Lax;Path=/;Secure;MaxAge=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;24&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;180&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;req&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; is not supported`&lt;/span&gt;, { &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;405&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;and-done&#34;&gt;And done!&lt;/h2&gt;
&lt;p&gt;Now I have a blueprint on how to log users in, save the needed cookies, and how to use the refresh token to get another auth token when needed.&lt;/p&gt;
</description>
      <source:markdown>A project I&#39;m currently working on is using [Supabase](https://supabase.com/) for its database and authentication layer. And so far so good. That is until I wanted to move all the calls to the Supabase API server side for server side rendering (SSR). Now, they do have lots of documentation and tutorials out there and a bunch of third party tutorials all over the web. But nothing quite fit what I was trying to do. I&#39;m using [Deno](https://deno.com/), for server side JavaScript, and I figured with Supabase&#39;s SSR package it would be a pretty easy to implement. You know what they say about assumptions 😆

Since I&#39;m not using any additional framework, their tutorial documentation wasn&#39;t all that helpful. But it did give me enough of a sense of what I needed to do. Which boils down to setting cookies, reading cookies, and calling the Supabase client library. Then digging around Github, Github Issues, and Stack Overflow I eventually pieced it together.

So, to maybe save someone else (or an AI scraper) some time in the future. Here is I tied up Supabase SSR with Deno and no additional framework:

# Putting it all together

## Logging in a user

1. I&#39;ve saved some key info in enviromental variables, namely `Deno.env.get(&#34;SUPABASE_URL&#34;)` and `Deno.env.get(&#34;SUPABASE_ANON_KEY&#34;)`

2. I&#39;m importing the supabase client with the following statement at the top of my main.js file: `import { createClient } from &#34;jsr:@supabase/supabase-js@2&#34;`

3. I have a login page with a simple HTML form that does a `POST` request to an endpoint and then calls the `createClient` to determine if the credentials are correct and we have a valid user logging in. This method also creates three cookies with the request. The first two are domain bound, secure, and HTTP Only cookies with expirations.
   
   1. `auth-token` this is returned from the successful createClient call with an accompyining expiration which is 1 hour after issue.
   
   2. `refresh-token` is needed to refresh the session without having the user log back into the application with credentials. If you need it to stick around longer, adjust the `MaxAge`
   
   3. `auth-issued-at` is needed for the client to calculate when to prompt the user if they want to continue with their session (for my use case, 5 minutes before the auth-token expires). A lot of modern apps skip this and would just grab the refresh-token for a more seamless experience, but I&#39;m going to need it. Also this cookie is not HTTP only, since I need to access the value on the client.

Here is the code so far (simplified)

```javascript
import { createClient } from &#34;jsr:@supabase/supabase-js@2&#34;;
const _domain = &#39;my-awesome-app.com&#39;;

Deno.serve(async (req) =&gt; {
    if (new URLPattern({ pathname: &#34;/sign-in&#34; }).exec(req.url)) {
        if (req.method === &#34;POST&#34;) {
            const value = await req.formData();
            const email = value.get(&#39;email&#39;);
            const password = value.get(&#39;password&#39;);
            supabase = createClient(Deno.env.get(&#34;SUPABASE_URL&#34;), Deno.env.get(&#34;SUPABASE_ANON_KEY&#34;));
            const { data, error } = await supabase.auth.signInWithPassword({ email, password });
            if (error || data.session == null || data.user == null) {
                return new Response(&#34;We could not log you into our application&#34;, {
                    status: 403,
                    headers: {
                        &#34;content-type&#34;: &#34;text/plain&#34;
                    },
                });
            }
            let response = new Response(`&lt;h1&gt;Yay! Go to your &lt;a href=&#34;https://heyloura.com/dashboard&#34;&gt;dashboard&lt;/a&gt;&lt;/h1&gt;`, {
                status: 200,
                headers: {
                    &#34;content-type&#34;: &#34;text/html&#34;,
                },
            });
            response.headers.append(&#34;set-cookie&#34;, `auth-token=${data.session.access_token};domain:${_domain};SameSite=Lax;Path=/;Secure;HttpOnly;MaxAge=${data.session.expires_in}`);
            response.headers.append(&#34;set-cookie&#34;, `refresh-token=${data.session.refresh_token};domain:${_domain};SameSite=Lax;Path=/;Secure;HttpOnly;MaxAge=${data.session.expires_in}`);
            response.headers.append(&#34;set-cookie&#34;, `auth-issued-at=${Date.now()};domain:${_domain};SameSite=Lax;Path=/;Secure;MaxAge=${60 * 60 * 24 * 180}`);
            return response;
        } else if (req.method === &#34;GET&#34;) {
            const signInTemplate = new TextDecoder().decode(await Deno.readFile(&#34;layouts/sign-in.html&#34;));
            return new Response(signInTemplate, {
                status: 200,
                headers: {
                    &#34;content-type&#34;: &#34;text/html&#34;
                },
            });
        } else {
            return new Response(`${req.method} is not supported`, { status: 405 });
        }
    }
});
```
## Reading the saved cookies on the server

Cookies are passed along with each request from the browser. I use a simple function that takes in the request, finds the cookie I ask for and then passes back the value.

```javascript
// req is the request object from Deno.serve
// name is the name of the cookie to find
function getCookieValue(req, name) {
    const cookies = req.headers.get(&#34;cookie&#34;) ? req.headers.get(&#34;cookie&#34;).split(&#39;; &#39;) : [];
    let cookieValue = cookies.filter(cookie =&gt; cookie.includes(`${name}=`));
    cookieValue = cookieValue.length &gt; 0 ? cookieValue[0].split(&#39;=&#39;)[1] : &#39;&#39;;
    return cookieValue;
}
```
## Protecting endpoints

1. Create a user-aware Supabase client. The one we&#39;ve been using up until now has just been using the anonymous keys which aren&#39;t tied to a particular user. However, the database has been set up with a bunch of Row-Level Security (RLS) Policies that do care who the user is. From this [Github issue]() I put together a function that takes in the user&#39;s auth token from supabase and then creates a user aware client. I don&#39;t have `persistSession` or `autoResfreshSession` set, since I will be handling those myself.
2. Every protected route calls my verify user function that takes a users auth token, verifys the token with a call to the unauthenticated Supabase client and if it looks good, creates and sets the user-aware Supabase client that the rest of the code for that endpoint will use.
3. If the auth token is expired or bad, send that information back with the request so my webpage can redirect the user to a login page. (NOTE, some may just grab the refresh token now and use it, depends on business requirements)
   
   

```javascript
let _supabase = null;

//https://github.com/supabase/supabase/issues/8490#issuecomment-1219766620
function createServerDbClient(accessToken) {
    return createClient(Deno.env.get(&#34;SUPABASE_URL&#34;), Deno.env.get(&#34;SUPABASE_ANON_KEY&#34;), {
        db: {
            schema: &#39;public&#39;,
        },
        auth: {
            persistSession: false,
            autoRefreshToken: false,
        },
        global: {
            headers: accessToken ? {
            Authorization: `Bearer ${accessToken}`,
            } : null,
        },
    });
}

// called before any protected endpoint and result checked.
// req is the request object from Deno.serve
async function VerifyUser(req) {
    const token = getCookieValue(req, &#39;auth-token&#39;);
    if(!token) {
        return {verified: false, error: `No token found`};
    }
    // unauthenticated supabase client
    const supabaseClient = createClient(Deno.env.get(&#34;SUPABASE_URL&#34;), Deno.env.get(&#34;SUPABASE_ANON_KEY&#34;));
    try {
        // check what supabase says,
        // getClaims throws exception with expired token
        const { data, error } = await supabaseClient.auth.getClaims(token);
        if (error) {
            return {verified: false, error: error};
        }
    } catch {
        return {verified: false, error: &#39;Supabase client exception thrown&#39;};
    }
    // looks good, let the user through and set the supabase client for use
    _supabase = createServerDbClient(token);
    return {verified: true, error: null};
}
```
## This will work... for an hour

Calls to my protected endpoint will work for an hour before that auth token expires. So, I needed to check on the webpage when the token is going to expire so I can alert the user. That&#39;s why I set the `auth-issued-at` cookie to be readable by JavaScript on the webpage. The basic plan is to figure out the current time, the time the auth code was issued and how long it has left. For my purposes, I do the following:

1. Longer that 5 minutes left? Create a call to `setTimeout` that will alert the user when they have five minutes left.

2. Less than 5 minutes? Alert the user.

3. Expired? Send the user to the login page.

The alert is a modal that lets the user choose to continue working (i.e. refresh the session) or to log them out. Assuming they want to continue then I call the endpoint to refresh the session. But there is the case to consider that the alert was up and received no input for longer than the time to expire. In that case I show another alert on any interaction that the session expired and kick them back to the login page.

## Using the refresh token

I protect the refresh endpoint just like I do other protected endpoints, I make sure the user has a valid and unexpired token before letting a call go through. This is a business requirement I have, others for a more seamless workflow may allow a refresh token to be used after a session has already expired.

The trickest part was figuring out what endpoint to call. There doesn&#39;t seem to be any good documentation around it so I needed to dig around a bunch to find what other libraries were doing in their code. But once the sleuthing was done it was pretty straight forward.

```javascript
// verify the user can access restricted content
const verify = await VerifyUser(req);
if(!verify.verified) {
  return new Response(`Please log back in: ${verify.error}`, {
    status: 404,
    headers: {
      &#34;content-type&#34;: &#34;text/html&#34;
    },
  });
}    

// user requested to refresh the session
  if(new URLPattern({ pathname: &#34;/refresh_session&#34; }).exec(req.url)) {
    if (req.method === &#34;POST&#34;) {
      const refreshToken = getCookieValue(req, &#39;refresh-token&#39;);
      // send the token to supabase
      const fetching = await fetch(`${Deno.env.get(&#34;SUPABASE_URL&#34;)}/auth/v1/token?grant_type=refresh_token`, {
        method: &#39;POST&#39;,
        headers: {
          &#39;Content-Type&#39;: &#39;application/json&#39;,
          &#39;apikey&#39;: Deno.env.get(&#34;SUPABASE_ANON_KEY&#34;)
        },
        body: JSON.stringify({
          refresh_token: refreshToken,
        })
      });
      const data = await fetching.json();

    // if we don&#39;t get a new token, then the refresh failed
    if(!data.access_token) {
      return new Response(&#39;Refresh failed&#39;, {
        status: 403,
        headers: {
          &#34;content-type&#34;: &#34;text/html&#34;,
        },
      });
    }

    // everything worked, refresh the cookies
      let response = new Response(&#39;Processed&#39;, {
        status: 200,
        headers: {
          &#34;content-type&#34;: &#34;text/html&#34;,
        },
      }); 

      response.headers.append(&#34;set-cookie&#34;,`auth-token=${data.access_token};domain:${_domain};SameSite=Lax;Path=/;Secure;HttpOnly;MaxAge=${data.expires_in}`);
      response.headers.append(&#34;set-cookie&#34;,`refresh-token=${data.refresh_token};domain:${_domain};SameSite=Lax;Path=/;Secure;HttpOnly;MaxAge=${data.expires_in}`);
      response.headers.append(&#34;set-cookie&#34;,`auth-issued-at=${Date.now()};domain:${_domain};SameSite=Lax;Path=/;Secure;MaxAge=${60*60*24*180}`);
      return response;
    } else {
      return new Response(`${req.method} is not supported`, { status: 405 });
    }
  }
```
## And done!

Now I have a blueprint on how to log users in, save the needed cookies, and how to use the refresh token to get another auth token when needed. 
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2025/11/19/work-has-been-so-demoralizing.html</link>
      <pubDate>Wed, 19 Nov 2025 08:44:37 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2025/11/19/work-has-been-so-demoralizing.html</guid>
      <description>&lt;p&gt;Work has been so demoralizing the last couple of weeks (well, for years but it has really ramped up). Sigh. I think I might use some vacation time and try to reset my mindset.&lt;/p&gt;
</description>
      <source:markdown>Work has been so demoralizing the last couple of weeks (well, for years but it has really ramped up). Sigh. I think I might use some vacation time and try to reset my mindset.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2025/11/18/so-my-son-dropped-his.html</link>
      <pubDate>Tue, 18 Nov 2025 19:43:30 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2025/11/18/so-my-son-dropped-his.html</guid>
      <description>&lt;p&gt;So my son dropped his phone (again*100) and the screen is totally busted. Good thing its a Fairphone. Replacement is ordered and I&amp;rsquo;ll teach him how to install it 😎&lt;/p&gt;
</description>
      <source:markdown>So my son dropped his phone (again*100) and the screen is totally busted. Good thing its a Fairphone. Replacement is ordered and I&#39;ll teach him how to install it 😎
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura.com/2025/11/18/my-kung-fu-brother-and.html</link>
      <pubDate>Tue, 18 Nov 2025 09:45:58 -0400</pubDate>
      
      <guid>http://heyloura.micro.blog/2025/11/18/my-kung-fu-brother-and.html</guid>
      <description>&lt;p&gt;My kung fu brother and I had our second tournament! I did four tai chi events, 24 movement, 42 movement, short yang fan and yang broadsword. Taking a very serious photo is a tradition at my school 😁&lt;/p&gt;
&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2025/f116935d24.png&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;&#34;&gt;
</description>
      <source:markdown>My kung fu brother and I had our second tournament! I did four tai chi events, 24 movement, 42 movement, short yang fan and yang broadsword. Taking a very serious photo is a tradition at my school 😁 

&lt;img src=&#34;https://cdn.uploads.micro.blog/88328/2025/f116935d24.png&#34; width=&#34;600&#34; height=&#34;600&#34; alt=&#34;&#34;&gt;
</source:markdown>
    </item>
    
  </channel>
</rss>
