<?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: Jyotirmaya Sahu</title>
    <description>The latest articles on DEV Community by Jyotirmaya Sahu (@iamdoctorj).</description>
    <link>https://dev.to/iamdoctorj</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%2F580109%2F87d60133-7264-41d6-bc1c-c27bcd185cc9.jpg</url>
      <title>DEV Community: Jyotirmaya Sahu</title>
      <link>https://dev.to/iamdoctorj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iamdoctorj"/>
    <language>en</language>
    <item>
      <title>Mastering SignalR Hub Security with Custom Token Authentication in .NET 8</title>
      <dc:creator>Jyotirmaya Sahu</dc:creator>
      <pubDate>Tue, 10 Dec 2024 12:01:23 +0000</pubDate>
      <link>https://dev.to/iamdoctorj/mastering-signalr-hub-security-with-custom-token-authentication-in-net-8-1boc</link>
      <guid>https://dev.to/iamdoctorj/mastering-signalr-hub-security-with-custom-token-authentication-in-net-8-1boc</guid>
      <description>&lt;p&gt;Recently I was working on a web development project that required me to transfer data using WebSockets to implement real-time communication. It was a React.js project with a .NET backend.&lt;/p&gt;

&lt;p&gt;While MSDN provides excellent top-level documentation, it often lacks the low-level details needed for advanced use cases.&lt;/p&gt;

&lt;p&gt;One such scenario is authenticating a SignalR Hub using a custom token. Yes, a custom token, not a JWT or the default Bearer token. This article explores how to achieve this. By the end, you will have a SignalR Hub that requires authentication and uses a custom token.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Custom Token
&lt;/h3&gt;

&lt;p&gt;The custom token we will use is a Base64-encoded delimited string of user information in the format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userId:userName
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this token, we will extract the &lt;code&gt;userId&lt;/code&gt; and &lt;code&gt;userName&lt;/code&gt; to create claims.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Setup
&lt;/h3&gt;

&lt;p&gt;Here are the basic steps to set up the project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a .NET project&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new webapi
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Add SignalR service&lt;/strong&gt;:&lt;br&gt;
In the &lt;code&gt;Program.cs&lt;/code&gt; file, register the SignalR service while building the application:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSignalR&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a Hub&lt;/strong&gt;:&lt;br&gt;
Create a directory named &lt;code&gt;hubs&lt;/code&gt; and add a file named &lt;code&gt;GameHub.cs&lt;/code&gt;. Implement the following:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GameHub&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hub&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnConnectedAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnConnectedAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnDisconnectedAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnDisconnectedAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exception&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;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Map the Hub&lt;/strong&gt;:&lt;br&gt;
Expose the &lt;code&gt;GameHub&lt;/code&gt; as an endpoint in &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapHub&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GameHub&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hubs/game"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implementing Custom Token Authentication
&lt;/h3&gt;

&lt;p&gt;To use a custom token and extract user information from it, we need a custom authentication scheme. In .NET, an authentication scheme is a named identifier that specifies a method or protocol used to authenticate users, like cookies, JWT bearer tokens, or Windows authentication. For this scenario, we’ll create a scheme called &lt;code&gt;CustomToken&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Authentication Scheme Implementation
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Define the Custom Token Scheme Options&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomTokenSchemeOptions&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AuthenticationSchemeOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CustomTokenSchemeOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;Events&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CustomTokenEvents&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CustomTokenEvents&lt;/span&gt; &lt;span class="n"&gt;Events&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomTokenEvents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;!;&lt;/span&gt;
       &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&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;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Define the Scheme Handler&lt;/strong&gt;:&lt;br&gt;
The &lt;code&gt;CustomTokenSchemeHandler&lt;/code&gt; contains the logic to validate tokens and extract user claims:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomTokenSchemeHandler&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AuthenticationHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomTokenSchemeOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CustomTokenEvents&lt;/span&gt; &lt;span class="n"&gt;Events&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomTokenEvents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;!;&lt;/span&gt;

   &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CustomTokenSchemeHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="n"&gt;IOptionsMonitor&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomTokenSchemeOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;ILoggerFactory&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;UrlEncoder&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

   &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AuthenticateResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;HandleAuthenticateAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;messageReceivedContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MessageReceivedContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MessageReceivedAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageReceivedContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageReceivedContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="nf"&gt;GetTokenFromQuery&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AuthenticateResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NoResult&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromBase64String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;decodedString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;userInfoArray&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;decodedString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

       &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userInfoArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
           &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userInfoArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
       &lt;span class="p"&gt;};&lt;/span&gt;
       &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ClaimsPrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ClaimsIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
       &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ticket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AuthenticationTicket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;principal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AuthenticateResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticket&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;GetTokenFromQuery&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;accessToken&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;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configure the Authentication Scheme&lt;/strong&gt;:&lt;br&gt;
Register the custom authentication scheme in &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CustomToken"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScheme&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomTokenSchemeOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CustomTokenSchemeHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"CustomToken"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CustomTokenEvents&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;OnMessageReceived&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
           &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"access_token"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
               &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

               &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StartsWithSegments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hubs/game"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
               &lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
               &lt;span class="p"&gt;}&lt;/span&gt;

               &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&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;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Disclaimer
&lt;/h3&gt;

&lt;p&gt;The implementation presented in this article is inspired by the BearerTokenScheme source code in the .NET official repository. Adjustments were made to suit the custom token requirements for this scenario.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping Up
&lt;/h3&gt;

&lt;p&gt;By implementing this custom token authentication scheme, you can secure your SignalR hubs and tailor the authentication process to your application's unique requirements. This approach allows for fine-grained control over token validation and claim extraction, ensuring a secure and robust real-time communication system.&lt;/p&gt;

&lt;p&gt;Feel free to extend this implementation with additional validations, logging, or integrations with external identity providers for a more comprehensive solution.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>authentication</category>
      <category>signalr</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Lets make a custom hook for client side Pagination in React</title>
      <dc:creator>Jyotirmaya Sahu</dc:creator>
      <pubDate>Mon, 06 Sep 2021 12:48:49 +0000</pubDate>
      <link>https://dev.to/iamdoctorj/lets-make-a-custom-hook-for-client-side-pagination-in-react-2ip9</link>
      <guid>https://dev.to/iamdoctorj/lets-make-a-custom-hook-for-client-side-pagination-in-react-2ip9</guid>
      <description>&lt;p&gt;Recently while making an application in React, I had to use pagination in one of my lists. So, I decided to keep it simple and came up with an idea of a custom hook.&lt;/p&gt;

&lt;p&gt;The custom hook will return a function, to which we can then pass the page number and it will return the first index and last index of the list along with the total number of pages. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPageNumber&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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;paginator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePagination&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="c1"&gt;//pageSize is the number of items we want to display per page.&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;firstIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;totalPages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;paginator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can use this information to slice up our list and implement pagination, we can store the page number in our state and update state based on user interaction.&lt;/p&gt;

&lt;p&gt;Now, let's jump into the code that works behind this. The code is fairly simple. In the following snippet, &lt;code&gt;numberOfRecords&lt;/code&gt; is the total number of items that the list has, and &lt;code&gt;recordsPerPage&lt;/code&gt; is the count of items we want to show per page.&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;usePagination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numberOfRecords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we calculate the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;noOfWholePages&lt;/code&gt; (Number of pages containing items equals to &lt;code&gt;recordsPerPage&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isLastPagePartial&lt;/code&gt; (This boolean variable tells if the last page is filled or has less items than &lt;code&gt;recordsPerPage&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;noOfRecordsInLastPage&lt;/code&gt; (This stores how many items are there in the last page. This will be 0 if &lt;code&gt;isLastPagePartial&lt;/code&gt; is false)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;noOfPages&lt;/code&gt; (The total number of pages)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;noOfWholePages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numberOfRecords&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&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;isLastPagePartial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;numberOfRecords&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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;noOfRecordsInLastPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;numberOfRecords&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&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;noOfPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;noOfWholePages&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLastPagePartial&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we need the paginator function to return from the hook. We will use the concept of javascript closures here. So, nothing can be changed in the function other than pageNumber.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recordsBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&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;firstIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firstIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;noOfPages&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;pageNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Last page&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLastPagePartial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;lastIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firstIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;noOfRecordsInLastPage&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;firstIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;noOfPages&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;Finally, we return this function from the hook.&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;return&lt;/span&gt; &lt;span class="nx"&gt;recordsBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's have a look at the complete code here.&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;usePagination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numberOfRecords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&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;noOfWholePages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numberOfRecords&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&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;isLastPagePartial&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;numberOfRecords&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&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;noOfRecordsInLastPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;numberOfRecords&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&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;noOfPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;noOfWholePages&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLastPagePartial&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;recordsBuilder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&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;firstIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firstIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;recordsPerPage&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;noOfPages&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;pageNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Last page&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLastPagePartial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;lastIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firstIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;noOfRecordsInLastPage&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;firstIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;noOfPages&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;recordsBuilder&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;I hope this post is helpful. Thank you and stay safe.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What I gained by completing 'JavaScript Algorithms and Data Structures' in freeCodeCamp.org</title>
      <dc:creator>Jyotirmaya Sahu</dc:creator>
      <pubDate>Sun, 16 May 2021 17:33:59 +0000</pubDate>
      <link>https://dev.to/iamdoctorj/what-i-gained-by-completing-javascript-algorithms-and-data-structures-in-freecodecamp-org-5boe</link>
      <guid>https://dev.to/iamdoctorj/what-i-gained-by-completing-javascript-algorithms-and-data-structures-in-freecodecamp-org-5boe</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is my personal opinion and I don't force anything on you. &lt;br&gt;
So be calm and keep learning.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From the last few days, I have been obsessed with completing lessons in freecodecamp.org. Well, after my first certification of 'Responsive Web design', I wanted the next one which is 'JavaScript Algorithms and Data Structures'. &lt;/p&gt;

&lt;h3&gt;
  
  
  So what did I gain ?
&lt;/h3&gt;

&lt;p&gt;Certainly, it was no loss to me. Practicing is never a loss. But here, it helped me with my weak points in javascript which otherwise I would never would have discovered.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing Regex&lt;/li&gt;
&lt;li&gt;constructor functions&lt;/li&gt;
&lt;li&gt;Prototypes&lt;/li&gt;
&lt;li&gt;Reduce function of Arrays&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And a lot more which would certainly help in my career.&lt;/p&gt;

&lt;h3&gt;
  
  
  And what did I lose ?
&lt;/h3&gt;

&lt;p&gt;Almost nothing, apart from a little amount of time which I would call an investment rather than a loss.&lt;/p&gt;

&lt;h3&gt;
  
  
  My suggestion
&lt;/h3&gt;

&lt;p&gt;It's obvious. My suggestion is to check freecodecamp.org and similar sites, especially if you are a beginner and never been there. Even if you are not a beginner, who knows, you might just stumble upon something new and that might help in your career. &lt;br&gt;
Its never bad to discover things.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>career</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>How to prevent exceptions when using google_sign_in in flutter</title>
      <dc:creator>Jyotirmaya Sahu</dc:creator>
      <pubDate>Sun, 18 Apr 2021 05:58:57 +0000</pubDate>
      <link>https://dev.to/iamdoctorj/how-to-prevent-exceptions-when-using-googlesignin-in-flutter-13l2</link>
      <guid>https://dev.to/iamdoctorj/how-to-prevent-exceptions-when-using-googlesignin-in-flutter-13l2</guid>
      <description>&lt;p&gt;I was building an app which requires me to link user's google drive in my application to store data. But doing that requires fetching the access token from google_sign_in.&lt;/p&gt;

&lt;p&gt;Now, although it is not a very difficult task, many people like me struggle with the exception&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 10: , null, null)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, this is something I banged my head for. Now, lets get to the point. How to avoid this ? The answer is really simple, by using firebase. But wait, not the google login provided by firebase, but only a firebase project with your android application added in it (Don't forget to add the SHA1 certificate though).&lt;/p&gt;

&lt;p&gt;Google cloud console and firebase console share projects, so a project created in firebase can be accessed in the cloud console as well. Now, let's see this through screenshots.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. First we go to the firebase console and click on create a new project.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;2. Then we fill in the name and all other pages and create the project.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;3. Wait for the project to get created.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;4. Click on add android application button.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;5. Fill in the package name, nickname and SHA1 certificate. The SHA1 can be fetched by running the following command in the android folder &lt;code&gt;./gradlew signingReport&lt;/code&gt; or &lt;code&gt;gradlew signingReport&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;6. Download the &lt;strong&gt;google-services.json&lt;/strong&gt; file and place it inside your android/app directory and complete the process.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;7. Don't forget to add the following to the respective files.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;8. Now, lets go the google cloud console, and we can find our newly created firebase project there with all necessary things already added.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Now, here we can configure the OAuth consent screen, and add scopes. This can be used now and login will work smoothly. We can edit this project and add web applications as well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
Don't reveal your SHA1 and secrets to anyone or ever push them to a public remote repository. They can be used maliciously.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>flutter</category>
      <category>googlecloud</category>
      <category>android</category>
      <category>firebase</category>
    </item>
    <item>
      <title>Is Native development is really better than cross-platform frameworks like flutter &amp; React Native?</title>
      <dc:creator>Jyotirmaya Sahu</dc:creator>
      <pubDate>Tue, 09 Mar 2021 08:28:46 +0000</pubDate>
      <link>https://dev.to/iamdoctorj/is-native-development-is-really-better-than-cross-platform-frameworks-like-flutter-react-native-266k</link>
      <guid>https://dev.to/iamdoctorj/is-native-development-is-really-better-than-cross-platform-frameworks-like-flutter-react-native-266k</guid>
      <description>&lt;p&gt;Today, I came accross a post in Medium where the author has explained why native development is better than cross-platform frameworks like flutter and react native, although they really help to increase productivity. &lt;/p&gt;

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

&lt;p&gt;I would like to know what everyone thinks, and what should be choosen or preferred in certain cases.&lt;/p&gt;

&lt;p&gt;Kindly provide your views on the topic.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>flutter</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>Copying array of Objects is not that straightforward</title>
      <dc:creator>Jyotirmaya Sahu</dc:creator>
      <pubDate>Sun, 28 Feb 2021 07:34:06 +0000</pubDate>
      <link>https://dev.to/iamdoctorj/copying-array-of-objects-is-not-that-straightforward-27j9</link>
      <guid>https://dev.to/iamdoctorj/copying-array-of-objects-is-not-that-straightforward-27j9</guid>
      <description>&lt;p&gt;So, you want to copy an array to create another new array, and not just copying the array reference to another variable. In C#, there are several ways to achieve this. &lt;br&gt;
Some of the ways are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Array.CopyTo()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Array.ConstrainedCopy()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Array.Clone()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These ways provide you with a new array that is different from the original array and not just two variables with the same reference.&lt;/p&gt;

&lt;p&gt;But, any object when created, is created in the heap and it's address is assigned to the reference variable. &lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;code&gt;Student s1 = new Student()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here, &lt;code&gt;new Student()&lt;/code&gt; is the creation of an Student object in the heap, and &lt;code&gt;s1&lt;/code&gt; holds its address.&lt;/p&gt;

&lt;p&gt;Same happens in an array. An array of objects or any complex type consists of reference variables holding the addresses of the actual objects present in the heap.&lt;/p&gt;

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

&lt;p&gt;As, we can see from the image, we can now understand that when we copy a similar array, we end up with a new array but with the elements holding addresses, or references to the same objects.&lt;/p&gt;

&lt;p&gt;Now, if we want a new set of objects with the same data, we may need to recreate the objects linearly. We can use the &lt;code&gt;Array.ConvertAll()&lt;/code&gt; method for that purpose.&lt;/p&gt;

&lt;p&gt;Refer to &lt;a href="https://www.telerik.com/blogs/copying-and-cloning-arrays-in-c" rel="noopener noreferrer"&gt;this&lt;/a&gt; article by &lt;strong&gt;Peter Mbanugo&lt;/strong&gt; for more details on the methods mentioned in this article.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This only applies to array of complex types. Array of primitive types will contain values as opposed to complex types which have references. So copying an array of primitive types will have no issues to deal with.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>csharp</category>
      <category>oop</category>
      <category>beginners</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>A Generic table in React with MaterialUI</title>
      <dc:creator>Jyotirmaya Sahu</dc:creator>
      <pubDate>Sat, 27 Feb 2021 16:24:26 +0000</pubDate>
      <link>https://dev.to/iamdoctorj/a-generic-table-in-react-with-materialui-3f26</link>
      <guid>https://dev.to/iamdoctorj/a-generic-table-in-react-with-materialui-3f26</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;&lt;br&gt;
This component is built upon an example on the MaterialUI components section, and hence some part of code may be same. This is just for someone who wants to save some time without trying to figure out all that is done in there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;We as react developers must have heard about MaterialUI. It's a fantastic UI library for react (not sure about react native) based on Google's material design. &lt;/p&gt;

&lt;p&gt;Now, for dealing with visualizing data in tabular form, it provides a &lt;strong&gt;Table&lt;/strong&gt; component. But configuring it to your needs may look like a pain if you are in a hurry.&lt;/p&gt;

&lt;h1&gt;
  
  
  A Generic Table component
&lt;/h1&gt;

&lt;p&gt;Here I have a created a DataTable component on the top of the &lt;strong&gt;Table&lt;/strong&gt; component. &lt;/p&gt;

&lt;p&gt;DataTable has the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can infer column names if directly array of rows are provided.&lt;/li&gt;
&lt;li&gt;Can handle sorting at single column level.&lt;/li&gt;
&lt;li&gt;Supports pagination.&lt;/li&gt;
&lt;li&gt;Supports custom elements or components inside a cell.&lt;/li&gt;
&lt;li&gt;Most importantly, we can always customize to our needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As, this is a plain React component, you can find the source below, and can improve upon it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Usage
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Basic usage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;DataTable
    columnData={[
        {
            id: 'name',
            name: 'Name',
            enableSort: true,
            align: "center"
        },
        {
            id: 'desc',
            name: 'Description',
            enableSort: true,
            align: "center"
        }
    ]}
    rows={[
       { name: 'First', desc: 'First Item' },
       { name: 'Second', desc: 'Second Item' }
    ]}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The DataTable component takes two parameters, &lt;strong&gt;&lt;em&gt;columnData&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;rows&lt;/em&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;em&gt;columnData&lt;/em&gt;&lt;/strong&gt; is an array of configuration values for columns, &lt;em&gt;id&lt;/em&gt;, &lt;em&gt;name&lt;/em&gt;, enableSort_, &lt;em&gt;align&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;em&gt;rows&lt;/em&gt;&lt;/strong&gt; is the array of objects or we can say it is our data to be displayed in the Table body.&lt;/p&gt;

&lt;p&gt;The pagination is same as an example on the MaterialUI components page.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is open to improvement in discussions and feedback.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Locking scroll of multiple containers simultaneously</title>
      <dc:creator>Jyotirmaya Sahu</dc:creator>
      <pubDate>Tue, 16 Feb 2021 13:19:37 +0000</pubDate>
      <link>https://dev.to/iamdoctorj/locking-scroll-of-multiple-containers-simultaneously-2jcl</link>
      <guid>https://dev.to/iamdoctorj/locking-scroll-of-multiple-containers-simultaneously-2jcl</guid>
      <description>&lt;p&gt;Hi Friends! Every one of us must have seen the commit comparer of Github or some other source control providers. There, we have two containers scrolling simultaneously so that we can have a look at &lt;br&gt;
the same place of both the files at the same time.&lt;/p&gt;

&lt;p&gt;I am talking of something like this...&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgnu3ck8w7vwfqg3blav.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmgnu3ck8w7vwfqg3blav.gif" alt="Notepad++ Code Compare" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Achieving this in the web is quite simple, with &lt;strong&gt;&lt;em&gt;JavaScript&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;Css&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I have made an example but it's horizontal. Yes! It seems quite odd but the main thing I wanna showcase here is the scrolling.&lt;/p&gt;

&lt;p&gt;Here, we can assign a &lt;strong&gt;scroll&lt;/strong&gt; event listener to the scroll sections or containers and access the &lt;em&gt;scrollLeft&lt;/em&gt; property of the scroll target.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f.addEventListener('scroll', (e) =&amp;gt; {
    console.log(e.target.scrollLeft);
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we assign the &lt;em&gt;scrollLeft&lt;/em&gt; value to our second container's &lt;em&gt;scrollLeft&lt;/em&gt;, and we have a working demo. And yes, we can do the same for the first container as well so that scrolling the second container will also scroll the first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f.addEventListener('scroll', (e) =&amp;gt; {
    s.scrollLeft = e.target.scrollLeft;
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But, now we have an issue. We can surely notice the stuttering of the scroll-bar. It's not smooth anymore. This is because setting the &lt;em&gt;scrollLeft&lt;/em&gt; value of any container will trigger the &lt;strong&gt;scroll&lt;/strong&gt; event on it.&lt;/p&gt;

&lt;p&gt;To tackle this, we utilize debouncing. We maintain two variables, &lt;em&gt;isFirstScrolling&lt;/em&gt; and &lt;em&gt;isSecondScrolling&lt;/em&gt;. We use this to track whether the other one is scrolling, and we can update the scroll position of that container. If it is scrolling, we should not update the scroll value, this causes the stuttering.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f.addEventListener('scroll', (e) =&amp;gt; {
  if(!isSecondScrolling) {
    isFirstScrolling = true;
    customDebounce("first");
    s.scrollLeft = e.target.scrollLeft;
  }
});

s.addEventListener('scroll', (e) =&amp;gt; {
  if(!isFirstScrolling) {
    isSecondScrolling = true;
    customDebounce("second");
    f.scrollLeft = e.target.scrollLeft;
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;&lt;em&gt;customDebounce&lt;/em&gt;&lt;/strong&gt; function helps to set the scroll trackers to &lt;em&gt;false&lt;/em&gt; once scrolling is over.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let timeOut;

const customDebounce = (tracker) =&amp;gt; {
  console.log(timeOut);
  clearTimeout(timeOut);
  console.log("cleared",timeOut);
  timeOut = setTimeout(() =&amp;gt; {
    if(tracker === "first")
      isFirstScrolling = !isFirstScrolling;
    else
      isSecondScrolling = !isSecondScrolling;
  }, 700);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, with this, we have our working example.&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/jyotirmayasahu38/embed/MWwqaVJ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Kindly check out the code and give valuable feedback to improve my further posts.&lt;/p&gt;

&lt;p&gt;Thank You. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>html</category>
      <category>css</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A simple control panel for application admins to manage users and privileges using Firebase </title>
      <dc:creator>Jyotirmaya Sahu</dc:creator>
      <pubDate>Sun, 31 Jan 2021 13:21:51 +0000</pubDate>
      <link>https://dev.to/iamdoctorj/a-simple-control-panel-for-application-admins-to-manage-users-and-privileges-using-firebase-3a43</link>
      <guid>https://dev.to/iamdoctorj/a-simple-control-panel-for-application-admins-to-manage-users-and-privileges-using-firebase-3a43</guid>
      <description>&lt;h3&gt;
  
  
  A simple control panel for application admins to manage users and privileges using Firebase.
&lt;/h3&gt;

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

&lt;p&gt;Firebase admin SDK is a server side SDK that allows us to interact with our Firebase project with admin privileges and perform certain actions to monitor and manager our project in our own way without using the Firebase console.&lt;/p&gt;

&lt;p&gt;We are going to create a web application through which we will try to perform the actions that the Firebase admin SDK provides us with.&lt;/p&gt;

&lt;p&gt;For that purpose we will need a front-end application which will act as the control panel and a back-end where we will integrate the admin SDK.&lt;/p&gt;

&lt;p&gt;We will walk-through the front-end in another Part.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This project is made using Ubuntu 20.04 OS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Pre-requisites
&lt;/h3&gt;

&lt;p&gt;Create a firebase project and turn on Authentication — email &amp;amp; password authentication, and Realtime Database.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="http://console.firebase.com" rel="noopener noreferrer"&gt;console.firebase.com&lt;/a&gt; to create a project and configure as above.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Part 1 — Making the back-end&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We will be using &lt;strong&gt;node-js&lt;/strong&gt;  &lt;strong&gt;as the back-end&lt;/strong&gt; for making a rest API that our front-end application will consume. We will be using &lt;strong&gt;Typescript&lt;/strong&gt; as it provides us a better and error free way to write &lt;strong&gt;Javascript&lt;/strong&gt; code.&lt;/p&gt;

&lt;p&gt;In the first step we are going to set up a node project to use Typescript. We will use express for making the rest API.&lt;/p&gt;

&lt;p&gt;After creating a new folder and opening the terminal, let’s run the commands to create the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Okay, now we have a &lt;strong&gt;&lt;em&gt;package.json&lt;/em&gt;&lt;/strong&gt; file. Let’s install the required dependencies for our project.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install express cors dotenv firebase-admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Also &lt;strong&gt;typescript&lt;/strong&gt; , &lt;strong&gt;tslint&lt;/strong&gt; and the type declarations for &lt;strong&gt;cors&lt;/strong&gt; and &lt;strong&gt;express&lt;/strong&gt; as &lt;strong&gt;dev-dependencies&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install typescript tslint @types/express @types/cors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now let’s make a few changes to the &lt;strong&gt;&lt;em&gt;package.json&lt;/em&gt;&lt;/strong&gt; to really integrate typescript in our build process. We will add a “start” key to the scripts object as follows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;“start”: “tsc &amp;amp;&amp;amp; node dist/index.js”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With this we are making sure that we are running the &lt;strong&gt;Typescript compiler&lt;/strong&gt; &lt;strong&gt;&lt;em&gt;(or tsc)&lt;/em&gt;&lt;/strong&gt; to transpile all the  &lt;strong&gt;.ts&lt;/strong&gt; files before running the application. We will modify the  &lt;strong&gt;&lt;em&gt;.tsconfig&lt;/em&gt;&lt;/strong&gt; file to mention the &lt;strong&gt;&lt;em&gt;dist&lt;/em&gt;&lt;/strong&gt; directory as the output directory for the typescript compiler later in the article.&lt;/p&gt;

&lt;p&gt;Let’s mention the &lt;strong&gt;“dist/index.js”&lt;/strong&gt; as the value of the &lt;strong&gt;main&lt;/strong&gt; property as this file will be the entry point for our application.&lt;/p&gt;

&lt;p&gt;With these changes, the &lt;strong&gt;&lt;em&gt;package.json&lt;/em&gt;&lt;/strong&gt; file should look similar to this.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Now, let’s add a &lt;strong&gt;&lt;em&gt;tsconfig.json&lt;/em&gt;&lt;/strong&gt; file to the project root with the following values. This file is a configuration file for typescript specific to that project. Here we mention the &lt;strong&gt;&lt;em&gt;“outDir” as “dist”&lt;/em&gt;&lt;/strong&gt; which makes sure that &lt;strong&gt;tsc&lt;/strong&gt; uses the dist directory as the output directory for the transpiled files.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now to configure Typescript linting for the project, in a terminal running in the root of the project, run the following command to generate &lt;strong&gt;&lt;em&gt;tslint.json&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./node_modules/.bin/tslint --init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Open the generated &lt;strong&gt;&lt;em&gt;tslint.json&lt;/em&gt;&lt;/strong&gt; file and the &lt;strong&gt;no-console&lt;/strong&gt; rule accordingly.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Now lets start configuring firebase-admin sdk in our project. For initializing the firebase-admin sdk we need to configure a service account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://firebase.google.com/docs/admin/setup#set-up-project-and-service-account" rel="noopener noreferrer"&gt;Follow this guide&lt;/a&gt; to configure service account, download the key and rename it as &lt;strong&gt;&lt;em&gt;service-key.json&lt;/em&gt;&lt;/strong&gt;. Place this file in the root of the project directory.&lt;/p&gt;

&lt;p&gt;This file should not be pushed to any remote location where it is subject to the risk of getting exposed. This file should be added to the  &lt;strong&gt;.gitignore&lt;/strong&gt; file in case of git.&lt;/p&gt;

&lt;p&gt;In this project we are using &lt;strong&gt;dotenv&lt;/strong&gt; to simplify the task of setting and using environment variables in multiple environments. So, we will create a  &lt;strong&gt;.env&lt;/strong&gt; file at the project root to where we can define the several values that we require as environment variables.&lt;/p&gt;

&lt;p&gt;Create a  &lt;strong&gt;.env&lt;/strong&gt; file and paste the following values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GOOGLE_APPLICATION_CREDENTIALS=service-key.json

DB_URL=&amp;lt;Your DB URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Find your DB URL in the firebase console on top of your realtime database.&lt;/p&gt;

&lt;p&gt;Now, we will create a directory structure as shown:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;src&lt;br&gt;&lt;br&gt;
 — models&lt;br&gt;&lt;br&gt;
—modules&lt;br&gt;&lt;br&gt;
 — — admin&lt;br&gt;&lt;br&gt;
 — — auth&lt;br&gt;&lt;br&gt;
 — — users&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create an &lt;strong&gt;&lt;em&gt;index.ts&lt;/em&gt;&lt;/strong&gt; file under the &lt;strong&gt;src&lt;/strong&gt; directory.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;&lt;em&gt;index.ts&lt;/em&gt;&lt;/strong&gt; file, let’s import the required modules.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import express from 'express';
import * as admin from 'firebase-admin';
import * as dotenv from 'dotenv';
import cors from 'cors';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, before we initialize the admin sdk, we need to configure &lt;strong&gt;dotenv&lt;/strong&gt; , in order to inject the values mentioned in the  &lt;strong&gt;.env&lt;/strong&gt; file as the environment variables.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const dotenvKey = dotenv.config();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here you can remove the constant assignment as we are not going to use the &lt;strong&gt;dotenvKey&lt;/strong&gt; constant in the project.&lt;/p&gt;

&lt;p&gt;Now, to initialize the firebase-admin sdk.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;admin.initializeApp({    
    credential: admin.credential.applicationDefault(),    
    databaseURL: process.env.DB_URL
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, firebase-admin will use the environment variable mapped by us in the  &lt;strong&gt;.env&lt;/strong&gt; file to access the &lt;strong&gt;&lt;em&gt;service-key.json&lt;/em&gt;&lt;/strong&gt; file. Also, we provide the &lt;strong&gt;databaseURL&lt;/strong&gt; as our application will access the &lt;strong&gt;realtime database&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now, let’s add the boilerplate code to create an &lt;strong&gt;express&lt;/strong&gt;  app.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**const** port = process.env.PORT || 8080;
**const** app = _express_(); 
app._use_(cors({origin: true}));
app._use_(express._json_());
app._use_(express._urlencoded_({extended: false}));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We use &lt;em&gt;cors&lt;/em&gt; to avoid the &lt;strong&gt;CORS&lt;/strong&gt; error while consuming endpoints from a browser.&lt;/p&gt;

&lt;p&gt;A great thing about Typescript is that it allows the use of Interfaces, which we can use in this case to define a schema for our user model. So, under the models directory, we will create a file named &lt;strong&gt;&lt;em&gt;UserModel.ts&lt;/em&gt;&lt;/strong&gt; , with the content as:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export interface User {
    uid: string,
    email: string | undefined,
    displayName: string | undefined,
    lastSignInTime: string,
    creationTime: string,
    emailVerified: boolean,
    admin: boolean
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Before proceeding with creating the routes, we need to secure the endpoints to prevent unauthorized access. For this purpose, we will create and use two middlewares, one for Authentication and another one for Authorization, i.e, to ensure whether the user requesting the operation has sufficient privileges.&lt;/p&gt;

&lt;p&gt;Let’s create two files, &lt;strong&gt;&lt;em&gt;authenticate.middleware.ts&lt;/em&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;em&gt;authorize.middleware.ts&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;We will use &lt;strong&gt;authenticate&lt;/strong&gt; to check if user has a valid idToken, and the &lt;strong&gt;authorize&lt;/strong&gt; to check if the user has the required privileges.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;strong&gt;authenticate&lt;/strong&gt; filter requires an &lt;strong&gt;Authorization&lt;/strong&gt; header with the value in the following form:- &lt;strong&gt;Bearer &lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;strong&gt;idToken&lt;/strong&gt; can be retrieved by signing-in to firebase using the &lt;a href="https://firebase.google.com/docs/reference/rest/auth" rel="noopener noreferrer"&gt;REST API for firebase Authentication&lt;/a&gt; for the moment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Proceeding further, we need to create a &lt;strong&gt;&lt;em&gt;UsersController.ts&lt;/em&gt;&lt;/strong&gt; file under &lt;em&gt;modules →users&lt;/em&gt; that will contain the methods that our routes for &lt;strong&gt;“/users”&lt;/strong&gt; path will utilize.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here, &lt;strong&gt;“//RA”&lt;/strong&gt; means the particular function requires admin privileges to perform its functionality.&lt;/p&gt;

&lt;p&gt;This name of the methods define its functionality in this case.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;getAllUsers()&lt;/strong&gt; &lt;em&gt;→Returns a list of all the users.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;getUser()&lt;/strong&gt; &lt;em&gt;→Accepts an uid and returns the particular user.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;updateUser()&lt;/strong&gt; &lt;em&gt;→Here we can perform many task, but in the current scope we will only set the displayName property of the user. You can play around with the function and find out all the fields that a user can have in firebase authentication.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;createUser()&lt;/strong&gt; &lt;em&gt;→Creates an user.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;createAdminUser()&lt;/strong&gt; &lt;em&gt;→Creates an user but with Admin privileges.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;deleteUser()&lt;/strong&gt; &lt;em&gt;→Deletes the user whose uid is provided.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;deleteUsers()&lt;/strong&gt; &lt;em&gt;→Deletes multiple users, uid array is fetched from body.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;forceSignOut()&lt;/strong&gt; &lt;em&gt;→Forcibly revoke a users refresh token, so that their login can’t continue.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;mapUserFromUserRecord()&lt;/strong&gt; &lt;em&gt;→An utility function, private to the file, which helps in extracting user from an&lt;/em&gt; &lt;strong&gt;&lt;em&gt;UserRecord&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;object returned by the admin sdk, returns a&lt;/em&gt; &lt;strong&gt;&lt;em&gt;User&lt;/em&gt;&lt;/strong&gt; _ object._&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here, we use &lt;strong&gt;customClaims&lt;/strong&gt; to store the admin role for an user. These &lt;strong&gt;customClaims&lt;/strong&gt; can only be retreived through the admin SDK. &lt;em&gt;(I didn’t find any way to retrieve them from the client sdk, Kindly mention in the feedback if I am wrong.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We need a router that can map different routes to the functions. For that purpose we create a file &lt;strong&gt;routes.ts&lt;/strong&gt; in the same directory. The file contains the following.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here, we define a configureRoutes function, which will take our express &lt;strong&gt;app&lt;/strong&gt; and add the routes. We pass &lt;strong&gt;true&lt;/strong&gt; to &lt;strong&gt;authorize&lt;/strong&gt; where the route requires admin privilege to perform the task.&lt;/p&gt;

&lt;p&gt;Similarly, we create two files, &lt;strong&gt;adminController.ts&lt;/strong&gt; and &lt;strong&gt;routes.ts&lt;/strong&gt; under &lt;em&gt;modules →admin&lt;/em&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Finally, we add the following import statements to the &lt;strong&gt;&lt;em&gt;index.ts&lt;/em&gt;&lt;/strong&gt; _ _file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { configureRoutes as configureRoutesForUsers } from './modules/users/routes';
import { configureRoutes as configureRoutesForAdmin } from './modules/admin/routes';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We listen for the endpoints by starting the server using the following piece of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.listen( port, () =&amp;gt; {
    console.log('Server listening at port '+ port);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally start the server on localhost using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; to test the endpoints. &lt;em&gt;Ex: &lt;a href="http://localhost:8080/users/" rel="noopener noreferrer"&gt;http://localhost:8080/users/&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We can add users to the project by using the firebase console or by using the &lt;a href="https://firebase.google.com/docs/reference/rest/auth" rel="noopener noreferrer"&gt;REST API for firebase&lt;/a&gt;. And thereafter we can retrieve the uid from the firebase console.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; For the first time, to add an admin, we can normally add an user and call the &lt;strong&gt;/admin/:uid/make&lt;/strong&gt; endpoint by passing &lt;strong&gt;false&lt;/strong&gt; to its &lt;strong&gt;authorize&lt;/strong&gt; filter. After that we can revert the value to  &lt;strong&gt;true&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kindly refer to the following Github repository for the complete project files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/i-am-jyotirmaya/Firebase-admin-demo-api" rel="noopener noreferrer"&gt;i-am-jyotirmaya/Firebase-admin-demo-api&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Thank You
&lt;/h4&gt;

</description>
      <category>express</category>
      <category>firebaseadminsdk</category>
      <category>typescript</category>
      <category>node</category>
    </item>
  </channel>
</rss>
