<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Aakash Goplani's Blog]]></title><description><![CDATA[Senior Frontend Developer. Mostly Angular and Svelte.

I will be sharing my day-to-day learning experiences here with the community.]]></description><link>https://blog.aakashgoplani.in</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 13:40:49 GMT</lastBuildDate><atom:link href="https://blog.aakashgoplani.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Drawbacks of SvelteKitAuth You Should Know]]></title><description><![CDATA[This is the final chapter of the series Comprehensive Guide to SvelteKitAuth: Secure Authentication for SvelteKit Apps. In the past 14 chapters, we have explored various use cases, how-to guides, and the advantages of using SvelteKitAuth as the authe...]]></description><link>https://blog.aakashgoplani.in/drawbacks-of-sveltekitauth-you-should-know</link><guid isPermaLink="true">https://blog.aakashgoplani.in/drawbacks-of-sveltekitauth-you-should-know</guid><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[auth.js]]></category><category><![CDATA[disadvantage]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sat, 13 Jul 2024 22:00:05 GMT</pubDate><content:encoded><![CDATA[<p>This is the final chapter of the series Comprehensive Guide to SvelteKitAuth: Secure Authentication for SvelteKit Apps. In the past 14 chapters, we have explored various use cases, how-to guides, and the advantages of using SvelteKitAuth as the authentication library for SvelteKit. In this article, I will highlight a few disadvantages or shortcomings of SvelteKitAuth that must be considered as well.</p>
<ol>
<li><p><strong>Complex Scenarios Workarounds:</strong> If your project demands intricate authentication flows or data handling beyond basic login/logout, you might find yourself working around SvelteKitAuth's core functionality, leading to less maintainable code. A few examples of complex scenarios that are not directly possible to work with SvelteKitAuth are:</p>
<ul>
<li><p><em>Communication between client and server and vice-versa</em>: If you want to update data from client to server in real-time, it is not possible to do it with SvelteKitAuth.</p>
</li>
<li><p><em>Sharing sessions like having multiple preview URLs</em>: If your project requires multiple preview URLs (like in the case of Vercel), that won't be possible as we cannot customize callbackUrl, making it impossible to share sessions on applications deployed on different domains.</p>
</li>
</ul>
</li>
<li><p><strong>Limited updates for SvelteKitAuth:</strong> Authjs is the successor to NextAuth, which was designed only for Next.js. Although Authjs aims to be framework agnostic, its main focus is still on Next.js. In SvelteKitAuth, there are many functionalities and configurations that cannot be done, which are possible and can be easily done in Next.js.</p>
</li>
<li><p><strong>Poor Documentation:</strong> While working on SvelteKitAuth, I had to frequently switch from the documentation of <a target="_blank" href="https://next-auth.js.org/"><strong>NextAuth</strong></a> and <a target="_blank" href="https://authjs.dev/"><strong>Authjs</strong></a> as the latter did not have all the necessary information. Recently, they did a major update on the documentation front, but the quality and depth from the SvelteKitAuth perspective are still very poor.</p>
</li>
<li><p><strong>Limited community support</strong>: The community support for SvelteKitAuth is almost negligible. To add to the pain, the authors (or maintainers) of this project have stressed multiple times over different threads that it is their side project and they have limited time to invest. So if you come across a potential blocker, due to limited community support and limited support from maintainers, you could find yourself stuck. Note: They have recently started a Discord community and also try to stay active on Twitter, but support for SvelteKitAuth still remains sparse!</p>
</li>
<li><p><strong>Developer Experience:</strong> If I speak from my personal experience, I've been one of the early adopters of SvelteKitAuth. I started when we had version <em>v0.2</em> and worked until <em>v1.0</em>, which is almost one year of working with SvelteKitAuth. This is the time when I realized that SvelteKitAuth is not suitable for my enterprise web application because of the reasons described above. I then switched over to <a target="_blank" href="https://lucia-auth.com/"><strong>Lucia</strong></a> and the experience so far has been outstanding.</p>
</li>
</ol>
<p>In conclusion, while SvelteKitAuth offers a range of features and benefits for authentication in SvelteKit applications, it is not without its drawbacks. The need for workarounds in complex scenarios, limited updates and focus on Next.js, poor documentation, and minimal community support can pose significant challenges. These factors can make it less suitable for enterprise-level applications or projects with intricate authentication requirements. My personal experience with SvelteKitAuth highlighted these limitations, leading me to switch to Lucia, which has proven to be a more robust solution for my needs. As with any technology, it's essential to weigh the pros and cons to determine the best fit for your specific project requirements.</p>
]]></content:encoded></item><item><title><![CDATA[Managing Shared Sessions Across Multiple Applications in SvelteKitAuth]]></title><description><![CDATA[In this article, we will explore how to manage shared sessions across multiple applications using SvelteKitAuth. We will cover scenarios where applications are hosted on the same domain as well as on different domains, providing practical examples an...]]></description><link>https://blog.aakashgoplani.in/managing-shared-sessions-across-multiple-applications-in-sveltekitauth</link><guid isPermaLink="true">https://blog.aakashgoplani.in/managing-shared-sessions-across-multiple-applications-in-sveltekitauth</guid><category><![CDATA[preview-url]]></category><category><![CDATA[preview-domain]]></category><category><![CDATA[Sveltekit]]></category><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[Auth0]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sat, 13 Jul 2024 20:59:45 GMT</pubDate><content:encoded><![CDATA[<p>In this article, we will explore how to manage shared sessions across multiple applications using SvelteKitAuth. We will cover scenarios where applications are hosted on the same domain as well as on different domains, providing practical examples and configurations to achieve seamless session sharing.</p>
<h3 id="heading-sharing-session-with-applications-on-the-same-domain">Sharing session with applications on the same domain</h3>
<p>Let's take an example where my application is hosted on <code>myapp.com</code> and we perform authentication on <code>auth.myapp.com</code>. In this example, the domain <code>myapp.com</code> is shared among two applications, making it easy to share the session between them. The session is stored in the <code>session-token</code> cookie. If the applications share the same domain, we can simply update the <em>domain</em> property in the cookies. This is the example from the <code>SvelteKitAuthConfig</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// hooks.server.ts</span>

<span class="hljs-keyword">import</span> { dev } <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/environment'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Handle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> { SvelteKitAuth, <span class="hljs-keyword">type</span> SvelteKitAuthConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit'</span>;

<span class="hljs-keyword">const</span> { handle: getAuthConfig } = SvelteKitAuth(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> useSecureCookies = !dev;
  <span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {
    ...
    cookies: {
      callbackUrl: {
        name: <span class="hljs-string">`<span class="hljs-subst">${useSecureCookies ? <span class="hljs-string">'__Secure-'</span> : <span class="hljs-string">''</span>}</span>authjs.callback-url`</span>,
        options: {
          httpOnly: <span class="hljs-literal">true</span>,
          sameSite: useSecureCookies ? <span class="hljs-string">'none'</span> : <span class="hljs-string">'lax'</span>,
          path: <span class="hljs-string">'/'</span>,
          secure: useSecureCookies,
          domain: <span class="hljs-string">'.myapp.com'</span>
        }
      },
      pkceCodeVerifier: {
        name: <span class="hljs-string">`<span class="hljs-subst">${useSecureCookies ? <span class="hljs-string">'__Secure-'</span> : <span class="hljs-string">''</span>}</span>authjs.pkce.code_verifier`</span>,
        options: {
          httpOnly: <span class="hljs-literal">true</span>,
          sameSite: useSecureCookies ? <span class="hljs-string">'none'</span> : <span class="hljs-string">'lax'</span>,
          path: <span class="hljs-string">'/'</span>,
          secure: useSecureCookies,
          domain: <span class="hljs-string">'.myapp.com'</span>
        }
      },
      sessionToken: {
        name: <span class="hljs-string">`<span class="hljs-subst">${useSecureCookies ? <span class="hljs-string">'__Secure-'</span> : <span class="hljs-string">''</span>}</span>authjs.session-token`</span>,
        options: {
          httpOnly: <span class="hljs-literal">true</span>,
          sameSite: useSecureCookies ? <span class="hljs-string">'none'</span> : <span class="hljs-string">'lax'</span>,
          path: <span class="hljs-string">'/'</span>,
          secure: useSecureCookies,
          domain: <span class="hljs-string">'.myapp.com'</span>
        }
      },
      state: {
        name: <span class="hljs-string">`<span class="hljs-subst">${useSecureCookies ? <span class="hljs-string">'__Secure-'</span> : <span class="hljs-string">''</span>}</span>authjs.state`</span>,
        options: {
          httpOnly: <span class="hljs-literal">true</span>,
          sameSite: useSecureCookies ? <span class="hljs-string">'none'</span> : <span class="hljs-string">'lax'</span>,
          path: <span class="hljs-string">'/'</span>,
          secure: useSecureCookies,
          domain: <span class="hljs-string">'.myapp.com'</span>
        }
      }
    },
  }
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle = getAuthConfig;
</code></pre>
<p>With this simple setup, we can share the session between two applications having a common domain. In the next section, we will learn how to share sessions with multiple applications that do not share a common domain.</p>
<h3 id="heading-sharing-session-with-applications-in-different-domains">Sharing session with applications in different domains</h3>
<p>Sharing sessions with applications on different domains is very tricky to implement in Authjs as they don't allow having a dynamic <code>callbackURL</code>. When we configure the <code>SvelteKitAuthConfig</code> object, we don't have any option to manually set the <code>callbackURL</code>. It is automatically set by Authjs and defaults to <code>${url.origin}/${base_path}/auth/callback/${provider-id}</code> (e.g., <code>http://localhost:4200/base/auth/callback/auth0</code>). Since it always picks up the current origin, we cannot customize the <code>callbackURL</code>, resulting in an authentication error:</p>
<pre><code class="lang-typescript">error {
  error: <span class="hljs-string">'unauthorized_client'</span>,
  error_description: <span class="hljs-string">'The redirect URI is wrong. You sent http://localhost:4201, and we expected http://localhost:4200'</span>
}
ERROR <span class="hljs-keyword">in</span> AUTH:  {
  <span class="hljs-string">"name"</span>:<span class="hljs-string">"CallbackRouteError"</span>,
  <span class="hljs-string">"type"</span>:<span class="hljs-string">"CallbackRouteError"</span>,
  <span class="hljs-string">"kind"</span>:<span class="hljs-string">"error"</span>,
  <span class="hljs-string">"message"</span>:<span class="hljs-string">"Read more at https://errors.authjs.dev#callbackrouteerror"</span>
}
</code></pre>
<p>The solution is to use our own proxy identity server and configure it with the <code>redirectProxyURL</code> property. The <code>redirectProxyURL</code> property in Auth.js is used to specify a proxy server that handles the redirection process during authentication. This is particularly useful when dealing with scenarios where applications are hosted on different domains and you need to manage cross-domain authentication. By setting the <code>redirectProxyURL</code>, you can centralize the callback handling to a single domain, which then forwards the authentication response to the appropriate application. This helps in overcoming the limitation of having a dynamic <code>callbackURL</code> in Auth.js.</p>
<p>Let's understand how this works <strong>for the application on the same domain</strong>:</p>
<ul>
<li><p>It makes a request to the OAuth provider by passing the authorization URL and a whitelisted <code>redirectURL</code>.</p>
</li>
<li><p>The OAuth provider validates the authentication and passes the authorization code to the application via the <code>redirectURL</code> that was provided earlier.</p>
</li>
<li><p>In this scenario, since the application was performing and maintaining the session in the same domain, the auto-generated <code>callbackURL</code> by Authjs is the same as the whitelisted <code>redirectURL</code> provided by us, and hence authentication was successful.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720899260992/a58d9fd6-1ad7-4552-a812-b78d88962ab4.png" alt class="image--center mx-auto" /></p>
<p><strong>For the application on a different domain</strong>:</p>
<ul>
<li><p>It makes a request to the OAuth provider by passing the authorization URL and a <code>redirectURL</code> that is not whitelisted.</p>
</li>
<li><p>Since the auto-generated <code>callbackURL</code> by Authjs is NOT the same as the whitelisted <code>redirectURL</code> provided by us, authentication fails.</p>
</li>
<li><p>To correct this, we must first create a temporary Identity server that will impersonate our application and interact with the OAuth provider.</p>
</li>
<li><p>We will first whitelist the <code>redirectURL</code> of this temporary identity server so that it is able to communicate seamlessly with the OAuth provider.</p>
</li>
<li><p>Our application passes the authorization request to this identity server instead of the actual OAuth. In the authorization request, it passes the <code>source_url</code>, which is the current application URL that triggers the authentication process. Our fake identity server will give back control to this particular URL.</p>
</li>
<li><p>Apart from passing the authorization request, our application also sets the <code>redirectProxyURL</code> property, which holds the value of the origin of the fake identity server.</p>
</li>
<li><p>The identity server then interacts with the OAuth provider and requests authentication.</p>
</li>
<li><p>The OAuth server validates the request and sends back the authorization code via the whitelisted <code>redirectURL</code> property of the fake identity server.</p>
</li>
<li><p>Once the authorization code is received, the fake identity server transfers back control to our application. It picks up the <code>source_url</code> that was passed earlier.</p>
</li>
<li><p>Since the <code>redirectProxyURL</code> property was set, our application realizes that the authentication was performed via a proxy source and it lets the session continue on our application.</p>
</li>
</ul>
<h4 id="heading-creating-identity-server">Creating Identity Server</h4>
<p>The identity server must be configured keeping the pattern of <code>authorizeURL</code> and the whitelisted <code>redirectURL</code> in mind. For example, let's consider the following pattern for our OAuth provider where the value of the <code>authorizeURL</code> is <code>https://oauth-provider/authorize</code> and for the whitelisted <code>redirectURL</code> is <code>https://my-app/auth/callback/oauth</code>. The folder structure of our new application will be:</p>
<pre><code class="lang-apache"><span class="hljs-attribute">routes</span>/
├── <span class="hljs-attribute">authorize</span>/
│   └── +<span class="hljs-attribute">page</span>.svelte
└── <span class="hljs-attribute">auth</span>/
    └── <span class="hljs-attribute">callback</span>/
        └── <span class="hljs-attribute">oauth</span>/
            └── +<span class="hljs-attribute">page</span>.svelte
</code></pre>
<p>Consider the value of the origin of our identity server is <code>https://oauth-identity.com/</code>. Our application will make a call to <code>https://oauth-identity.com/authorize?source_url=https://my-app.com/</code>. Please note that we are passing the <code>source_url</code> where we expect the control back from our identity server once the authentication is done.</p>
<p>The authorization code in the identity server will do the following:</p>
<ul>
<li><p>It will save the <code>source_url</code> in the browser's local storage, where the key could be a random string like "state" and the value will be <code>{ redirect_uri: https://my-app.com }</code>.</p>
</li>
<li><p>It will then invoke the OAuth provider's authorization URL by passing in the whitelisted URL of the fake identity server.</p>
</li>
</ul>
<p>Once the authentication is completed, the control will come back to our identity server's whitelisted URL, i.e., <code>https://oauth-identity.com/auth/callback/oauth/+page.svelte</code>. This file will perform the following operations:</p>
<ul>
<li><p>It will pick the <code>source_url</code> from the browser's local storage that was saved in the previous step.</p>
</li>
<li><p>It will append <code>/auth/callback/&lt;provider-id&gt;</code> as a suffix to the <code>source_url</code> so that when the control is passed back to our application, Authjs is able to recognize this callback pattern.</p>
</li>
<li><p>It will append the authorization code that was received from the OAuth and send back control to our application, i.e., <code>https://my-app.com/auth/callback/auth0?code=abcd1234</code>.</p>
</li>
</ul>
<p>Finally, control comes back to our application, and since the <code>redirectProxyURL</code> property was configured, Authjs will realize that the authentication was performed by a proxy server. It will pick up the authorization code and make a token and userinfo call and proceed ahead to create and save the <code>session-token</code> cookie.</p>
<p>Here is the gist of the <code>SvelteKitAuthConfig</code> object:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// hooks.server.ts</span>

<span class="hljs-keyword">import</span> { dev } <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/environment'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Handle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> { SvelteKitAuth, <span class="hljs-keyword">type</span> SvelteKitAuthConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit'</span>;

<span class="hljs-keyword">const</span> { handle: getAuthConfig } = SvelteKitAuth(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> useSecureCookies = !dev;
  <span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {
    providers: [
      id: <span class="hljs-string">'auth0'</span>,
      authorization: {
        url: <span class="hljs-string">`https://sveltekit-auth-identity-server.vercel.app/authorize?source_url=<span class="hljs-subst">${event.url.origin}</span>`</span>,
        params: {
          scope: <span class="hljs-string">'openid name email profile'</span>,
          redirect_uri: <span class="hljs-string">`https://sveltekit-auth-identity-server.vercel.app/auth/callback/auth1`</span>,
        }
      },
      token: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>oauth/token`</span>,
      userinfo: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>userinfo`</span>,
      redirectProxyUrl: <span class="hljs-string">'https://sveltekit-auth-identity-server.vercel.app/auth'</span>
    ]
  }
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle = getAuthConfig;
</code></pre>
<p>While working on the local machine, you can run code in two IDEs: one will execute the code of the main application, say, on port 4201, and one will execute the code of the identity server on port 4200. In that use case, you can configure:</p>
<pre><code class="lang-typescript">authorization: {
  url: <span class="hljs-string">`http://localhost:4200/authorize?source_url=<span class="hljs-subst">${event.url.origin}</span>`</span>,
  params: {
    scope: <span class="hljs-string">'openid name email profile'</span>,
    redirect_uri: <span class="hljs-string">`http://localhost:4200/auth/callback/auth0`</span>
  }
},
redirectProxyUrl: <span class="hljs-string">'http://localhost:4200/auth'</span>
</code></pre>
<p>With this configuration, we are able to share sessions among multiple applications spread across different domains.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In conclusion, managing shared sessions across multiple applications using SvelteKitAuth can be effectively achieved through careful configuration and understanding of domain-specific challenges. For applications sharing the same domain, a straightforward update to the cookie's domain property ensures seamless session sharing. However, for applications on different domains, implementing a proxy identity server becomes essential. This server handles the redirection process, allowing cross-domain authentication by centralizing callback handling. By following the outlined steps and configurations, developers can ensure secure and efficient session management across diverse application environments.</p>
<p>Here is the link to the GitHub repository with the codebase for the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth/tree/proxy-identity-config">main application</a> and the one with the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth-Authorize-Server">identity application</a>. Here is the <a target="_blank" href="https://sveltekit-auth-preview.vercel.app/">link</a> to view a live demo of this use case. In the next article, we will explore the shortcomings or the <a target="_blank" href="https://blog.aakashgoplani.in/drawbacks-of-sveltekitauth-you-should-know">limitations of the SvelteKitAuth</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Steps to Build Custom Pages and Handle Events in SvelteKitAuth]]></title><description><![CDATA[Auth.js automatically creates simple, unbranded authentication pages for handling Sign in, Sign out, Email Verification and displaying error messages. The options displayed on the sign-up page are automatically generated based on the providers specif...]]></description><link>https://blog.aakashgoplani.in/steps-to-build-custom-pages-and-handle-events-in-sveltekitauth</link><guid isPermaLink="true">https://blog.aakashgoplani.in/steps-to-build-custom-pages-and-handle-events-in-sveltekitauth</guid><category><![CDATA[auth.js]]></category><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[pages]]></category><category><![CDATA[events]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Tue, 09 Jul 2024 19:00:56 GMT</pubDate><content:encoded><![CDATA[<p>Auth.js automatically creates simple, unbranded authentication pages for handling Sign in, Sign out, Email Verification and displaying error messages. The options displayed on the sign-up page are automatically generated based on the providers specified in the options passed to Auth.js. It also provides us with the flexibility to customize them as per our requirements. In this article, we will explore the Pages option in Auth.js, its importance, and various use-cases.</p>
<h3 id="heading-pages">Pages</h3>
<p>In Auth.js (formerly known as NextAuth.js), pages refer to custom user interface pages that you can create to handle various authentication-related actions. These pages allow you to customize the look and feel of the authentication process to better match your application's design and user experience. The need for custom pages arises when the default pages provided by Auth.js do not meet your specific requirements or branding guidelines.</p>
<p>You can configure custom pages for different actions such as sign-in, sign-out, and error handling. This is done by specifying the URLs of your custom pages in the Auth.js configuration. For example, you can create custom sign-in and sign-out pages to provide a more personalized experience for your users.</p>
<p>Examples of custom pages include:</p>
<ol>
<li><p><strong>Built-in</strong>: The default pages provided by Auth.js.</p>
</li>
<li><p><strong>Sign-in</strong>: A custom page where users can log in to your application.</p>
</li>
<li><p><strong>Sign-out</strong>: A custom page that users see when they log out.</p>
</li>
<li><p><strong>Error</strong>: A custom page to display error messages related to authentication.</p>
</li>
</ol>
<h4 id="heading-how-to-configure-custom-pages">How to configure custom pages?</h4>
<p>Configuring custom pages is very easy; all we have to do is provide the path of the corresponding <em>+page.svelte</em> file in the <code>SvelteKitAuthConfig</code> object.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Handle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> { SvelteKitAuth, <span class="hljs-keyword">type</span> SvelteKitAuthConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit'</span>;
<span class="hljs-keyword">import</span> { VERCEL_SECRET, CLIENT_ID, CLIENT_SECRET, ISSUER, WELL_KNOWN } <span class="hljs-keyword">from</span> <span class="hljs-string">'$env/static/private'</span>;

<span class="hljs-keyword">const</span> { handle: getAuthConfig } = SvelteKitAuth(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {
    ...
    pages: {
      signIn: <span class="hljs-string">'/auth/signin'</span>, <span class="hljs-comment">// src/routes/auth/signin/+page.svelte</span>
      signOut: <span class="hljs-string">'/auth/signout'</span>, <span class="hljs-comment">// src/routes/auth/signout/+page.svelte</span>
      error: <span class="hljs-string">'/auth/error'</span> <span class="hljs-comment">// src/routes/auth/error/+page.svelte</span>
    }
  };
  <span class="hljs-keyword">return</span> config;
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle = SvelteKitAuth(getAuthConfig) satisfies Handle;
</code></pre>
<p>With this simple configuration, we can display our own brand of pages for sign-in, sign-out, and error.</p>
<h4 id="heading-reference">Reference</h4>
<ol>
<li><p><a target="_blank" href="https://authjs.dev/getting-started/session-management/custom-pages">https://authjs.dev/getting-started/session-management/custom-pages</a></p>
</li>
<li><p><a target="_blank" href="https://authjs.dev/guides/pages/built-in-pages">https://authjs.dev/guides/pages/built-in-pages</a></p>
</li>
<li><p><a target="_blank" href="https://authjs.dev/guides/pages/signin">https://authjs.dev/guides/pages/signin</a></p>
</li>
<li><p><a target="_blank" href="https://authjs.dev/guides/pages/signout">https://authjs.dev/guides/pages/signout</a></p>
</li>
<li><p><a target="_blank" href="https://authjs.dev/guides/pages/error">https://authjs.dev/guides/pages/error</a></p>
</li>
<li><p><a target="_blank" href="https://next-auth.js.org/configuration/pages">https://next-auth.js.org/configuration/pages</a></p>
</li>
</ol>
<p>In the next section we will learn about the Events available with Auth.js.</p>
<h3 id="heading-events">Events</h3>
<p>In Auth.js, events are hooks that allow you to execute custom code in response to specific actions during the authentication process. These events can be used to extend the functionality of your authentication system, such as logging, analytics, or triggering other side effects. Some of the key events in Auth.js include:</p>
<ol>
<li><p><strong>signin</strong>: Triggered when a user signs in.</p>
</li>
<li><p><strong>signout</strong>: Triggered when a user signs out.</p>
</li>
<li><p><strong>session</strong>: Sent at the end of a request for the current session.</p>
</li>
</ol>
<h4 id="heading-how-to-configure-and-respond-to-the-custom-events">How to configure and respond to the custom events?</h4>
<p>Configuring custom events is very easy; all we have to do is provide the path of the corresponding <em>+page.svelte</em> file in the <code>SvelteKitAuthConfig</code> object.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Handle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> { SvelteKitAuth, <span class="hljs-keyword">type</span> SvelteKitAuthConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit'</span>;
<span class="hljs-keyword">import</span> { VERCEL_SECRET, CLIENT_ID, CLIENT_SECRET, ISSUER, WELL_KNOWN } <span class="hljs-keyword">from</span> <span class="hljs-string">'$env/static/private'</span>;

<span class="hljs-keyword">const</span> { handle: getAuthConfig } = SvelteKitAuth(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {
    ...
    events: {
      signOut(message) {
        <span class="hljs-comment">// message.token &amp; message.session</span>
      },
      signIn({ account, user, isNewUser, profile }) { ... },
      session({ session, token }) { ... }
    }
  };
  <span class="hljs-keyword">return</span> config;
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle = SvelteKitAuth(getAuthConfig) satisfies Handle;
</code></pre>
<p>With this simple configuration, we can respond to the events in case of sign-in and sign-out.</p>
<h4 id="heading-reference-1">Reference</h4>
<ol>
<li><a target="_blank" href="https://next-auth.js.org/configuration/events">https://next-auth.js.org/configuration/events</a></li>
</ol>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In conclusion, customizing authentication pages and handling events in SvelteKitAuth allows you to create a more personalized and seamless user experience. By configuring custom pages, you can ensure that your application's branding and design are consistent throughout the authentication process. Additionally, leveraging events in Auth.js enables you to execute custom code in response to specific actions, enhancing the functionality and flexibility of your authentication system. Whether you need to log user activities, integrate analytics, or trigger other side effects, these features provide the tools necessary to tailor the authentication flow to your specific needs.</p>
<p>Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth"><strong>GitHub repository</strong></a> with the codebase. In the next article, we will learn to <a target="_blank" href="https://blog.aakashgoplani.in/managing-shared-sessions-across-multiple-applications-in-sveltekitauth">how to manage shared session</a> between different applications in SvelteKitAuth.</p>
]]></content:encoded></item><item><title><![CDATA[How to Exchange Data Between Client and Server Using SvelteKitAuth]]></title><description><![CDATA[In this article, we will explore how to exchange data between the client and server using SvelteKitAuth. We will delve into the importance of JWT and session callbacks, and how to synchronize and store sessions effectively.
Updating and Syncing data ...]]></description><link>https://blog.aakashgoplani.in/how-to-exchange-data-between-client-and-server-using-sveltekitauth</link><guid isPermaLink="true">https://blog.aakashgoplani.in/how-to-exchange-data-between-client-and-server-using-sveltekitauth</guid><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[Sveltekit]]></category><category><![CDATA[auth.js]]></category><category><![CDATA[client-server communication]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Mon, 08 Jul 2024 19:59:24 GMT</pubDate><content:encoded><![CDATA[<p>In this article, we will explore how to exchange data between the client and server using SvelteKitAuth. We will delve into the importance of JWT and session callbacks, and how to synchronize and store sessions effectively.</p>
<h3 id="heading-updating-and-syncing-data-from-the-client-to-the-server">Updating and Syncing data from the Client to the Server</h3>
<p>Since the <code>SvelteKitAuthConfig</code> is attached to the hooks, it triggers with every API call that is intercepted by the hooks. The <code>SvelteKitAuthConfig</code> comes with two important callback functions, i.e., <code>jwt</code> and <code>session</code>, that trigger on every network request intercepted by the hooks.</p>
<p>The <code>jwt</code> callback holds the <code>token</code> property, which includes the authenticated user's token and profile data. This information constitutes the session information that will be shared site-wide. This information is stored in an encrypted format in the <code>session-token</code> cookie.</p>
<p>On every network request, this callback decrypts the <code>session-token</code> cookie, reads the information, performs operations, and then encrypts it back and saves it in the <code>session-token</code> cookie. This token is then passed to the <code>session</code> callback, which forms the session object that will be shared site-wide and can be accessed on the server as <code>const session = await locals.auth()</code> and on the client-side as <code>const session = $page.data.session</code>.</p>
<p>Hence, in order to update the user information, we must use the <code>jwt</code> callback so that it is preserved in the <code>session-token</code> cookie and later pass the information to the <code>session</code> callback so that the information is accessible on both the server and the client side.</p>
<h4 id="heading-the-first-step-updating-the-config-object"><strong>The First Step: Updating the Config Object</strong></h4>
<p>With this knowledge in hand, let's configure the <code>jwt</code> and the <code>session</code> callback in the <code>SvelteKitAuthConfig</code> object.</p>
<pre><code class="lang-typescript">{
  callbacks: {
    <span class="hljs-keyword">async</span> jwt({ token, account, profile }) {
      ...
      <span class="hljs-keyword">const</span> userQuery = event.request.headers.get(<span class="hljs-string">'query'</span>) || event.url.searchParams.get(<span class="hljs-string">'query'</span>);
      <span class="hljs-keyword">if</span> (userQuery === <span class="hljs-string">'update-user-data'</span>) {
        <span class="hljs-keyword">try</span> {
          <span class="hljs-keyword">const</span> clonedRequest = event.request.clone();
          <span class="hljs-keyword">const</span> clonedBody = clonedRequest.body;
          <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> Response(clonedBody);
          <span class="hljs-keyword">const</span> body = <span class="hljs-keyword">await</span> response.json();
          <span class="hljs-keyword">if</span> (!isEmpty(body)) {
            token = {
              ...token,
              ...body
            };
          }
        } <span class="hljs-keyword">catch</span> (ex: <span class="hljs-built_in">any</span>) {
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Unable to update user data for url'</span>, ex?.message);
        }
      }
      <span class="hljs-keyword">return</span> token;
    },
    <span class="hljs-keyword">async</span> session({ session, token }) {
      <span class="hljs-keyword">if</span> (session.user) {
        <span class="hljs-keyword">if</span> (token?.access_token) {
          session.user = { ...session.user, ...token } <span class="hljs-keyword">as</span> <span class="hljs-built_in">any</span>;
        }
      }
      <span class="hljs-keyword">return</span> session;
    }
  }
}
</code></pre>
<p>Let's take a moment to understand what the above code snippet actually does:</p>
<ul>
<li><p>Since the <code>jwt</code> callback is invoked for every API request intercepted by the hooks, we cherry-pick the ones that have a particular flag passed via the header or the query params, which will instruct us that we must perform data communication operations. For the sake of this example, we use the string <em>update-user-data</em>.</p>
</li>
<li><p>Once we get hold of this flag, we will fetch the payload from the request and update the <code>token</code> property of the <code>jwt</code> callback. Remember, this is the same property that will be preserved in an encrypted format in the <code>session-cookie</code>. Side Note: cookie size up to 4kb could be saved at any given time due to the browser's storage limitation. However, if we pass too much data into the <code>token</code> property, they will be chunked into multiple cookies, i.e., <code>session-cookie_0</code>, <code>session-cookie_1</code>, and so on.</p>
</li>
<li><p>Finally, we pass the <code>token</code> property to the <code>session</code> callback so that it is available for use on both the client and the server side.</p>
</li>
</ul>
<h4 id="heading-the-second-step-creating-the-update-endpoint"><strong>The Second Step: Creating the Update Endpoint</strong></h4>
<p>As we discussed in the last section, we need to intercept specific API requests that have flags which will enable us to kickstart the data operation. So in this section, we will create an API route that will be used by the client to send the payload which must be synced and updated on the server side.</p>
<p>So we create an API route in the file <em>src/routes/api/update-user-data/+server.ts</em>. The aim of this API route is to pass data to the <code>jwt</code> callback. The API route then sends the updated data back, which we can catch by invoking the <code>auth()</code> method. We then return the updated object back to the client.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RequestHandler } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> { isEmpty } <span class="hljs-keyword">from</span> <span class="hljs-string">'lodash-es'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> POST = (<span class="hljs-keyword">async</span> ({ locals }) =&gt; {
  <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> locals.auth();
  <span class="hljs-keyword">const</span> user = session?.user;

  <span class="hljs-keyword">if</span> (!isEmpty(user) &amp;&amp; !isEmpty(session)) {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify({
        data: user
      }), { status: <span class="hljs-number">200</span> });
    } <span class="hljs-keyword">catch</span> (error: <span class="hljs-built_in">any</span>) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Error while updating user data: '</span>, error?.message, <span class="hljs-string">'. Sending existing data'</span>);
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify({ data: user }), { status: <span class="hljs-number">200</span> });
    }
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify({ data: <span class="hljs-string">'user is not authorized to update data'</span> }), { status: <span class="hljs-number">401</span> });
  }
}) satisfies RequestHandler;
</code></pre>
<h4 id="heading-the-third-step-client-initiates-the-update-request"><strong>The Third Step: Client Initiates the Update Request</strong></h4>
<p>The client triggers the API route that we created in the previous section and sends the payload object along with it.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">let</span> userData = $page.data.session?.user?.fav_num || <span class="hljs-string">''</span>;

  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateUserData</span>(<span class="hljs-params"></span>) </span></span></span><span class="javascript">{
    <span class="hljs-keyword">const</span> request = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/update-user-data?query=update-user-data'</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
        <span class="hljs-attr">fav_num</span>: <span class="hljs-string">`My favourite number is: <span class="hljs-subst">${<span class="hljs-built_in">Math</span>.ceil(<span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">100</span>)}</span>`</span>
      }</span><span class="xml"><span class="javascript">)
    });
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> request.json();
    userData = response?.data?.fav_num;
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">on:click</span>=</span></span><span class="javascript">{updateUserData}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">class</span>=<span class="hljs-string">"button"</span>&gt;</span>Update user data<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

</span><span class="javascript">{</span><span class="hljs-keyword">#if</span><span class="javascript"> userData}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Updated user-data </span><span class="javascript">{userData}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</span><span class="javascript">{</span><span class="hljs-keyword">/if</span><span class="javascript">}</span>
</code></pre>
<p>With these three steps, the data is synced and updated by the client to the server. In the next section, we will learn how to update and sync the data from the server to the client.</p>
<h3 id="heading-updating-and-syncing-data-from-the-server-to-the-client"><strong>Updating and Syncing Data from the Server to the Client</strong></h3>
<p>The only way for the server to send the information back to the client is via hydration. The server load functions, i.e., <em>+layout.server.ts</em> and <em>+page.server.ts</em>, can send data to the client by returning an object. Only objects that can be serialized using <a target="_blank" href="https://github.com/rich-harris/devalue"><strong>devalue</strong></a> can be used. Here is an example where the server <a target="_blank" href="https://github.com/rich-harris/devalue">load fu</a>nction returns the session property to the client.</p>
<p><strong>NOTE</strong>: Properties returned by the layout server load function will be av<a target="_blank" href="https://github.com/rich-harris/devalue">ailable</a> to all the child routes, whereas the properties r<a target="_blank" href="https://github.com/rich-harris/devalue">eturned</a> by the page server load will only be available to the current route. If both functions return the same property, the one that returns the last will precede over others.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// server load functions</span>

<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { LayoutServerLoad } <span class="hljs-keyword">from</span> <span class="hljs-string">'./$types'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load: LayoutServerLoad = <span class="hljs-keyword">async</span> (event) =&gt; {
    <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> event.locals.auth();
    <span class="hljs-keyword">return</span> { session };
};
</code></pre>
<p>The client can access this information using the <code>$page.data</code> property.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ page }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/stores'</span>;

  <span class="hljs-keyword">let</span> session = $page.data.session;
  <span class="hljs-keyword">let</span> user = $page.data.session.user;
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this article, we explored the process of exchanging data between the client and server using SvelteKitAuth. We delved into the importance of JWT and session callbacks, and how to effectively synchronize and store sessions. By configuring the <code>jwt</code> and <code>session</code> callbacks, creating an update endpoint, and initiating update requests from the client, we ensured seamless data synchronization from the client to the server. Additionally, we discussed how the server can send information back to the client through hydration using server load functions. By following these steps, you can maintain a consistent and secure data exchange between the client and server in your SvelteKit applications.</p>
<p>Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth"><strong>GitHub repository</strong></a> with the codebase. In the next article, we will learn to <a target="_blank" href="https://blog.aakashgoplani.in/steps-to-build-custom-pages-and-handle-events-in-sveltekitauth">how to build custom pages and handle events</a> in SvelteKitAuth.</p>
]]></content:encoded></item><item><title><![CDATA[How to Implement Refresh Token Rotation in SvelteKitAuth]]></title><description><![CDATA[Refresh token rotation is the practice of updating an access_token on behalf of the user, without requiring interaction (eg.: re-sign in). access_token are usually issued for a limited time. After they expire, the service verifying them will ignore t...]]></description><link>https://blog.aakashgoplani.in/how-to-implement-refresh-token-rotation-in-sveltekitauth</link><guid isPermaLink="true">https://blog.aakashgoplani.in/how-to-implement-refresh-token-rotation-in-sveltekitauth</guid><category><![CDATA[token-rotation]]></category><category><![CDATA[Sveltekit]]></category><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[auth.js]]></category><category><![CDATA[access-token]]></category><category><![CDATA[refresh-token]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 20:49:29 GMT</pubDate><content:encoded><![CDATA[<p>Refresh token rotation is the practice of updating an <code>access_token</code> on behalf of the user, without requiring interaction (eg.: re-sign in). <code>access_token</code> are usually issued for a limited time. After they expire, the service verifying them will ignore the value. Instead of asking the user to sign in again to obtain a new <code>access_token</code>, certain providers support exchanging a <code>refresh_token</code> for a new <code>access_token</code>, renewing the expiry time. Refreshing your <code>access_token</code> with other providers will look very similar, you will just need to adjust the endpoint and potentially the contents of the body being sent to them in the request.</p>
<p>Using <code>jwt</code> and <code>session</code> callbacks in the provider configuration, we can persist OAuth tokens and refresh them when they expire. These callbacks trigger every time a call is made to <code>auth()</code>, i.e., <code>event.locals.auth()</code>.</p>
<p>The <strong>first step</strong> is to create an API route that will refresh the token. In the file <em>src/routes/api/renew-token/+server.ts</em>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { isEmpty } <span class="hljs-keyword">from</span> <span class="hljs-string">'lodash-es'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RequestHandler } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> { CLIENT_ID, CLIENT_SECRET, ISSUER, API_IDENTIFIER } <span class="hljs-keyword">from</span> <span class="hljs-string">'$env/static/private'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> POST = (<span class="hljs-keyword">async</span> ({ fetch, locals }) =&gt; {
  <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> locals.auth();
  <span class="hljs-keyword">const</span> user = session?.user;

  <span class="hljs-keyword">if</span> (!isEmpty(user)) {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> request = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>oauth/token`</span>, {
        method: <span class="hljs-string">'POST'</span>,
        headers: {
          <span class="hljs-string">'content-type'</span>: <span class="hljs-string">'application/x-www-form-urlencoded'</span>
        },
        body: <span class="hljs-keyword">new</span> URLSearchParams({
          grant_type: <span class="hljs-string">'client_credentials'</span>,
          client_id: CLIENT_ID,
          client_secret: CLIENT_SECRET,
          audience: API_IDENTIFIER <span class="hljs-comment">// ${ISSUER}/api/v2/</span>
        })
      });
      <span class="hljs-keyword">await</span> request.json();
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Response from API: <span class="hljs-subst">${<span class="hljs-built_in">JSON</span>.stringify(response)}</span>`</span>);
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify(response), { status: <span class="hljs-number">200</span> });
    } <span class="hljs-keyword">catch</span> (error: <span class="hljs-built_in">any</span>) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Error while updating token data: <span class="hljs-subst">${error?.message}</span>. Reusing existing tokens!`</span>);
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify({
        access_token: user.access_token,
        expires_in: user.expires_in,
        token_type: <span class="hljs-string">'Bearer'</span>
      }), { status: <span class="hljs-number">200</span> });
    }
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify({
      message: <span class="hljs-string">'User is not authorized to rotate access-token'</span>
    }), { status: <span class="hljs-number">401</span> });
  }
}) satisfies RequestHandler;
</code></pre>
<p>Now, the <strong>second step</strong> is to configure the <code>jwt</code> and <code>session</code> callbacks in the provider's configuration to trigger token rotation upon invocation of the target API route:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// hooks.server.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { handle: getAuthConfig } = SvelteKitAuth(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {
    ....,
    callbacks: {
      <span class="hljs-keyword">async</span> jwt({ token, account, profile }) {
        <span class="hljs-comment">/**
         * This callback triggers multiple times. For the very first time,
         * token -&gt; { name, email, picture, sub }
         * account -&gt; { all_tokens }
         * profile -&gt; { all_user_details_and_custom-attributes }
         * trigger -&gt; { signin, signut, update }
         * For second and successive times,
         * token -&gt; { name, email, picture, sub, iat, exp, jti }
         * account -&gt; undefined
         * profile -&gt; undefined
         * trigger -&gt; undefined
        */</span>
        <span class="hljs-comment">// store init values that must be passed to session cb, if this line is skipped then</span>
        <span class="hljs-comment">// { name, email, picture, sub, iat, exp, jti } will always be undefined in session cb</span>
        <span class="hljs-keyword">try</span> {
          <span class="hljs-keyword">if</span> (!isEmpty(account)) {
            token = { ...token, ...account };
          }
          <span class="hljs-keyword">if</span> (!isEmpty(profile)) {
            token = { ...token, ...(profile <span class="hljs-keyword">as</span> <span class="hljs-built_in">any</span>) };
          }

          <span class="hljs-comment">// update user data on request</span>
          <span class="hljs-keyword">const</span> userQuery = event.request.headers.get(<span class="hljs-string">'query'</span>) || event.url.searchParams.get(<span class="hljs-string">'query'</span>);

          <span class="hljs-comment">// refresh token post 30 minutes</span>
          <span class="hljs-keyword">if</span> (
            token &amp;&amp;
            (isTokenRefreshRequired(token.token_expires_in <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>) || userQuery === <span class="hljs-string">'update-token-data'</span>)
          ) {
            <span class="hljs-keyword">const</span> tokenRequest = <span class="hljs-keyword">await</span> event.fetch(
              event.url.origin + <span class="hljs-string">'/api/renew-token'</span>,
              { method: <span class="hljs-string">'POST'</span> }
            );
            <span class="hljs-keyword">const</span> updatedToken = <span class="hljs-keyword">await</span> tokenRequest.json();
            <span class="hljs-keyword">if</span> (updatedToken.access_token) {
              token = {
                ...token,
                ...updatedToken
              };
            }
          }
        } <span class="hljs-keyword">catch</span> (e: <span class="hljs-built_in">any</span>) {
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'ERROR in AUTH JWT CALLBACK: '</span>, e?.message);
        }
        <span class="hljs-keyword">return</span> token;
      },
      <span class="hljs-keyword">async</span> session({ session, token }) {
        <span class="hljs-keyword">try</span> {
          <span class="hljs-comment">// This callback triggers multiple times</span>
          <span class="hljs-keyword">if</span> (session.user) {
            <span class="hljs-keyword">if</span> (token?.access_token) {
              session.user = { ...session.user, ...token } <span class="hljs-keyword">as</span> <span class="hljs-built_in">any</span>;
            }
          }
        } <span class="hljs-keyword">catch</span> (e: <span class="hljs-built_in">any</span>) {
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'ERROR in AUTH SESSION CALLBACK: '</span>, e?.message);
        }
        <span class="hljs-keyword">return</span> session;
      }
    },
  }
});

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isTokenRefreshRequired</span>(<span class="hljs-params">issued_at: <span class="hljs-built_in">string</span></span>) </span>{
  <span class="hljs-keyword">return</span> +difference([<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(+issued_at), <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(), <span class="hljs-string">'minutes'</span>]) &gt; <span class="hljs-number">29</span>;
}
</code></pre>
<p>In the <code>jwt</code> callback, we trigger the token rotation request in two scenarios:</p>
<ol>
<li><p><strong>Automatically</strong>, after every 30 minutes (just for the sake of example).</p>
</li>
<li><p>Once the user has <strong>manually</strong> requested token rotation.</p>
</li>
</ol>
<p>It is important to save the updated <code>access_token</code> in the <code>token</code> property, as the contents of this <code>token</code> property are encrypted and saved in the <em>session-token</em> cookie. So when a new request is made, the same token is decrypted, and hence the value must be preserved.</p>
<p>As we know that the <code>jwt</code> and <code>session</code> callbacks trigger every time a call to <code>auth()</code> is made, to trigger the refresh token, we will have to create a dummy API route that will trigger the renew token process:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// FILE -&gt; src/routes/api/trigger-renew-token/+server.ts</span>

<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RequestHandler } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> { isEmpty } <span class="hljs-keyword">from</span> <span class="hljs-string">'lodash-es'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> POST = (<span class="hljs-keyword">async</span> ({ locals }) =&gt; {
  <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> locals.auth();
  <span class="hljs-keyword">const</span> returnValue = !isEmpty(session)
    ? { status: <span class="hljs-number">200</span> }
    : { status: <span class="hljs-number">401</span> };
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify(returnValue), { status: <span class="hljs-number">200</span> });
}) satisfies RequestHandler;
</code></pre>
<p>Finally, we invoke it from the client side:</p>
<pre><code class="lang-svelte"><span class="hljs-comment">&lt;!-- some *.svelte file --&gt;</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">refreshToken</span>(<span class="hljs-params"></span>) </span></span></span><span class="javascript">{
    <span class="hljs-keyword">const</span> request = <span class="hljs-keyword">await</span> fetch(
      <span class="hljs-string">'/api/trigger-renew-token?query=update-token-data'</span>,
      { <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span> }
    }</span><span class="xml">);
  }
<span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">on:click</span>=</span></span><span class="javascript">{refreshToken}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">class</span>=<span class="hljs-string">"button"</span>&gt;</span>Refresh Token<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
</code></pre>
<p>In conclusion, implementing refresh token rotation in SvelteKitAuth is a crucial practice for maintaining secure and seamless user sessions. By leveraging <code>jwt</code> and <code>session</code> callbacks, we can efficiently manage OAuth tokens, ensuring they are refreshed without user intervention. This approach not only enhances the user experience by avoiding frequent re-authentication but also maintains the integrity and security of the session. By following the outlined steps, including creating an API route for token renewal and configuring the necessary callbacks, developers can ensure that access tokens are consistently updated and preserved, providing a robust authentication mechanism in their SvelteKit applications.</p>
<p>Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth"><strong>GitHub repository</strong></a> with the codebase. In the next article, we will learn <a target="_blank" href="https://blog.aakashgoplani.in/how-to-exchange-data-between-client-and-server-using-sveltekitauth">how to effectively communicate data between client and the server</a> in SvelteKitAuth.</p>
]]></content:encoded></item><item><title><![CDATA[How to Manage Sessions in SvelteKit with SvelteKitAuth]]></title><description><![CDATA[We will explore two approaches to managing sessions in SvelteKit: one on the server side and the other on the client side.
Managing session on the Server side
As soon as the authentication is successful, SvelteKitAuth populates user sessions within l...]]></description><link>https://blog.aakashgoplani.in/how-to-manage-sessions-in-sveltekit-with-sveltekitauth</link><guid isPermaLink="true">https://blog.aakashgoplani.in/how-to-manage-sessions-in-sveltekit-with-sveltekitauth</guid><category><![CDATA[session management]]></category><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[auth.js]]></category><category><![CDATA[Sveltekit]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 20:39:19 GMT</pubDate><content:encoded><![CDATA[<p>We will explore two approaches to managing sessions in SvelteKit: one on the server side and the other on the client side.</p>
<h3 id="heading-managing-session-on-the-server-side">Managing session on the Server side</h3>
<p>As soon as the authentication is successful, SvelteKitAuth populates user sessions within <em>locals</em> that are accessible across the server-side code (i.e., <em>hooks.server.ts</em>, <em>+page.server.ts</em>, <em>+layout.server.ts</em>, <em>+server.ts</em>). Here is the programmatic representation of the statement:</p>
<p><em>src/hooks.server.ts</em></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> { handle: getAuthConfig } = SvelteKitAuth(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {...}
});
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle = SvelteKitAuth(getAuthConfig) satisfies Handle;
<span class="hljs-comment">// this adds { users, expires } property to locals</span>
</code></pre>
<p>In server files, i.e., <em>src/\</em>.server.ts*,</p>
<p>We can access the session using the <code>auth()</code> method. Here, the <code>session</code> object will hold the <code>user</code> property that contains the user information and the <code>expires</code> property that depicts until which session is valid.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// [page | layout].server.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = <span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> event.locals.auth();
  <span class="hljs-keyword">const</span> isUserLoggedIn = !isEmpty(session?.user);
  <span class="hljs-keyword">if</span> (isUserLoggedIn) {
    <span class="hljs-comment">// execute business logic</span>
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// redirect user to sign-in oage</span>
  }
  <span class="hljs-keyword">return</span> { session };
};

<span class="hljs-comment">// +server.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> POST = (<span class="hljs-keyword">async</span> ({ locals }) =&gt; {
  <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> locals.auth();
  <span class="hljs-keyword">const</span> isUserLoggedIn = !isEmpty(session?.user);
  <span class="hljs-keyword">if</span> (isUserLoggedIn) {
    <span class="hljs-comment">// execute business logic</span>
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// redirect user to sign-in oage</span>
  }
};
</code></pre>
<p>We can verify if the user is authenticated or not based on the <code>user</code> and <code>expires</code> properties.</p>
<p><strong>NOTE</strong>: The <code>expires</code> property updates itself every time the call to <code>auth()</code> is made, thus extending the user session.</p>
<p>Lastly, the page and layout server files can return this <code>session</code> object so that it is available on the client side as well.</p>
<h3 id="heading-managing-session-on-the-client-side">Managing session on the Client side</h3>
<p>The session management on the client starts from the <code>session</code> property that was returned by parent layout or page server files. It will be available inside the <code>$page</code> store, in the <code>data</code> property: <code>$page.data</code>. In this case, we return an object with the <code>session</code> property, which is what we are accessing in the other code paths.</p>
<p>In the <em>src/routes/+page.svelte.ts</em> file, we can access the session variable:</p>
<pre><code class="lang-typescript">&lt;script&gt;
  <span class="hljs-keyword">import</span> { page } <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/stores'</span>;
&lt;/script&gt;

{#<span class="hljs-keyword">if</span> $page.data?.session?.user}
  &lt;span&gt;Display User specific Information&lt;/span&gt;
{/<span class="hljs-keyword">if</span>}
</code></pre>
<p>In universal load functions, <em>src/routes/+[layout | page].ts</em>, we can access the session variable:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = (<span class="hljs-keyword">async</span> ({ data }) =&gt; {
  <span class="hljs-keyword">const</span> session = data.session;
  <span class="hljs-keyword">const</span> isUserLoggedIn = !isEmpty(session?.user);
  <span class="hljs-keyword">if</span> (isUserLoggedIn) {
    <span class="hljs-comment">// execute business logic</span>
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// redirect user to sign-in oage</span>
  }

  <span class="hljs-keyword">return</span> { session };
});
</code></pre>
<p>This mechanism allows us to protect components from unauthorized access, i.e., <strong>Handling Authorization Per Component</strong>.</p>
<p>Coming to the second use case to protect routes/paths from unauthorized access, i.e., <strong>Handling Authorization Per Route</strong>. For this scenario, we first create an API route that returns the current session status.</p>
<p><em>src/routes/api/get-session-status/+server.ts</em></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { RequestHandler } <span class="hljs-keyword">from</span> <span class="hljs-string">"@sveltejs/kit"</span>;
<span class="hljs-keyword">import</span> { isEmpty } <span class="hljs-keyword">from</span> <span class="hljs-string">"lodash-es"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> GET = (<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">let</span> returnValue = { status: <span class="hljs-number">200</span> };
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> event.locals.auth();
    <span class="hljs-keyword">const</span> user = session?.user;
    <span class="hljs-keyword">const</span> isUserLoggedIn = !isEmpty(session) &amp;&amp; !isEmpty(user);
    returnValue = isUserLoggedIn ? { status: <span class="hljs-number">200</span> } : { status: <span class="hljs-number">401</span> };
  } <span class="hljs-keyword">catch</span> (ex: <span class="hljs-built_in">any</span>) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Exception occured while quering user session: <span class="hljs-subst">${ex?.message}</span>`</span>);
    returnValue = { status: <span class="hljs-number">401</span> };
  }

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify(returnValue), { status: <span class="hljs-number">200</span> });
}) satisfies RequestHandler;
</code></pre>
<p>Before navigating to each route, we can check if session is defined or not</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ beforeNavigate, goto }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/navigation'</span>;

  beforeNavigate(<span class="hljs-keyword">async</span> (</span></span><span class="javascript">{ to, cancel }</span><span class="xml">) =&gt; </span><span class="javascript">{
    <span class="hljs-keyword">if</span> (to?.url &amp;&amp; to.url.pathname !== <span class="hljs-string">'/'</span>) {
      <span class="hljs-comment">// check user session on every navigation</span>
      <span class="hljs-keyword">const</span> request = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-built_in">window</span>.location.origin}</span>/api/get-session-status`</span>);
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> request.json();
      <span class="hljs-keyword">const</span> publicURLs = [<span class="hljs-string">'/ssr-login'</span>, <span class="hljs-string">'/ssr-logout'</span>, <span class="hljs-string">'/public'</span>, <span class="hljs-string">'/'</span>];
      <span class="hljs-keyword">if</span> (response.status !== <span class="hljs-number">200</span> &amp;&amp; !publicURLs.includes(<span class="hljs-built_in">window</span>.location.pathname)) {
        <span class="hljs-comment">// user is not-logged in, redirect to sign-in screen</span>
        cancel();
        goto(<span class="hljs-string">`/`</span>);
      }
    }</span><span class="xml"><span class="javascript">
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
  });
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In conclusion, managing sessions in SvelteKit with SvelteKitAuth can be effectively handled both on the server side and the client side. On the server side, sessions are managed using the <code>auth()</code> method, which provides a <code>session</code> object containing user information and session expiration details. This session object can be accessed across various server-side files and returned to the client side for further use. On the client side, the session data is available in the <code>$page</code> store, allowing for component-level and route-level authorization checks. By leveraging these mechanisms, developers can ensure secure and efficient session management in their SvelteKit applications.</p>
<p>Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth"><strong>GitHub repository</strong></a> with the codebase. In the next article, we will learn <a target="_blank" href="https://blog.aakashgoplani.in/how-to-implement-refresh-token-rotation-in-sveltekitauth">how to rotate the refresh token</a> in SvelteKitAuth.</p>
]]></content:encoded></item><item><title><![CDATA[User Sign Out: Application vs OAuth Provider]]></title><description><![CDATA[In previous articles, we explored how to sign out users from both the client-side and server-side. In this article, we will delve into the differences between signing out a user from the Application layer versus the OAuth layer.
When you trigger a si...]]></description><link>https://blog.aakashgoplani.in/user-sign-out-application-vs-oauth-provider</link><guid isPermaLink="true">https://blog.aakashgoplani.in/user-sign-out-application-vs-oauth-provider</guid><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[auth.js]]></category><category><![CDATA[Auth0]]></category><category><![CDATA[Sveltekit]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 20:26:20 GMT</pubDate><content:encoded><![CDATA[<p>In previous articles, we explored how to sign out users from both the <a target="_blank" href="https://blog.aakashgoplani.in/streamlining-client-side-sign-in-and-sign-out-processes">client-side</a> and <a target="_blank" href="https://blog.aakashgoplani.in/optimizing-server-side-login-and-logout-processes">server-side</a>. In this article, we will delve into the differences between signing out a user from the Application layer versus the OAuth layer.</p>
<p>When you trigger a <code>signOut()</code>, SvelteKitAuth logs the user out from your application by clearing the <code>session-token</code> cookie and resetting the <code>Session</code> to <code>null</code>. However, the user is still active in the OAuth provider's session layer. You can read more about this in the official <a target="_blank" href="https://auth0.com/docs/authenticate/login/logout"><strong>Auth0 documentation</strong></a>, and this holds true for all <a target="_blank" href="https://auth0.com/docs/authenticate/login/logout">OAuth providers.</a></p>
<p>You can verify the above statement in the following way:</p>
<ul>
<li><p>If you log in for the very first time using <code>signIn()</code>, you'll see a pop-up from your OAuth provider for credentials.</p>
</li>
<li><p>Now log out using <code>signOut()</code>. Verify that the <code>session-token</code> and the <code>Session</code> are nullified.</p>
</li>
<li><p>Log in again with <code>signIn()</code>. This time, you won't see any pop-up asking for credentials; instead, you will be auto-logged in the moment you press the sign-in button!</p>
</li>
</ul>
<p>This proves that the user session was still active in the Auth0 session layer. If you want to clear the user session on Auth0's session layer as well, you will have to log out the user from Auth0 using the OIDC endpoint.</p>
<pre><code class="lang-svelte"><span class="hljs-comment">&lt;!-- src/routes/logout/+page.svelte file --&gt;</span><span class="xml">
<span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ onMount }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte'</span>;
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ page }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/stores'</span>;

  onMount(<span class="hljs-keyword">async</span> () =&gt; </span></span><span class="javascript">{
    <span class="hljs-keyword">const</span> idToken = $page.data?.session?.user.id_token <span class="hljs-keyword">as</span> string;
    <span class="hljs-built_in">window</span>.location.href =
            <span class="hljs-keyword">import</span>.meta.env.VITE_ISSUER +
            <span class="hljs-string">`oidc/logout?post_logout_redirect_uri=<span class="hljs-subst">${<span class="hljs-built_in">encodeURIComponent</span>(
                <span class="hljs-built_in">window</span>.location.origin
            )}</span>&amp;id_token_hint=<span class="hljs-subst">${idToken}</span>`</span>;
  }</span><span class="xml">);
<span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<p>The above code snippet showcases <a target="_blank" href="https://auth0.com/docs/authenticate/login/logout/log-users-out-of-auth0"><strong>one of the many ways</strong></a> to log out users from the Auth0 session layer.</p>
<p>We need to do one more configuration in our Auth0 application. We need to add the URL to the <em>Allowed Logout URLs</em> option. I always redirect users to the home page, so I've given the URL of my root page. If you wish to redirect the user to any other page, that URL must be whitelisted here.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690471381211/618c3aed-a1d1-4756-ba85-f03a1a4adfb5.png?auto=compress,format&amp;format=webp" alt /></p>
<p>In this article, we explored the differences between signing out a user from the Application layer and the OAuth layer. We demonstrated how SvelteKitAuth handles user sign-out by clearing the session token and resetting the session, while the OAuth provider's session remains active. We also provided a method to log out users from the OAuth provider's session layer using the OIDC endpoint and highlighted the importance of configuring the Allowed Logout URLs in Auth0. Understanding these differences and configurations ensures a more secure and seamless user experience.</p>
<p>Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth"><strong>GitHub repository</strong></a> with the codebase. In the next section, we will delve into <a target="_blank" href="https://blog.aakashgoplani.in/how-to-manage-sessions-in-sveltekit-with-sveltekitauth">managing sessions</a> within the application.</p>
]]></content:encoded></item><item><title><![CDATA[Optimizing Server-Side Login and Logout Processes]]></title><description><![CDATA[In the previous article, we explored how to authenticate users on the Client side. In this section, we will delve into the server-side authentication process. There are two ways in which we can initiate the server-side authentication:
Using Form Acti...]]></description><link>https://blog.aakashgoplani.in/optimizing-server-side-login-and-logout-processes</link><guid isPermaLink="true">https://blog.aakashgoplani.in/optimizing-server-side-login-and-logout-processes</guid><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[auth.js]]></category><category><![CDATA[Auth0]]></category><category><![CDATA[Sveltekit]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 20:14:26 GMT</pubDate><content:encoded><![CDATA[<p>In the previous article, we explored how to <a target="_blank" href="https://blog.aakashgoplani.in/streamlining-client-side-sign-in-and-sign-out-processes"><strong>authenticate users on the Client side</strong></a>. In this section, we will delve into the server-side authentication process. There are two ways in which we can initiate the server-side authentication:</p>
<h3 id="heading-using-form-actions">Using Form Actions</h3>
<p><code>&lt;SignIn /&gt;</code> and <code>&lt;SignOut /&gt;</code> are components that <code>@auth/sveltekit</code> provides out of the box - they handle the sign-in/sign-out flow, and can be used as-is as a starting point or customized for your own components.</p>
<p>The detailed example is provided in the <a target="_blank" href="https://authjs.dev/reference/sveltekit#server-side"><strong>official documentation</strong></a>, so I'll skip this approach and move to the next one.</p>
<h3 id="heading-using-programatically-to-auto-sign-in-and-sign-out-users">Using programatically to auto sign-in and sign-out users</h3>
<p>If your organization has outsourced the authentication mechanism to a third-party vendor, you will not have the flexibility to use form actions (as described in the previous section) to perform authentication. You will have to programatically login user and this is how you could do that:</p>
<h4 id="heading-sign-in-flow">Sign-in Flow</h4>
<p>We can programmatically redirect users to the <em>login</em> route via <em>hooks</em> if they are unauthenticated and carry on with silent sign-in.</p>
<p>The procedure is exactly the same as in client-side authentication. The difference is we have to carry out each step manually, which otherwise was done by SvelteKitAuth for us in the client-side flow.</p>
<ol>
<li><p>Prepare payload for sign-in and pass <em>callbackUrl</em> and other params if required</p>
</li>
<li><p>Make a POST call to <code>/auth/signin/&lt;provider-id&gt;</code> by passing <code>Content-Type</code> and <code>X-Auth-Return-Redirect</code> header values</p>
</li>
<li><p>Get the URL for sign-in as response (this is the same URL that we pass as authorization URL in the provider) and redirect user for authentication.</p>
</li>
</ol>
<pre><code class="lang-typescript"><span class="hljs-comment">// -&gt; `/login/page.server.ts`</span>

<span class="hljs-keyword">import</span> { redirect } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { PageServerLoad } <span class="hljs-keyword">from</span> <span class="hljs-string">'./$types'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = (<span class="hljs-keyword">async</span> ({ fetch, locals, url: _url }) =&gt; {
  <span class="hljs-keyword">let</span> url = <span class="hljs-string">''</span>;
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> locals.auth();
    <span class="hljs-keyword">if</span> (!session?.user) {
      <span class="hljs-keyword">const</span> params = <span class="hljs-keyword">new</span> URLSearchParams();
      params.append(<span class="hljs-string">'scope'</span>, <span class="hljs-string">'api openid profile email'</span>);

      <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> URLSearchParams();
      formData.append(<span class="hljs-string">'redirect'</span>, <span class="hljs-string">'true'</span>);
      formData.append(<span class="hljs-string">'callbackUrl'</span>, <span class="hljs-string">`<span class="hljs-subst">${_url.origin}</span>/ssr-login`</span>);

      <span class="hljs-keyword">const</span> signInRequest = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/auth/signin/auth1? '</span> + params.toString(), {
        method: <span class="hljs-string">'POST'</span>,
        headers: {
          <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/x-www-form-urlencoded'</span>,
          <span class="hljs-string">'X-Auth-Return-Redirect'</span>: <span class="hljs-string">'1'</span>
        },
        body: formData.toString()
      });
      <span class="hljs-keyword">const</span> signInResponse = <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> Response(signInRequest.body).json();

      <span class="hljs-keyword">if</span> (signInResponse?.url) {
        url = signInResponse.url;
      }
    }
  } <span class="hljs-keyword">catch</span> (e: <span class="hljs-built_in">any</span>) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Exception thrown while auto-sign-in: '</span>, e);
  }

  <span class="hljs-keyword">if</span> (url) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Auto login user: '</span>, url);
    <span class="hljs-keyword">throw</span> redirect(<span class="hljs-number">302</span>, url);
  }
}) satisfies PageServerLoad;
</code></pre>
<p><strong>NOTE</strong>: In the above code snippet, if you provide the option of <em>callbackUrl</em> within <em>formData</em>, that will be the output of <em>signInResponse,</em> else it will default to the URL of the page that initiated the sign-in request!</p>
<h4 id="heading-sign-out-flow">Sign-out Flow</h4>
<p>The process is exactly similar to the one that we saw for the sign-in flow in the previous section.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// -&gt; src/routes/logout/+page.server.ts file</span>
<span class="hljs-keyword">import</span> { redirect } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { PageServerLoad } <span class="hljs-keyword">from</span> <span class="hljs-string">'./$types'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load = (<span class="hljs-keyword">async</span> ({ fetch, locals }) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> locals.auth();
    <span class="hljs-keyword">if</span> (session &amp;&amp; !!session.user?.access_token) {
      <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> URLSearchParams();
      formData.append(<span class="hljs-string">'redirect'</span>, <span class="hljs-string">'false'</span>);
      formData.append(<span class="hljs-string">'callbackUrl'</span>, <span class="hljs-string">`<span class="hljs-subst">${_url.origin}</span>`</span>);

      <span class="hljs-keyword">const</span> signOutRequest = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/auth/signout'</span>, {
        method: <span class="hljs-string">'POST'</span>,
        headers: {
          <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/x-www-form-urlencoded'</span>,
          <span class="hljs-string">'X-Auth-Return-Redirect'</span>: <span class="hljs-string">'1'</span>
        },
        body: formData.toString()
      });
    }
  } <span class="hljs-keyword">catch</span> (e: <span class="hljs-built_in">any</span>) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Exception thrown while auto-sign-out: '</span>, e);
  }
}) satisfies PageServerLoad;
</code></pre>
<p><strong>NOTE</strong>: After signing out, we must reload the page. If we use the inbuilt <code>signOut()</code>, SvelteKitAuth auto reloads the page and redirects to the <em>callbackUrl</em> or to the URL that initiated the sign-in request. Since we programmatically logged users out, it is our responsibility to reload the page afterward.</p>
<pre><code class="lang-typescript">&lt;!-- src/routes/logout/+page.svelte file --&gt;
&lt;script lang=<span class="hljs-string">"ts"</span>&gt;
  <span class="hljs-keyword">import</span> { onMount } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte'</span>;
  <span class="hljs-keyword">import</span> { page } <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/stores'</span>;
  <span class="hljs-keyword">import</span> { goto } <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/navigation'</span>;

  onMount(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// session-storage ensures that we reload only once!</span>
    <span class="hljs-keyword">const</span> isAppReloaded = sessionStorage.getItem(<span class="hljs-string">'reloadApp'</span>) || <span class="hljs-string">'false'</span>;
    <span class="hljs-keyword">if</span> (isAppReloaded === <span class="hljs-string">'false'</span>) {
      sessionStorage.setItem(<span class="hljs-string">'reloadApp'</span>, <span class="hljs-string">'true'</span>);
      <span class="hljs-built_in">window</span>.location.reload();
    }
  });
&lt;/script&gt;
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this article, we have explored the intricacies of optimizing server-side login and logout processes. We began by discussing the use of form actions provided by <code>@auth/sveltekit</code> for handling authentication flows seamlessly. However, recognizing that not all organizations have the flexibility to use these form actions, we delved into the programmatic approach for auto sign-in and sign-out, which is particularly useful when dealing with third-party authentication vendors.</p>
<p>We detailed the steps involved in the sign-in flow, including preparing the payload, making a POST call, and redirecting users for authentication. Similarly, we covered the sign-out flow, emphasizing the importance of reloading the page after logging out to ensure a smooth user experience.</p>
<p>By understanding both client-side and server-side authentication processes, you are now equipped to implement robust and flexible authentication mechanisms in your applications.</p>
<p>Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth"><strong>GitHub repository</strong></a> with the codebase. In the next section, we will explore <a target="_blank" href="https://blog.aakashgoplani.in/user-sign-out-application-vs-oauth-provider">the differences between signing out a user from the application versus signing out from the OAuth provider</a>, providing further insights into managing user sessions effectively.</p>
]]></content:encoded></item><item><title><![CDATA[Streamlining Client-Side Sign-In and Sign-Out Processes]]></title><description><![CDATA[In the previous articles, we covered the basics of SvelteKitAuth and learned how to configure OAuth applications using both built-in and custom OAuth providers. In this article, we will focus on enhancing the user's sign-in and sign-out experience.
T...]]></description><link>https://blog.aakashgoplani.in/streamlining-client-side-sign-in-and-sign-out-processes</link><guid isPermaLink="true">https://blog.aakashgoplani.in/streamlining-client-side-sign-in-and-sign-out-processes</guid><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[auth.js]]></category><category><![CDATA[Auth0]]></category><category><![CDATA[Sveltekit]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 19:59:10 GMT</pubDate><content:encoded><![CDATA[<p>In the previous articles, we covered the basics of SvelteKitAuth and learned how to configure OAuth applications using both built-in and custom OAuth providers. In this article, we will focus on enhancing the user's sign-in and sign-out experience.</p>
<p>There are two ways to initiate the authentication flow: client-side and server-side. In this article, we will go through a step-by-step approach for client-side authentication.</p>
<p>We have two methods, <code>signIn()</code> and <code>signOut()</code>, from <code>@auth/sveltekit/client</code> to perform client-side sign-in and sign-out actions, respectively.</p>
<h3 id="heading-sign-in-flow">Sign-in Flow</h3>
<p><code>signIn()</code> is the client-side method to initiate a sign-in flow or send the user to the sign-in page listing all possible providers. It automatically adds the CSRF token to the request.</p>
<p>Use the <code>signIn()</code> method with the following properties:</p>
<ul>
<li><p><strong>providerId</strong>: This is the "id" property that we specified in the previous sections. This is optional; if omitted, it defaults to the first id property specified in the SvelteKit configuration.</p>
</li>
<li><p><strong>options</strong>: This is an optional property where we can specify the <em>callbackURL</em>, i.e., the URL to which the user should be redirected once sign-in is successful. In some cases, you might want to handle the sign-in response on the same page and disable the default redirection. For example, if an error occurs (like wrong credentials given by the user), you might want to handle the error on the same page. For that, you can pass <code>redirect: false</code> in the second parameter object.</p>
</li>
<li><p><strong>Additional parameters</strong>: It is also possible to pass additional parameters to the <code>/authorize</code> endpoint through the third argument of <code>signIn()</code>.</p>
</li>
</ul>
<p>Although this is more than enough, if you still want to dive deeper into more options, you can read more configuration details in the <a target="_blank" href="https://next-auth.js.org/getting-started/client#redirects-to-sign-in-page-when-clicked"><strong>official documentation</strong></a>.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> { signIn } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit/client'</span>;
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">on:click</span>=<span class="hljs-string">{()</span> =&gt;</span> signIn(
  'auth0', {
    redirect: false,
    callbackUrl: 'http://localhost:4000/about'
  },
  {
    scope: 'api openid profile email'
  }
)}&gt;Sign In with Auth0<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<h3 id="heading-sign-out-flow">Sign-out Flow</h3>
<p>The <code>signOut()</code> method logs the user out by removing the session cookie. It automatically adds the CSRF token to the request.</p>
<p>Like the <code>signIn()</code> method, you can pass a <em>callbackURL</em> and <em>redirect</em> option. More details are available in the <a target="_blank" href="https://next-auth.js.org/getting-started/client#signout"><strong>official documentation</strong></a>.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> { signOut } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit/client'</span>;
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">on:click</span>=<span class="hljs-string">{()</span> =&gt;</span> signOut()} class="button"&gt;Sign out<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-comment">&lt;!-- OR --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">on:click</span>=<span class="hljs-string">{()</span> =&gt;</span> signOut({
  redirect: true,
  callbackUrl: 'url-post-logout'
})} class="button"&gt;Sign out with optional params<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p><strong>NOTE</strong>: If you don't provide the <em>callbackUrl</em> option, it will redirect you to the page that initiated the sign-in request.</p>
<p>In this article, we explored the client-side sign-in and sign-out processes using SvelteKitAuth. By leveraging the <code>signIn()</code> and <code>signOut()</code> methods from <code>@auth/sveltekit/client</code>, we can streamline the authentication flow, providing a seamless user experience. Whether specifying a provider, handling callbacks, or managing errors, these methods offer flexibility and control.</p>
<p>Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth"><strong>GitHub repository</strong></a> with the codebase. In the next section, we will go through <a target="_blank" href="https://blog.aakashgoplani.in/optimizing-server-side-login-and-logout-processes">server-side sign-in and sign-out flow</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Enhancing SvelteKitAuth with Custom Type Additions]]></title><description><![CDATA[Often, your IDE might complain that certain types are unrecognizable. This is common in SvelteKitAuth. To fix this, we need to add custom typings. Here is how we do it:
Step 1: Create a file named types/auth.d.ts at the same level as src. Here, we wi...]]></description><link>https://blog.aakashgoplani.in/enhancing-sveltekitauth-with-custom-type-additions</link><guid isPermaLink="true">https://blog.aakashgoplani.in/enhancing-sveltekitauth-with-custom-type-additions</guid><category><![CDATA[Types]]></category><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[auth.js]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 19:48:13 GMT</pubDate><content:encoded><![CDATA[<p>Often, your IDE might complain that certain types are unrecognizable. This is common in SvelteKitAuth. To fix this, we need to add custom typings. Here is how we do it:</p>
<p>Step 1: Create a file named <em>types/auth.d.ts</em> at the same level as <em>src</em>. Here, we will extend the core <code>Session</code> type with our custom type.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { DefaultSession } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/core/types'</span>;

<span class="hljs-keyword">declare</span> <span class="hljs-keyword">module</span> '@auth/core/types' {
    <span class="hljs-keyword">interface</span> Session {
        user: DefaultSession[<span class="hljs-string">'user'</span>] &amp; Partial&lt;IUser&gt;;
    }
}

<span class="hljs-keyword">interface</span> IUser {
    name: <span class="hljs-built_in">string</span>;
    email: <span class="hljs-built_in">string</span>;
    image: <span class="hljs-built_in">string</span>;
    picture: <span class="hljs-built_in">string</span>;
    sub: <span class="hljs-built_in">string</span>;
    access_token: <span class="hljs-built_in">string</span>;
    id_token: <span class="hljs-built_in">string</span>;
    scope: <span class="hljs-built_in">string</span>;
    expires_in: <span class="hljs-built_in">number</span>;
    token_type: <span class="hljs-built_in">string</span>;
    expires_at: <span class="hljs-built_in">number</span>;
    provider: <span class="hljs-built_in">string</span>;
    <span class="hljs-keyword">type</span>: <span class="hljs-built_in">string</span>;
    providerAccountId: <span class="hljs-built_in">string</span>;
    nickname: <span class="hljs-built_in">string</span>;
    updated_at: <span class="hljs-built_in">string</span>;
    email_verified: <span class="hljs-built_in">boolean</span>;
    iss: <span class="hljs-built_in">string</span>;
    aud: <span class="hljs-built_in">string</span>;
    iat: <span class="hljs-built_in">number</span>;
    exp: <span class="hljs-built_in">number</span>;
    sid: <span class="hljs-built_in">string</span>;
    jti: <span class="hljs-built_in">string</span>;
    fav_num: <span class="hljs-built_in">string</span>;
}
</code></pre>
<p>Step 2: Update the <em>tsconfig.json</em> file's <em>types</em> and <em>include</em> properties.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: <span class="hljs-string">"./.svelte-kit/tsconfig.json"</span>,
  <span class="hljs-attr">"compilerOptions"</span>: {
    ...,
    <span class="hljs-attr">"types"</span>: [<span class="hljs-string">"vite/client"</span>, <span class="hljs-string">"svelte"</span>, <span class="hljs-string">"@auth/core"</span>, <span class="hljs-string">"@auth/sveltekit"</span>]
  },
  <span class="hljs-attr">"include"</span>: [
    ...,
    <span class="hljs-string">"types/**/*.ts"</span>
  ]
}
</code></pre>
<p>Step 3: Restart your IDE.</p>
<p>In conclusion, enhancing SvelteKitAuth with custom type additions is a straightforward process that can significantly improve your development experience. By creating a custom typings file and updating your TypeScript configuration, you can ensure that your IDE recognizes all necessary types, reducing errors and improving code quality. Remember to restart your IDE after making these changes to apply the updates effectively. This small but crucial step can make a big difference in maintaining a smooth and efficient development workflow.</p>
<p>Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth"><strong>GitHub repository</strong></a> with the codebase. In the next section, we will delve into initiating <a target="_blank" href="https://blog.aakashgoplani.in/streamlining-client-side-sign-in-and-sign-out-processes">client-side sign-in and sign-out flows</a>, further streamlining the user authentication journey.</p>
]]></content:encoded></item><item><title><![CDATA[How to Integrate Multiple OAuth Providers in SvelteKitAuth]]></title><description><![CDATA[In previous articles, we explored how to integrate both built-in and custom providers in SvelteKit. In this section, we'll demonstrate how to configure multiple providers simultaneously.
Let's first discuss the scenarios where such a configuration is...]]></description><link>https://blog.aakashgoplani.in/how-to-integrate-multiple-oauth-providers-in-sveltekitauth</link><guid isPermaLink="true">https://blog.aakashgoplani.in/how-to-integrate-multiple-oauth-providers-in-sveltekitauth</guid><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[auth.js]]></category><category><![CDATA[Sveltekit]]></category><category><![CDATA[Auth0]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 19:27:49 GMT</pubDate><content:encoded><![CDATA[<p>In previous articles, we explored how to integrate both <a target="_blank" href="https://blog.aakashgoplani.in/step-by-step-guide-to-using-built-in-auth0-oauth-provider-with-sveltekitauth">built-in</a> and <a target="_blank" href="https://blog.aakashgoplani.in/step-by-step-guide-to-using-custom-oauth-provider-with-sveltekitauth">custom</a> providers in SvelteKit. In this section, we'll demonstrate how to configure multiple providers simultaneously.</p>
<p>Let's first discuss the scenarios where such a configuration is beneficial. For instance, offering multiple sign-in options like Google, GitHub, or LinkedIn can enhance user convenience and flexibility.</p>
<p>Here’s how to achieve this:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// hooks.server.ts</span>

<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Handle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> Auth0Provider <span class="hljs-keyword">from</span> <span class="hljs-string">"@auth/core/providers/auth0"</span>;
<span class="hljs-keyword">import</span> { SvelteKitAuth, <span class="hljs-keyword">type</span> SvelteKitAuthConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit'</span>;
<span class="hljs-keyword">import</span> { VERCEL_SECRET, CLIENT_ID, CLIENT_SECRET, ISSUER, WELL_KNOWN } <span class="hljs-keyword">from</span> <span class="hljs-string">'$env/static/private'</span>;

<span class="hljs-keyword">const</span> { handle: getAuthConfig } = SvelteKitAuth(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {
    providers: [{
      Auth0Provider({
        id: <span class="hljs-string">'auth0'</span>,
        name: <span class="hljs-string">'built-in-oauth-provider'</span>,
        clientId: CLIENT_ID,
        clientSecret: CLIENT_SECRET,
        issuer: ISSUER
      }),
      id: <span class="hljs-string">'auth1'</span>,
      name: <span class="hljs-string">'custom-oauth-provider'</span>,
      <span class="hljs-keyword">type</span>: <span class="hljs-string">'oidc'</span>,
      client: {
        token_endpoint_auth_method: <span class="hljs-string">'client_secret_post'</span>
      },
      clientId: CLIENT_ID,
      clientSecret: CLIENT_SECRET,
      issuer: ISSUER,
      wellKnown: WELL_KNOWN,
      checks: [<span class="hljs-string">'pkce'</span>],
      authorization: {
        url: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>authorize`</span>, <span class="hljs-comment">// 'http://localhost:4200/authorize</span>
        params: {
          scope: <span class="hljs-string">'openid name email profile'</span>,
          redirect_uri: <span class="hljs-string">`<span class="hljs-subst">${event.url.origin}</span>/auth/callback/auth1`</span>
        }
      },
      token: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>oauth/token`</span>,
      userinfo: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>userinfo`</span>
    }],
    secret: VERCEL_SECRET,
    debug: <span class="hljs-literal">true</span>,
    trustHost: <span class="hljs-literal">true</span>,
    session: {
      strategy: <span class="hljs-string">'jwt'</span>,
      maxAge: <span class="hljs-number">1800</span> <span class="hljs-comment">// 30 mins</span>
    },
    logger: {
      error: <span class="hljs-keyword">async</span> (error: <span class="hljs-built_in">any</span>) =&gt; {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Error trace from SvelteKitAuth:'</span>, error);
      }
    }
  };
  <span class="hljs-keyword">return</span> config;
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle = SvelteKitAuth(config) satisfies Handle;
</code></pre>
<p>The main differentiating factor is the <code>id</code> property. The <code>id</code> property uniquely identifies which provider should be used for authentication. The <code>name</code> property acts as a label or a detailed description that helps us differentiate the providers from each other.</p>
<p>We can switch the providers based on the <code>id</code> example:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">on:click</span>=<span class="hljs-string">{()</span> =&gt;</span> signIn('auth0')} &gt;
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Sign In with Built-in Auth0 Provider<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">on:click</span>=<span class="hljs-string">{()</span> =&gt;</span> signIn('auth1')} &gt;
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Sign In with Custom Auth0 Provider<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>In conclusion, integrating multiple OAuth providers in SvelteKitAuth significantly enhances user experience by offering diverse sign-in options. This flexibility not only improves user convenience but also broadens the potential user base by accommodating various preferences. By following the outlined steps, developers can seamlessly configure multiple providers, ensuring a smooth and secure authentication process.</p>
<p>Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth"><strong>GitHub repository</strong></a> with the codebase. In the next article, we will learn how to enhance <a target="_blank" href="https://blog.aakashgoplani.in/enhancing-sveltekitauth-with-custom-type-additions">custom typings with SvelteKitAuth</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Step-by-Step Guide to using custom OAuth provider with SvelteKitAuth]]></title><description><![CDATA[In the previous article, we covered the integration of a built-in OAuth provider with SvelteKitAuth. In this article, we will delve into integrating a custom OAuth provider with SvelteKitAuth.
A custom provider can be particularly useful when (a) you...]]></description><link>https://blog.aakashgoplani.in/step-by-step-guide-to-using-custom-oauth-provider-with-sveltekitauth</link><guid isPermaLink="true">https://blog.aakashgoplani.in/step-by-step-guide-to-using-custom-oauth-provider-with-sveltekitauth</guid><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[Auth0]]></category><category><![CDATA[Sveltekit]]></category><category><![CDATA[auth.js]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 19:04:17 GMT</pubDate><content:encoded><![CDATA[<p>In the <a target="_blank" href="https://blog.aakashgoplani.in/step-by-step-guide-to-using-built-in-auth0-oauth-provider-with-sveltekitauth"><strong>previous article</strong></a>, we covered the integration of a built-in OAuth provider with SvelteKitAuth. In this article, we will delve into integrating a custom OAuth provider with SvelteKitAuth.</p>
<p>A custom provider can be particularly useful when (a) you require extensive customization in your authentication mechanism, or (b) the authentication provider you're using is not currently supported by Auth.js.</p>
<p>Here is the code snippet for the custom provider. Let us now deep dive into custom providers and the properties that are used within the provider.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// hooks.server.ts</span>

<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { SvelteKitAuthConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit'</span>;
<span class="hljs-keyword">import</span> { VERCEL_SECRET, CLIENT_ID, CLIENT_SECRET, ISSUER, WELL_KNOWN } <span class="hljs-keyword">from</span> <span class="hljs-string">'$env/static/private'</span>;

<span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {
  providers: [{
    id: <span class="hljs-string">'auth0'</span>,
    name: <span class="hljs-string">'custom-oauth-provider'</span>,
    <span class="hljs-keyword">type</span>: <span class="hljs-string">'oidc'</span>,
    client: {
      token_endpoint_auth_method: <span class="hljs-string">'client_secret_post'</span>
    },
    clientId: CLIENT_ID,
    clientSecret: CLIENT_SECRET,
    issuer: ISSUER,
    wellKnown: WELL_KNOWN,
    checks: [<span class="hljs-string">'pkce'</span>],
    authorization: {
      url: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>authorize`</span>, <span class="hljs-comment">// 'http://localhost:4200/authorize</span>
      params: {
        scope: <span class="hljs-string">'openid name email profile'</span>,
        redirect_uri: <span class="hljs-string">`<span class="hljs-subst">${event.url.origin}</span>/auth/callback/auth0`</span>
      }
    },
    token: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>oauth/token`</span>,
    userinfo: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>userinfo`</span>
  }],
  secret: VERCEL_SECRET,
  debug: <span class="hljs-literal">true</span>,
  session: {
    maxAge: <span class="hljs-number">1800</span> <span class="hljs-comment">// 30 mins</span>
  }
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle = SvelteKitAuth(config) satisfies Handle;
</code></pre>
<ul>
<li><p><code>id</code>, <code>name</code>, <code>clientId</code>, <code>clientSecret</code>, <code>issuer</code> and <code>wellKnown</code> - these configurations remain the same as they were discussed in the last section. The focus here will be on the <code>type</code> property. The <code>type</code> property specifies the type of authentication mechanism, allowed values are: "<em>oidc</em>", "<em>oauth</em>", "<em>credentials</em>", and "<em>email</em>".</p>
</li>
<li><p>If your provider is OpenID Connect (OIDC) compliant, the recommendation is to use the <code>wellKnown</code> option instead. OIDC usually returns an <code>id_token</code> from the <code>token</code> endpoint. <code>SvelteKitAuth</code> can decode the <code>id_token</code> to get the user information, instead of making an additional request to the <code>userinfo</code> endpoint.</p>
</li>
<li><p>In case your provider is not OIDC compliant, we have the option to customize the configuration by using a combination of the following properties. You can find more information in the <a target="_blank" href="https://next-auth.js.org/configuration/providers/oauth"><strong>docs</strong></a>.</p>
<ul>
<li><p><strong>authorization</strong>: This is the URL for authentication. There are two ways to use this option:</p>
<ol>
<li><p>You can either set <code>authorization</code> to be a full URL, like <code>"https://example.com/oauth/authorization?scope=email"</code>.</p>
</li>
<li><p>Use an object with <code>url</code> and <code>params</code> like so</p>
<pre><code class="lang-typescript">  authorization: {
    url: <span class="hljs-string">"https://example.com/oauth/authorization"</span>,
    params: { scope: <span class="hljs-string">"email"</span> }
  }
</code></pre>
</li>
</ol>
</li>
<li><p><strong>token:</strong> This is the URL that will fetch token information. There are three ways to use this option:</p>
<ol>
<li><p>You can either set <code>token</code> to be a full URL, like <code>"https://example.com/oauth/token?some=param"</code>.</p>
</li>
<li><p>Use an object with <code>url</code> and <code>params</code> like so</p>
<pre><code class="lang-typescript">  token: {
    url: <span class="hljs-string">"https://example.com/oauth/token"</span>,
    params: { some: <span class="hljs-string">"param"</span> }
  }
</code></pre>
</li>
<li><p>Completely take control of the request:</p>
<pre><code class="lang-typescript">  token: {
    url: <span class="hljs-string">"https://example.com/oauth/token"</span>,
    <span class="hljs-keyword">async</span> conform(response) {
      <span class="hljs-keyword">if</span> (response.ok) {
        <span class="hljs-keyword">const</span> body = <span class="hljs-keyword">await</span> response.clone().json()
        <span class="hljs-keyword">if</span> (body?.response?.access_token) {
          <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify(body.response), response)
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (body?.access_token) {
          <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">"Token response conforms to the standard, workaround not needed."</span>)
        }
      }
      <span class="hljs-keyword">return</span> response
    }
  }
</code></pre>
</li>
</ol>
</li>
<li><p><strong>userinfo</strong>: A <code>userinfo</code> endpoint returns information about the logged-in user. It is not part of the OAuth specification but is usually available for most providers. There are three ways to use this option:</p>
<ol>
<li><p>You can either set <code>userinfo</code> to be a full URL, like <code>"https://example.com/oauth/userinfo?some=param"</code>.</p>
</li>
<li><p>Use an object with <code>url</code> and <code>params</code> like so</p>
<pre><code class="lang-typescript">  userinfo: {
    url: <span class="hljs-string">"https://example.com/oauth/userinfo"</span>,
    params: { some: <span class="hljs-string">"param"</span> }
  }
</code></pre>
</li>
<li><p>Completely take control of the request:</p>
<pre><code class="lang-typescript">  userinfo: {
    url: <span class="hljs-string">"https://example.com/oauth/userinfo"</span>,
    <span class="hljs-comment">// The result of this method will be the input to the `profile` callback.</span>
    <span class="hljs-keyword">async</span> conform(response) {
      <span class="hljs-keyword">if</span> (response.ok) {
        <span class="hljs-keyword">const</span> body = <span class="hljs-keyword">await</span> response.clone().json()
        <span class="hljs-keyword">if</span> (body?.response?.access_token) {
          <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify(body.response), response)
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (body?.access_token) {
          <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">"Token response conforms to the standard, workaround not needed."</span>)
        }
      }
      <span class="hljs-keyword">return</span> response
    }
  }
</code></pre>
</li>
</ol>
</li>
</ul>
</li>
</ul>
<p>And that's it! We are ready with our custom OAuth Auth0 provider. Here is the final snippet that we will be using in the <em>hooks.server.ts</em> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Handle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> { SvelteKitAuth, <span class="hljs-keyword">type</span> SvelteKitAuthConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit'</span>;
<span class="hljs-keyword">import</span> { VERCEL_SECRET, CLIENT_ID, CLIENT_SECRET, ISSUER, WELL_KNOWN } <span class="hljs-keyword">from</span> <span class="hljs-string">'$env/static/private'</span>;

<span class="hljs-keyword">const</span> { handle: getAuthConfig } = SvelteKitAuth(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {
    providers: [{
      id: <span class="hljs-string">'auth0'</span>,
      name: <span class="hljs-string">'custom-oauth-provider'</span>,
      <span class="hljs-keyword">type</span>: <span class="hljs-string">'oidc'</span>,
      client: {
        token_endpoint_auth_method: <span class="hljs-string">'client_secret_post'</span>
      },
      clientId: CLIENT_ID,
      clientSecret: CLIENT_SECRET,
      issuer: ISSUER,
      wellKnown: WELL_KNOWN,
      checks: [<span class="hljs-string">'pkce'</span>],
      authorization: {
        url: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>authorize`</span>, <span class="hljs-comment">// 'http://localhost:4200/authorize</span>
        params: {
          scope: <span class="hljs-string">'openid name email profile'</span>,
          redirect_uri: <span class="hljs-string">`<span class="hljs-subst">${event.url.origin}</span>/auth/callback/auth0`</span>
        }
      },
      token: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>oauth/token`</span>,
      userinfo: <span class="hljs-string">`<span class="hljs-subst">${ISSUER}</span>userinfo`</span>
    }],
    secret: VERCEL_SECRET,
    debug: <span class="hljs-literal">true</span>,
    trustHost: <span class="hljs-literal">true</span>,
    session: {
      strategy: <span class="hljs-string">'jwt'</span>,
      maxAge: <span class="hljs-number">1800</span> <span class="hljs-comment">// 30 mins</span>
    },
    logger: {
      error: <span class="hljs-keyword">async</span> (error: <span class="hljs-built_in">any</span>) =&gt; {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Error trace from SvelteKitAuth:'</span>, error);
      }
    }
  };
  <span class="hljs-keyword">return</span> config;
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle = SvelteKitAuth(getAuthConfig) satisfies Handle;
</code></pre>
<p>In conclusion, integrating a custom OAuth provider with SvelteKitAuth offers flexibility and extensive customization options for your authentication mechanism. By understanding and configuring properties such as <code>authorization</code>, <code>token</code>, and <code>userinfo</code>, you can tailor the authentication process to meet your specific needs. Additionally, the ability to include multiple providers in the same configuration enhances the versatility of your application. With these steps, you are well-equipped to implement a robust and customized authentication system using SvelteKitAuth.</p>
<p>Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth">GitHub repository</a> with the codebase. In the next article, we will go through the steps on <a target="_blank" href="https://blog.aakashgoplani.in/how-to-integrate-multiple-oauth-providers-in-sveltekitauth">configuring multiple providers with SvelteKitAuth</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Step-by-Step Guide to Using built-in (Auth0) OAuth provider with SvelteKitAuth]]></title><description><![CDATA[In the previous article, we covered the basics of SvelteKitAuth. In this article, we will delve into integrating the built-in OAuth provider with SvelteKit.
Basic Configuration
Below is the code snippet for using the built-in Auth0 OAuth provider:
//...]]></description><link>https://blog.aakashgoplani.in/step-by-step-guide-to-using-built-in-auth0-oauth-provider-with-sveltekitauth</link><guid isPermaLink="true">https://blog.aakashgoplani.in/step-by-step-guide-to-using-built-in-auth0-oauth-provider-with-sveltekitauth</guid><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[Auth0]]></category><category><![CDATA[Sveltekit]]></category><category><![CDATA[oauth]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 18:49:21 GMT</pubDate><content:encoded><![CDATA[<p>In the <a target="_blank" href="https://blog.aakashgoplani.in/setting-up-auth0-and-adding-sveltekitauth-to-your-app">previous article</a>, we covered the basics of SvelteKitAuth. In this article, we will delve into integrating the built-in OAuth provider with SvelteKit.</p>
<h3 id="heading-basic-configuration">Basic Configuration</h3>
<p>Below is the code snippet for using the built-in Auth0 OAuth provider:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// hooks.server.ts</span>

<span class="hljs-keyword">import</span> Auth0Provider <span class="hljs-keyword">from</span> <span class="hljs-string">"@auth/core/providers/auth0"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { SvelteKitAuthConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit'</span>;

<span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {
  providers: [
    Auth0Provider({
      id: <span class="hljs-string">'auth0'</span>,
      name: <span class="hljs-string">'Auth0'</span>,
      clientId: <span class="hljs-string">'-client-id-'</span>,
      clientSecret: <span class="hljs-string">'-client-secret-'</span>,
      issuer: <span class="hljs-string">'https://dev-****.auth0.com/'</span>,  <span class="hljs-comment">// &lt;- remember to add trailing `/` </span>
      wellKnown: <span class="hljs-string">'https://dev-****.auth0.com/.well-known/openid-configuration'</span>
    }) <span class="hljs-keyword">as</span> Provider
  ],
  secret: <span class="hljs-string">'random-32-bit-hexadecimal-string'</span>,
  session: {
    strategy: <span class="hljs-string">'jwt'</span>,
    maxAge: <span class="hljs-number">3600</span> 
  },
  trustHost: <span class="hljs-literal">true</span>
  ...
}
</code></pre>
<p>Let discuss about these properties in detail:</p>
<ul>
<li><p><strong>id</strong>: It is a string value that you can assign to uniquely identify the provider. As we can see from the syntax <code>providers[{...}]</code> is an array, and hence this id property helps in referencing a particular provider in the case where we have multiple providers. Also, this particular id is used in the callback URL pattern. Our callback URL is <a target="_blank" href="http://localhost:4000/auth/callback/auth0"><strong><em>localhost:4000/auth/callback/auth0</em></strong></a>; here "auth0" comes from this "id" parameter. It is an optional parameter; if we skip this, it defaults to the in-built provider that we are using, in this case, "Auth0".</p>
</li>
<li><p><strong>name</strong>: This is an optional property wherein you can provide any string value, preferred one is the name of the OAuth provider you're using i.e., "Auth0"</p>
</li>
<li><p><strong>clientId</strong>: is a unique identifier that is assigned to an application when it registers with an OAuth 2.0 service provider. The <code>clientId</code> is used by the application to authenticate itself to the service provider and to obtain access tokens. Client Id value can be obtained from the OAuth application. In your Auth0 application, go to -&gt; <em>Settings</em> tab -&gt; <em>Basic Information Section</em> -&gt; Get <em>Client ID</em> string.</p>
</li>
<li><p><strong>clientSecret</strong>: is a secret key that is used to protect the client ID. The <code>clientSecret</code> is not shared with the service provider and must be kept confidential by the application. Client Secret value, similar to Client ID, can be retrieved from the Basic Information Section of your Auth0 application.</p>
</li>
<li><p><strong>issuer</strong>: The issuer is the domain of your Auth0 application, which can be fetched from the Basic Information Section of your Auth0 application.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690471007022/6c04ed69-0697-40f3-89d2-f156e4336a40.png?auto=compress,format&amp;format=webp" alt /></p>
</li>
<li><p><strong>wellKnown</strong>: The <em>wellKnown</em> URL is a preassigned stable endpoint that a server uses every time it runs. It contains information about where to fetch tokens and user information post-authentication.</p>
</li>
<li><p><strong>secret</strong>: This is a random string used as a salt for encryption. It should be a 32-character Hexadecimal string. You can generate one from <a target="_blank" href="https://generate-secret.vercel.app/32"><strong>this site</strong></a>.</p>
</li>
<li><p><strong>trustHost</strong>: Auth.js relies on the incoming request’s <code>host</code> header to function correctly. For this reason, this property needs to be set to <code>true</code>.</p>
<p>  Make sure that your deployment platform sets the <code>host</code> header safely.</p>
</li>
<li><p><strong>session</strong>: This sets the strategy used for authentication. Available options are "<em>database</em>" and "<em>jwt</em>". It defaults to "<em>jwt</em>". For this tutorial series, we will be using "<em>jwt</em>". You can read more about <a target="_blank" href="https://authjs.dev/concepts/session-strategies">session strategy here</a>.</p>
</li>
</ul>
<p>These were the core properties that should suffice the basic requirement for authentication. However, we will discuss a few more properties that enhance the developer experience to manage authentication.</p>
<ul>
<li><p><strong>debug</strong>: This will use the <code>console</code> methods to log out many details about the authentication process, including requests, responses, errors, and database requests and responses.</p>
</li>
<li><p><strong>basePath</strong>: If your application has a base path configured, then you must set the same base path here as well.</p>
</li>
<li><p><strong>cookies</strong>: SvelteKitAuth sets a minimum of 3 and a maximum of 5 cookies based on the configuration of the provider.</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> { dev } <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/environment'</span>;

  <span class="hljs-keyword">const</span> useSecureCookies = !dev;

  cookies: {
    callbackUrl: {
      name: <span class="hljs-string">`<span class="hljs-subst">${useSecureCookies ? <span class="hljs-string">'__Secure-'</span> : <span class="hljs-string">''</span>}</span>authjs.callback-url`</span>,
      options: {
        httpOnly: <span class="hljs-literal">true</span>,
        sameSite: useSecureCookies ? <span class="hljs-string">'none'</span> : <span class="hljs-string">'lax'</span>,
        path: <span class="hljs-string">'/'</span>,
        secure: useSecureCookies
      }
    },
    ...
  }
</code></pre>
<ol>
<li><p><strong><em>callbackUrl</em></strong>: This property saves callbackUrl</p>
</li>
<li><p><strong><em>csrfToken</em></strong>: This property saves the CSRF token that is generated by SvelteKit (by default, if on else by SvelteKitAuth)</p>
</li>
<li><p><strong><em>pkceCodeVerifier</em></strong>: This sets pkce cookie only once check for PKCE is enabled</p>
</li>
<li><p><strong>sessionToken</strong>: This is a very important cookie and sets the actual session of the user.</p>
</li>
<li><p><strong><em>state</em></strong>: This sets pkce cookie only once check for STATE is enabled</p>
</li>
</ol>
</li>
</ul>
<p>    You can change these default configurations, however, <strong>This is an advanced option.</strong> Advanced options are passed the same way as basic options, but <strong>may have complex implications</strong> or side effects. You should <strong>try to avoid using advanced options</strong> unless you are very comfortable using them. <a target="_blank" href="https://authjs.dev/reference/core#cookies">More Reading</a>...</p>
<ul>
<li><p><strong>callbacks</strong>: Callbacks are asynchronous functions you can use to control what happens when an action is performed. Callbacks are <em>extremely powerful</em>, especially in scenarios involving JSON Web Tokens as they <strong>allow you to implement access controls without a database</strong> and to <strong>integrate with external databases or APIs</strong>.</p>
<p>  There are 4 types of callbacks: (1) <em>jwt,</em> (2) <em>session,</em> (3) <em>redirect</em> and (4) <em>signIn.</em></p>
<pre><code class="lang-typescript">  optional callbacks: {
    jwt: <span class="hljs-function">(<span class="hljs-params">params</span>) =&gt;</span> Awaitable&lt;<span class="hljs-literal">null</span> | JWT&gt;;
    redirect: <span class="hljs-function">(<span class="hljs-params">params</span>) =&gt;</span> Awaitable&lt;<span class="hljs-built_in">string</span>&gt;;
    session: <span class="hljs-function">(<span class="hljs-params">params</span>) =&gt;</span> Awaitable&lt;Session | DefaultSession&gt;;
    signIn: <span class="hljs-function">(<span class="hljs-params">params</span>) =&gt;</span> Awaitable&lt;<span class="hljs-built_in">string</span> | <span class="hljs-built_in">boolean</span>&gt;;
  };
</code></pre>
<p>  We will discuss some of these in detail in our next tutorial on creating a custom OAuth provider. However, if you want to utilize these options within in-built providers, you can read the details from <a target="_blank" href="https://authjs.dev/reference/sveltekit/types#callbacks">official documentation</a></p>
</li>
<li><p><strong>logger</strong>: You can customize the logging output by providing your own logger. This is useful if you want to send logs to a logging service or if you want to customize the format of the logs.</p>
<pre><code class="lang-typescript">  logger: {
    error(code, ...message) {
      <span class="hljs-built_in">console</span>.error(code, message)
    },
    warn(code, ...message) {
      <span class="hljs-built_in">console</span>.warn(code, message)
    },
    debug(code, ...message) {
      <span class="hljs-built_in">console</span>.debug(code, message)
    },
  },
</code></pre>
</li>
<li><p><strong>events</strong>: Events are asynchronous functions that do not return a response; they are useful for audit logging. You can specify a handler for any of these events below - e.g., for debugging or to create an audit log. The content of the message object varies depending on the flow (e.g., OAuth or Email authentication flow, JWT or database sessions, etc.), but typically contains a user object and/or contents of the JSON Web Token and other information relevant to the event.</p>
<pre><code class="lang-typescript">  events: {
    createUser: <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> Awaitable&lt;<span class="hljs-built_in">void</span>&gt;;
    linkAccount: <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> Awaitable&lt;<span class="hljs-built_in">void</span>&gt;;
    session: <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> Awaitable&lt;<span class="hljs-built_in">void</span>&gt;;
    signIn: <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> Awaitable&lt;<span class="hljs-built_in">void</span>&gt;;
    signOut: <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> Awaitable&lt;<span class="hljs-built_in">void</span>&gt;;
    updateUser: <span class="hljs-function">(<span class="hljs-params">message</span>) =&gt;</span> Awaitable&lt;<span class="hljs-built_in">void</span>&gt;;
  };
</code></pre>
</li>
<li><p><strong>pages</strong>: Specify URLs to be used if you want to create custom sign-in, sign-out, and error pages. Pages specified will override the corresponding built-in page. We have 4 types of pages:</p>
<pre><code class="lang-typescript">  pages: {
    signIn: <span class="hljs-string">'/auth/signin'</span>,
    signOut: <span class="hljs-string">'/auth/signout'</span>,
    error: <span class="hljs-string">'/auth/error'</span>,
    verifyRequest: <span class="hljs-string">'/auth/verify-request'</span>,
    newUser: <span class="hljs-string">'/auth/new-user'</span>
  }
</code></pre>
<p>  Here are a few examples from the official documentation:</p>
<ol>
<li><p><a target="_blank" href="https://authjs.dev/guides/pages/built-in-pages">Built-in pages</a></p>
</li>
<li><p><a target="_blank" href="https://authjs.dev/guides/pages/signin">Custom sign-in page</a></p>
</li>
<li><p><a target="_blank" href="https://authjs.dev/guides/pages/signout">Custom sign-out page</a></p>
</li>
<li><p><a target="_blank" href="https://authjs.dev/guides/pages/error">Custom Error page</a></p>
</li>
</ol>
</li>
</ul>
<p>    We will go through a few examples in depth later in this tutorial series.</p>
<h3 id="heading-environment-variables">Environment Variables</h3>
<p>We must set a couple of mandatory environment variables:</p>
<ol>
<li><p>AUTH_URL: This environment variable is mostly unnecessary with v5 as the host is inferred from the request headers. However, if you are using a different base path, you can set this environment variable as well. For example, <code>AUTH_URL=</code><a target="_blank" href="http://localhost:3000/web/auth"><code>http://localhost:3000/web/auth</code></a> or <code>AUTH_URL=</code><a target="_blank" href="https://company.com/app1/auth"><code>https://company.com/app1/auth</code></a></p>
</li>
<li><p>CLIENT_ID and CLIENT_SECRET: It is recommended to save CLIENT_ID and CLIENT_SECRET in the <em>.env</em> file and access via <code>$env/static/private</code>.</p>
</li>
<li><p>ISSUER and WELL_KNOWN: Same strategy as above for these two variables as well.</p>
</li>
</ol>
<p>Here are few other <a target="_blank" href="https://authjs.dev/getting-started/deployment#environment-variables">environment variables</a> that you may find useful.</p>
<h3 id="heading-final-setup">Final Setup</h3>
<p>With these additional properties, out updated configuration will be:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { AUTH_SECRET, CLIENT_ID, CLIENT_SECRET, ISSUER, WELL_KNOWN } <span class="hljs-keyword">from</span> <span class="hljs-string">'$env/static/private'</span>;

<span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {
  providers: [
    Auth0Provider({
      id: <span class="hljs-string">'auth0'</span>,
      name: <span class="hljs-string">'Auth0'</span>,
      clientId: CLIENT_ID,
      clientSecret: CLIENT_SECRET,
      issuer: ISSUER,
      wellKnown: WELL_KNOWN
    }) <span class="hljs-keyword">as</span> Provider
  ],
  secret: AUTH_SECRET,
  session: {
    strategy: <span class="hljs-string">'jwt'</span>,
    maxAge: <span class="hljs-number">3600</span> 
  },
  trustHost: <span class="hljs-literal">true</span>,
  debug: <span class="hljs-literal">true</span>,
  logger: {
    error: <span class="hljs-keyword">async</span> (error: <span class="hljs-built_in">any</span>) =&gt; {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Error trace from SvelteKitAuth:'</span>, error);
    }
  }
}
</code></pre>
<p>We must use this piece of code in the <em>hooks.server.ts</em> file. <em>Why?</em> Because we want authentication to happen on every network request we make. We want to ensure that protected resources are not accessed if the user is not authenticated. In the SvelteKit ecosystem, hooks act as a middleware that taps request and response. Hence, this is the ideal place where we want our Authentication code to be integrated.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// hooks.server.ts</span>

<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Handle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
<span class="hljs-keyword">import</span> Auth0Provider <span class="hljs-keyword">from</span> <span class="hljs-string">"@auth/core/providers/auth0"</span>;
<span class="hljs-keyword">import</span> { SvelteKitAuth, <span class="hljs-keyword">type</span> SvelteKitAuthConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'@auth/sveltekit'</span>;

<span class="hljs-keyword">const</span> { handle: getAuthConfig } = SvelteKitAuth(<span class="hljs-keyword">async</span> (event) =&gt; {
  <span class="hljs-keyword">const</span> config: SvelteKitAuthConfig = {...};
  <span class="hljs-keyword">return</span> config;
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle = getAuthConfig;
</code></pre>
<p><code>SvelteKitAuth(() =&gt; {})</code> is the callback expose by <code>SvelteKitAuth</code> that return three functions:</p>
<ul>
<li><p><em>handle</em>: This must be used within handle function in hooks</p>
</li>
<li><p><em>signIn</em>: User for server side sign-in.</p>
</li>
<li><p><em>signOut</em>: Used for server side sign-out.</p>
</li>
</ul>
<p>For the sake of this tutorial, we only need the <code>handle</code> callback that will be consumed by our hooks file. We will discuss the other two callbacks in detail in the later part of the tutorial.</p>
<p>Integrating the built-in Auth0 OAuth provider with SvelteKit can significantly streamline the authentication process in your application. By understanding and configuring the essential properties such as <code>id</code>, <code>clientId</code>, <code>clientSecret</code>, and <code>issuer</code>, you can ensure a secure and efficient authentication flow. Additionally, leveraging advanced options like <code>callbacks</code>, <code>logger</code>, and <code>events</code> can enhance the developer experience and provide greater control over the authentication process. Setting up the necessary environment variables and placing the authentication code in the <code>hooks.server.ts</code> file ensures that protected resources are accessed only by authenticated users. With this setup, you are well-equipped to manage authentication in your SvelteKit application, paving the way for more advanced configurations and custom providers in the <a target="_blank" href="https://blog.aakashgoplani.in/step-by-step-guide-to-using-custom-oauth-provider-with-sveltekitauth">next tutorial</a>. Here is the link to the <a target="_blank" href="https://github.com/aakash14goplani/SvelteKitAuth">GitHub repository</a> with the codebase.</p>
]]></content:encoded></item><item><title><![CDATA[Setting Up Auth0 and Adding SvelteKitAuth to Your App]]></title><description><![CDATA[This article demonstrates how to integrate SvelteKitAuth with SvelteKit using OAuth providers, specifically focusing on the Auth0 provider. Before diving into the integration process, it's essential to have an Auth0 application set up. This tutorial ...]]></description><link>https://blog.aakashgoplani.in/setting-up-auth0-and-adding-sveltekitauth-to-your-app</link><guid isPermaLink="true">https://blog.aakashgoplani.in/setting-up-auth0-and-adding-sveltekitauth-to-your-app</guid><category><![CDATA[Auth0]]></category><category><![CDATA[Sveltekit]]></category><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[auth.js]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 18:29:31 GMT</pubDate><content:encoded><![CDATA[<p>This article demonstrates how to integrate SvelteKitAuth with SvelteKit using OAuth providers, specifically focusing on the Auth0 provider. Before diving into the integration process, it's essential to have an Auth0 application set up. This tutorial will guide you through creating an Auth0 application and configuring it for use with SvelteKitAuth.</p>
<h3 id="heading-creating-a-auth0-application">Creating a Auth0 application</h3>
<p>Before we start coding, we need to have an Auth0 application. Let's create one.</p>
<ul>
<li><p>Assuming you have a registered account with <a target="_blank" href="https://auth0.com/"><strong>Auth0</strong></a>, go to the dashboard and on left-hand side navigation, click on "Applications" -&gt; "Create Application".</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1683708094739/fe68066d-4595-4a85-a6c5-656b4b001fa2.png?auto=compress,format&amp;format=webp" alt /></p>
</li>
<li><p>Enter the Application name and choose the application type as "Regular Web Applications".</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1683708211719/e2ef2188-29c8-4110-8735-b7dd69ea803a.png?auto=compress,format&amp;format=webp" alt /></p>
</li>
<li><p>You should now reach the dashboard page. Click on the "Settings" tab.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690470802329/91600809-1872-4798-8282-aa04e9da986e.png?auto=compress,format&amp;format=webp" alt /></p>
</li>
<li><p>Scroll down to the section "Application URIs" -&gt; "Allowed Callback URLs". A callback URL is a URL that is invoked after OAuth authorization for the consumer. <strong>SvelteKitAuth has a specific pattern for callback URLs</strong> which is <code>&lt;origin&gt;/auth/callback/providerId</code> We must follow this pattern. We will enter two values here, one for local development <code>http://localhost:4000/auth/callback/auth0</code> and the other for where we host our site <code>https://my-app.vercel.app/auth/callback/auth0</code> We can also use a wildcard pattern to whitelist the entire domain: <code>https://*.vercel.app</code>.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1683709181987/984dadff-d714-416d-8864-777b3ea9a39a.png?auto=compress,format&amp;format=webp" alt /></p>
</li>
<li><p>The next section will be "Allowed web origins" within "Application URIs" <code>http://localhost:4000, https://*.vercel.app</code></p>
</li>
<li><p>Click on "Save Changes".</p>
</li>
</ul>
<p>With these changes, our OAuth application is now ready, and we can configure it with SvelteKitAuth.</p>
<h3 id="heading-integrating-auth0-application-with-sveltekit-using-sveltekitauth">Integrating Auth0 application with SvelteKit using SvelteKitAuth</h3>
<p>In SvelteKit, authentication is typically handled using community-supported libraries and services. In this series of articles, we will be focusing on OAuth provider - <strong>Auth0</strong></p>
<p>Before we begin, let's install two dependencies:</p>
<pre><code class="lang-json"><span class="hljs-string">"dependencies"</span>: {
  ...,
  <span class="hljs-attr">"@auth/core"</span>: <span class="hljs-string">"^0.34.1"</span>,
  <span class="hljs-attr">"@auth/sveltekit"</span>: <span class="hljs-string">"^1.4.1"</span>
}
</code></pre>
<p>There are two ways to integrate OAuth providers with our application:</p>
<ol>
<li><p><strong>Using built-in OAuth providers</strong></p>
<ul>
<li><p>A built-in OAuth provider typically refers to a service or platform that offers OAuth authentication as part of its core functionality.</p>
</li>
<li><p>These services are designed to handle the complexities of OAuth authentication, including token management, user sessions, and integration with various identity providers (like Auth0, Google, GitHub, etc.).</p>
</li>
<li><p>Integration with these built-in providers often involves using SDKs or libraries provided by the service, which abstracts much of the OAuth protocol details and makes integration straightforward.</p>
</li>
</ul>
</li>
<li><p><strong>Using custom OAuth providers</strong></p>
<ul>
<li><p>A custom OAuth provider refers to implementing OAuth authentication yourself or using a custom implementation not provided by a dedicated authentication service.</p>
</li>
<li><p>This approach requires you to set up your own OAuth server, which manages the OAuth flow, token generation, and user authentication.</p>
</li>
<li><p>Advantages of a custom OAuth provider include greater control over the authentication process, customization to fit specific application needs, and potentially lower cost (as you manage the infrastructure yourself).</p>
</li>
<li><p>However, implementing a custom OAuth provider requires a good understanding of OAuth protocols (like OAuth 2.0), security considerations (such as token management and secure communication), and may involve more development effort compared to using a built-in provider.</p>
</li>
</ul>
</li>
</ol>
<p><strong>Key Differences:</strong></p>
<ul>
<li><p><strong>Complexity and Maintenance:</strong> Built-in OAuth providers typically abstract away much of the complexity of OAuth authentication, making integration easier and reducing maintenance overhead. Custom OAuth providers require more setup and ongoing maintenance.</p>
</li>
<li><p><strong>Control and Customization:</strong> Custom OAuth providers offer more control and customization options but require deeper technical expertise and effort to implement securely.</p>
</li>
<li><p><strong>Integration Effort:</strong> Built-in OAuth providers often provide SDKs and libraries that simplify integration with various platforms, whereas custom OAuth implementations require you to handle integration details manually.</p>
</li>
</ul>
<p>In summary, the choice between a built-in OAuth provider and a custom OAuth provider depends on factors like your project's specific requirements, desired level of control, and your team's expertise in handling authentication protocols and security. For most applications, leveraging a well-established built-in OAuth provider like Auth0, Firebase Authentication, or others can often provide a good balance of ease of use, security, and functionality.</p>
<p>Stay tuned for the next article, where we will delve deeper into integrating SvelteKitAuth with the Auth0 OAuth provider.</p>
]]></content:encoded></item><item><title><![CDATA[How OAuth Operates: A Simple Breakdown]]></title><description><![CDATA[In the previous article, we explored what SvelteKitAuth is all about. One keyword that we frequently mentioned was "OAuth" authentication. But what exactly is OAuth?
If I were to explain OAuth to a 5 years old:
Imagine you have a box full of toys and...]]></description><link>https://blog.aakashgoplani.in/how-oauth-operates-a-simple-breakdown</link><guid isPermaLink="true">https://blog.aakashgoplani.in/how-oauth-operates-a-simple-breakdown</guid><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[Sveltekit]]></category><category><![CDATA[authentication]]></category><category><![CDATA[auth.js]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 18:29:13 GMT</pubDate><content:encoded><![CDATA[<p>In the previous article, we explored what SvelteKitAuth is all about. One keyword that we frequently mentioned was "<strong>OAuth</strong>" authentication. But what exactly is OAuth?</p>
<h3 id="heading-if-i-were-to-explain-oauth-to-a-5-years-old">If I were to explain OAuth to a 5 years old:</h3>
<p>Imagine you have a box full of toys and you want to share them with your friend. But you don't want to just give them the whole box, because there might be some toys you don't want them to play with yet.</p>
<p>This is kind of like how websites work sometimes. They have information that they want to share with other websites, but they don't want to give them full access to everything.</p>
<p>OAuth is like a special way of sharing. Here's how it works:</p>
<ol>
<li><p><strong>You ask your friend's parent:</strong> You tell your friend's parent (like a website's login) that you want to play with some of their child's toys (like the website's information).</p>
</li>
<li><p><strong>The parent asks your friend:</strong> The parent asks their child (the website asks the user) if it's okay for you to play with some of their toys (access some information).</p>
</li>
<li><p><strong>Your friend gives a special key:</strong> If your friend says yes, they give you a special key (like an authorization code) that lets you open a small box of toys (access a specific part of the information).</p>
</li>
<li><p><strong>You show the key to get the toys:</strong> You take that key back to the parent (the website sends the code back to a special server) and show it to them.</p>
</li>
<li><p><strong>The parent gives you the small box:</strong> The parent sees the key and knows it's okay, so they give you a small box of toys to play with (the website gives the special key to access the specific information you requested).</p>
</li>
</ol>
<p>This way, your friend (the website) gets to control what you can play with (what information you can access), and their parent (the login system) makes sure it's okay.</p>
<h3 id="heading-a-little-more-technical-explanation-of-previous-version">A little more technical explanation of previous version:</h3>
<p>OAuth works behind the scenes when you use a website or app to sign in with another account, like signing into Facebook with your Google account. Here's a simplified breakdown:</p>
<p><strong>The Players:</strong></p>
<ul>
<li><p><strong>You (the Resource Owner):</strong> The person using the website or app.</p>
</li>
<li><p><strong>Your Main Account (the Client):</strong> The account you want to use to sign in (like Google).</p>
</li>
<li><p><strong>The Website/App (the Resource Server):</strong> The website or app you're trying to access.</p>
</li>
<li><p><strong>Login Service (the Authorization Server):</strong> The service handling the login process (like Google in this case).</p>
</li>
</ul>
<p><strong>The Flow:</strong></p>
<ol>
<li><p><strong>Website asks to Sign In:</strong> The website or app asks you to sign in with your main account.</p>
</li>
<li><p><strong>Choose Account &amp; Permissions:</strong> You choose your main account (like Google) and see what information the website wants to access (like your name and email).</p>
</li>
<li><p><strong>Login to Main Account:</strong> You log in to your main account if needed.</p>
</li>
<li><p><strong>Permission Decision:</strong> You decide to allow or deny the website access to your information.</p>
</li>
<li><p><strong>Code Sent Back (if allowed):</strong> If you allow, your main account sends a special code back to the website.</p>
</li>
<li><p><strong>Website Exchanges Code for Token:</strong> The website sends the code back to the login service to get a special token.</p>
</li>
<li><p><strong>Website Access Granted:</strong> The login service verifies the code and sends a token back to the website. This token acts like a key that lets the website access your information securely.</p>
</li>
<li><p><strong>Website Gets Your Information:</strong> The website uses the token to get your information from your main account, without needing your password.</p>
</li>
</ol>
<p><strong>Benefits:</strong></p>
<ul>
<li><p><strong>Security:</strong> You don't share your main account password with the website.</p>
</li>
<li><p><strong>Convenience:</strong> You can sign in easily without creating a new account for every website.</p>
</li>
<li><p><strong>Control:</strong> You control what information the website can access.</p>
</li>
</ul>
<p>This is a basic explanation, and OAuth can be more complex depending on the situation. But hopefully, this gives you a general idea of how it works!</p>
<h3 id="heading-oauth-with-pkce">OAuth with PKCE</h3>
<p>OAuth 2.0 with PKCE (Proof Key for Code Exchange) is an extra secure version of the regular OAuth flow we talked about earlier. It's kind of like adding a special lock to the code exchange process. Here's how it works:</p>
<ol>
<li><p><strong>App Makes Secret Key:</strong> Imagine the app you're using makes up a super secret message (the code verifier) like a fun codeword only they know.</p>
</li>
<li><p><strong>Secret Key Gets Disguised:</strong> The app scrambles this codeword into something unrecognizable (the code challenge). This disguised code is like the secret key hidden inside a puzzle box.</p>
</li>
<li><p><strong>Login Request with Puzzle:</strong> The app sends the disguised code (code challenge) to the login service along with your request to sign in.</p>
</li>
<li><p><strong>Login and Permission:</strong> You log in to your main account and decide if the app can access your information.</p>
</li>
<li><p><strong>Authorization Code Sent:</strong> If you say yes, the login service sends a special code (authorization code) back to the app.</p>
</li>
<li><p><strong>App Reveals Secret Key:</strong> The app now reveals the original secret message (code verifier) it created earlier.</p>
</li>
<li><p><strong>Checking the Puzzle:</strong> The login service checks if the unscrambled code (code verifier) matches the disguised code (code challenge) it received before.</p>
</li>
<li><p><strong>Token Granted (if match):</strong> If the codes match, it proves the app is who it says it is. Then, the login service gives the app a special token to access your information.</p>
</li>
</ol>
<p><strong>Why PKCE?</strong></p>
<p>Regular OAuth flow might send the authorization code openly, which could be risky if someone peeked at it. PKCE adds a layer of security because only the real app that created the secret message can solve the puzzle and get the token.</p>
<p><strong>Think of it like this:</strong></p>
<ul>
<li><p>The secret message (code verifier) is like a special key you keep hidden.</p>
</li>
<li><p>The disguised code (code challenge) is like a secret message someone else can see, but they can't figure out the real key from it.</p>
</li>
<li><p>Matching the codes proves the app has the real key and is who it claims to be.</p>
</li>
</ul>
<p>Understanding OAuth and its enhanced version with PKCE is crucial for implementing secure authentication in modern web applications. OAuth provides a robust framework for delegating access without sharing passwords, ensuring both security and convenience for users. By incorporating PKCE, the security of the OAuth flow is further strengthened, making it resilient against interception attacks. In the next article, we will delve into the practical aspects of configuring the SvelteKitAuth authentication library with SvelteKit, enabling you to implement these concepts effectively in your applications. Stay tuned for a step-by-step guide to secure your SvelteKit apps with SvelteKitAuth.</p>
<h3 id="heading-references">References</h3>
<p><a target="_blank" href="https://authjs.dev/concepts/oauth">https://authjs.dev/concepts/oauth</a></p>
]]></content:encoded></item><item><title><![CDATA[Introduction to SvelteKitAuth]]></title><description><![CDATA[Authentication is a critical aspect of web applications, ensuring that users can securely access their accounts and data. In SvelteKit, while there is no built-in authentication system, we can implement robust authentication using various methods suc...]]></description><link>https://blog.aakashgoplani.in/introduction-to-sveltekitauth</link><guid isPermaLink="true">https://blog.aakashgoplani.in/introduction-to-sveltekitauth</guid><category><![CDATA[SvelteKitAuth]]></category><category><![CDATA[authentication]]></category><category><![CDATA[Sveltekit]]></category><category><![CDATA[auth.js]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Sun, 07 Jul 2024 15:30:50 GMT</pubDate><content:encoded><![CDATA[<p>Authentication is a critical aspect of web applications, ensuring that users can securely access their accounts and data. In SvelteKit, while there is no built-in authentication system, we can implement robust authentication using various methods such as database credential matching or external OAuth providers. This guide will focus on using SvelteKitAuth and OAuth providers to implement secure authentication in SvelteKit applications. By the end of this guide, you'll have a solid understanding of how to set up and manage authentication in SvelteKit.</p>
<h3 id="heading-what-is-sveltekitauth"><strong>What is SvelteKitAuth?</strong></h3>
<ul>
<li><p>SvelteKitAuth is part of the <a target="_blank" href="https://authjs.dev/">Auth.js</a> project - Authentication for Web which is an open-source library that is easy to use.</p>
</li>
<li><p>SvelteKitAuth is an extension of <em>NextAuth</em> - a popular authentication framework from the <em>Next</em> / <em>React</em> world. NextAuth is now getting a major overhaul and is now becoming <em>Auth.js</em></p>
</li>
<li><p>It can be used with any OAuth2 or OpenID Connect provider and has built-in support for 68+ popular services like Google, Facebook, Auth0, Apple etc.</p>
</li>
<li><p>Apart from OAuth authentication, it has built-in support for authentication using email/passwordless/magic link/username/password.</p>
</li>
<li><p>We can configure SvelteKitAuth to use database sessions (MySQL, Postgres, MSSQL, MongoDB etc.) or JWT.</p>
</li>
<li><p>It comes with a built-in security mechanism having features like Signed, prefixed, server-only cookies, has built-in CSRF protection &amp; doesn't rely on client-side JavaScript.</p>
</li>
</ul>
<h3 id="heading-how-does-sveltekitauth-work-under-the-hood"><strong>How does SvelteKitAuth work under the hood?</strong></h3>
<p>To understand how SvelteKitAuth works under the hood, let's explore its internal architecture and the steps involved in the authentication flow.</p>
<ol>
<li><p><strong>Configuration</strong>: When setting up SvelteKitAuth, you define a configuration file that specifies authentication providers, such as Google, GitHub, or a custom provider. The configuration also includes settings like secret keys, session storage options, and callback URLs.</p>
</li>
<li><p><strong>Authentication Providers</strong>: SvelteKitAuth supports multiple authentication providers, and you can choose the ones you want to enable. Each provider has its configuration options, such as client ID, client secret, scopes, and authorization URLs.</p>
</li>
<li><p><strong>Client-Side Flow</strong>: When a user initiates the authentication process, SvelteKitAuth handles the flow by redirecting them to the respective authentication providers login page. This typically involves generating a state and storing it in a server-side session to prevent CSRF attacks.</p>
</li>
<li><p><strong>Callback URL</strong>: After successful authentication with the provider, the user is redirected back to a callback URL specified in the SvelteKitAuth configuration. This URL is typically an API route that SvelteKitAuth exposes.</p>
</li>
<li><p><strong>Server-Side Flow</strong>: SvelteKitAuth receives the callback request on the specified API route. It validates the received data, including the state parameter, to ensure it matches the stored session state. This step prevents CSRF attacks.</p>
</li>
<li><p><strong>Token Exchange</strong>: SvelteKitAuth then exchanges the authorization code received from the authentication provider with an access token. This token allows SvelteKitAuth to make authenticated API requests on behalf of the user.</p>
</li>
<li><p><strong>User Information</strong>: With the access token, SvelteKitAuth retrieves the user's profile information from the authentication provider's API. It can fetch details like name, email, profile picture, or any other data that the provider makes available.</p>
</li>
<li><p><strong>Session Management</strong>: SvelteKitAuth creates a session for the authenticated user, typically using a secure HTTP-only cookie or a JWT (JSON Web Token). This session contains the user's data and is used for subsequent authenticated requests.</p>
</li>
<li><p><strong>Persistent Sessions</strong>: SvelteKitAuth can optionally store session information in a database or other storage mechanisms. This allows users to remain logged in even if the server restarts or the user refreshes the page.</p>
</li>
<li><p><strong>Hooks and Events</strong>: SvelteKitAuth provides various hooks and events that you can use to customize the authentication flow, add additional functionality, or integrate with external systems. Examples include the <code>$page.data.session</code> for accessing the session data in components and event hooks like <code>signIn</code> or <code>signOut</code> for performing actions on authentication events.</p>
</li>
</ol>
<h3 id="heading-why-choose-sveltekitauth"><strong>Why choose SvelteKitAuth?</strong></h3>
<p>SvelteKitAuth supports <strong>OAuth 1.0</strong>, <strong>1.0A</strong>, <strong>2.0</strong> and <strong>OpenID Connect</strong> and has built-in support for the most popular sign-in services like Google, GitHub, Auth0, Salesforce etc. It also supports multiple popular database adapters like MySQL, Postgres, MSSQL, MongoDB etc. Apart from that it provides a mechanism to create a custom OAuth provider and custom database adapter as well.</p>
<p>Overall, SvelteKitAuth abstracts away the complexities of authentication and provides a unified API for integrating with multiple authentication providers. It handles the authentication flow, token exchange, and session management, allowing developers to focus on building their applications without getting caught up in the intricacies of authentication protocols.</p>
]]></content:encoded></item><item><title><![CDATA[Comparing i18n libraries in SvelteKit: svelte-i18n, sveltekit-i18n and typesafe-i18n]]></title><description><![CDATA[Based on my learnings from localizing applications in SvelteKit with svelte-i18n, sveltekit-i18n and typesafe-i18n, I've come up with advantages and limitations of each of these libraries.
svelte-i18n
Features

It is a simple and minimalistic library...]]></description><link>https://blog.aakashgoplani.in/comparing-i18n-libraries-in-sveltekit-svelte-i18n-sveltekit-i18n-and-typesafe-i18n</link><guid isPermaLink="true">https://blog.aakashgoplani.in/comparing-i18n-libraries-in-sveltekit-svelte-i18n-sveltekit-i18n-and-typesafe-i18n</guid><category><![CDATA[Sveltekit]]></category><category><![CDATA[i18n]]></category><category><![CDATA[svelte-i18n]]></category><category><![CDATA[sveltekit-i18n]]></category><category><![CDATA[typesafe-i18n]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Wed, 02 Aug 2023 12:15:00 GMT</pubDate><content:encoded><![CDATA[<p>Based on my learnings from localizing applications in SvelteKit with <a target="_blank" href="https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-svelte-i18n"><em>svelte-i18n</em></a>, <a target="_blank" href="https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-sveltekit-i18n"><em>sveltekit-i18n</em></a> and <a target="_blank" href="https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-typesafe-i18n"><em>typesafe-i18n</em></a>, I've come up with advantages and limitations of each of these libraries.</p>
<h3 id="heading-svelte-i18n">svelte-i18n</h3>
<h4 id="heading-features">Features</h4>
<ul>
<li><p>It is a simple and minimalistic library that uses <a target="_blank" href="https://formatjs.io/"><strong>formatjs</strong></a> for localizing messages and supports the ICU message syntax.</p>
</li>
<li><p>It offers to load translations in a synchronous as well as asynchronous manner as per requirement.</p>
</li>
<li><p>Provides a decent number of options for formatting and pluralization.</p>
</li>
</ul>
<h4 id="heading-ease-of-use">Ease of Use</h4>
<ul>
<li><p>Since this library uses native Svelte stores for implementing localization, it feels like part of the ecosystem and hence very easy to get started with!</p>
</li>
<li><p>If you have a simple requirement of changing translations based on a given locale with limited formatting, this should be your go-to library.</p>
</li>
</ul>
<h4 id="heading-size">Size</h4>
<ul>
<li>As of v3.7.0, the size of the <em>svelte-i18n</em> package is 48.3 kB in the minified form and 14.2 kB in minified + gzipped form. (<a target="_blank" href="https://bundlephobia.com/package/svelte-i18n@3.7.0">source</a>)</li>
</ul>
<h4 id="heading-community-support">Community Support</h4>
<ul>
<li><p>It has <a target="_blank" href="https://www.npmjs.com/package/svelte-i18n">29,978 downloads/week</a> as of the time of writing this article.</p>
</li>
<li><p>The only downside is, this library is not actively managed. Several issues are on the rise and resolution is almost zero.</p>
</li>
</ul>
<h4 id="heading-limitations">Limitations</h4>
<ul>
<li><p>There are very less options available for formatting and pluralization.</p>
</li>
<li><p>As mentioned before, the library is not actively managed.</p>
</li>
</ul>
<hr />
<h3 id="heading-sveltekit-i18n">sveltekit-i18n</h3>
<h4 id="heading-features-1">Features</h4>
<ul>
<li><p>It is <strong>built for SvelteKit</strong> and has good SSR support.</p>
</li>
<li><p>We can load translations from API, database and local file system.</p>
</li>
<li><p>The translations are loaded for visited pages only. (and only once!)</p>
</li>
<li><p>Component-scoped translations: you can create multiple instances with custom definitions.</p>
</li>
<li><p>It provides good TS support and has no external dependencies.</p>
</li>
<li><p>Supports ICU syntax + provides a native parsing engine as well.</p>
</li>
</ul>
<h4 id="heading-ease-of-use-1">Ease of Use</h4>
<ul>
<li>It is very easy to use. I found it even easier while working with the ICU parser (instead of the default parser).</li>
</ul>
<h4 id="heading-size-1">Size</h4>
<ul>
<li>As of v2.4.2, the size of the <em>sveltekit-i18n</em> package is 14 kB in the minified form and 4.6 kB in minified + gzipped form. (<a target="_blank" href="https://bundlephobia.com/package/sveltekit-i18n@2.4.2">source</a>)</li>
</ul>
<h4 id="heading-community-support-1">Community Support</h4>
<ul>
<li><p>It has <a target="_blank" href="https://www.npmjs.com/package/sveltekit-i18n">3,052 downloads/week</a> as of the time of writing this article.</p>
</li>
<li><p>Even though the number of downloads is less than the <em>svelte-i18n</em> but good news is that this library is actively maintained.</p>
</li>
</ul>
<h4 id="heading-limitations-1">Limitations</h4>
<ul>
<li>While working with pluralization, understanding the syntax of the default parser could be a bit challenging at the start.</li>
</ul>
<hr />
<h3 id="heading-typesafe-i18n">typesafe-i18n</h3>
<h4 id="heading-features-2">Features</h4>
<ul>
<li><p>It is a lightweight, easy-to-use syntax and has no external dependencies.</p>
</li>
<li><p>Because of its strong typings, it prevents you from making mistakes (also in plain JavaScript projects).</p>
</li>
<li><p>Unlike the other two libraries, typesafe-i18n can be used with any framework and supports both TypeScript and JavaScript.</p>
</li>
<li><p>Creates boilerplate code for you which ensures the well-structured flow of i18n in your codebase.</p>
</li>
<li><p>Supports plural rules and allows formatting of values e.g. locale-dependent date or number formats.</p>
</li>
<li><p>It also supports switch-case statements e.g. for gender-specific output.</p>
</li>
<li><p>Provides an option for asynchronous loading of locales.</p>
</li>
<li><p>It supports SSR (Server-Side Rendering) and can be used for frontend, backend and API projects.</p>
</li>
<li><p>Import and Export translations from/to files or services.</p>
</li>
</ul>
<h4 id="heading-ease-of-use-2">Ease of Use</h4>
<ul>
<li><p>It has some initial learning curve but as soon as we become familiar with the <em>typesafe-i18n</em> ecosystem, this library is not only easy to use but super convenient as well!</p>
</li>
<li><p><em>Personally, this is my go-to library for i18n</em>.</p>
</li>
</ul>
<h4 id="heading-size-2">Size</h4>
<ul>
<li>As of v5.26.0, the size of the <em>typesafe-i18n</em> package is 2.8 kB in the minified form and 1.3 kB in minified + gzipped form. (<a target="_blank" href="https://bundlephobia.com/package/typesafe-i18n@5.26.0">source</a>)</li>
</ul>
<h4 id="heading-community-support-2">Community Support</h4>
<ul>
<li><p>It has <a target="_blank" href="https://www.npmjs.com/package/typesafe-i18n">13,588 downloads/week</a> as of the time of writing this article.</p>
</li>
<li><p>This library is actively maintained.</p>
</li>
</ul>
<h4 id="heading-limitations-2">Limitations</h4>
<ul>
<li>It has a huge eco-system (generators, detectors, importers, exporters etc.) and hence learning curve is a bit steep.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Internationalization in SvelteKit with typesafe-i18n]]></title><description><![CDATA[This is the final article of three-part series to demonstrate i18n in SvelteKit. In the previous article, we worked our way with sveltekit-i18n and in this article, we will work on integrating typesafe-i18n with SvelteKit.
The typesafe-i18n is a full...]]></description><link>https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-typesafe-i18n</link><guid isPermaLink="true">https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-typesafe-i18n</guid><category><![CDATA[Sveltekit]]></category><category><![CDATA[i18n]]></category><category><![CDATA[internationalization]]></category><category><![CDATA[localization]]></category><category><![CDATA[typesafe-i18n]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Tue, 01 Aug 2023 17:20:25 GMT</pubDate><content:encoded><![CDATA[<p>This is the final article of <a target="_blank" href="https://blog.aakashgoplani.in/series/i18n-in-sveltekit">three-part series</a> to demonstrate i18n in SvelteKit. In the <a target="_blank" href="https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-sveltekit-i18n">previous article</a>, we worked our way with <em>sveltekit-i18n</em> and in this article, we will work on integrating <em>typesafe-i18n</em> with SvelteKit.</p>
<p>The <em>typesafe-i18n</em> is a fully type-safe and lightweight internationalization library for all your TypeScript and JavaScript projects. One thing that makes <em>typesafe-i18n</em> stand out is that it is a generic library and not limited to Svelte, you can use it with any of your JavaScript or TypeScript projects.</p>
<p>This library introduces a full-fledged flow for maintaining localization in your application. The moment you install this library, it generates a specific folder structure encompassing all the i18n-specific files.</p>
<h2 id="heading-understanding-typesafe-i18n-ecosystem-and-nomenclature">Understanding <em>typesafe-i18n</em> ecosystem and nomenclature</h2>
<p>There are six main packages that I want to explain before we start with actual implementation.</p>
<ol>
<li><p><strong>Generators</strong>:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator">Generators</a>, as the name says generate boilerplate code specific to i18n and look for changes that you make to locale files.</p>
</li>
<li><p>Once we have installed <em>typesafe-i18n</em> and run the command <code>typesafe-i18n</code>, the generator kicks in and will generate the following project structure within the <em>src/i18n</em> directory. By default locales for <code>de</code> and <code>en</code> will be auto-generated.</p>
<pre><code class="lang-abap">  src/
    i18n/
      en/
        <span class="hljs-keyword">index</span>.ts
      de/
        <span class="hljs-keyword">index</span>.ts
      custom-<span class="hljs-keyword">types</span>.ts
      formatters.ts
      i18n-<span class="hljs-keyword">types</span>.ts
      i18n-util.async.ts
      i18n-util.sync.ts
      i18n-util.ts
</code></pre>
</li>
<li><p>Let's go through these generated files as they will be very useful when we localize our application:</p>
<ul>
<li><p><code>en/index.ts</code>: If 'en' is your base locale, the file <em>src/i18n/en/index.ts</em> will contain your translations. Whenever you make changes to this file, the generator will create updated type definitions.</p>
</li>
<li><p><code>custom-types.ts</code><br />  To define types that are unknown to <em>typesafe-i18n</em>.</p>
</li>
<li><p><code>formatters.ts</code><br />  In this file, you can configure the formatters to use inside your translations. The <code>Formatters</code> type gets generated automatically by reading all your translations from the base locale file.</p>
</li>
<li><p><code>i18n-types.ts</code><br />  Type definitions are generated in this file. You don't have to understand them. They are just here to help TypeScript understand, how you need to call the translation functions.</p>
</li>
<li><p><code>i18n-util.async.ts</code><br />  This file contains the logic to load individual locales asynchronously. <strong>This is the preferred approach</strong> and we will be using this approach while localizing our application.</p>
</li>
<li><p><code>i18n-util.sync.ts</code><br />  This file contains the logic to load your locales synchronously.</p>
</li>
<li><p><code>i18n-util.ts</code><br />  This file contains wrappers with type information around the base i18n functions.</p>
</li>
</ul>
</li>
<li><p>These files will be auto-updated (except <em>formatters.ts</em> and <em>custom-types.ts</em>) every time you make changes to your locale, if you manually make any changes to these files, they will be over-written!</p>
</li>
<li><p>We can configure generators as per our requirements. Here is the <a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/generator">list of available options</a> that can be utilized to customize generators. In this article, I'll be keeping things simple and won't be customizing them.</p>
</li>
</ul>
</li>
<li><p><strong>Detectors:</strong></p>
<ul>
<li><p>Locale detection is a key part of any i18n solution. Therefore <em>typesafe-i18n</em> provides a solution to detect a user's locale.</p>
</li>
<li><p><a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/detectors">Dectotors</a> exports multiple utility methods that enable us to detect locale on the client and server side. We will see a few of them in sections <strong>Detect the locale on the server side</strong> and <strong>Detect the locale on the client side.</strong></p>
</li>
</ul>
</li>
<li><p><strong>Runtime:</strong></p>
<ul>
<li><p>The <a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime">runtime package</a> provides the means of writing and using translations in <em>typesafe-i18n</em>. It exposes wrappers that enable us to pluralize, format and translate our messages.</p>
</li>
<li><p>From amongst many utilities exported by this package, three important ones that I want to highlight are:</p>
<ul>
<li><p><strong><em>i18nString</em></strong>: It is represented as <strong>LLL</strong>. It is used for simple string interpolation.</p>
</li>
<li><p><strong><em>i18nObject</em></strong>: It is represented as <strong>LL</strong>. It is useful in scenarios when we want to provide translations for a particular locale asynchronously i.e. load one locale at a time. <em>We will be using this method in our application</em>.</p>
</li>
<li><p><strong><em>i18n</em></strong>: It is represented as <strong>L</strong>. It is useful in scenarios when we want to load all the locales synchronously.</p>
</li>
</ul>
</li>
<li><p>As mentioned before, I'll be using the <strong>LL</strong> approach in this article. Here is the <a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime">link</a> to official documentation that explains how to use the other two approaches.</p>
</li>
</ul>
</li>
<li><p><strong>Formatters</strong></p>
<ul>
<li>This <a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/formatters">package</a> exports multiple utility methods useful for formatting. We will see a few of them in the section on <strong>Formatting.</strong></li>
</ul>
</li>
<li><p><strong>Importers:</strong></p>
<ul>
<li>This <a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/importer">package</a> is used for importing language files that come from an API, spreadsheet or JSON files. I won't be covering details about this package in this article.</li>
</ul>
</li>
<li><p><strong>Exporters:</strong></p>
<ul>
<li>This <a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/exporter">package</a> is used for exporting language files to a service or an API. I won't be covering details about this package in this article.</li>
</ul>
</li>
</ol>
<p>That's it with the fundamentals, let's start with localizing our application.</p>
<h2 id="heading-application-structure">Application Structure</h2>
<p>Before we begin, I want to give you a detailed walkthrough of the application that we will be working on. You can find the code in the <a target="_blank" href="https://github.com/aakash14goplani/sveltekit-with-typesafe18n">GitHub repository</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690910323040/d14c6da9-0986-485f-b88f-7bd56b4a3c0a.png" alt class="image--center mx-auto" /></p>
<p>As we can see from the image, this will be a simple single-page application demonstrating i18n features like <em>locale switching</em>, <em>pluralization</em> and <em>formatting</em>.</p>
<p>In the next section, we will work on integrating the <em>typesafe-i18n</em> library with SvelteKit.</p>
<h2 id="heading-integration-with-sveltekit">Integration with SvelteKit</h2>
<ol>
<li><p><strong>Install and Configure the <em>typesafe-i18n</em> library</strong></p>
<ul>
<li><p>This first step is to install this library as a dependency in our SvelteKit project. We will also need to install <code>npm-run-all</code> package as the dev-dependency. <strong>NOTE</strong>: Use <em>npm</em>, I tried with <em>pnpm</em> but was not able to execute this command.</p>
<pre><code class="lang-abap">  npm i --save-dev npm-<span class="hljs-keyword">run</span>-<span class="hljs-keyword">all</span>
  npx typesafe-i18n --setup-auto
</code></pre>
</li>
<li><p>This command will run the setup process and <strong>automatically detect</strong> the config needed. If you want to manually setup the configuration, the command for that is: <code>npx typesafe-i18n --setup</code></p>
</li>
<li><p>This will generate a config file <em>.typesafe-i18n.json.</em> In this file, we will add a new property <code>outputPath</code> to generate folder structure within the <em>src/lib/i18n</em> (default is <em>src/i18n</em>) so that we can access those files with SvelteKit <code>$lib</code> feature.</p>
<pre><code class="lang-json">  {
    ...,
    <span class="hljs-attr">"outputPath"</span>: <span class="hljs-string">"./src/lib/i18n/"</span>
  }
</code></pre>
</li>
<li><p>The next step is to update the <em>package.json</em> file to include the script for running the <em>typesafe-i18n</em> CLI along with the Vite development server.</p>
<pre><code class="lang-json">  <span class="hljs-string">"scripts"</span> : {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"npm-run-all --parallel vite typesafe-i18n"</span>,
    <span class="hljs-attr">"vite"</span>: <span class="hljs-string">"vite dev"</span>,
    <span class="hljs-attr">"typesafe-i18n"</span>: <span class="hljs-string">"typesafe-i18n"</span>,
    ...
  }
</code></pre>
</li>
<li><p>If we run the script <code>npm run dev</code>, it will generate a folder structure for your locales inside the <code>src/lib/i18n</code> folder. By default locales for <code>de</code> and <code>en</code> will be auto-generated, we will customize this in the next section.</p>
</li>
</ul>
</li>
<li><p><strong>Define Locale Dictionary</strong></p>
<ul>
<li><p>Now that we have installed this library, it's time to identify the areas in our demo application that need localization. We then define the <strong>locale dictionaries</strong> within the <em>src/lib/i18n</em> folder generated by <em>typesafe-i18n</em>. A locale dictionary is a regular JSON object which contains message definitions for a certain language.</p>
</li>
<li><p>The best thing about <em>typesafe-i18n</em> is all the dictionaries will be typed. We can use <code>BaseTranslation</code> type for the same.</p>
</li>
<li><p>In our example application, I want five fields to be localized: The main heading, Lable for locale switching, Body text (the paragraph) and Text for pluralization.</p>
</li>
<li><p>For the sake of this application, I will define dictionaries in three languages - English (<em>en.json</em>), Hindi (<em>hi.json</em>) and French (<em>fr.json</em>) within the <em>src/lib/i18n</em> folder.</p>
</li>
<li><p>By default <code>en/index.ts</code> and <code>de/index.ts</code> will be pre-configured. We will rename <code>de</code> to <code>hi</code> and create a new entry for the <code>fr/index.ts</code> language.</p>
<pre><code class="lang-typescript">  <span class="hljs-comment">// file -&gt; src/lib/i18n/en/index.ts</span>
  <span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { BaseTranslation } <span class="hljs-keyword">from</span> <span class="hljs-string">'../i18n-types'</span>;

  <span class="hljs-keyword">const</span> en = {
    heading: <span class="hljs-string">'Internationalization in SvelteKit'</span>,
    toggle_label: <span class="hljs-string">'Select Locale'</span>,
    body_text:
          <span class="hljs-string">'This is a small example to demonstrate i18n functionality in SvelteKit using typesafe-i18n library. typesafe-i18n is a fully type-safe and lightweight internationalization library for all your TypeScript and JavaScript projects. typesafe-i18n comes with an API that allows other services to read and update translations. Total number of npm downloads per week as of {date:Date|simpleDate} are {download:number|simpleNumber}.'</span>,
    awards: <span class="hljs-string">'You have {{ not won any awards | won exactly ?? award | won ?? awards }}!'</span>,
    time: <span class="hljs-string">'{value:Date|simpleTime}'</span>,
    date: <span class="hljs-string">'{value:Date|simpleDate}'</span>,
    currency: <span class="hljs-string">'{value:number|simpleCurrency}'</span>
  } satisfies BaseTranslation;

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> en;

  <span class="hljs-comment">// file -&gt; src/lib/i18n/hi/index.ts</span>
  <span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { BaseTranslation } <span class="hljs-keyword">from</span> <span class="hljs-string">'../i18n-types'</span>;

  <span class="hljs-keyword">const</span> hi = {
    heading: <span class="hljs-string">'SvelteKit में अंतर्राष्ट्रीयकरण'</span>,
    toggle_label: <span class="hljs-string">'भाषा चुने'</span>,
    body_text:
          <span class="hljs-string">'यह typesafe-i18n लाइब्रेरी का उपयोग करके SvelteKit में i18n कार्यक्षमता प्रदर्शित करने के लिए एक छोटा सा उदाहरण है। typesafe-i18n आपके सभी टाइपस्क्रिप्ट और जावास्क्रिप्ट प्रोजेक्ट्स के लिए पूरी तरह से टाइप-सुरक्षित और हल्के अंतर्राष्ट्रीयकरण लाइब्रेरी है। typesafe-i18n एक एपीआई के साथ आता है जो अन्य सेवाओं को अनुवाद पढ़ने और अपडेट करने की अनुमति देता है।। {date:Date|simpleDate} तक प्रति सप्ताह npm डाउनलोड की कुल संख्या {download:number|simpleNumber} है|'</span>,
    awards: <span class="hljs-string">'आपने {{ कोई पुरस्कार नहीं जीता | बिल्कुल ?? पुरस्कार जीता | ?? पुरस्कार जीते }} है|'</span>,
    time: <span class="hljs-string">'{value:Date|simpleTime}'</span>,
    date: <span class="hljs-string">'{value:Date|simpleDate}'</span>,
    currency: <span class="hljs-string">'{value:number|simpleCurrency}'</span>
  } satisfies BaseTranslation;

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> hi;

  <span class="hljs-comment">// etc... all other languages that you wish to support</span>
</code></pre>
</li>
<li><p>Pay attention to the syntax <code>{value: &lt;type&gt;|&lt;formatter}</code> Let's break this syntax down into three parts:</p>
<ul>
<li><p><code>{value}</code> is a placeholder that will be populated with a value of a particular locale during runtime. As the locale changes, this field will be recomputed.</p>
</li>
<li><p><code>{value: &lt;type&gt;}</code> is used to specify the data type of value e.g. <em>number</em>, <em>Date</em> etc.</p>
</li>
<li><p><code>{value: &lt;type&gt;|&lt;formatter&gt;}</code> is used to specify a value with a particular data type along with a formatter to format value based on a particular locale.</p>
</li>
</ul>
</li>
<li><p>As soon you update these locale dictionary (translation) files, typesafe-i18n will recompute all the files within the <code>src/lib/i18n</code> directory.</p>
</li>
</ul>
</li>
<li><p><strong>Detect the locale on the server side</strong></p>
<ul>
<li><p>Now that our library is installed and the locale dictionary is ready, it is time to create an entry point that will load the assets based on the user locale and initialize the library with a specific locale.</p>
</li>
<li><p>We can make use of <a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/detectors">detectors</a> to detect the locale. Now this package exports multiple utilities to detect locale and I cannot cover every of those in this article, I'll advise readers to go through every method and pick the best one as per requirement.</p>
</li>
<li><p>In the below code snippet within the <em>hooks.server.ts</em> file, I am querying request headers using <code>initAcceptLanguageHeaderDetector()</code> to determine if the locale is present. If yes, then I call <code>detectLocale(...)</code> that returns the final locale to be used. If no locale is returned from headers, I query cookies for the same using <code>initRequestCookiesDetector()</code> If cookies don't return anything, I use the default locale i.e. <code>en</code></p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Handle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
  <span class="hljs-keyword">import</span> { loadLocaleAsync } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/i18n/i18n-util.async'</span>;
  <span class="hljs-keyword">import</span> { setLocale } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/i18n/i18n-svelte'</span>;
  <span class="hljs-keyword">import</span> { detectLocale } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/i18n/i18n-util'</span>;
  <span class="hljs-keyword">import</span> {
    initAcceptLanguageHeaderDetector,
    initRequestCookiesDetector
  } <span class="hljs-keyword">from</span> <span class="hljs-string">'typesafe-i18n/detectors'</span>;

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle: Handle = <span class="hljs-keyword">async</span> ({ event, resolve }) =&gt; {
    <span class="hljs-keyword">let</span> deafultLocale = <span class="hljs-string">'en'</span>;

    <span class="hljs-keyword">const</span> acceptLanguageHeaderDetector = initAcceptLanguageHeaderDetector(event.request);
    <span class="hljs-keyword">const</span> localeFromHeaders = detectLocale(acceptLanguageHeaderDetector);

    <span class="hljs-keyword">if</span> (!localeFromHeaders) {
      <span class="hljs-comment">// if locale is not present in headers, try fetching it from cookies</span>
      <span class="hljs-keyword">const</span> requestCookiesDetector = initRequestCookiesDetector({
        cookies: event.cookies.get(<span class="hljs-string">'lang'</span>) || <span class="hljs-string">''</span>
      });
      <span class="hljs-keyword">const</span> localeFromCookies = detectLocale(requestCookiesDetector);
      <span class="hljs-keyword">if</span> (localeFromCookies) {
        deafultLocale = localeFromCookies;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// add in cookes</span>
        event.cookies.set(<span class="hljs-string">'lang'</span>, deafultLocale, { path: <span class="hljs-string">'/'</span> });
      }
    } <span class="hljs-keyword">else</span> {
      deafultLocale = localeFromHeaders;
    }
    <span class="hljs-keyword">const</span> locale = detectLocale(<span class="hljs-function">() =&gt;</span> [deafultLocale]);
    <span class="hljs-comment">// Load it</span>
    <span class="hljs-keyword">await</span> loadLocaleAsync(locale);
    <span class="hljs-comment">// Set it</span>
    setLocale(locale);
    <span class="hljs-comment">// [OPTIONAL] set locale within locals property</span>
    event.locals.locale = deafultLocale;
    <span class="hljs-keyword">return</span> resolve(event);
  };
</code></pre>
</li>
<li><p>[OPTIONAL] If you see any typescript error in the line <code>event.locals.locale = deafultLocale;</code> then update the file <em>src/app.d.ts</em> to include <code>locale</code> property.</p>
<pre><code class="lang-typescript">  <span class="hljs-comment">// See https://kit.svelte.dev/docs/types#app</span>
  <span class="hljs-comment">// for information about these interfaces</span>
  <span class="hljs-keyword">declare</span> <span class="hljs-built_in">global</span> {
    <span class="hljs-keyword">namespace</span> App {
      <span class="hljs-comment">// interface Error {}</span>
      <span class="hljs-keyword">interface</span> Locals {
        locale: <span class="hljs-built_in">string</span>;
      }
      <span class="hljs-comment">// interface PageData {}</span>
      <span class="hljs-comment">// interface Platform {}</span>
    }
  }

  <span class="hljs-keyword">export</span> {};
</code></pre>
</li>
<li><p>Once the locale is detected, we asynchronously load the corresponding translation messages for that particular locale using <code>loadLocaleAsync(locale)</code>. Finally, we set the store <code>setLocale(locale)</code> to save the current locale across the server.</p>
</li>
</ul>
</li>
<li><p><strong>Detect the locale on the client side</strong></p>
<ul>
<li><p>Once we have the locale set up on the server side, it's time to load the locale on the client side as well. In the file <em>+layout.ts</em>, once the client-side is initialized, we detect the locale from session storage using <code>sessionStorageDetector()</code>, if the locale is not found we pick the locale from the user's browser configuration using <code>navigatorDetector()</code></p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> { browser } <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/environment'</span>;
  <span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { LayoutLoad } <span class="hljs-keyword">from</span> <span class="hljs-string">'./$types'</span>;
  <span class="hljs-keyword">import</span> { setLocale } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/i18n/i18n-svelte'</span>;
  <span class="hljs-keyword">import</span> { detectLocale } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/i18n/i18n-util'</span>;
  <span class="hljs-keyword">import</span> { loadLocaleAsync } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/i18n/i18n-util.async'</span>;
  <span class="hljs-keyword">import</span> { sessionStorageDetector, navigatorDetector } <span class="hljs-keyword">from</span> <span class="hljs-string">'typesafe-i18n/detectors'</span>;

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load: LayoutLoad = <span class="hljs-keyword">async</span> (event) =&gt; {
    <span class="hljs-keyword">if</span> (browser) {
      <span class="hljs-keyword">const</span> deafultLocale = <span class="hljs-string">'en'</span>;
      <span class="hljs-keyword">const</span> locale =
        detectLocale(sessionStorageDetector) || detectLocale(navigatorDetector) || deafultLocale;

      <span class="hljs-keyword">await</span> loadLocaleAsync(locale);
      setLocale(locale);
    }
    <span class="hljs-keyword">return</span> event.data;
  };
</code></pre>
</li>
<li><p>Similar to the server side, once the locale is detected, we asynchronously load the corresponding translation messages for that particular locale using <code>loadLocaleAsync(locale)</code>. Finally, we set the store <code>setLocale(locale)</code> to save the current locale across the application.</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-localizing-application">Localizing Application</h2>
<p>Now that we have configured the library and have the initial locale set, we're ready to start localizing our app. To do that we simply import <code>$LL</code> and pass message id in any component that needs to be translated.</p>
<p>Let's take a look at our locale file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> en = {
  heading: <span class="hljs-string">'Internationalization in SvelteKit'</span>,
  toggle_label: <span class="hljs-string">'Select Locale'</span>,
  body_text: <span class="hljs-string">'... {date:Date|simpleDate} are {download:number|simpleNumber}.'</span>,
  ...
} satisfies BaseTranslation;
</code></pre>
<p>To set the main heading, the label for locale switching, we simply invoke <code>$LL.&lt;message-id&gt;()</code>:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="javascript">{$LL.heading()}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span></span><span class="javascript">{$LL.toggle_label()}</span><span class="xml">: <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
</code></pre>
<p>We can also pass additional parameters to <code>$LL</code> to populate values at runtime based on the current locale. For example, <code>body_text</code> message-id has two variables <code>date</code> of type <em>Date</em> and <code>download</code> of type <em>number</em>. We can pass the same variables as an object in <code>$LL</code></p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span></span><span class="javascript">{$LL.body_text({
  <span class="hljs-attr">download</span>: <span class="hljs-number">16711</span>,
  <span class="hljs-attr">date</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-number">2023</span>, <span class="hljs-number">6</span>, <span class="hljs-number">14</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>)
})}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
</code></pre>
<h2 id="heading-locale-switching">Locale Switching</h2>
<p>To switch between locales, we must first load the translations asynchronously for the new locale using <code>loadLocaleAsync()</code> and then update the store using <code>setLocale()</code></p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ browser }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/environment'</span>;
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ onDestroy, onMount }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte'</span>;
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ LL, setLocale }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/i18n/i18n-svelte'</span>;
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ loadLocaleAsync }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/i18n/i18n-util.async'</span>;

  <span class="hljs-keyword">let</span> value: Locales = <span class="hljs-string">'en'</span>;

  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleLocaleChange</span>(<span class="hljs-params">event: Event</span>) </span></span></span><span class="javascript">{
    event.preventDefault();
    value = event?.target?.value;
    <span class="hljs-keyword">await</span> loadLocaleAsync(value);
    setLocale(value);
    sessionStorage.setItem(<span class="hljs-string">'lang'</span>, value);
  }</span><span class="xml"><span class="javascript">

  onMount(<span class="hljs-function">() =&gt;</span> </span></span><span class="javascript">{
    <span class="hljs-keyword">const</span> valueFromSession = sessionStorage.getItem(<span class="hljs-string">'lang'</span>) || <span class="hljs-string">'en'</span>;
    value = valueFromSession <span class="hljs-keyword">as</span> Locales;
    sessionStorage.setItem(<span class="hljs-string">'lang'</span>, valueFromSession);
  }</span><span class="xml"><span class="javascript">)

  onDestroy(<span class="hljs-function">() =&gt;</span> </span></span><span class="javascript">{
    <span class="hljs-keyword">if</span> (browser) {
      sessionStorage.removeItem(<span class="hljs-string">'lang'</span>);
    }
  }</span><span class="xml">)
<span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container__toggle"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span></span><span class="javascript">{$LL.toggle_label()}</span><span class="xml">: <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">select</span> </span></span><span class="javascript">{value}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">on:change</span>=</span></span><span class="javascript">{handleLocaleChange}</span><span class="xml"><span class="hljs-tag">&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"en"</span>&gt;</span>English<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"hi"</span>&gt;</span>Hindi<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"fr"</span>&gt;</span>French<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>One more point, in the last section, we detected locale in the <em>+layout.ts</em> file using <code>sessionStorageDetector()</code> and hence we must update the session storage to reflect the latest locale value. Also, the key used in session storage must be "<strong>lang</strong>" only, this is a requirement from <em>typesafe-i18n</em>.</p>
<h2 id="heading-pluralization">Pluralization</h2>
<p>The typesafe-i18n provides multiple ways in which we can pluralize a string. I would encourage readers to go through <a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/runtime#plural">examples</a> in their documentation as it would be not possible for me to cover them all.</p>
<p>For this example application, the syntax for pluralization used will be:</p>
<pre><code class="lang-typescript">{{ zero | one | other }}
</code></pre>
<p>Consider the following message-id from our translation file:</p>
<pre><code class="lang-typescript">awards: <span class="hljs-string">'You have {{ not won any awards | won exactly ?? award | won ?? awards }}!'</span>,
</code></pre>
<p>This is how we are going to localize:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span></span><span class="javascript">{$LL.awards(randomNumber)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
</code></pre>
<p>Now if we compare the message-id with the plural syntax that we have used, this is how things will break up:</p>
<ul>
<li><p>if <code>randomNumber = 0</code>, the output will be "<em>not won any awards</em>"</p>
</li>
<li><p>if <code>randomNumber = 1</code>, the output will be "<em>won exactly 1 award</em>". Here <code>??</code> is the value of the argument that was passed to <code>$LL()</code></p>
</li>
<li><p>if <code>randomNumber &gt; 1</code>, the output will be "<em>won 5 awards</em>" (assuming <code>randomNumber = 5</code>)</p>
</li>
</ul>
<h2 id="heading-formatting">Formatting</h2>
<p>Coming to the last section of the article where I will discuss formatting Date, Time, Number and Currency using inbuild Formatters.</p>
<p>The typesafe-i18n has one entire <a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/formatters">package</a> dedicated to formatters. It enables us to use inbuild formatters as well as provides utilities to create custom formatters.</p>
<p>We will write all formatter utilities within the file <em>src/lib/i8n/formatters.ts</em>. We will use <code>time()</code>, <code>date()</code> and <code>number()</code> formatters from <code>typesafe-i18n/formatters</code>. These methods are wrappers around the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl">Intl</a> namespace.</p>
<p>The idea here is to create an object with the custom keys and export it and later while localizing, chain them with respective message-id.</p>
<p>As a first step, we export an object with our custom format options.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { FormattersInitializer } <span class="hljs-keyword">from</span> <span class="hljs-string">'typesafe-i18n'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Locales, Formatters } <span class="hljs-keyword">from</span> <span class="hljs-string">'./i18n-types'</span>;
<span class="hljs-keyword">import</span> { date, time, <span class="hljs-built_in">number</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'typesafe-i18n/formatters'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> initFormatters: FormattersInitializer&lt;Locales, Formatters&gt; = <span class="hljs-function">(<span class="hljs-params">locale: Locales</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> formatters: Formatters = {
    simpleDate: date(locale, { year: <span class="hljs-string">'numeric'</span>, month: <span class="hljs-string">'long'</span>, day: <span class="hljs-string">'numeric'</span> }),
    simpleTime: time(locale, { hour: <span class="hljs-string">'numeric'</span>, minute: <span class="hljs-string">'numeric'</span> }),
    simpleNumber: <span class="hljs-built_in">number</span>(locale),
    simpleCurrency: <span class="hljs-built_in">number</span>(locale, { style: <span class="hljs-string">'currency'</span>, currency: getCurrencyCode(locale) })
  };

  <span class="hljs-keyword">return</span> formatters;
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCurrencyCode</span>(<span class="hljs-params">value: Locales</span>): <span class="hljs-title">string</span> </span>{
  <span class="hljs-keyword">switch</span> (value) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'en'</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">'USD'</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">'hi'</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">'INR'</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-string">'fr'</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">'EUR'</span>;
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">return</span> <span class="hljs-string">'USD'</span>;
  }
}
</code></pre>
<p>Now we can use those keys to the chain with our message-id in our translation files.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> en = {
  ...
  time: <span class="hljs-string">'{value:Date|simpleTime}'</span>,
  date: <span class="hljs-string">'{value:Date|simpleDate}'</span>,
  currency: <span class="hljs-string">'{value:number|simpleCurrency}'</span>
} satisfies BaseTranslation;
</code></pre>
<p>The last step is to just invoke <code>$LL()</code> and pass relevant arguments.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Time: </span><span class="javascript">{ $LL.time({ <span class="hljs-attr">value</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>() }) }</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Date: </span><span class="javascript">{ $LL.date({ <span class="hljs-attr">value</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>() }) }</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Currency: </span><span class="javascript">{ $LL.currency({ <span class="hljs-attr">value</span>: <span class="hljs-number">16711</span> }) }</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Finally, we were able to localize our application using <em>typesafe-i18n</em>. You can find the code in the <a target="_blank" href="https://github.com/aakash14goplani/sveltekit-with-typesafe18n">GitHub repo</a> and <a target="_blank" href="https://sveltekit-with-typesafei18n.vercel.app/">link</a> to the live demo.</p>
<p>This was the final article of <a target="_blank" href="https://blog.aakashgoplani.in/series/i18n-in-sveltekit">three-part series</a> to demonstrate i18n in SvelteKit. In the <a target="_blank" href="https://blog.aakashgoplani.in/comparing-i18n-libraries-in-sveltekit-svelte-i18n-sveltekit-i18n-and-typesafe-i18n">next article</a>, I'll be comparing three libraries: svelte-i18n, sveltekit-i18n and typesafe-i18n.</p>
<h2 id="heading-references">References</h2>
<ul>
<li><p><a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main">Official Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/utils">Util Functions</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Internationalization in SvelteKit with sveltekit-i18n]]></title><description><![CDATA[This is the second article of three-part series to demonstrate i18n in SvelteKit. In the previous article, we worked our way with svelte-i18n and in this article, we will work on integrating sveltekit-i18n with SvelteKit.
The sveltekit-i18n is a tiny...]]></description><link>https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-sveltekit-i18n</link><guid isPermaLink="true">https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-sveltekit-i18n</guid><category><![CDATA[Sveltekit]]></category><category><![CDATA[i18n]]></category><category><![CDATA[internationalization]]></category><category><![CDATA[translation]]></category><category><![CDATA[sveltekit-i18n]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Tue, 01 Aug 2023 17:10:37 GMT</pubDate><content:encoded><![CDATA[<p>This is the second article of <a target="_blank" href="https://blog.aakashgoplani.in/series/i18n-in-sveltekit">three-part series</a> to demonstrate i18n in SvelteKit. In the <a target="_blank" href="https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-svelte-i18n">previous article</a>, we worked our way with <em>svelte-i18n</em> and in this article, we will work on integrating <em>sveltekit-i18n</em> with SvelteKit.</p>
<p>The <em>sveltekit-i18n</em> is a tiny library with no external dependencies, built for Svelte and SvelteKit. Key features include:</p>
<ul>
<li><p>SvelteKit ready.</p>
</li>
<li><p>SSR support.</p>
</li>
<li><p>Custom data sources: no matter if you are using local files or remote API to get your translations.</p>
</li>
<li><p>Module-based: your translations are loaded for visited pages only (and only once!)</p>
</li>
<li><p>Component-scoped translations: you can create multiple instances with custom definitions.</p>
</li>
<li><p>Custom modifiers: you can modify the input data the way you need.</p>
</li>
<li><p>TS support.</p>
</li>
<li><p>No external dependencies.</p>
</li>
</ul>
<h3 id="heading-application-structure">Application Structure</h3>
<p>Before we begin, I want to give you a detailed walkthrough of the application that we will be working on. You can find the code in the <a target="_blank" href="https://github.com/aakash14goplani/sveltekit-with-sveltekiti18n">GitHub repository</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690909653107/8f1d9d88-1a94-4660-a08f-f863c731a37b.png" alt class="image--center mx-auto" /></p>
<p>As we can see from the image, this will be a simple single-page application demonstrating i18n features like <em>locale switching</em>, <em>pluralization</em> and <em>formatting</em>.</p>
<p>In the next section, we will work on integrating the <em>sveltekit-i18n</em> library with SvelteKit.</p>
<h3 id="heading-integration-with-sveltekit">Integration with SvelteKit</h3>
<ol>
<li><p><strong>Install the <em>sveltekit-i18n</em> library</strong></p>
<ul>
<li><p>This first step is to install this library as a dependency in our SvelteKit project:</p>
<pre><code class="lang-typescript">  pnpm i sveltekit-i18n
</code></pre>
</li>
</ul>
</li>
<li><p><strong>Define Locale Dictionary</strong></p>
<ul>
<li><p>Now that we have installed this library, it's time to identify the areas in our application that need localization. We then define the <strong>locale dictionaries</strong> within the <em>src/lib/lang</em> folder. A locale dictionary is a regular JSON object which contains message definitions for a certain language.</p>
</li>
<li><p>In our example application, I want five fields to be localized: The main heading, Lable for locale switching, Button label text, Body text (the paragraph) and Text for pluralization.</p>
</li>
<li><p>For the sake of this application, I will define dictionaries in three languages - English (<em>en.json</em>), Hindi (<em>hi.json</em>) and French (<em>fr.json</em>) within the <em>src/lib/lang</em> folder.</p>
<pre><code class="lang-json">  <span class="hljs-comment">// en.json</span>
  {
    <span class="hljs-attr">"heading"</span>: <span class="hljs-string">"Internationalization in SvelteKit"</span>,
    <span class="hljs-attr">"toggle_label"</span>: <span class="hljs-string">"Select Locale"</span>,
    <span class="hljs-attr">"button_label_0"</span>: <span class="hljs-string">"{{value:number}}"</span>,
    <span class="hljs-attr">"button_label_1"</span>: <span class="hljs-string">"{{value:number}}"</span>,
    <span class="hljs-attr">"button_label_2"</span>: <span class="hljs-string">"{{value:number}}"</span>,
    <span class="hljs-attr">"body_text"</span>: <span class="hljs-string">"This is a small example to demonstrate i18n functionality in SvelteKit using sveltekit-i18n library. sveltekit-i18n is a tiny library with no external dependencies, built for Svelte and SvelteKit. It glues @sveltekit-i18n/base and @sveltekit-i18n/parser-default together to provide you the most straightforward sveltekit-i18n solution. Total number of npm downloads per week as of {{dateValue:date}} are {{download:number}}."</span>,
    <span class="hljs-attr">"awards"</span>:<span class="hljs-string">"You have {{award:gt; 0: {{award; 1:won exactly {{award}} award; default:won {{award}} awards}}; default:not won any awards}}!"</span>,
    <span class="hljs-attr">"date"</span>: <span class="hljs-string">"{{val:date}}"</span>,
    <span class="hljs-attr">"time"</span>: <span class="hljs-string">"Only 'time' formatter is not available, it must be DateTime formatter"</span>,
    <span class="hljs-attr">"number"</span>: <span class="hljs-string">"{{value:currency}}"</span>
  }

  <span class="hljs-comment">// hi.json</span>
  {
    <span class="hljs-attr">"heading"</span>: <span class="hljs-string">"SvelteKit में अंतर्राष्ट्रीयकरण"</span>,
    <span class="hljs-attr">"toggle_label"</span>: <span class="hljs-string">"भाषा चुने"</span>,
    <span class="hljs-attr">"button_label_0"</span>: <span class="hljs-string">"{{value:number}}"</span>,
    <span class="hljs-attr">"button_label_1"</span>: <span class="hljs-string">"{{value:number}}"</span>,
    <span class="hljs-attr">"button_label_2"</span>: <span class="hljs-string">"{{value:number}}"</span>,
    <span class="hljs-attr">"body_text"</span>: <span class="hljs-string">"यह sveltekit-i18n लाइब्रेरी का उपयोग करके SvelteKit में i18n कार्यक्षमता प्रदर्शित करने के लिए एक छोटा सा उदाहरण है। sveltekit-i18n एक छोटी लाइब्रेरी है जिसमें कोई बाहरी निर्भरता नहीं है, जो Svelte और SvelteKit के लिए बनाई गई है। यह आपको सबसे सीधा sveltekit-i18n समाधान प्रदान करने के लिए @sveltekit-i18n/base और @sveltekit-i18n/parser-default को एक साथ जोड़ता है। {{dateValue:date}} तक प्रति सप्ताह npm डाउनलोड की कुल संख्या {{download:number}} है|"</span>,
    <span class="hljs-attr">"awards"</span>: <span class="hljs-string">"आपने {{award:gt; 0: {{award; 1:बिल्कुल {{award}} पुरस्कार जीता; default:{{award}} पुरस्कार जीते }}; default:कोई पुरस्कार नहीं जीता }} है|"</span>,
    <span class="hljs-attr">"date"</span>: <span class="hljs-string">"{{val:date}}"</span>,
    <span class="hljs-attr">"time"</span>: <span class="hljs-string">"केवल 'समय' फ़ॉर्मेटर उपलब्ध नहीं है, यह डेटटाइम फ़ॉर्मेटर होना चाहिए"</span>,
    <span class="hljs-attr">"number"</span>: <span class="hljs-string">"{{value:currency}}"</span>
  }

  <span class="hljs-comment">// etc... all other languages that you wish to support</span>
</code></pre>
</li>
<li><p>Pay attention to the syntax <code>{{value: &lt;type&gt;}}</code> Let's break this syntax down into two parts:</p>
<ul>
<li><p><code>{value}</code> is a placeholder that will be populated with a value of a particular locale during runtime. As the locale changes, this field will be recomputed.</p>
</li>
<li><p><code>{value: &lt;type&gt;}</code> is used to specify the data type of value e.g. <em>number</em>, <em>Date</em> etc.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Defining entry point and mode for initializing the <em>sveltekit-i18n</em> library</strong></p>
<ul>
<li><p>Now that our library is installed and the locale dictionary is ready, it is time to create an entry point that will load the assets based on the user locale and initialize the library with a specific locale.</p>
</li>
<li><p>This entry point will be invoked as soon as the application bootstraps - once on the client side and once on the server side.</p>
</li>
<li><p>We will create the helper methods in the <em>src/lib/translations.ts</em> file.</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> i18n, { <span class="hljs-keyword">type</span> Config } <span class="hljs-keyword">from</span> <span class="hljs-string">'sveltekit-i18n'</span>;

  <span class="hljs-keyword">const</span> config: Config&lt;&gt; = {
    initLocale: <span class="hljs-string">'en'</span>,
    loaders: [
      {
        locale: <span class="hljs-string">'en'</span>,
        key: <span class="hljs-string">''</span>,
        loader: <span class="hljs-keyword">async</span> () =&gt; (<span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./lang/en.json'</span>)).default
      },
      {
        locale: <span class="hljs-string">'hi'</span>,
        key: <span class="hljs-string">''</span>,
        loader: <span class="hljs-keyword">async</span> () =&gt; (<span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./lang/hi.json'</span>)).default
      },
      {
        locale: <span class="hljs-string">'fr'</span>,
        key: <span class="hljs-string">''</span>,
        loader: <span class="hljs-keyword">async</span> () =&gt; (<span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./lang/fr.json'</span>)).default
      }
    ]
  };

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { t, loading, locales, locale, initialized, translations, loadTranslations } =
      <span class="hljs-keyword">new</span> i18n(config);
</code></pre>
</li>
<li><p>The above code snippet loads the locale files (<em>en.json</em>, <em>hi.json</em> and <em>fr.json</em>) and registers them with the library. Now when the user switches the locale, the corresponding translation file will be used.</p>
</li>
<li><p>We also provide <code>initLocale</code> and the translations will be initialized immediately using this locale.</p>
</li>
<li><p>The other property is <code>loaders</code>. You can use <code>loaders</code> to define your asynchronous translation load. Each loader can include:</p>
<ul>
<li><p><code>locale</code> : locale (e.g. <code>en</code>, <code>de</code>).</p>
</li>
<li><p><code>key</code>: represents the translation namespace. This key is used as a translation prefix so it should be module-unique. You can access your translation later using <code>$t('key.yourTranslation')</code>. It shouldn't include <code>.</code> (dot) character.</p>
</li>
<li><p><code>loader</code>: is a function returning a <code>Promise</code> with translation data. You can use it to load files locally or fetch them from your API.</p>
</li>
</ul>
</li>
<li><p>There are other options as well, but for the sake of simplicity, I won't be using them all. You can refer to them in the <a target="_blank" href="https://github.com/sveltekit-i18n/lib/blob/master/docs/README.md">official documentation</a>.</p>
</li>
<li><p>Finally, we export out few helper properties and methods that will be used throughout our application for localization.</p>
<ul>
<li><p><code>$t</code>: This is a readable store using which you can obtain your translations for a given translation key and interpolation variables. Example: <code>$t('heading')</code> or <code>$t('body_text', { variable: 'value' })</code></p>
</li>
<li><p><code>$loading</code>: A readable store that indicates whether translations are loading or not.</p>
</li>
<li><p><code>$locales</code>: A readable store, containing all instance locales.</p>
</li>
<li><p><code>$initialized</code>: This readable store returns <code>true</code> after the first translation is successfully initialized.</p>
</li>
<li><p><code>$translations</code>: A readable store, containing all preprocessed translations.</p>
</li>
<li><p><code>loadTranslations(locale: string, route: string)</code>: This functions loads the translation based on provided locale and route.</p>
</li>
</ul>
</li>
<li><p>There are other options as well, but for the sake of simplicity, I won't be using them all. You can refer to them in the <a target="_blank" href="https://github.com/sveltekit-i18n/base/tree/master/docs">official documentation</a>.</p>
</li>
</ul>
</li>
<li><p><strong>Load the translations</strong></p>
<ul>
<li><p>We load the translations in the <em>+layout.ts</em> file.</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { LayoutLoad } <span class="hljs-keyword">from</span> <span class="hljs-string">'./$types'</span>;
  <span class="hljs-keyword">import</span> { browser } <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/environment'</span>;
  <span class="hljs-keyword">import</span> { loadTranslations } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/translations'</span>;

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load: LayoutLoad = <span class="hljs-keyword">async</span> ({ url }) =&gt; {
    <span class="hljs-keyword">const</span> { pathname } = url;
    <span class="hljs-keyword">const</span> initLocale = getInitialLocale();

    <span class="hljs-keyword">await</span> loadTranslations(initLocale, pathname);

    <span class="hljs-keyword">return</span> { locale: initLocale, route: pathname };
  };

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getInitialLocale</span>(<span class="hljs-params"></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">if</span> (browser) {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">window</span>.navigator.language.split(<span class="hljs-string">'-'</span>)[<span class="hljs-number">0</span>];
      }
      <span class="hljs-keyword">catch</span>(e) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">'en'</span>;
      }
    }

    <span class="hljs-keyword">return</span> <span class="hljs-string">'en'</span>;
  }
</code></pre>
<blockquote>
<p>For the sake of explanation, I am keeping things simple and limiting them to use the default language configured in the user's browser but you can extend this logic to set locale returned by parent layout or by any other means and set it accordingly!</p>
</blockquote>
</li>
<li><p>One last step is to update the locale for the requests that are hitting our servers. We need to tell the server what language is being used. The easiest way to set the locale is in the <em>hooks.server.ts</em> file.</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Handle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
  <span class="hljs-keyword">import</span> { locale } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/translations'</span>;

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle: Handle = <span class="hljs-keyword">async</span> ({ event, resolve }) =&gt; {
    <span class="hljs-keyword">const</span> lang = event.request.headers.get(<span class="hljs-string">'accept-language'</span>)?.split(<span class="hljs-string">','</span>)[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">if</span> (lang) {
      locale.set(lang);
    }
    <span class="hljs-keyword">return</span> resolve(event);
  };
</code></pre>
</li>
<li><p>In the above example, we intercept every request and look for a header with the key "<em>accept-language</em>" and set it as the current locale.</p>
<blockquote>
<p>For the sake of explanation, I am keeping things simple and limiting them to query headers only but you can extend this logic to query cookies and URL parameters to compute the locale and set it accordingly!</p>
</blockquote>
</li>
</ul>
</li>
</ol>
<h3 id="heading-localizing-application">Localizing Application</h3>
<p>Before we start with localization, it is important to ensure the default locale is set up and that we do have an initial set of key-value message pairs for translations. To do so we can make use of <code>$initialized</code> store.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { t, initialized } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/translations'</span>;

&lt;div <span class="hljs-keyword">class</span>=<span class="hljs-string">"content"</span>&gt;
  {#<span class="hljs-keyword">if</span> $initialized}
    &lt;h1&gt;{$t(<span class="hljs-string">'heading'</span>)}&lt;/h1&gt;
  {:<span class="hljs-keyword">else</span>}
    &lt;div&gt;Locale initializing...&lt;/div&gt;
  {/<span class="hljs-keyword">if</span>}
&lt;/div&gt;
</code></pre>
<p>Now that we have configured the library and have the initial locale set, we're ready to start localizing our app. To do that we simply import <code>$t</code> and pass the message-id in any component that needs to be translated.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { t } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/translations'</span>;
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'page_title'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
</code></pre>
<p>From the context of our application, let us revisit our translation file:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"heading"</span>: <span class="hljs-string">"Internationalization in SvelteKit"</span>,
  <span class="hljs-attr">"toggle_label"</span>: <span class="hljs-string">"Select Locale"</span>,
  <span class="hljs-attr">"button_label_0"</span>: <span class="hljs-string">"{{value:number}}"</span>,
  <span class="hljs-attr">"button_label_1"</span>: <span class="hljs-string">"{{value:number}}"</span>,
  <span class="hljs-attr">"button_label_2"</span>: <span class="hljs-string">"{{value:number}}"</span>,
  <span class="hljs-attr">"body_text"</span>: <span class="hljs-string">"This is a small example to demonstrate i18n functionality in SvelteKit using sveltekit-i18n library. sveltekit-i18n is a tiny library with no external dependencies, built for Svelte and SvelteKit. It glues @sveltekit-i18n/base and @sveltekit-i18n/parser-default together to provide you the most straightforward sveltekit-i18n solution. Total number of npm downloads per week as of {{dateValue:date}} are {{download:number}}."</span>,
  <span class="hljs-attr">"awards"</span>:<span class="hljs-string">"You have {{award:gt; 0: {{award; 1:won exactly {{award}} award; default:won {{award}} awards}}; default:not won any awards}}!"</span>,
  <span class="hljs-attr">"date"</span>: <span class="hljs-string">"{{val:date}}"</span>,
  <span class="hljs-attr">"time"</span>: <span class="hljs-string">"Only 'time' formatter is not available, it must be DateTime formatter"</span>,
  <span class="hljs-attr">"number"</span>: <span class="hljs-string">"{{value:currency}}"</span>
}
</code></pre>
<p>To set the main heading, the label for locale switching and the button label text, we simply invoke <code>$t()</code> and pass in the message-id as explained above:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'heading'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'toggle_label'</span>)}</span><span class="xml">: <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'button_label_0'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'button_label_1'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'button_label_2'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
</code></pre>
<p>We can also pass additional parameters to <code>$t()</code> and the syntax is:</p>
<pre><code class="lang-typescript">$t(messageId: <span class="hljs-built_in">string</span>, vars?: Record&lt;<span class="hljs-built_in">any</span>, <span class="hljs-built_in">any</span>&gt;): <span class="hljs-built_in">string</span>
</code></pre>
<p>In the message-id, we had something like:</p>
<pre><code class="lang-json"><span class="hljs-string">"body_text"</span>: <span class="hljs-string">"...  {{dateValue:date}} are {{download:number}}."</span>
</code></pre>
<p>We can use the new syntax that we just saw and fill in the values of the <em>dateValue</em> and the <em>download</em> <strong>placeholders</strong> (<code>{{ ... }}</code>) dynamically:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'body_text'</span>,
  { <span class="hljs-attr">dateValue</span>: <span class="hljs-built_in">Date</span>.UTC(<span class="hljs-number">2023</span>, <span class="hljs-number">6</span>, <span class="hljs-number">14</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>), <span class="hljs-attr">download</span>: <span class="hljs-number">3722</span> }
)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
</code></pre>
<p>We can extend this snippet to format the Date as well</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'body_text'</span>,
  { <span class="hljs-attr">dateValue</span>: <span class="hljs-built_in">Date</span>.UTC(<span class="hljs-number">2023</span>, <span class="hljs-number">6</span>, <span class="hljs-number">14</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>), <span class="hljs-attr">download</span>: <span class="hljs-number">3722</span> },
  { <span class="hljs-attr">date</span>: { <span class="hljs-attr">year</span>: <span class="hljs-string">"numeric"</span>, <span class="hljs-attr">month</span>: <span class="hljs-string">"long"</span>, <span class="hljs-attr">day</span>: <span class="hljs-string">"numeric"</span> }}</span><span class="xml">
)}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
</code></pre>
<p><strong>Note that</strong> <code>;</code>, <code>:</code>, <code>{</code> and <code>}</code> characters are used as placeholder identifiers and separators, so you shouldn't use them within your definition keys and values. You should use their escaped form instead (<code>\\;</code>, <code>\\:</code>, <code>\\{</code> or <code>\\}</code>).</p>
<h3 id="heading-type-safety-in-translations-using-modifiers">Type Safety in Translations using Modifiers</h3>
<p>Let's revisit the last example from our translation file and observe the syntax:</p>
<pre><code class="lang-json"><span class="hljs-string">"body_text"</span>: <span class="hljs-string">"...  {{dateValue:date}} are {{download:number}}."</span>
</code></pre>
<p>The <em>sveltekit-i18n</em> allows us to define the type of value we are expecting at runtime using modifiers.</p>
<p>The Modifiers don't represent the payload value directly, but they can use it for further calculations. The syntax for modifiers is:</p>
<pre><code class="lang-basic">{{ placeholder: modifier }} 
Example: {{dateValue:date}}, {{download:number}}
</code></pre>
<p>The <em>sveltekit-i18n</em> provides multiple inbuild modifiers as well as the flexibility to create custom modifiers. I won't be able to cover all modifiers in this article, so I'll recommend going through <a target="_blank" href="https://github.com/sveltekit-i18n/parsers/tree/master/parser-default#modifiers">official documentation</a> for more examples and use cases.</p>
<h3 id="heading-locale-switching">Locale Switching</h3>
<p>To switch between locales, we make use of the <code>$locale</code> store variable. The <code>locale</code> store defines what is the current locale.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ locale }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/translations'</span>;

  <span class="hljs-keyword">let</span> value: string = <span class="hljs-string">'en'</span>;

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleLocaleChange</span>(<span class="hljs-params">event: Event</span>) </span></span></span><span class="javascript">{
    event.preventDefault();
    value = event?.target?.value;
    $locale = value;
  }</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">select</span> </span></span><span class="javascript">{value}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">on:change</span>=</span></span><span class="javascript">{handleLocaleChange}</span><span class="xml"><span class="hljs-tag">&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"en"</span> <span class="hljs-attr">selected</span>&gt;</span>English<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"hi"</span>&gt;</span>Hindi<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"fr"</span>&gt;</span>French<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span></span>
</code></pre>
<p>We can get the list of locales available in our application using the <code>$locales</code></p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { locales } <span class="hljs-keyword">from</span> <span class="hljs-string">'$lib/translations'</span>;
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

</span><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> $locales <span class="hljs-keyword">as</span> locale, i}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=</span></span><span class="javascript">{locale}</span><span class="xml"><span class="hljs-tag">&gt;</span></span><span class="javascript">{locale.toUpperCase()}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
</span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">
</span><span class="hljs-comment">&lt;!-- $locales = ['en', 'hi', 'fr'] --&gt;</span>
</code></pre>
<h3 id="heading-pluralization">Pluralization</h3>
<p>Let's start with Pluralization. Consider the following message-id:</p>
<pre><code class="lang-json"><span class="hljs-string">"awards"</span>:<span class="hljs-string">"You have {{award:gt; 0: {{award; 1:won exactly {{award}} award; default:won {{award}} awards}}; default:not won any awards}}!"</span>
</code></pre>
<p>The syntax for pluralizing a string in the <em>sveltekit-i18n</em> is as follows:</p>
<pre><code class="lang-abap">{{ variable:<span class="hljs-keyword">modifier</span>; <span class="hljs-number">0</span>:{{ variable }} {{ variable; <span class="hljs-number">1</span>: some-<span class="hljs-keyword">message</span>; <span class="hljs-keyword">default</span>: some-<span class="hljs-keyword">message</span> }}; <span class="hljs-keyword">default</span>:some-<span class="hljs-keyword">default</span>-<span class="hljs-keyword">message</span>; }}
</code></pre>
<p>While pluralizing a string we make use of modifiers. We compare the input value against the modifier and execute conditions and display the message. If none of the conditions satisfies, we display a default value!</p>
<p>Concerning our application example, consider the following invocation:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'awards'</span>, { <span class="hljs-attr">award</span>: randomNumber })}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
</code></pre>
<p>In the given example, we make use of <code>gt</code> modifier (<em>input value is greater than the value in your definition</em>). Statement <code>{{ award:gt; ... }}</code> means the input variable <em>award</em> value should be compared and if that is greater than 0, display a specific message else display a default message.</p>
<ul>
<li><p>if <code>award = 1</code>, the modifier will execute the condition within <code>1: some-message</code> and output will be "You have won exactly 1 award". Here <code>{{award}}</code> specifies the value of the variable.</p>
</li>
<li><p>if <code>award &gt; 1</code>, the modifier will execute the condition within <code>default: some-message</code> and output will be "You have won 5 awards" (assuming <em>award = 5</em>)</p>
</li>
<li><p>if <code>award = 0</code>, since we have used <code>gt</code> modifier, it will always execute certain conditions if the value &gt; 0 else will execute <code>default: some-default-message</code> and output will be "You have not won any awards". Since we are using <code>gt</code> modifier, we can omit the condition post <code>0:</code> i.e. <code>0:{{ ... }}</code> can be <code>0:</code> as it will always be ignored!</p>
</li>
</ul>
<h3 id="heading-parsers">Parsers</h3>
<p>The Parsers are responsible for interpreting and translating messages. The <em>sveltekit-i18n</em> comes with two parsers: The <a target="_blank" href="https://github.com/sveltekit-i18n/parsers/tree/master/parser-default">default parser</a> and the <a target="_blank" href="https://github.com/sveltekit-i18n/parsers/tree/master/parser-icu">ICU parser</a>.</p>
<p>Until this point in the article, we have been using the default parser. However, we can adapt the ICU parser as well. To do this, we have to first install the dependency of the ICU parser</p>
<pre><code class="lang-abap">pnpm i @sveltekit-i18n/parser-icu
</code></pre>
<p>We need to make a few changes in the <em>src/lib/translations.ts</em> file</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> i18n <span class="hljs-keyword">from</span> <span class="hljs-string">'sveltekit-i18n'</span>;
<span class="hljs-keyword">import</span> parser <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltekit-i18n/parser-icu'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Config } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltekit-i18n/parser-icu'</span>;

<span class="hljs-keyword">const</span> config: Config&lt;Partial&lt;Params&gt;&gt; = {
  initLocale: <span class="hljs-string">'en'</span>,
  parser: parser(),
  loaders: [ ... ]
}
</code></pre>
<p>Let's walk through these changes:</p>
<ul>
<li><p>Update import of type <code>Config</code> from <code>sveltekit-i18n</code> to <code>@sveltekit-i18n/parser-icu</code></p>
</li>
<li><p>Import <em>parser</em> from <code>@sveltekit-i18n/parser-icu</code></p>
</li>
<li><p>Add an instance of the <em>parser</em> within the existing configuration.</p>
</li>
</ul>
<p>That's it now we are ready to use ICU syntax for formatting and pluralization.</p>
<p><strong>NOTE</strong>: We can either use the ICU parser or the default parser and cannot mix them!</p>
<h3 id="heading-formatting">Formatting</h3>
<p>In this section, I will discuss formatting Date and Currency using inbuild modifiers.</p>
<p>The <em>sveltekit-i18n</em> provides inbuild modifiers as well as provisions to create a custom modifier. For the sake of simplicity, I will only be explaining how to use inbuild modifiers. You can refer to <a target="_blank" href="https://github.com/sveltekit-i18n/parsers/tree/master/parser-default#modifiers">this guide</a> on how to create custom modifiers.</p>
<p>The <em>sveltekit-i18n</em> provides the <code>date</code> and <code>currency</code> modifiers that we can use to format data accordingly. The syntax for formatting is:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span></span><span class="javascript">{ $t(<span class="hljs-string">'message-id'</span>, { <span class="hljs-attr">variable</span>: value }, { <span class="hljs-attr">date</span>: { date-format-option } }</span><span class="xml">) }<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span></span><span class="javascript">{ $t(<span class="hljs-string">'message-id'</span>, { <span class="hljs-attr">variable</span>: value }, { <span class="hljs-attr">currency</span>: { currency-format-option } }</span><span class="xml">) }<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
</code></pre>
<p>Coming back to our example application, we can now format the date and currency:</p>
<pre><code class="lang-json"><span class="hljs-comment">// our translation file</span>
{
  ...,
  <span class="hljs-attr">"date"</span>: <span class="hljs-string">"{{val:date}}"</span>,
  <span class="hljs-attr">"number"</span>: <span class="hljs-string">"{{value:currency}}"</span>
}
</code></pre>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container__content__formatter"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Date: <span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'date'</span>, { <span class="hljs-attr">val</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>() }, { <span class="hljs-attr">date</span>: { <span class="hljs-attr">year</span>: <span class="hljs-string">"numeric"</span>, <span class="hljs-attr">month</span>: <span class="hljs-string">"long"</span>, <span class="hljs-attr">day</span>: <span class="hljs-string">"numeric"</span> } }</span><span class="xml">)}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Currency: <span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'number'</span>, { <span class="hljs-attr">value</span>: <span class="hljs-number">3722</span> }, { <span class="hljs-attr">currency</span>: { <span class="hljs-attr">style</span>: <span class="hljs-string">"currency"</span>, <span class="hljs-attr">currency</span>: <span class="hljs-string">"INR"</span> } }</span><span class="xml">)}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<h3 id="heading-miscellaneous">Miscellaneous</h3>
<p>The one last thing I want to cover before concluding this article is type binding the translation configuration. If you're following this article you will see typescript errors within <code>$t()</code> in <em>+page.svelte</em> i.e. the file that we have used for localization.</p>
<p>To correct this, we must define all the variables used for translations within the <em>src/lib/translations.ts</em> file. Refer to the <em>en.json</em> translation file, we make use of 5 variables. We can create an interface out of them and pass it to our configuration object:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> Params {
  dateValue: <span class="hljs-built_in">number</span>;
  value: <span class="hljs-built_in">number</span>;
  download: <span class="hljs-built_in">number</span>;
  award: <span class="hljs-built_in">number</span>;
  val: <span class="hljs-built_in">Date</span>;
}

<span class="hljs-keyword">const</span> config: Config&lt;Partial&lt;Params&gt;&gt; = {
  initLocale: <span class="hljs-string">'en'</span>,
  ...
}
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Finally, we were able to localize our application using <em>sveltekit-i18n</em>. You can find the code in the <a target="_blank" href="https://github.com/aakash14goplani/sveltekit-with-sveltekiti18n">GitHub repo</a> and <a target="_blank" href="https://sveltekit-with-sveltekiti18n.vercel.app/">link</a> to the live demo.</p>
<p>This was the second article of <a target="_blank" href="https://blog.aakashgoplani.in/series/i18n-in-sveltekit">three-part series</a> to demonstrate i18n in SvelteKit. In the next article, I'll be explaining <a target="_blank" href="https://blog.aakashgoplani.in/internationalization-in-sveltekit-using-typesafe-i18n">i18n in SvelteKit with <em>typesafe-i18n</em></a>.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><p>Core <a target="_blank" href="https://github.com/sveltekit-i18n/lib/tree/master/docs">properties and methods</a></p>
</li>
<li><p>Base <a target="_blank" href="https://github.com/sveltekit-i18n/base/tree/master/docs">properties and methods</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/sveltekit-i18n/parsers/tree/master/parser-default">Default Parser</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/sveltekit-i18n/parsers/tree/master/parser-icu">ICU Parser</a></p>
</li>
<li><p>Multiple working project <a target="_blank" href="https://github.com/sveltekit-i18n/lib/tree/master/examples">examples</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Internationalization in SvelteKit with svelte-i18n]]></title><description><![CDATA[This is the first article of three-part series to demonstrate i18n in SvelteKit. In this article, we will work on integrating svelte-i18n with SvelteKit.
svelte-i18n helps you localize your app using the reactive tools Svelte provides. By using store...]]></description><link>https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-svelte-i18n</link><guid isPermaLink="true">https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-svelte-i18n</guid><category><![CDATA[Sveltekit]]></category><category><![CDATA[i18n]]></category><category><![CDATA[internationalization]]></category><category><![CDATA[localization]]></category><category><![CDATA[svelte-i18n]]></category><dc:creator><![CDATA[Aakash Goplani]]></dc:creator><pubDate>Tue, 01 Aug 2023 16:55:36 GMT</pubDate><content:encoded><![CDATA[<p>This is the first article of <a target="_blank" href="https://blog.aakashgoplani.in/series/i18n-in-sveltekit">three-part series</a> to demonstrate i18n in SvelteKit. In this article, we will work on integrating <em>svelte-i18n</em> with SvelteKit.</p>
<p><em>svelte-i18n</em> helps you localize your app using the reactive tools Svelte provides. By using stores to keep track of the current <code>locale</code>, <code>dictionary</code> of messages and <code>format</code> messages, it keeps everything neat, in sync and easy to use on svelte files.</p>
<p>Under the hood, <em>svelte-i18n</em> uses <a target="_blank" href="https://formatjs.io/">formatjs</a> for localizing messages. It allows <em>svelte-i18n</em> to support the ICU message syntax.</p>
<h3 id="heading-application-structure">Application Structure</h3>
<p>Before we begin, I want to give you a detailed walkthrough of the application that we will be working on. You can find the code in the <a target="_blank" href="https://github.com/aakash14goplani/sveltekit-with-sveltei18n">GitHub repository</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1690286047702/54c0dc32-f5c3-453e-85b0-7f986af6c5d7.png" alt class="image--center mx-auto" /></p>
<p>As we can see from the image, this will be a simple single-page application demonstrating i18n features like <em>locale switching</em>, <em>pluralization</em> and <em>formatting</em>.</p>
<p>In the next section, we will work on integrating the <em>svelte-i18n</em> library with SvelteKit.</p>
<h3 id="heading-integration-with-sveltekit">Integration with SvelteKit</h3>
<ol>
<li><p><strong>Install <em>svelte-i18n</em> library</strong></p>
<ul>
<li><p>This first step is to install this library as a dependency in our SvelteKit project:</p>
<pre><code class="lang-typescript">  pnpm i svelte-i18n
</code></pre>
</li>
<li><p>[OPTIONAL] If you're using <code>VSCode</code> and want to have your messages previewed alongside your components, check out the <a target="_blank" href="https://github.com/antfu/i18n-ally">i18n-ally</a> and their <a target="_blank" href="https://github.com/antfu/i18n-ally/wiki/FAQ">FAQ</a> to see how to set it up.</p>
</li>
</ul>
</li>
<li><p><strong>Define Locale Dictionary</strong></p>
<ul>
<li><p>Now that we have installed this library, it's time to identify the areas in our application that need localization. We then define the <strong>locale dictionaries</strong> within the <em>src/lib/lang</em> folder. A locale dictionary is a regular JSON object which contains message definitions for a certain language.</p>
</li>
<li><p>In our example application, I want five fields to be localized: The main heading, Lable for locale switching, Button label text, Body text (the paragraph) and Text for pluralization.</p>
</li>
<li><p>We also have the requirement for formatting date, time and currency but for that, we don't need a special entry in the locale dictionary. We will implement the same using inbuild methods provided by the <em>svelte-i18n</em> for formatting.</p>
</li>
<li><p>For the sake of this application, I will define dictionaries in three languages - English (<em>en.json</em>), Hindi (<em>hi.json</em>) and French (<em>fr.json</em>) within the <em>src/lib/lang</em> folder.</p>
<pre><code class="lang-json">  <span class="hljs-comment">// en.json</span>
  {
    <span class="hljs-attr">"heading"</span>: <span class="hljs-string">"Internationalization in SvelteKit"</span>,
    <span class="hljs-attr">"toggle_label"</span>: <span class="hljs-string">"Select Locale"</span>,
    <span class="hljs-attr">"button_label"</span>: <span class="hljs-string">"Generate Awards"</span>,
    <span class="hljs-attr">"body_text"</span>: <span class="hljs-string">"This is a small example to demonstrate i18n functionality in SvelteKit using svelte-i18n library. svelte-i18n helps you localize your app using the reactive tools Svelte provides. By using stores to keep track of the current locale, dictionary of messages and to format messages, we keep everything neat, in sync and easy to use on your svelte files. Total number of npm downloads per week as of {date} are {download}."</span>,
    <span class="hljs-attr">"awards"</span>: <span class="hljs-string">"You have {n, plural, =0 { not won any awards } one { won exactly # award } other { won # awards }}!"</span>
  }

  <span class="hljs-comment">// hi.json</span>
  {
    <span class="hljs-attr">"heading"</span>: <span class="hljs-string">"SvelteKit में अंतर्राष्ट्रीयकरण"</span>,
    <span class="hljs-attr">"toggle_label"</span>: <span class="hljs-string">"भाषा चुने"</span>,
    <span class="hljs-attr">"button_label"</span>: <span class="hljs-string">"पुरस्कार उत्पन्न करें"</span>,
    <span class="hljs-attr">"body_text"</span>: <span class="hljs-string">"svelte-i18n लाइब्रेरी का उपयोग करके SvelteKit में i18n कार्यक्षमता प्रदर्शित करने के लिए यह एक छोटा सा उदाहरण है। svelte-i18n, Svelte द्वारा प्रदान किए गए प्रतिक्रियाशील टूल का उपयोग करके आपके ऐप को स्थानीयकृत करने में आपकी सहायता करता है। वर्तमान स्थान, संदेशों के शब्दकोश पर नज़र रखने और संदेशों को प्रारूपित करने के लिए स्टोर का उपयोग करके, हम सब कुछ साफ-सुथरा, सिंक में रखते हैं और आपकी विस्तृत फ़ाइलों पर उपयोग में आसान रखते हैं। {date} तक प्रति सप्ताह NPM डाउनलोड की कुल संख्या {download} है|"</span>,
    <span class="hljs-attr">"awards"</span>: <span class="hljs-string">"आपने {n, plural, =0 { कोई पुरस्कार नहीं जीता } one { बिल्कुल # पुरस्कार जीता } other { # पुरस्कार जीते }} है|"</span>
  }

  <span class="hljs-comment">// etc... all other languages that you wish to support</span>
</code></pre>
</li>
<li><p>Pay attention to the syntax <code>{value}</code> Here <code>{value}</code> is a placeholder that will be populated with a value of a particular locale during runtime. As the locale changes, this field will be recomputed.</p>
</li>
</ul>
</li>
<li><p><strong>Defining entry point and mode for initializing <em>svelte-i18n</em> library</strong></p>
<ul>
<li><p>Now that our library is installed and the locale dictionary is ready, it is time to create an entry point that will load the assets based on the user locale and initialize the library with a specific locale.</p>
</li>
<li><p>This entry point will be invoked as soon as the application bootstraps - once on the client side and once on the server side.</p>
</li>
<li><p>We will create the helper methods in the <em>src/lib/i18n.ts</em> file.</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> { browser } <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/environment'</span>;
  <span class="hljs-keyword">import</span> { init, register } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte-i18n'</span>;

  <span class="hljs-keyword">const</span> defaultLocale = <span class="hljs-string">'en'</span>;

  register(<span class="hljs-string">'en'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./lang/en.json'</span>));
  register(<span class="hljs-string">'hi'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./lang/hi.json'</span>));
  register(<span class="hljs-string">'fr'</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./lang/fr.json'</span>));

  init({
    fallbackLocale: defaultLocale,
    initialLocale: browser ? <span class="hljs-built_in">window</span>.navigator.language : defaultLocale
  });
</code></pre>
</li>
<li><p>The above code snippet loads the locale files (<em>en.json</em>, <em>hi.json</em> and <em>fr.json</em>) and registers them with the library. Now when the user switches the locale, the corresponding translation file will be used. We also provide <code>initialLocale</code> and a <code>fallbackLocale.</code> <strong>Please note that the</strong> <code>fallbackLocale</code> <strong>is always loaded, independent of the current locale, since only some messages can be missing</strong>. Let's go through each method in detail.</p>
</li>
<li><p><a target="_blank" href="https://github.com/kaisermann/svelte-i18n/blob/46b025ceebeb9bd68df0a2f30cc3c0775049ed85/docs/Methods.md#init"><code>init()</code></a> is responsible for configuring some of the library behaviors such as the global fallback and initial locales. Must be called before setting a locale and displaying your view.</p>
</li>
<li><p><a target="_blank" href="https://github.com/kaisermann/svelte-i18n/blob/46b025ceebeb9bd68df0a2f30cc3c0775049ed85/docs/Methods.md#register"><code>register()</code></a> adds a new dictionary of messages to a certain locale i.e. it will register your local dictionaries (<em>en.json</em> etc.) and get all translation keys ready for you.</p>
</li>
<li><p>Now there are two ways to register/add dictionaries to your locale:</p>
<ul>
<li><p>the <strong>synchronous way</strong> using <a target="_blank" href="https://github.com/kaisermann/svelte-i18n/blob/46b025ceebeb9bd68df0a2f30cc3c0775049ed85/docs/Methods.md#addmessages"><code>addMessages()</code></a> that imports your locale JSON files</p>
</li>
<li><p>the <strong>asynchronous way</strong> using <code>register()</code> The asynchronous way is a more performant way to load your dictionaries. This way, only the files registered to the current locale will be loaded. As the locale value changes, it will automatically load the registered loaders for the new locale.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Initialize the library on the server side</strong></p>
<ul>
<li><p>The rule is to initialize the <em>svelte-i18n</em> library with a locale as soon as the application boots up. For SSR we need to tell the server what language is being used. This could use <em>cookies</em> or the <em>accept-language</em> header for example. The easiest way to set the locale is in the server hook.</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Handle } <span class="hljs-keyword">from</span> <span class="hljs-string">'@sveltejs/kit'</span>;
  <span class="hljs-keyword">import</span> { locale } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte-i18n'</span>;

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handle: Handle = <span class="hljs-keyword">async</span> ({ event, resolve }) =&gt; {
    <span class="hljs-keyword">const</span> lang = event.request.headers.get(<span class="hljs-string">'accept-language'</span>)?.split(<span class="hljs-string">','</span>)[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">if</span> (lang) {
      locale.set(lang);
    }
    <span class="hljs-keyword">return</span> resolve(event);
  };
</code></pre>
</li>
<li><p>In the above example, we intercept every request and look for a header with the key "<em>accept-language</em>" and set it as the current locale.</p>
<blockquote>
<p>For the sake of explanation, I am keeping things simple and limiting them to query headers only but you can extend this logic to query cookies and URL parameters to compute the locale and set it accordingly!</p>
</blockquote>
</li>
</ul>
</li>
<li><p><strong>Initialize the library on the client side</strong></p>
<ul>
<li><p>Once the backend is all wired up, we will now proceed with client-side initialization in the <em>+layout.ts</em></p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> { browser } <span class="hljs-keyword">from</span> <span class="hljs-string">'$app/environment'</span>;
  <span class="hljs-keyword">import</span> <span class="hljs-string">'$lib/i18n'</span>; <span class="hljs-comment">// Import to initialize. Very Important!</span>
  <span class="hljs-keyword">import</span> { locale, waitLocale } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte-i18n'</span>;
  <span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { LayoutLoad } <span class="hljs-keyword">from</span> <span class="hljs-string">'./$types'</span>;

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> load: LayoutLoad = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (browser) {
      locale.set(<span class="hljs-built_in">window</span>.navigator.language);
    }
    <span class="hljs-keyword">await</span> waitLocale();
  };
</code></pre>
</li>
<li><p>In the above code example, as soon as the browser is initialized, we set locale to the default language configured in the user's browser.</p>
</li>
<li><p>After that we invoke <a target="_blank" href="https://github.com/kaisermann/svelte-i18n/blob/46b025ceebeb9bd68df0a2f30cc3c0775049ed85/docs/Methods.md#waitlocale"><code>waitLocale()</code></a> This method executes the queue of the locale. If the queue isn't resolved yet, the same promise is returned.</p>
<blockquote>
<p>For the sake of explanation, I am keeping things simple and limiting them to use the default language configured in the user's browser but you can extend this logic to set locale returned by parent layout or by any other means and set it accordingly!</p>
</blockquote>
</li>
</ul>
</li>
</ol>
<h3 id="heading-localizing-application">Localizing Application</h3>
<p>Before we start with localization, it is important to ensure the default locale is set up and that we do have an initial set of key-value message pairs for translations. To do so we can implement a derived store variable in the <code>src/lib/i18n.ts</code> file and react to it accordingly:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { derived } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte/store'</span>;
<span class="hljs-keyword">import</span> { locale } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte-i18n'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> isLocaleLoaded = derived(locale, <span class="hljs-function">(<span class="hljs-params">$locale</span>) =&gt;</span> <span class="hljs-keyword">typeof</span> $locale === <span class="hljs-string">'string'</span>);
</code></pre>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"content"</span>&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">#if</span><span class="javascript"> $isLocaleLoaded}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="javascript">{$_(<span class="hljs-string">'heading'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">:else</span><span class="javascript">}</span><span class="xml">
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Locale initializing...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  </span><span class="javascript">{</span><span class="hljs-keyword">/if</span><span class="javascript">}</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
</code></pre>
<p>Now that we have configured the library and have the initial locale set, we're ready to start localizing our app. To do that we simply import <code>$_</code> and pass message id in any component that needs to be translated.</p>
<p>The <code>$_</code> is the implementation of <code>format()</code> from <a target="_blank" href="https://formatjs.io/">formnatjs</a>. To format a message is as simple as executing the <code>$_</code> method and passing the message-id:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { _ } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte-i18n'</span>;
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="javascript">{$_(<span class="hljs-string">'page_title'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
</code></pre>
<p>It has two aliases <code>$t</code> and <code>$format</code>. So we can do any of the following:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="javascript">{$_(<span class="hljs-string">'page_title'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="javascript">{$t(<span class="hljs-string">'page_title'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="javascript">{$format(<span class="hljs-string">'page_title'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
</code></pre>
<p>From the context of our application, let us revisit our translation file:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"heading"</span>: <span class="hljs-string">"Internationalization in SvelteKit"</span>,
  <span class="hljs-attr">"toggle_label"</span>: <span class="hljs-string">"Select Locale"</span>,
  <span class="hljs-attr">"button_label"</span>: <span class="hljs-string">"Generate Awards"</span>,
  <span class="hljs-attr">"body_text"</span>: <span class="hljs-string">"This is a small example to demonstrate i18n functionality in SvelteKit using svelte-i18n library. svelte-i18n helps you localize your app using the reactive tools Svelte provides. By using stores to keep track of the current locale, dictionary of messages and to format messages, we keep everything neat, in sync and easy to use on your svelte files. Total number of npm downloads per week as of {date} are {download}."</span>,
  <span class="hljs-attr">"awards"</span>: <span class="hljs-string">"..."</span>
}
</code></pre>
<p>To set the main heading, the label for locale switching and the button label text, we simply invoke <code>$_()</code> and pass in the message-id as explained above:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span></span><span class="javascript">{$_(<span class="hljs-string">'heading'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span></span><span class="javascript">{$_(<span class="hljs-string">'toggle_label'</span>)}</span><span class="xml">: <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span></span><span class="javascript">{$_(<span class="hljs-string">'button_label'</span>)}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
</code></pre>
<p>We can also pass additional parameters to <code>$_()</code> and the syntax is:</p>
<pre><code class="lang-typescript">$_(messageId: <span class="hljs-built_in">string</span>, options?: MessageObject): <span class="hljs-built_in">string</span>
$_(options: MessageObject): <span class="hljs-built_in">string</span>

<span class="hljs-keyword">interface</span> MessageObject {
  id?: <span class="hljs-built_in">string</span>;
  locale?: <span class="hljs-built_in">string</span>;
  format?: <span class="hljs-built_in">string</span>;
  <span class="hljs-keyword">default</span>?: <span class="hljs-built_in">string</span>;
  values?: Record&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span> | <span class="hljs-built_in">Date</span>&gt;;
}
</code></pre>
<p>In the message-id, we had something like:</p>
<pre><code class="lang-json"><span class="hljs-string">"body_text"</span>: <span class="hljs-string">"...  {date} are {download}."</span>
</code></pre>
<p>We can use the new syntax that we just saw and fill in the values of the <em>date</em> and the <em>download</em> <strong>placeholders</strong> (<code>{ ... }</code>) dynamically:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span></span><span class="javascript">{$_(<span class="hljs-string">'body_text'</span>, {
  <span class="hljs-attr">values</span>: {
    <span class="hljs-attr">download</span>: $number(<span class="hljs-number">30242</span>),
    <span class="hljs-attr">date</span>: $date(<span class="hljs-built_in">Date</span>.UTC(<span class="hljs-number">2023</span>, <span class="hljs-number">6</span>, <span class="hljs-number">14</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>), { <span class="hljs-attr">year</span>: <span class="hljs-string">"numeric"</span>, <span class="hljs-attr">month</span>: <span class="hljs-string">"long"</span>, <span class="hljs-attr">day</span>: <span class="hljs-string">"numeric"</span> })
  }</span><span class="xml">
})}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
</code></pre>
<p>For now, ignore <code>$number()</code> and <code>$date()</code> I'll discuss them in detail, in the following section about the Formatters. For now, switching to the next section where we will learn how to switch between locales!</p>
<h3 id="heading-locale-switching">Locale Switching</h3>
<p>To switch between locales, we make use of the <code>$locale</code> store variable.</p>
<p>The <code>locale</code> store defines what is the current locale. When its value is changed, before updating the actual stored value, <em>svelte-i18n</em> sees if there are any message loaders registered for the new locale:</p>
<ul>
<li><p>If yes, changing the <code>locale</code> is an async operation.</p>
</li>
<li><p>If no, the locale's dictionary is fully loaded and changing the locale is a sync operation.</p>
</li>
</ul>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ locale }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte-i18n'</span>;

  <span class="hljs-keyword">let</span> value: string = <span class="hljs-string">'en'</span>;

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleLocaleChange</span>(<span class="hljs-params">event: Event</span>) </span></span></span><span class="javascript">{
    event.preventDefault();
    value = event?.target?.value;
    $locale = value;
  }</span><span class="xml">
<span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">select</span> </span></span><span class="javascript">{value}</span><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">on:change</span>=</span></span><span class="javascript">{handleLocaleChange}</span><span class="xml"><span class="hljs-tag">&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"en"</span> <span class="hljs-attr">selected</span>&gt;</span>English<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"hi"</span>&gt;</span>Hindi<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"fr"</span>&gt;</span>French<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span></span>
</code></pre>
<p>We can get the list of locales available in our application using the <code>$locales</code></p>
<pre><code class="lang-svelte"><span class="javascript">{</span><span class="hljs-keyword">#each</span><span class="javascript"> $locales <span class="hljs-keyword">as</span> locale, i}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=</span></span><span class="javascript">{locale}</span><span class="xml"><span class="hljs-tag">&gt;</span></span><span class="javascript">{locale.toUpperCase()}</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
</span><span class="javascript">{</span><span class="hljs-keyword">/each</span><span class="javascript">}</span><span class="xml">
</span><span class="hljs-comment">&lt;!-- $locales = ['en', 'hi', 'fr'] --&gt;</span>
</code></pre>
<p>While changing the locales, we can also make use of the <code>$loading</code> store that can detect if the app is currently fetching any enqueued message definitions.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { isLoading } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte-i18n'</span>
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

</span><span class="javascript">{</span><span class="hljs-keyword">#if</span><span class="javascript"> $isLoading}</span><span class="xml">
  Please wait...
</span><span class="javascript">{</span><span class="hljs-keyword">:else</span><span class="javascript">}</span><span class="xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">Nav</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">Main</span> /&gt;</span>
</span><span class="javascript">{</span><span class="hljs-keyword">/if</span><span class="javascript">}</span>
</code></pre>
<p>We can also check the list of all messages with a particular locale using <code>$dictionary</code> The <code>$dictionary</code> store is responsible for holding all loaded message definitions for each locale.</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span><span class="javascript">
  <span class="hljs-keyword">import</span> { dictionary } <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte-i18n'</span>

  <span class="hljs-built_in">console</span>.log($dictionary); <span class="hljs-comment">// { en: {...}, hi: {...}, fr: {...} }</span>
</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>
</code></pre>
<h3 id="heading-pluralization">Pluralization</h3>
<p>As I have mentioned before that <em>svelte-i18n</em> uses <a target="_blank" href="https://formatjs.io/docs/core-concepts/icu-syntax/#plural-format">ICU syntax</a>, and we can leverage that to implement Pluralization.</p>
<p>Consider the following message-id:</p>
<pre><code class="lang-json"><span class="hljs-string">"awards"</span>: <span class="hljs-string">"You have {n, plural, =0 { not won any awards } one { won exactly # award } other { won # awards }}!"</span>
</code></pre>
<p>If we break that into a simple format and compare it with ICU syntax <code>{ key, plural, matches }</code></p>
<pre><code class="lang-abap">{ n, plural, =<span class="hljs-number">0</span> { some-<span class="hljs-keyword">message</span> } one { some-<span class="hljs-keyword">message</span> # } other { some-<span class="hljs-keyword">message</span> # } }
</code></pre>
<p>Here <code>n</code> is the <em>key</em> that will be passed via <code>$_()</code> followed by a <em>plural</em> keyword. <em>matches</em> resemble the rest of the conditions where if:</p>
<ul>
<li><p><code>n=0</code> we can output custom messages within <code>{...}</code> Example: <code>=0 { not won any awards }</code></p>
</li>
<li><p><code>n=1</code> for plural category <code>one</code> we can output custom messages within <code>{...}</code> Here <code>#</code> represents the value of <code>n</code> which will be <code>1</code> in this case. Example: <code>one { won exactly # award }</code></p>
</li>
<li><p><code>n=any-number</code> for plural category <code>other</code> we can output custom messages within <code>{...}</code> Example: <code>other { won # awards }</code></p>
</li>
</ul>
<p>Coming back to our example:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span></span><span class="javascript">{$_(<span class="hljs-string">'awards'</span>, { <span class="hljs-attr">values</span>: { <span class="hljs-attr">n</span>: randomNumber } }</span><span class="xml">)}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
</code></pre>
<ul>
<li><p>if <code>n = 0</code>, output = <em>You have not won any awards!</em></p>
</li>
<li><p>if <code>n = 1</code>, output = <em>You have won exactly 1 award!</em></p>
</li>
<li><p>if <code>n = 10</code>, output = <em>You have won 10 awards!</em></p>
</li>
</ul>
<h3 id="heading-formatting">Formatting</h3>
<p>Coming to the last section of the article where I will discuss formatting Date, Time, Number and Currency using inbuild Formatters.</p>
<p><em>svelte-i18n</em> provides inbuild formatters as well as provisions to create a custom formatter. For the sake of simplicity, I will only be explaining how to use inbuild formatters. You can refer to <a target="_blank" href="https://github.com/kaisermann/svelte-i18n/blob/46b025ceebeb9bd68df0a2f30cc3c0775049ed85/docs/Formatting.md">this guide</a> on how to create custom formatters.</p>
<p>svelte-i18n provides <code>$time()</code>, <code>$date()</code> and <code>$number()</code> formatters that we can use to format data accordingly.</p>
<p>The following are the available Formats:</p>
<p><strong>Number:</strong></p>
<ul>
<li><p><code>currency</code>: <code>{ style: 'currency' }</code></p>
</li>
<li><p><code>percent</code>: <code>{ style: 'percent' }</code></p>
</li>
<li><p><code>scientific</code>: <code>{ notation: 'scientific' }</code></p>
</li>
<li><p><code>engineering</code>: <code>{ notation: 'engineering' }</code></p>
</li>
<li><p><code>compactLong</code>: <code>{ notation: 'compact', compactDisplay: 'long' }</code></p>
</li>
<li><p><code>compactShort</code>: <code>{ notation: 'compact', compactDisplay: 'short' }</code></p>
</li>
</ul>
<p><strong>Date:</strong></p>
<ul>
<li><p><code>short</code>: <code>{ month: 'numeric', day: 'numeric', year: '2-digit' }</code></p>
</li>
<li><p><code>medium</code>: <code>{ month: 'short', day: 'numeric', year: 'numeric' }</code></p>
</li>
<li><p><code>long</code>: <code>{ month: 'long', day: 'numeric', year: 'numeric' }</code></p>
</li>
<li><p><code>full</code>: <code>{ weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }</code></p>
</li>
</ul>
<p><strong>Time:</strong></p>
<ul>
<li><p><code>short</code>: <code>{ hour: 'numeric', minute: 'numeric' }</code></p>
</li>
<li><p><code>medium</code>: <code>{ hour: 'numeric', minute: 'numeric', second: 'numeric' }</code></p>
</li>
<li><p><code>long</code>: <code>{ hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short' }</code></p>
</li>
<li><p><code>full</code>: <code>{ hour: 'numeric', minute: 'numeric', second: 'numeric', timeZoneName: 'short' }</code></p>
</li>
</ul>
<p>Coming back to our example application, we can now format date, time and currency:</p>
<pre><code class="lang-svelte"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"ts"</span>&gt;</span><span class="javascript">
  <span class="hljs-keyword">import</span> </span></span><span class="javascript">{ time, date, number }</span><span class="xml"><span class="javascript"> <span class="hljs-keyword">from</span> <span class="hljs-string">'svelte-i18n'</span>;
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Time: </span><span class="javascript">{ $time(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(), { <span class="hljs-attr">hour</span>: <span class="hljs-string">"numeric"</span>, <span class="hljs-attr">minute</span>: <span class="hljs-string">"numeric"</span>, <span class="hljs-attr">second</span>: <span class="hljs-string">"numeric"</span> }) }</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Date: </span><span class="javascript">{ $date(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(), { <span class="hljs-attr">year</span>: <span class="hljs-string">"numeric"</span>, <span class="hljs-attr">month</span>: <span class="hljs-string">"long"</span>, <span class="hljs-attr">day</span>: <span class="hljs-string">"numeric"</span> }) }</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Currency: </span><span class="javascript">{ $number(<span class="hljs-number">2</span>, { <span class="hljs-attr">style</span>: <span class="hljs-string">"currency"</span>, <span class="hljs-attr">currency</span>: <span class="hljs-string">"INR"</span> }) }</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Number: </span><span class="javascript">{ $number(<span class="hljs-number">2</span>) }</span><span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
</code></pre>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Finally, we were able to localize our application using <em>svelte-i18n</em>. You can find the code in the <a target="_blank" href="https://github.com/aakash14goplani/sveltekit-with-sveltei18n">GitHub repo</a> and <a target="_blank" href="https://sveltekit-with-sveltei18n.vercel.app/">link</a> to the live demo.</p>
<p>This was the first article of <a target="_blank" href="https://blog.aakashgoplani.in/series/i18n-in-sveltekit">three-part series</a> to demonstrate i18n in SvelteKit. In the next article, I'll be explaining <a target="_blank" href="https://blog.aakashgoplani.in/internationalization-in-sveltekit-with-sveltekit-i18n">i18n in SvelteKit with <em>sveltekit-i18n</em> library</a>.</p>
<h3 id="heading-references">References</h3>
<ul>
<li><p><a target="_blank" href="https://github.com/kaisermann/svelte-i18n/blob/46b025ceebeb9bd68df0a2f30cc3c0775049ed85/docs/Methods.md">List of Methods</a> in svelte-i18n</p>
</li>
<li><p><a target="_blank" href="https://github.com/kaisermann/svelte-i18n/blob/46b025ceebeb9bd68df0a2f30cc3c0775049ed85/docs/CLI.md">CLI commands</a> reference</p>
</li>
<li><p><a target="_blank" href="https://github.com/kaisermann/svelte-i18n/blob/46b025ceebeb9bd68df0a2f30cc3c0775049ed85/docs/Formatting.md">Formatters</a></p>
</li>
</ul>
]]></content:encoded></item></channel></rss>