<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ashley Connor</title>
    <description>The latest articles on DEV Community by Ashley Connor (@ashleyconnor).</description>
    <link>https://dev.to/ashleyconnor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F377159%2F101ab129-96dc-46ab-b9ec-7d24d2bb87eb.jpeg</url>
      <title>DEV Community: Ashley Connor</title>
      <link>https://dev.to/ashleyconnor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ashleyconnor"/>
    <language>en</language>
    <item>
      <title>Why the Python Standard Library Needs a run_sync()</title>
      <dc:creator>Ashley Connor</dc:creator>
      <pubDate>Thu, 24 Apr 2025 19:53:29 +0000</pubDate>
      <link>https://dev.to/ashleyconnor/why-the-python-standard-library-needs-a-runsync-g3c</link>
      <guid>https://dev.to/ashleyconnor/why-the-python-standard-library-needs-a-runsync-g3c</guid>
      <description>&lt;p&gt;I recently updated a small script I wrote to automate file uploads to a website with no API. It uses the excellent &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; project to drive a browser - making the tedious task of filling out forms painless.&lt;/p&gt;

&lt;p&gt;But there was one big annoyance: authentication.&lt;/p&gt;

&lt;p&gt;The upload forms sit behind a login screen that requires a username, password, and TOTP token. To get around this, I initially used &lt;a href="https://playwright.dev/python/docs/api/class-browsertype#browser-type-launch-persistent-context" rel="noopener noreferrer"&gt;Playwright's &lt;code&gt;launch_persistent_context&lt;/code&gt;&lt;/a&gt; so that cookies could persist across sessions. This worked fine, but I still had to log in every 7 days when the session expired.&lt;/p&gt;

&lt;p&gt;I wanted to fix that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter 1Password
&lt;/h2&gt;

&lt;p&gt;The credentials to this website are stored in my personal 1Password Vault. 1Password also has a &lt;a href="https://github.com/1Password/onepassword-sdk-python" rel="noopener noreferrer"&gt;Python SDK&lt;/a&gt;. Perfect, right?&lt;/p&gt;

&lt;p&gt;Well...&lt;/p&gt;

&lt;p&gt;For reasons I still don’t fully understand, 1Password provides &lt;strong&gt;only&lt;/strong&gt; an &lt;strong&gt;async&lt;/strong&gt; Python SDK—no synchronous methods at all.&lt;/p&gt;

&lt;p&gt;Naively, I installed the SDK and started calling it from my existing (synchronous) Playwright code.&lt;/p&gt;

&lt;p&gt;Now the script could fetch credentials including the TOTP from 1Password. That meant I no longer needed persistent context or manual logins. But then I hit this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: It looks like you are using Playwright Sync API inside the asyncio loop. Please use the Async API instead.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Playwright smartly offers both sync and async APIs. So I switched to using &lt;code&gt;async_playwright&lt;/code&gt; and then tediously sprinkled &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; throughout my code.&lt;/p&gt;

&lt;p&gt;That fixed the issue. The script now worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait...But why?
&lt;/h2&gt;

&lt;p&gt;Before committing these changes, I paused. Had I just rewritten a bunch of working code to accommodate one library's decision?&lt;/p&gt;

&lt;p&gt;Was there no way to bridge async and sync without a full rewrite?&lt;/p&gt;

&lt;p&gt;It turns out there is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coro&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_running_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# No running loop
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coro&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Already in an event loop (e.g. Jupyter, FastAPI, etc.)
&lt;/span&gt;        &lt;span class="c1"&gt;# Run the coroutine in a new thread
&lt;/span&gt;        &lt;span class="n"&gt;result_container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coro&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;result_container&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;

        &lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result_container&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gnarly snippet lets you run an &lt;code&gt;async&lt;/code&gt; function and retrieve its result in synchronous code.&lt;/p&gt;

&lt;p&gt;How it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Attempt to fetch the &lt;a href="https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.get_running_loop" rel="noopener noreferrer"&gt;running async loop&lt;/a&gt;

&lt;ol&gt;
&lt;li&gt;If a loop is available we create a container for our async functions result&lt;/li&gt;
&lt;li&gt;Create an inner function &lt;code&gt;runner&lt;/code&gt; to execute our async function and push the result into the container&lt;/li&gt;
&lt;li&gt;Create a thread to execute our &lt;code&gt;runner&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.python.org/3/library/threading.html#threading.Thread.start" rel="noopener noreferrer"&gt;Start&lt;/a&gt; the thread&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.python.org/3/library/threading.html#threading.Thread.join" rel="noopener noreferrer"&gt;Join&lt;/a&gt; the thread (this blocks our current thread until the &lt;code&gt;thread&lt;/code&gt; is finished)&lt;/li&gt;
&lt;li&gt;Return the result&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;If a &lt;code&gt;RuntimeError&lt;/code&gt; is raised, no loop is running, we catch the exception and return a call to &lt;a href="https://docs.python.org/3/library/asyncio-runner.html#asyncio.run" rel="noopener noreferrer"&gt;&lt;code&gt;run&lt;/code&gt;&lt;/a&gt; which takes care of executing our async function for us&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Now I could move all my 1Password interactions to a dedicated async function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_onepassword_creds&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="n"&gt;credentials&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_onepassword_creds&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more full async rewrite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Isn’t This Built-In?
&lt;/h2&gt;

&lt;p&gt;I was happy with this result - but it begs the question. Why is all of this required? Surely this is a common enough problem that a standard library solution is warranted?&lt;/p&gt;

&lt;p&gt;Well...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I am unenthusiastic about providing this functionality in the stdlib. This is really not something that a well-behaved async application should do, and having the API would legitimize an idiom that is likely to come back and stab you in the back when you least need it. You would be better off refactoring your application so you don’t need this ugly hack. (Of course, I understand that realistically that’s not always possible, but if your specific app’s transition to an async world requires this hack, well, it’s only 7 lines of code.)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Guido van Rossum&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://discuss.python.org/t/calling-coroutines-from-sync-code-2/24093/2" rel="noopener noreferrer"&gt;Source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That thread includes a simpler (but less robust) example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coro&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_running_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coro&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_until_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coro&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this fails if the current loop is already running—like in Jupyter, FastAPI, or some test environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mess We’re In
&lt;/h2&gt;

&lt;p&gt;So there you have it.&lt;/p&gt;

&lt;p&gt;Want to call async code from sync Python? Just copy-paste these &lt;del&gt;7&lt;/del&gt; 15 lines into every project.&lt;/p&gt;

&lt;p&gt;It’s not ideal. And it's brittle: &lt;code&gt;get_running_loop()&lt;/code&gt; wasn’t even available before Python 3.7.&lt;/p&gt;

&lt;p&gt;There are other real-world use cases too - like debugging with &lt;code&gt;pdb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# fail
(Pdb) await my_async_func()
*** SyntaxError: 'await' outside function

# works
(Pdb) result = run_sync(my_async_func())
(Pdb) print(result)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A poor developer experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As more libraries adopt async-only interfaces and as more developers try to mix async and sync code, this problem will only get worse.&lt;/p&gt;

&lt;p&gt;Python needs a standard library solution for this. It’s time the core team reconsiders their position.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Authenticated Server Rendered pages with SvelteKit and Supabase</title>
      <dc:creator>Ashley Connor</dc:creator>
      <pubDate>Thu, 24 Jun 2021 20:27:13 +0000</pubDate>
      <link>https://dev.to/ashleyconnor/authenticated-server-rendered-pages-with-sveltekit-and-supabase-pif</link>
      <guid>https://dev.to/ashleyconnor/authenticated-server-rendered-pages-with-sveltekit-and-supabase-pif</guid>
      <description>&lt;p&gt;If your &lt;a href="https://kit.svelte.dev" rel="noopener noreferrer"&gt;SvelteKit&lt;/a&gt; web application has an authenticated page that contains private data from &lt;a href="https://supabase.io" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt; and you want to provide your users with a server rendered page, you'll need a way to make authenticated calls from your &lt;a href="https://kit.svelte.dev/docs#routing-endpoints" rel="noopener noreferrer"&gt;SvelteKit endpoints&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this post I'll explain how you can pass your Supabase &lt;a href="https://jwt.io" rel="noopener noreferrer"&gt;JWT&lt;/a&gt; to your SvelteKit endpoint and store it as a cookie.&lt;/p&gt;

&lt;p&gt;This cookie can then be used to make authenticated calls to Supabase on a users behalf which allows you to generate authenticated server rendered pages.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;Server Side Rendering provides a better user experience as it provides the browser with a an immediate response that can be rendered without waiting on Javascript requests.&lt;/p&gt;

&lt;p&gt;A user may have Javascript disabled on their browser and SSR will provide a somewhat usable experience.&lt;/p&gt;

&lt;p&gt;I personally dislike the "flicker" that is common to web applications that depend on data retrieved after the page has first loaded.&lt;/p&gt;




&lt;h2&gt;
  
  
  How
&lt;/h2&gt;

&lt;p&gt;In order to make authenticated calls to Supabase within our SvelteKit endpoints we need to send the JWT to the server and respond with a cookie.&lt;/p&gt;

&lt;p&gt;We can do this in our &lt;code&gt;auth.onAuthStateChange()&lt;/code&gt; callback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/__layout.svelte&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$app/stores&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$lib/supabaseClient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setAuthCookie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unsetAuthCookie&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$lib/utils/session&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// this should run on every page so I put this code in my `__layout.svelte` file&lt;/span&gt;
&lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onAuthStateChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGNED_OUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setAuthCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_session&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;unsetAuthCookie&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;setAuthCookie&lt;/code&gt; makes a request to an endpoint with the JWT and responds with a cookie and &lt;code&gt;unsetAuthCookie&lt;/code&gt; unsets the same cookie:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/utils/session.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setServerSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/auth.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;same-origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setAuthCookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setServerSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGNED_IN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unsetAuthCookie&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setServerSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SIGNED_OUT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's important to include the event as the &lt;code&gt;supabase-js&lt;/code&gt; library expects it.&lt;/p&gt;

&lt;p&gt;Our endpoint to set the cookie looks like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/routes/api/auth.json.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="cm"&gt;/*, res: Response (read the notes below) */&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Unlike, Next.js API handlers you don't get the response object in a SvelteKit endpoint. As a result, you cannot invoke the below method to set cookies on the responses.&lt;/span&gt;
  &lt;span class="c1"&gt;// await supabaseClient.auth.api.setAuthCookie(req, res);&lt;/span&gt;
  &lt;span class="c1"&gt;// `supabaseClient.auth.api.setAuthCookie(req, res)` is dependent on both the request and the responses&lt;/span&gt;
  &lt;span class="c1"&gt;// `req` used to perform few validations before setting the cookies&lt;/span&gt;
  &lt;span class="c1"&gt;// `res` is used for setting the cookies&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You're probably thinking - "Where is the cookie set?" - that has to be done in our SvelteKit &lt;a href="https://kit.svelte.dev/docs#hooks" rel="noopener noreferrer"&gt;&lt;code&gt;hook&lt;/code&gt;&lt;/a&gt; because we don't have access to the response object to pass to the &lt;code&gt;supabase.auth.setAuthCookie&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;So our &lt;code&gt;hooks.js&lt;/code&gt; file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/hooks.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Parses `req.headers.cookie` adding them as attribute `req.cookies, as `auth.api.getUserByCookie` expects parsed cookies on attribute `req.cookies`&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expressStyleRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toExpressRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// We can then fetch the authenticated user using this cookie&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUserByCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expressStyleRequest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Add the user and the token to our locals so they are available on all SSR pages&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;expressStyleRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sb:token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// If we have a token, set the supabase client to use it so we can make authorized requests as that user&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// if auth request - set cookie in response headers&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/auth.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAuthCookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;toExpressResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toSvelteKitResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our helper functions &lt;code&gt;toExpressRequest&lt;/code&gt;, &lt;code&gt;toExpressResponse&lt;/code&gt;, &lt;code&gt;toSvelteKitResponse&lt;/code&gt; look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/utils/expressify.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cookie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Converts a SvelteKit request to a Express compatible request.
 * Supabase expects the cookies to be parsed.
 * @param {SvelteKit.Request} req
 * @returns Express.Request
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toExpressRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookie&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Converts a SvelteKit response into an Express compatible response.
 * @param {SvelteKit.Response} resp
 * @returns Express.Response
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toExpressResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;getHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
    &lt;span class="na"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Converts an Express style response to a SvelteKit compatible response
 * @param {Express.Response} resp
 * @returns SvelteKit.Response
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toSvelteKitResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;returnAbleResp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;returnAbleResp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Issues
&lt;/h2&gt;

&lt;p&gt;This works but it isn't ideal for the following reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Supabase stores the refresh token in &lt;code&gt;localstorage&lt;/code&gt; which makes it vulnerable to &lt;a href="https://owasp.org/www-community/attacks/xss/" rel="noopener noreferrer"&gt;XSS&lt;/a&gt;. This is done so the &lt;code&gt;supabase-js&lt;/code&gt; client library can refresh the token on the users behalf.&lt;/li&gt;
&lt;li&gt;There's no refresh mechanism on a SSR request.&lt;/li&gt;
&lt;li&gt;It doesn't follow the best practices for &lt;a href="https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/" rel="noopener noreferrer"&gt;JWTs on frontend clients&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Potential improvements
&lt;/h2&gt;

&lt;p&gt;Any improvement will likely make Supbase Auth more difficult to use, so what I'm proposing would be opt-in for users that want to improve their auth security.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Allow configuration of the refresh endpoint so we can proxy the request to Supabase ourselves. This would allow us to set a refresh token in a cookie and remove it from localstorage. There would need to be some extra security work in order to make sure a client wasn't sending the request directly to a Supabase auth endpoint.&lt;/li&gt;
&lt;li&gt;Expose a function to generate the cookie using just the token. This would save us having to generate Express-style request/response objects just to get the cookie.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;All of the code above and more is available in my repository &lt;a href="https://github.com/ashleyconnor/sveltekit-supabase-demo" rel="noopener noreferrer"&gt;sveltekit-supabase-demo&lt;/a&gt;. If you have any suggestions or feedback feel free to open an issue or PR.&lt;/p&gt;




&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;A huge thanks to &lt;a href="https://github.com/one-aalam" rel="noopener noreferrer"&gt;Aftab Alam&lt;/a&gt; for providing a &lt;a href="https://github.com/one-aalam/svelte-starter-kit/tree/auth-supabase" rel="noopener noreferrer"&gt;SvelteKit template&lt;/a&gt; that was a source for much of the code above.&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>sveltekit</category>
      <category>supabase</category>
      <category>jwt</category>
    </item>
    <item>
      <title>Deploying SvelteKit using Cloudflare Workers</title>
      <dc:creator>Ashley Connor</dc:creator>
      <pubDate>Mon, 21 Jun 2021 00:20:44 +0000</pubDate>
      <link>https://dev.to/ashleyconnor/deploying-sveltekit-using-cloudflare-workers-16ni</link>
      <guid>https://dev.to/ashleyconnor/deploying-sveltekit-using-cloudflare-workers-16ni</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Recently I've been using SvelteKit as the frontend for a project I've been working on and after some local development I wanted to get it up and&lt;br&gt;
running on the cloud.&lt;/p&gt;

&lt;p&gt;I chose &lt;a href="https://workers.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt; as I already have an account with them (this blog is hosted on &lt;a href="https://pages.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare Pages&lt;/a&gt;) however, the documentation on using the &lt;a href="https://github.com/sveltejs/kit/tree/master/packages/adapter-cloudflare-workers" rel="noopener noreferrer"&gt;adapter-cloudflare-workers&lt;/a&gt; is pretty sparse so I'll cover how I got it deployed here.&lt;/p&gt;
&lt;h2&gt;
  
  
  SvelteKit adapter configuration
&lt;/h2&gt;

&lt;p&gt;If you don't have an existing SvelteKit project you can follow allowing by generating the demo project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm init svelte@next my-cloudflare-workers-demo
? Which Svelte app template? › - Use arrow-keys. Return to submit.
❯   SvelteKit demo app &lt;span class="c"&gt;# select this&lt;/span&gt;
    Skeleton project
✔ Use TypeScript? … No / Yes
✔ Add ESLint &lt;span class="k"&gt;for &lt;/span&gt;code linting? … No / Yes
✔ Add Prettier &lt;span class="k"&gt;for &lt;/span&gt;code formatting? … No / Yes
✔ Copied project files

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-cloudflare-workers-demo
&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you're at the root of your project we can install the adapter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm i @sveltejs/adapter-cloudflare-workers@next
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's configure the adapter in our &lt;code&gt;svelte.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// first import the adapter&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;adapter&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sveltejs/adapter-cloudflare-workers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// then in the config object, add an adapter key under kit, and call the imported library&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;kit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// hydrate the &amp;lt;div id="svelte"&amp;gt; element in src/app.html&lt;/span&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#svelte&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all that's required from SvelteKit. Now onto the Cloudflare Workers configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrangler
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;To deploy to Cloudflare we will install the &lt;a href="https://developers.cloudflare.com/workers/cli-wrangler/install-update" rel="noopener noreferrer"&gt;Wrangler CLI&lt;/a&gt; tool and generating an empty configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm i @cloudflare/wrangler &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;wrangler init &lt;span class="nt"&gt;--site&lt;/span&gt; my-cloudflare-workers-demo
🕵️  You can find your zone_id &lt;span class="k"&gt;in &lt;/span&gt;the right sidebar of a zone&lt;span class="s1"&gt;'s overview tab at https://dash.cloudflare.com
🕵️  You can find your account_id in the right sidebar of your account'&lt;/span&gt;s Workers page
🕵️  You will need to update the following fields &lt;span class="k"&gt;in &lt;/span&gt;the created wrangler.toml file before continuing:
- account_id
✨  Successfully scaffolded workers site
✨  Successfully created a &lt;span class="sb"&gt;`&lt;/span&gt;wrangler.toml&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's follow the instructions given and login to our &lt;a href="https://dash.cloudflare.com" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt; dashboard to find our &lt;code&gt;account_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Login and then click on the "Workers" menu item on the right-hand side of the page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fff8qdpaczsuoauluf129.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fff8qdpaczsuoauluf129.png" alt="Cloudflare Workers menu item" width="742" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy your Account ID from the sidebar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsh6aczit734ld1oelqnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsh6aczit734ld1oelqnw.png" alt="Account ID" width="589" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Optionally if you have a domain setup with Cloudflare that you wish to use with your Cloudflare Workers, grab the &lt;code&gt;zone_id&lt;/code&gt; from your domain's dashboard.&lt;/p&gt;

&lt;p&gt;Now edit your &lt;code&gt;wrangler.toml&lt;/code&gt; configuration file with those values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;account_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'4b05422c8548d995a6132421d4c31bba'&lt;/span&gt;
&lt;span class="py"&gt;zone_id&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'2ea755fa5c01136f1d07a2013c2d1c6f'&lt;/span&gt; &lt;span class="c"&gt;# optional, if you don't specify this a workers.dev subdomain will be used.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we also need to make some adjustments to the &lt;code&gt;site&lt;/code&gt; key pointing it to the directories that will be generated by running build stage later on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="py"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"./build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;entry-point&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"./workers-site"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Authorization
&lt;/h3&gt;

&lt;p&gt;Next, let's authorize our Wrangler client so we can deploy our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wrangler login
Allow Wrangler to open a page &lt;span class="k"&gt;in &lt;/span&gt;your browser? &lt;span class="o"&gt;[&lt;/span&gt;y/n]
y
💁  Opened a &lt;span class="nb"&gt;link &lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;your default browser: https://dash.cloudflare.com/wrangler?key&lt;span class="o"&gt;=&lt;/span&gt;...
💁  Validating credentials...
✨  Successfully configured. You can find your configuration file at: /Users/ashleyconnor/.wrangler/config/default.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build our project and deploy
&lt;/h2&gt;

&lt;p&gt;First, we need to build our project which we can do using &lt;code&gt;npm run build&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run build

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; my-cloudflare-workers-demo@0.0.1 build
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; svelte-kit build

vite v2.3.7 building &lt;span class="k"&gt;for &lt;/span&gt;production...
✓ 34 modules transformed.
...
✓ 27 modules transformed.
.svelte-kit/output/server/app.js   64.33kb
...
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Using @sveltejs/adapter-cloudflare-workers
  ✔ &lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the &lt;code&gt;wrangler publish&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;wrangler publish
up to &lt;span class="nb"&gt;date &lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;0.729s
found 0 vulnerabilities

✨  Built successfully, built project size is 33 KiB.
🌀  Created namespace &lt;span class="k"&gt;for &lt;/span&gt;Workers Site &lt;span class="s2"&gt;"__my-cloudflare-workers-demo-workers_sites_assets"&lt;/span&gt;
✨  Success
🌀  Uploading site files
✨  Successfully published your script to
 https://my-cloudflare-workers-demo.ashconnor.workers.dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your Server Side Rendered SvelteKit application should be up and running on the URL above.&lt;/p&gt;

&lt;p&gt;You can visit the demo SvelteKit application running on Cloudflare Workers &lt;a href="https://my-cloudflare-workers-demo.ashconnor.workers.dev" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>svelte</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Configuring self-signed SSL certificates for local development</title>
      <dc:creator>Ashley Connor</dc:creator>
      <pubDate>Thu, 10 Jun 2021 17:04:37 +0000</pubDate>
      <link>https://dev.to/ashleyconnor/configuring-self-signed-ssl-certificates-for-local-development-35c5</link>
      <guid>https://dev.to/ashleyconnor/configuring-self-signed-ssl-certificates-for-local-development-35c5</guid>
      <description>&lt;p&gt;Sometimes it's preferible to keep your local development environment as close to production as possible.&lt;/p&gt;

&lt;p&gt;In this post I'll cover how to configure self-signed SSL certificates using a project called &lt;a href="https://github.com/FiloSottile/mkcert" rel="noopener noreferrer"&gt;&lt;code&gt;mkcert&lt;/code&gt;&lt;/a&gt; which makes&lt;br&gt;
creating, installing and removing self-signed certificates easier than ever.&lt;/p&gt;

&lt;p&gt;The instructions are slightly different depending on your local environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Common&lt;/li&gt;
&lt;li&gt;WSL2&lt;/li&gt;
&lt;li&gt;Firefox on Windows&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Common
&lt;/h2&gt;

&lt;p&gt;The first thing you will need is to install &lt;a href="https://github.com/FiloSottile/mkcert" rel="noopener noreferrer"&gt;mkcert&lt;/a&gt; which can be done via &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;&lt;code&gt;homebrew&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://docs.brew.sh/Homebrew-on-Linux" rel="noopener noreferrer"&gt;&lt;code&gt;homebrew&lt;/code&gt; for Linux&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;mkcert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you intend to use Firefox, you should also install &lt;a href="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS" rel="noopener noreferrer"&gt;&lt;code&gt;nss&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;nss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next run &lt;code&gt;mkcert&lt;/code&gt; and pass in the the domain names and IPs you want the certificate to include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mkcert mywebsite.test localhost 127.0.0.1 ::1

Created a new certificate valid &lt;span class="k"&gt;for &lt;/span&gt;the following names 📜
 - &lt;span class="s2"&gt;"mywebsite.test"&lt;/span&gt;
 - &lt;span class="s2"&gt;"localhost"&lt;/span&gt;
 - &lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;
 - &lt;span class="s2"&gt;"::1"&lt;/span&gt;

The certificate is at &lt;span class="s2"&gt;"./mywebsite.test+3.pem"&lt;/span&gt; and the key at &lt;span class="s2"&gt;"./mywebsite.test+3-key.pem"&lt;/span&gt; ✅

It will expire on 7 September 2023
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mkcert&lt;/code&gt; also accepts wildcards but some browsers (Firefox) will not accept those certificates.&lt;/p&gt;




&lt;p&gt;After the certificates are generated we can install the local &lt;a href="https://en.wikipedia.org/wiki/Certificate_authority" rel="noopener noreferrer"&gt;CA&lt;/a&gt; by running &lt;code&gt;mkcert&lt;/code&gt; with the install flag. This only needs to be done once as this CA will be used to sign all future certificates generated with &lt;code&gt;mkcert&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mkcert &lt;span class="nt"&gt;-install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this is you first time installing certificates using &lt;code&gt;mkcert&lt;/code&gt; you should see the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;The &lt;span class="nb"&gt;local &lt;/span&gt;CA is now installed &lt;span class="k"&gt;in &lt;/span&gt;the system trust store! ⚡️
&lt;span class="c"&gt;# or...&lt;/span&gt;
The &lt;span class="nb"&gt;local &lt;/span&gt;CA is already installed &lt;span class="k"&gt;in &lt;/span&gt;the system trust store! 👍
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point your can use your generated certificates with your development server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flask
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;flask run &lt;span class="nt"&gt;--cert&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mywebsite.test+3.pem &lt;span class="nt"&gt;--key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mywebsite.test+3-key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rails
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rails s &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="s1"&gt;'ssl://127.0.0.1:3000?key=mywebsite.test+3-key.pem&amp;amp;cert=mywebsite.test+3.pem'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  WSL2
&lt;/h2&gt;

&lt;p&gt;If you're running the &lt;a href="https://docs.microsoft.com/en-us/windows/wsl/install-win10" rel="noopener noreferrer"&gt;WSL2&lt;/a&gt; then there are few extra steps in order to get the Windows 10 host to accept the validity of the certificates.&lt;/p&gt;

&lt;p&gt;First we want to install &lt;code&gt;mkcert&lt;/code&gt; on Windows which we can do using &lt;a href="https://chocolatey.org/" rel="noopener noreferrer"&gt;&lt;code&gt;chocolatey&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open a Powershell terminal using the &lt;a href="https://adamtheautomator.com/wp-content/uploads/2020/11/FromSearch-1.png" rel="noopener noreferrer"&gt;administrator user&lt;/a&gt; and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;choco&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;install&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mkcert&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;code&gt;mkcert&lt;/code&gt; with the install flag like we did before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# first set the CAROOT path to point to the root CA we generated on WSL2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# you can get this directory by running mkcert -CAROOT from a WSL2 terminal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# if we don't do this it will install a different root CA and we will get warnings&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$CAROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"\\wsl&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;\Ubuntu\home\ashley\.local\share\mkcert\"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mkcert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-install&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;CA&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;installed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;trust&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;⚡️&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Note:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Firefox&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;support&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;platform.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ℹ️&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a popup like the one below. Click "Yes" to install the CA on the Windows 10 host.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2hz8zfah5yyada0315a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv2hz8zfah5yyada0315a.png" alt="You are about to install a certificate from a root authority (CA) claiming to represent..." width="616" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Firefox on Windows
&lt;/h2&gt;

&lt;p&gt;After that's installed let's fix Firefox so it doesn't complain that our certs are invalid.&lt;/p&gt;

&lt;p&gt;To do that open the Firefox browser and navigate to the settings and search for certificates:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xazor0f4x4le2ig307x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xazor0f4x4le2ig307x.png" alt="Firefox search results for " width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on "View Certificates"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0hlgkrdhfk80fyhsulu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0hlgkrdhfk80fyhsulu.png" alt="Firefox certificates manager" width="800" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next click on "Import". We want to locate the root CA from our Linux instance. Mine was located here but yours will be different depending on your WSL2 linux distro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;\\wsl&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;\Ubuntu\home\ashley\.local\share\mkcert\rootCA.pem&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed you will see your local CA in the list of Authorities:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6lct97yq808lzngm08k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6lct97yq808lzngm08k.png" alt="Firefox certificates manager - authorities tab" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now if we visit our local development server in Firefox on our Windows host we should see the page load without any warnings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Firefox
&lt;/h3&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vm22ysnuuscbhw09o5m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vm22ysnuuscbhw09o5m.png" alt="Firefox web browser localhost:5000 over HTTPS with no warnings" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Edge
&lt;/h3&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh599gtlytiui4abn615w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh599gtlytiui4abn615w.png" alt="Edge web browser localhost:5000 over HTTPS with no warnings" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Chrome
&lt;/h3&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajt668c0qgm2sp4frsnn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajt668c0qgm2sp4frsnn.png" alt="Chrome web browser localhost:5000 over HTTPS with no warnings" width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>security</category>
    </item>
  </channel>
</rss>
