<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wild Animal</title>
    <description>The latest articles on DEV Community by Wild Animal (@wild_animal_96b2b14e183f8).</description>
    <link>https://dev.to/wild_animal_96b2b14e183f8</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1798691%2F5f6b2e45-3845-4c19-8f19-21e5c577e79e.png</url>
      <title>DEV Community: Wild Animal</title>
      <link>https://dev.to/wild_animal_96b2b14e183f8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wild_animal_96b2b14e183f8"/>
    <language>en</language>
    <item>
      <title>Understanding CDN Cache in NextJs</title>
      <dc:creator>Wild Animal</dc:creator>
      <pubDate>Thu, 05 Sep 2024 15:03:43 +0000</pubDate>
      <link>https://dev.to/wild_animal_96b2b14e183f8/understanding-cdn-cache-in-nextjs-3ind</link>
      <guid>https://dev.to/wild_animal_96b2b14e183f8/understanding-cdn-cache-in-nextjs-3ind</guid>
      <description>&lt;h2&gt;
  
  
  Understanding CDN Cache in Next.js
&lt;/h2&gt;

&lt;p&gt;Next.js has emerged as a popular framework for building fast, user-friendly web applications. The framework claims to automatically and proactively handle caching within applications. However, is this really the case?&lt;/p&gt;

&lt;p&gt;Recently, I encountered a challenge while developing my book review website, &lt;a href="https://refanswer.com" rel="noopener noreferrer"&gt;refanswer.com&lt;/a&gt;. To reduce the load on the server when accessing the Firestore database, I needed to implement caching between requests.&lt;/p&gt;

&lt;p&gt;Next.js indeed implements a caching mechanism for server-side request data. This can be achieved using the &lt;strong&gt;optimized fetch&lt;/strong&gt; method to access API URLs for data retrieval or by using the &lt;strong&gt;cache method provided by React&lt;/strong&gt; to wrap data-fetching methods from third-party libraries.&lt;/p&gt;

&lt;p&gt;You can find detailed instructions for the former &lt;a href="https://nextjs.org/docs/app/building-your-application/caching#data-cache" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and for the latter &lt;a href="https://nextjs.org/docs/app/building-your-application/caching#react-cache-function" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Revalidate at most every hour&lt;/span&gt;
&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/db&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These methods address the issue of repeatedly requesting the same data within a single request. For example, the &lt;code&gt;generateMetadata&lt;/code&gt; method and the server component might both use the same ID to fetch data from the database or API.&lt;/p&gt;

&lt;p&gt;However, the lifecycle of these caching methods is limited to individual requests. In other words, &lt;strong&gt;the cached data is only available within the same request and cannot be shared across different requests&lt;/strong&gt;. While this is useful and significantly reduces the number of requests between the server and the data source, I had a higher requirement: I wanted to share data across multiple user requests to the server.&lt;/p&gt;

&lt;p&gt;This concept is similar to that of a CDN (Content Delivery Network). However, the Next.js documentation does not emphasize this aspect. Given Vercel's reputation for providing edge network services, Vercel seems to be the default solution to this problem. Yet, without further configuration, Vercel will not proactively resolve these issues.&lt;/p&gt;

&lt;p&gt;To achieve this functionality, you need to configure the &lt;code&gt;Cache-Control&lt;/code&gt; headers in &lt;strong&gt;next.config.mjs&lt;/strong&gt;, &lt;strong&gt;vercel.json&lt;/strong&gt;, or &lt;strong&gt;middleware.ts&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Cache-Control Directives
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;max-age:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Specifies the maximum time a resource is considered fresh in both browser and shared caches. It has a lower priority compared to &lt;code&gt;s-maxage&lt;/code&gt; and is commonly used to control the overall caching duration for a resource across all caching layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;s-maxage:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Specifies the maximum time a resource is considered fresh in shared caches only. It takes precedence over &lt;code&gt;max-age&lt;/code&gt; for these caches and is typically used to extend or reduce the caching duration specifically for proxy servers to optimize content delivery.&lt;/p&gt;

&lt;p&gt;In summary, &lt;code&gt;max-age&lt;/code&gt; has a lower priority and is typically used to set cache durations for both browser and shared caches, while &lt;code&gt;s-maxage&lt;/code&gt; has a higher priority and is specifically used to set cache durations for shared caches in the network.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuring Cache-Control
&lt;/h3&gt;

&lt;p&gt;To configure cache-control, set the values of &lt;code&gt;max-age&lt;/code&gt; and &lt;code&gt;s-maxage&lt;/code&gt; according to your requirements. A typical configuration in middleware might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Set Cache-Control headers&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public, max-age=3600, s-maxage=7200, stale-while-revalidate=59&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;&lt;code&gt;stale-while-revalidate&lt;/code&gt;&lt;/strong&gt; directive specifies a time window during which the cache can continue to use a resource even if it has expired, while revalidating and updating the resource in the background. I won’t go into too much detail about it here.&lt;/p&gt;

&lt;p&gt;In addition to setting &lt;code&gt;Cache-Control&lt;/code&gt; headers in middleware, you can also set them in &lt;strong&gt;next.config.mjs&lt;/strong&gt; and &lt;strong&gt;vercel.json&lt;/strong&gt; (only valid for the Vercel network). For example:&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;next.config.mjs&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/(.*)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public, max-age=60, s-maxage=3600, stale-while-revalidate=60&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;vercel.json&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/about.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cache-Control"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"s-maxage=1, stale-while-revalidate=59"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more details, please visit: &lt;a href="https://vercel.com/docs/edge-network/caching" rel="noopener noreferrer"&gt;Vercel Caching Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Dynamic Routing
&lt;/h2&gt;

&lt;p&gt;In a dynamic route page, such as &lt;code&gt;app/[slug]/page.tsx&lt;/code&gt;, setting &lt;code&gt;Cache-Control&lt;/code&gt; headers alone is not sufficient. You must also simulate the dynamic route page as a static page. A simple way to achieve this is by using the following syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;force-static&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;revalidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the page a static page, allowing the &lt;code&gt;Cache-Control&lt;/code&gt; headers in the response to take effect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2561n79hfac4e5yme5a0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2561n79hfac4e5yme5a0.png" alt="Image description" width="789" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>cache</category>
      <category>vercel</category>
    </item>
    <item>
      <title>Integrate Google, Microsoft, Github multi-provider authentication in NextJS using Firebase Auth</title>
      <dc:creator>Wild Animal</dc:creator>
      <pubDate>Thu, 05 Sep 2024 11:38:09 +0000</pubDate>
      <link>https://dev.to/wild_animal_96b2b14e183f8/integrate-google-microsoft-github-multi-provider-authentication-in-nextjs-using-firebase-auth-279k</link>
      <guid>https://dev.to/wild_animal_96b2b14e183f8/integrate-google-microsoft-github-multi-provider-authentication-in-nextjs-using-firebase-auth-279k</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ih5spd59lleocp2vpnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ih5spd59lleocp2vpnp.png" alt="Image description" width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I started developing my book review blog site &lt;a href="https://refanswer.com" rel="noopener noreferrer"&gt;refanswer.com&lt;/a&gt; using NextJS 14 with Google Firebase a month ago. Now that the website is live, I will document the solutions to several tricky problems encountered during the website development process and share them with Medium netizens.&lt;/p&gt;

&lt;p&gt;This article mainly explains the problems encountered when NextJS 14 is connected to Google Authentication, and how I solved them.&lt;/p&gt;

&lt;p&gt;Let me first present the solution I chose so that readers can better understand the process.&lt;/p&gt;

&lt;p&gt;I used several main packages including &lt;strong&gt;firebase, firebase-admin, and iron-session&lt;/strong&gt; to implement client-side login, server-side authentication, and data sharing between the two.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client Side Configuration
&lt;/h2&gt;

&lt;p&gt;Create a project and a web application under the project in the Google Firebase Console, and obtain key information such as: &lt;strong&gt;apiKey, authDomain&lt;/strong&gt;, etc. (&lt;strong&gt;Please pay special attention to the authDomain parameter here, which will be very useful later&lt;/strong&gt;). Provide this information to the configuration items of the Firebase client SDK to initialize the application, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// lib/firebase.ts

import { initializeApp } from "firebase/app";
import { getAnalytics } from "firebase/analytics";
import { env } from "../../env";

const firebaseConfig = {
 authDomain:"projectId.firebaseapp.com"
 apiKey: "apiKey",
 projectId: "projectId",
 storageBucket: "projectId.appspot.com",
 messagingSenderId: "some_numbers",
 appId: "some_letters",
 measurementId: "some_letters",
};

export const firebaseApp = initializeApp(firebaseConfig);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These parameters initialize the client application to request the Google Firebase Auth service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Side Configuration
&lt;/h2&gt;

&lt;p&gt;The most important process of server-side configuration is to create a service account in the Google Firebase Console and obtain the account private key for the server to authorize requests to Firebase.&lt;/p&gt;

&lt;p&gt;The benefit of using a service account is that confidential data is placed on the server side for interaction, while reducing the complexity of Firebase security configuration.&lt;/p&gt;

&lt;p&gt;After creating a service account, you will get a json file containing a private key. This file can be copied to any non-public location in your project directory, and then introduced in the Firebase Admin configuration file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// lib/firebaseAdmin.ts

import admin from "firebase-admin";
import { getAuth } from "firebase-admin/auth";
import { getFirestore } from "firebase-admin/firestore";
interface FirebaseError extends Error {
 code: string;
}

if (!admin.apps.length) {
 admin.initializeApp({
 credential: admin.credential.cert(
 require("file_path_of_the_private_key.json")
 ),
 });
}

export const db = getFirestore();
export const auth = getAuth();
export const isFirebaseError = (error: any): error is FirebaseError =&amp;gt; {
 return error &amp;amp;&amp;amp; typeof error.code === "string";
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enable Firebase Auth
&lt;/h2&gt;

&lt;p&gt;Then, enable Firebase Authentication in the Google Firebase Console and enable the three providers Google, Microsoft, and Github in its login method.&lt;/p&gt;

&lt;p&gt;Each provider you enable requires you to enter the clientId and clientKey. Here is a brief description. If you have any questions, please leave a message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google Provider&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is easiest to enable Google as a provider. You just need to turn on the switch, check the associated web application, click the Save button, and the clientId and clientKey will be automatically generated.&lt;/p&gt;

&lt;p&gt;It is important to note that the authDomain you configured in lib/firebase.ts is the domain name of the Google Provider callback URL.&lt;/p&gt;

&lt;p&gt;Since I am not using the host provided by Google, but Vercel’s edge network service, I cannot use the default authDomain provided by Google.&lt;/p&gt;

&lt;p&gt;The default authDomain is refanswer-com.firebaseapp.com, and the corresponding callback URL is &lt;a href="https://refanswer-com.firebaseapp.com/__/auth/handler" rel="noopener noreferrer"&gt;https://refanswer-com.firebaseapp.com/__/auth/handler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I can only use the real domain name of my website, it is refanswer.com, and the callback address becomes &lt;a href="https://refanswer.com/__/auth/handler" rel="noopener noreferrer"&gt;https://refanswer.com/__/auth/handler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then, in the Auth settings page, add the authorized domains: refanswer.com&lt;/p&gt;

&lt;p&gt;To use your own domain as the callback URL’s domain, you must also implement the /__/auth/handler route to properly handle the data on callback.&lt;/p&gt;

&lt;p&gt;A simple way is to download the files directly from the Google server and save them in the public/&lt;strong&gt;/auth/ directory, and configure the routing in next.config.mjs so that the URL &lt;a href="https://refanswer.com/" rel="noopener noreferrer"&gt;https://refanswer.com/&lt;/a&gt;&lt;/strong&gt;/auth/handler can be correctly accessed to the public/__/auth/handler.js file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// public/__/auth/download.sh

wget https://projectId.firebaseapp.com/__/auth/handler -O handler
wget https://projectId.firebaseapp.com/__/auth/handler.js -O handler.js
wget https://projectId.firebaseapp.com/__/auth/experiments.js -O experiments.js
wget https://projectId.firebaseapp.com/__/auth/iframe -O iframe
wget https://projectId.firebaseapp.com/__/auth/iframe.js -O iframe.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure next.config.mjs file：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// next.config.mjs

// …
headers: async () =&amp;gt; {
 return [
      {
        source: "/__/auth/(handler|iframe)",
        headers: [
          {
            key: "Content-Type",
            value: "text/html; charset=utf-8",
          },
        ],
      },
   ];
 },
//…
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, you can use your own domain name to implement login callback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microsoft Provider&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the Microsoft provider, you need to log in to &lt;a href="https://entra.microsoft.com/" rel="noopener noreferrer"&gt;https://entra.microsoft.com/&lt;/a&gt; to create an application, provide a callback address with your own domain name, and get the clientId and clientKey, fill in the google firebase auth method page, and then enable it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Github Provider&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For Github providers, you need to log in to github.com and create an application in the developer settings, and provide the above callback address to obtain clientId and clientKey.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conflicts between multiple providers for one email
&lt;/h2&gt;

&lt;p&gt;The Google Firebase Auth console provides a user account association feature that specifies how to handle conflicts when a user logs in to your application from different providers using the same email address.&lt;/p&gt;

&lt;p&gt;The default option is to associate accounts that use the same email address. This has the advantage of keeping a one-to-one correspondence between the account and the email.&lt;/p&gt;

&lt;p&gt;The difficulty is that you have to automate this process in your application.&lt;/p&gt;

&lt;p&gt;The official solution is to catch errors during the user login process, obtain the provider previously used for the email from the error message, and then prompt the user whether to use the previous provider to log in to the application.&lt;/p&gt;

&lt;p&gt;The main implementation process is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// components/Auth.tsx

import {
  getAuth,
  signInWithRedirect,
  getRedirectResult,
  GoogleAuthProvider,
  GithubAuthProvider,
  OAuthProvider,
  AuthProvider,
  signInWithPopup,
} from "firebase/auth";

export default function Auth(){
// ...
  useEffect(() =&amp;gt; {
    if (searchParmas.has("redirected")) {
      setLoading(true);
      getRedirectResult(auth)
        .then((result) =&amp;gt; {
          if (!result) {
            console.log("No redirect result");
            setLoading(false);
            return;
          }
          // sign in to server
          auth.currentUser
            ?.getIdToken()
            .then((idToken) =&amp;gt; {
              if (!user) {
                setIsPending(true);
                postRequest&amp;lt;any&amp;gt;("/api/signInWithIdToken", { idToken })
                  .then((res) =&amp;gt; {
                    setUser(res.user);
                    setEmitter({ name: "close" });
                  })
                  .catch((err) =&amp;gt; {})
                  .finally(() =&amp;gt; setIsPending(false));
              }
            })
            .catch((error) =&amp;gt; {
              alert(error.message);
            });
        })
        .catch(async (error) =&amp;gt; {
          // Handle Errors here.
          const errorCode = error.code;
          const errorMessage = error.message;
          // The email of the user's account used.
          const customData = error.customData;
          // The AuthCredential type that was used.
          const currentCredential = OAuthProvider.credentialFromError(error);
          // Get sign-in methods for this email.
          const signInMethods = await fetchSignInMethodsForEmail(
            auth,
            error.customData.email
          );
          console.error(
            "errorCode:" + errorCode,
            "errorMessage:" + errorMessage,
            "customData:" + customData,
            "currentCredential:" + currentCredential,
            "signInMethods:" + signInMethods
          );
          // Found conflicts from errorCode
          if (errorCode === "auth/account-exists-with-different-credential") {
            if (signInMethods.length &amp;gt; 0) {
              const existingProvider = getProviderForSignInMethod(
                signInMethods[0]
              );
              confirmAlert({
                title: `Sign in with ${signInMethods[0]}?`,
                message: `You have already logged in from ${signInMethods[0]} using ${customData.email}. You can only use the method you have logged in using this email. Do you want to log in with ${signInMethods[0]}?`,
                closeOnClickOutside: true,
                buttons: [
                  {
                    label: "Continue",
                    onClick: () =&amp;gt; {
                      signInWithRedirect(
                        auth,
                        existingProvider as AuthProvider
                      );
                    },
                  },
                  {
                    label: "Cancel",
                    onClick: () =&amp;gt; {
                      console.log("Cancel");
                    },
                  },
                ],
              });
            } else {
              alert(errorMessage);
            }
          } else {
            alert(errorMessage);
          }
          setLoading(false);
        })
        .finally(() =&amp;gt; setLoading(false));
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [searchParmas]);
// ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a very important detail here, you must uncheck the “Email enumeration protection (recommended)” checkbox in user actions on the Firebase Auth settings page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhfwq8nj86pnhb4bko95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhfwq8nj86pnhb4bko95.png" alt="Image description" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Otherwise, the value of signInMethods is empty and you will not be able to get the previous provider.&lt;/p&gt;

&lt;p&gt;This article ends here. If you have any questions, please leave a message.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>firebase</category>
      <category>google</category>
      <category>microsoft</category>
    </item>
  </channel>
</rss>
