<?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: Hash Rekayasa Teknologi</title>
    <description>The latest articles on DEV Community by Hash Rekayasa Teknologi (@hash-id).</description>
    <link>https://dev.to/hash-id</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%2Forganization%2Fprofile_image%2F5727%2F16f5ed98-926c-470b-bd6c-252ee148a30d.jpg</url>
      <title>DEV Community: Hash Rekayasa Teknologi</title>
      <link>https://dev.to/hash-id</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hash-id"/>
    <language>en</language>
    <item>
      <title>Access ip camera android and display to our monitor using java</title>
      <dc:creator>Yogi khoirul Anwar</dc:creator>
      <pubDate>Thu, 18 Aug 2022 04:16:00 +0000</pubDate>
      <link>https://dev.to/hash-id/access-ip-camera-android-and-display-to-our-monitor-using-java-hd3</link>
      <guid>https://dev.to/hash-id/access-ip-camera-android-and-display-to-our-monitor-using-java-hd3</guid>
      <description>&lt;p&gt;Hi developers, today we will make a cctv monitoring using java language. Did you know how java can acces ip of cctv camera? It’s sound so attractive, right? &lt;br&gt;
Okay, in this article. We will realize how to access ip of cctv using java language 😁 &lt;br&gt;
First time, we need to prepare some tools to make it perfect. There are :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Text editor. The text editor we will using netbeans 8.1 IDE.&lt;/li&gt;
&lt;li&gt;Javacv library. You can download opencv library on &lt;a href="https://github.com/junaid67/ip-camera-java-cv-libraries.git"&gt;https://github.com/junaid67/ip-camera-java-cv-libraries.git&lt;/a&gt; . And then you have to extract download folder in your pc&lt;/li&gt;
&lt;li&gt;Ip webcam application to get ip of camera. You can get and install it in playstore .&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Okay, let’s start it.&lt;br&gt;
We must make a project on netbeans. To make a new project you can use &lt;code&gt;ctrl+shif+n&lt;/code&gt; with your keyboard . Then you will see the project options, select the java application as shown below. After that click next.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5PHu4uiZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vvwb1sb1luqerrz18bc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5PHu4uiZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8vvwb1sb1luqerrz18bc.png" alt="Image description" width="614" height="427"&gt;&lt;/a&gt;&lt;br&gt;
After that give project name with &lt;code&gt;konekcctv&lt;/code&gt;, then &lt;code&gt;click finish button&lt;/code&gt; to make a new project. Please see this image below.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Gijh-2cj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ny4fobr3lquny7mxy3nd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Gijh-2cj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ny4fobr3lquny7mxy3nd.png" alt="Image description" width="656" height="456"&gt;&lt;/a&gt;&lt;br&gt;
When finished, a new workspace or new project will shown as image below.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8SFUXPK3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jyfrunjc17yofhmtgedn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8SFUXPK3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jyfrunjc17yofhmtgedn.png" alt="Image description" width="880" height="457"&gt;&lt;/a&gt;&lt;br&gt;
In the &lt;code&gt;konekcctv.java&lt;/code&gt; source code file, there is a konekcctv class and a main class that is used to execute the program code that will be created.&lt;br&gt;
After that, we need to add the javacv library that we have downloaded in the project folder. &lt;br&gt;
First, right-click on the libraries folder, then select add jar/folder and choose all of extracted javacv library in your pc.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bTFFkk07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lm22ym6b0s1g9kbv6gaq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bTFFkk07--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lm22ym6b0s1g9kbv6gaq.png" alt="Image description" width="386" height="383"&gt;&lt;/a&gt;&lt;br&gt;
After the libraries are imported, all javacv libraries will appear in the libraries folder. See the image below.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hmxk4KWi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9mkhtl5aff2iojkk68ty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hmxk4KWi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9mkhtl5aff2iojkk68ty.png" alt="Image description" width="373" height="486"&gt;&lt;/a&gt;&lt;br&gt;
After everything is done, the next step is to start coding.&lt;br&gt;
First we need to import the javacv library which was added in the libraries folder earlier. We import outside the konekcctv class,&lt;br&gt;
&lt;code&gt;Import com.googlecode.javacv.canvasframe;&lt;br&gt;
Import com.googlecode.javacv.opencvframegrabber;&lt;br&gt;
Import com.googlecode.javacv.cpp.opencv_core.iplimage;&lt;/code&gt;&lt;br&gt;
Please see as shown below,&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ax9_BFyP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/awrhsmtrpyjcl8jxvne3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ax9_BFyP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/awrhsmtrpyjcl8jxvne3.png" alt="Image description" width="700" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Import function &lt;code&gt;com.googlecode.javacv.canvasframe;&lt;/code&gt; is to display the capture frame on our pc screen.&lt;/li&gt;
&lt;li&gt;Then import &lt;code&gt;com.googlecode.javacv.opencvframegrabber;&lt;/code&gt; we use to retrieve where the video is. Video can be a ready-made video or video from an ip camera.&lt;/li&gt;
&lt;li&gt;And import &lt;code&gt;com.googlecode.javacv.cpp.opencv_core.iplimage;&lt;/code&gt; is to get the ip address of the camera or cctv used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After finished of importing,&lt;br&gt;
We will make a function to connect ip camera and display of video in camera to our pc as shown below,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static void main(String[] args) throws FrameGrabber.Exception {
    OpenCVFrameGrabber frameGrabber = new OpenCVFrameGrabber("http://192.168.87.96:8080/video?dummy=param.mjpg");
    frameGrabber.setFormat("mjpeg");
    frameGrabber.start();
    IplImage iPimg = frameGrabber.grab();
    CanvasFrame canvasFrame = new CanvasFrame("Camera");
    canvasFrame.setCanvasSize(iPimg.width(), iPimg.height());

    while (canvasFrame.isVisible() &amp;amp;&amp;amp; (iPimg = frameGrabber.grab()) != null) {
        canvasFrame.showImage(iPimg);
    }
    frameGrabber.stop();
    canvasFrame.dispose();
    System.exit(0);
}`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bChMDwUp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8k92d0b2kd01kzxloa0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bChMDwUp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8k92d0b2kd01kzxloa0y.png" alt="Image description" width="880" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing to do is to create a framegrabber variable to get the video file by calling the camera's ip address using &lt;br&gt;
&lt;code&gt;opencvframegrabber.&lt;br&gt;
opencvframegrabber framegrabber = new opencvframegrabber("http://192.168.0.2:8080/video?dummy=param.mjpg");&lt;/code&gt;&lt;br&gt;
&lt;code&gt;http://192.168.0.2:8080/video?dummy=param.mjpg&lt;/code&gt; is the ip camera address on the ip camera in android application. Each application has its own ip, so match the ip camera with the ip camera on your own device.&lt;/p&gt;

&lt;p&gt;Then we set the format of the video to be displayed on our pc with the code &lt;code&gt;framegrabber.setformat("mjpeg");&lt;/code&gt;&lt;br&gt;
and the frame grabber is executed with the code &lt;code&gt;framegrabber.start();&lt;/code&gt;&lt;br&gt;
Then to return the ip image or ip camera we create a variable &lt;code&gt;ipimg = framegrabber.grab();&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Framegrabber.grab()&lt;/code&gt; is used to return the ip of the framegrabber. &lt;/p&gt;

&lt;p&gt;Then we will display the capture of the ip camera with&lt;br&gt;
&lt;code&gt;canvasframe canvasframe = new canvasframe("camera");&lt;/code&gt;&lt;br&gt;
&lt;code&gt;canvasframe.setcanvassize(ipimg.width(), ipimg.height());&lt;/code&gt;&lt;br&gt;
We need to create a canvasframe variable like the source code above. The canvasframe will be used to make the size of the camera view on our pc screen.&lt;br&gt;
And the last step is to create a function to display frames on our pc screen as follows:&lt;br&gt;
&lt;code&gt;While (canvasframe.isvisible() &amp;amp;&amp;amp; (ipimg = framegrabber.grab()) != null) {&lt;br&gt;
            canvasframe.showimage(ipimg);&lt;br&gt;
        }&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After everything is done, we can run the file in the run project section of netbeans, or we can use the shortcut &lt;code&gt;shif+f6&lt;/code&gt; . But before running the project, the ip camera application on android needs to be turned on first. Then our android and pc need to use the same network otherwise this framegrabber will not find the ip of the camera that has been activated.&lt;/p&gt;

&lt;p&gt;This image below is the result of running application,&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bim8PjOm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xybbo7klih56emstkdwc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bim8PjOm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xybbo7klih56emstkdwc.png" alt="Image description" width="588" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Good Luck!! 😁&lt;/p&gt;

</description>
      <category>opencv</category>
      <category>java</category>
    </item>
    <item>
      <title>Penggunaan Branch Feature pada Git Flow</title>
      <dc:creator>Deby Silvia Agnes</dc:creator>
      <pubDate>Thu, 11 Aug 2022 02:19:00 +0000</pubDate>
      <link>https://dev.to/hash-id/penggunaan-branch-feature-pada-git-flow-5gpa</link>
      <guid>https://dev.to/hash-id/penggunaan-branch-feature-pada-git-flow-5gpa</guid>
      <description>&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%2Fimages.unsplash.com%2Fphoto-1590595906931-81f04f0ccebb%3Fixlib%3Drb-1.2.1%26ixid%3DMnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8%26auto%3Dformat%26fit%3Dcrop%26w%3D2070%26q%3D80" 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%2Fimages.unsplash.com%2Fphoto-1590595906931-81f04f0ccebb%3Fixlib%3Drb-1.2.1%26ixid%3DMnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8%26auto%3Dformat%26fit%3Dcrop%26w%3D2070%26q%3D80" alt="Photo by Richy Great on Unsplash" width="2070" height="1380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Terdapat beberapa macam branching workflow yang dapat digunakan oleh pengguna git. Diantaranya Git Flow, Github Flow, Gitlab Flow, dan One Flow. Penjelasan lengkapnya seperti yang dijelaskan dalam artikel &lt;a href="https://medium.com/@patrickporto/4-branching-workflows-for-git-30d0aaee7bf" rel="noopener noreferrer"&gt;4 Branching Workflows For Git&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Salah satu yang terkenal dan kebetulan digunakan di perusahaan saya sekarang, adalah Git Flow.&lt;br&gt;&lt;br&gt;
Git Flow dipublikasikan oleh &lt;a href="https://nvie.com/about/" rel="noopener noreferrer"&gt;Vincent Driessen&lt;/a&gt; dan sangat populer karena cocok untuk kolaborasi tim dan scaling the development team. Info selengkapnya terdapat &lt;a href="https://nvie.com/posts/a-successful-git-branching-model/" rel="noopener noreferrer"&gt;disini&lt;/a&gt;. &lt;/p&gt;
&lt;h2&gt;
  
  
  Perkenalan
&lt;/h2&gt;

&lt;p&gt;Branch utama dalam Git FLow adalah : &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Master&lt;/strong&gt; merupakan branch yang menyimpan official release history. Jadi hanya berisi kode yang di-deploy ke production-server, sudah lolos testing, dan dirilis. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Develop&lt;/strong&gt; merupakan branch pre-production yang digunakan untuk development yang nantinya memiliki branch feature, release, dan bugfix. Jadi, penambahan fitur baru, perbaikan kode, dan development yang dilakukan oleh developer — developer lain berada pada branch ini. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Dalam Git Flow juga dapat menandai semua commit pada branch master dengan version number. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Cara Installasi
&lt;/h2&gt;

&lt;p&gt;Disini kita bisa menggunakan versi CLI maupun versi extension di editor Visual Studio Code (VSCode). Namun, karena saya terbiasa menggunakan CLI, maka saya lebih nyaman menggunakan CLI. Karena menurut saya, penggunaan extension di editor VSCode cukup membuat saya bingung dan takut terjadi kesalahan (&lt;em&gt;human error on click&lt;/em&gt;). Sehingga saya hanya menggunakan beberapa fitur saja. Namun semua itu kembali kepada kalian yang menggunakan. Disini saya akan mengulas cara installasi di keduanya. &lt;/p&gt;
&lt;h3&gt;
  
  
  Installasi di CLI
&lt;/h3&gt;

&lt;p&gt;Windows: ikuti instruksi disini. git-flow-avh sudah termasuk di dalam paket Git for Windows. &lt;/p&gt;

&lt;p&gt;Linux: sudo apt-get install git-flow &lt;/p&gt;

&lt;p&gt;Untuk informasi instalasi pada OS yang lainnya, dapat dilihat di &lt;a href="https://github.com/petervanderdoes/gitflow-avh/wiki/Installation" rel="noopener noreferrer"&gt;installasi gitflow-avh&lt;/a&gt;. &lt;/p&gt;
&lt;h3&gt;
  
  
  Installasi Extension di VSCode
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Cari extension “ gitflow” di VSCode. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Biasanya yang saya gunakan adalah extension milik vector-of-bool : &lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Fmz7ezwzmj4th4eycn9vv.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%2Fmz7ezwzmj4th4eycn9vv.png" alt="Extension Gitflow by dokumentasi penulis" width="800" height="329"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Penggunaan
&lt;/h2&gt;

&lt;p&gt;Penggunaan yang akan saya jelaskan disini adalah menggunakan CLI. Karena jika menggunakan extension, sudah terdapat panduan yang dapat di ikuti. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Langkah pertama yang harus dilakukan adalah melakukan inisialisasi git flow untuk local repo :
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git flow init -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F3pnntzu808byd9eg55f0.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%2F3pnntzu808byd9eg55f0.png" alt="Git Flow Init -d by dokumentasi penulis" width="800" height="314"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;-d&lt;/strong&gt; menandakan bahwa kita menggunakan versi default dari git flow. &lt;br&gt;
Perintah ini akan membuat 2 branch, yaitu &lt;strong&gt;master&lt;/strong&gt; dan &lt;strong&gt;develop&lt;/strong&gt; &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshjggqkm0fniwopfwej6.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%2Fshjggqkm0fniwopfwej6.png" alt="Branch default Git Flow by dokumentasi penulis" width="523" height="68"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;2.Selain itu, kita juga otomatis checkout ke branch develop &lt;/p&gt;

&lt;p&gt;3.Kemudian juga otomatis ada initial commit &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%2F2122hp39r8m7ounc6mzt.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%2F2122hp39r8m7ounc6mzt.png" alt="Initial Commit by dokumentasi penulis" width="800" height="112"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h3&gt;
  
  
  Buat Feature
&lt;/h3&gt;

&lt;p&gt;4.Selanjutnya, kita dapat melakukan develop project. Jika kita akan menambahkan fitur, kita kerjakan di branch feature dengan membuat branch feature terlebih dahulu dengan perintah :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git flow feature start &amp;lt;nama_feature&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fnt62lxqsugayqxczxu9p.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%2Fnt62lxqsugayqxczxu9p.png" alt="Feature Start by dokumentasi penulis" width="723" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5.Sekarang kita sudah ada di branch feature/function-balances. Kita dapat mulai ngoding disini untuk membuat fitur tersebut. &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%2Fxxqo5hf52y0pafu0swmv.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%2Fxxqo5hf52y0pafu0swmv.png" alt="Ada di branch feature/&amp;lt;nama_feature&amp;gt; by dokumentasi penulis" width="599" height="85"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;6.Jika telah selesai membuat fitur, cek status perubahan menggunakan perintah :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fwygriq8gdrgym1s0zww5.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%2Fwygriq8gdrgym1s0zww5.png" alt="Git status by dokumentasi penulis" width="744" height="273"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;7.Sebelum melakukan commit, pastikan file yang berubah telah di add. Kita boleh memasukkan semua file dalam satu commit (agar lebih cepat) atau boleh juga jika ingin setiap satu file dalam satu commit. Namun disini saya memasukkan semua file dalam satu commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add . 

git commit -m "&amp;lt;message&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F6pri04n2zbxxkw63q0gv.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%2F6pri04n2zbxxkw63q0gv.png" alt="Git Add dan Commit All Changes by dokumentasi penulis" width="800" height="91"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;8.Jika git status sudah seperti dibawah ini, maka artinya perubahan yang kita lakukan sudah selesai kita commit semua. &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%2Fsiglm9ies59sddm804j0.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%2Fsiglm9ies59sddm804j0.png" alt="Status Nothing To Commit by dokumentasi penulis" width="570" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Publish Feature Untuk Review
&lt;/h3&gt;

&lt;p&gt;9.Setelah kita melakukan perubahan, sebaiknya feature kita dapat di review dahulu oleh anggota tim yang lain. Sehingga feature di publish terlebih dahulu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git flow feature publish &amp;lt;nama_feature&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Ffoee80a3bamq0fxfpciw.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%2Ffoee80a3bamq0fxfpciw.png" alt="Publish Feature Untuk di Review by dokumentasi penulis" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;10.Kita buat PR (Pull Request) ke branch &lt;strong&gt;develop&lt;/strong&gt;. PR digunakan di &lt;strong&gt;Github&lt;/strong&gt;, namun jika menggunakan &lt;strong&gt;Gitlab&lt;/strong&gt;, maka namanya ada &lt;strong&gt;Merge Request&lt;/strong&gt;. Disini saya menggunakan github. &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%2Fb8hsiv6m3a2ytw54ew1v.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%2Fb8hsiv6m3a2ytw54ew1v.png" alt="PR di Github by dokumentasi penulis" width="796" height="369"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Klik compare &amp;amp; pull request untuk membuat PR ke branch &lt;strong&gt;develop&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;11.Setelah code review selesai, maka bisa merge melalui web atau melalui terminal dengan perintah :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git flow feature finish &amp;lt;nama_feature&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fvimdlaen198sy4xwpfcb.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%2Fvimdlaen198sy4xwpfcb.png" alt="Finish Feature from review by dokumentasi penulis" width="800" height="309"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;12.Lakukan &lt;code&gt;push&lt;/code&gt; ke branch develop &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%2F59zhzazar783uahycnb7.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%2F59zhzazar783uahycnb7.png" alt="Push ke develop by dokumentasi penulis" width="390" height="35"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;13.Karena branch &lt;strong&gt;feature&lt;/strong&gt; sudah di merge ke &lt;strong&gt;develop&lt;/strong&gt; dan tidak digunakan lagi, maka branch feature di local komputer kita &lt;code&gt;bisa kita hapus&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Baik sekian penggunaan git flow melalui CLI kali ini.&lt;br&gt;&lt;br&gt;
Jika menggunakan extension VSCode dapat lebih mudah karena tinggal klik klik saja. Namun penggunaan yang nyaman yang mana, itu kembali lagi kepada masing — masing developer yang menggunakan.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Source: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@patrickporto/4-branching-workflows-for-git-30d0aaee7bf" rel="noopener noreferrer"&gt;4 branching workflows for Git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nvie.com/posts/a-successful-git-branching-model/" rel="noopener noreferrer"&gt;A successful Git branching model&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow" rel="noopener noreferrer"&gt;Gitflow Workflow | Atlassian Git Tutorial&lt;/a&gt;&lt;br&gt;
&lt;a href="https://masputih.com/2017/11/git-flow-01-intro" rel="noopener noreferrer"&gt;Git Flow 01 - Intro&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/easyread/gitflow-in-practice-1d8a2bbdc3a1" rel="noopener noreferrer"&gt;Gitflow in Practice&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@rafavinnce/gitflow-branch-guide-8a523360c053" rel="noopener noreferrer"&gt;Gitflow — Branch Guide&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@olivier.bossel/git-flow-the-right-way-to-go-f2a65c315818" rel="noopener noreferrer"&gt;Git-flow : The right way to go&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Lessons Learned from Developing Serverless Workflow Runtime Implementation</title>
      <dc:creator>Muhammad Yahya Muhaimin</dc:creator>
      <pubDate>Tue, 26 Jul 2022 01:23:02 +0000</pubDate>
      <link>https://dev.to/hash-id/lessons-learned-from-developing-serverless-workflow-runtime-implementation-2fh8</link>
      <guid>https://dev.to/hash-id/lessons-learned-from-developing-serverless-workflow-runtime-implementation-2fh8</guid>
      <description>&lt;p&gt;At Hash Rekayasa Teknologi, we've been developing and using &lt;a href="https://www.hash.id/mocobaas/" rel="noopener noreferrer"&gt;MocoBaaS&lt;/a&gt;, a Backend-as-a-Service solution.&lt;br&gt;
One of the features for implementing business logic is Custom Script.&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%2Fihrz0zzyx2wdet3dftmx.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%2Fihrz0zzyx2wdet3dftmx.png" alt="MocoBaaS Custom Script flow" width="668" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This feature has served us well for many use cases.&lt;br&gt;
However, there are some use cases that consist of multiple steps. They can be implemented by "chaining" multiple scripts, one script triggering another. While this can get the job done, it's hard to keep track of the steps that were executed.&lt;/p&gt;

&lt;p&gt;Imagine we have a use case like Marketplace Order:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;DISCLAIMER: It may not represent a real world use case.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Create Order&lt;/li&gt;
&lt;li&gt;Confirm Payment&lt;/li&gt;
&lt;li&gt;Confirm Delivery&lt;/li&gt;
&lt;li&gt;Confirm Completed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It can be done by defining this flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Script: &lt;code&gt;create-order&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Triggered By: HTTP source&lt;/li&gt;
&lt;li&gt;Triggers: &lt;code&gt;create-order-success&lt;/code&gt; event&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Script: &lt;code&gt;confirm-payment&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Triggered By: Event source&lt;/li&gt;
&lt;li&gt;Triggers: &lt;code&gt;confirm-payment-success&lt;/code&gt; event&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Script: &lt;code&gt;confirm-delivery&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Triggered By: Event source&lt;/li&gt;
&lt;li&gt;Triggers: &lt;code&gt;confirm-delivery-success&lt;/code&gt; event&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Script: &lt;code&gt;confirm-completed&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Triggered By: Event source&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With the flow above, the scripts were executed as is. There is no centralized mechanism of tracking the executed steps, whether they were executed properly or not.&lt;/p&gt;
&lt;h2&gt;
  
  
  Serverless Workflow to the rescue
&lt;/h2&gt;

&lt;p&gt;Among workflow languages out there, we choose &lt;a href="https://serverlessworkflow.io" rel="noopener noreferrer"&gt;Serverless Workflow&lt;/a&gt;. It's a vendor-neutral, open-source and community-driven workflow ecosystem.&lt;br&gt;
The workflow definition can be written in JSON or YAML format.&lt;br&gt;
And then there are SDKs available in various programming languanges, like Java, Go, TypeScript, .NET, Python.&lt;/p&gt;

&lt;p&gt;The Marketplace Order use case above can be defined like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;marketplaceorder&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;
&lt;span class="na"&gt;specVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.7"&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Marketplace Order Workflow&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create and process orders on the marketplace.&lt;/span&gt;
&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateOrder&lt;/span&gt;
&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;createOrderFunction&lt;/span&gt;
    &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mocobaas://marketplace-order#create-order&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;confirmPaymentFunction&lt;/span&gt;
    &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mocobaas://marketplace-order#confirm-payment&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;confirmDeliveryFunction&lt;/span&gt;
    &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mocobaas://marketplace-order#confirm-delivery&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;confirmCompletedFunction&lt;/span&gt;
    &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mocobaas://marketplace-order#confirm-completed&lt;/span&gt;
&lt;span class="na"&gt;states&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CreateOrder&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;operation&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;functionRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;createOrderFunction&lt;/span&gt;
    &lt;span class="na"&gt;transition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfirmPayment&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfirmPayment&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;operation&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;functionRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;confirmPaymentFunction&lt;/span&gt;
    &lt;span class="na"&gt;transition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfirmDelivery&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfirmDelivery&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;operation&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;functionRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;confirmDeliveryFunction&lt;/span&gt;
    &lt;span class="na"&gt;transition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfirmCompleted&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfirmCompleted&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;operation&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;functionRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;confirmCompletedFunction&lt;/span&gt;
    &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is the diagram visualization:&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%2F7dwpfcght3yl3qfiizui.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%2F7dwpfcght3yl3qfiizui.png" alt="Marketplace Order workflow diagram" width="254" height="704"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you are new to Serverless Workflow, or workflow in general, you may have so many questions about that 😁&lt;/p&gt;

&lt;p&gt;I recommend you to watch this presentation:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/I66zBOpnUy0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;And then read the official Serverless Workflow examples and specification:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Version 0.7: &lt;a href="https://github.com/serverlessworkflow/specification/blob/0.7.x/examples/README.md" rel="noopener noreferrer"&gt;examples&lt;/a&gt;, &lt;a href="https://github.com/serverlessworkflow/specification/blob/0.7.x/specification.md" rel="noopener noreferrer"&gt;specification&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Version 0.8: &lt;a href="https://github.com/serverlessworkflow/specification/blob/0.8.x/examples/README.md" rel="noopener noreferrer"&gt;examples&lt;/a&gt;, &lt;a href="https://github.com/serverlessworkflow/specification/blob/0.8.x/specification.md" rel="noopener noreferrer"&gt;specification&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Let me continue the story...&lt;/p&gt;

&lt;p&gt;What we need to build is a runtime implementation that execute workflows based on the definitions.&lt;br&gt;
Golang has become an important part of our stack at Hash Rekayasa Teknologi. So we simply choose the &lt;a href="https://github.com/serverlessworkflow/sdk-go" rel="noopener noreferrer"&gt;Go SDK for Serverless Workflow&lt;/a&gt;. Although I didn't try other SDKs, I'm sure there shouldn't be much difference to what I'm using here.&lt;br&gt;
The most important question with the SDK: &lt;strong&gt;What it does and doesn't?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parse workflow JSON and YAML definitions.&lt;/li&gt;
&lt;li&gt;One workflow definition has a hierarchical structure. Each definition from top level to sub-levels will be represented &lt;a href="https://pkg.go.dev/github.com/serverlessworkflow/sdk-go/v2/model" rel="noopener noreferrer"&gt;as a Model&lt;/a&gt;, such as Workflow, State, Action, Function, Retry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It doesn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is no Workflow Instance representation. For the execution, you have to define the unique identifier yourself.&lt;/li&gt;
&lt;li&gt;The duration values in ISO 8601 duration format are not parsed.&lt;/li&gt;
&lt;li&gt;The workflow expressions in jq format are not parsed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With those limitations, there doesn't seem to be much we can do with the SDK. Just parse the workflow definition and use the hierarchical structure as a guide for executions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;sw&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"path/filepath"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/google/uuid"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/serverlessworkflow/sdk-go/v2/model"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/serverlessworkflow/sdk-go/v2/parser"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;StartWorkflowResult&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;InstanceID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"instanceId"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;workflows&lt;/span&gt; &lt;span class="k"&gt;map&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workflow&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;LoadWorkflows&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;definitionsDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"definitions"&lt;/span&gt;

    &lt;span class="n"&gt;dirEntries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadDir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;definitionsDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;workflows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&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="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Workflow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;dirEntries&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&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;path&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;definitionsDir&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;wf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromFile&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="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;workflows&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wf&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;StartWorkflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&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;input&lt;/span&gt; &lt;span class="k"&gt;map&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="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;StartWorkflowResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;wf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;workflows&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;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Workflow not found: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;instanceID&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Start a new instance.&lt;/span&gt;
    &lt;span class="c"&gt;// Parameters: instanceID, wf, input&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;StartWorkflowResult&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;instanceID&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we store the Workflow models in a map, so the &lt;code&gt;LoadWorkflows()&lt;/code&gt; function only needs to be called once.&lt;br&gt;
And then the &lt;code&gt;StartWorkflow()&lt;/code&gt; function will be called in every execution.&lt;/p&gt;
&lt;h2&gt;
  
  
  Take notes for the implemented features
&lt;/h2&gt;

&lt;p&gt;We may not implement all the features in the specification. One thing we can do is document them. Each feature will have status:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;implemented according to the spec 🟢🟢&lt;/li&gt;
&lt;li&gt;implemented, but not according to the spec or using own standard 🟢🔴&lt;/li&gt;
&lt;li&gt;not/not yet implemented 🔴&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I took notes on a spreadsheet. You can see it &lt;a href="https://docs.google.com/spreadsheets/d/1x92axugs8grW-wPCOF0E246fRYrZsxY97aSYI2Qzs40/edit?usp=sharing" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
I use my native language, Bahasa Indonesia.&lt;br&gt;
And it's not complete. I take note of a definition only when I start implementing it.&lt;/p&gt;

&lt;p&gt;Let's see one example, the Function Definition:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As we know, service call is defined here.&lt;/li&gt;
&lt;li&gt;The workflow runtime is written in Go, while the scripts are written in JavaScript (Node.js).&lt;/li&gt;
&lt;li&gt;MocoBaaS already has an internal RPC mechanism, so we want to use "custom" type.&lt;/li&gt;
&lt;li&gt;In spec v0.8, there's "custom" type. But as of this writing, the Go SDK only supports spec v0.7.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, we tried to stick to the spec as far as we could. But sometimes we have to use our own standards.&lt;/p&gt;
&lt;h2&gt;
  
  
  Executing workflow
&lt;/h2&gt;

&lt;p&gt;The Marketplace Order Workflow has a linear flow, from create order to confirm completed. This is the directory structure containing workflow definition and scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
└── marketplace-order
    ├── definition.sw.yaml
    └── scripts
        ├── confirm-completed.js
        ├── confirm-delivery.js
        ├── confirm-payment.js
        └── create-order.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The end result will be a JSON like this:&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;"createOrder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confirmPayment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confirmDelivery"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confirmCompleted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;When the workflow is executed, starting with &lt;code&gt;create-order.js&lt;/code&gt;, data is a new object:&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&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="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, &lt;code&gt;confirm-payment.js&lt;/code&gt; extends the data from previous state:&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&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="na"&gt;data&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="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;confirmPayment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking workflow execution
&lt;/h2&gt;

&lt;p&gt;As written in the spec:&lt;br&gt;
&lt;em&gt;Depending on their workflow definition, workflow instances can be short-lived or can execute for days, weeks, or years.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is no recommendation on how to store the tracking information. Any database can be used.&lt;br&gt;
We need to handle these requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One instance can have more than one states.&lt;/li&gt;
&lt;li&gt;The state's data input is typically the previous state's data output.&lt;/li&gt;
&lt;li&gt;If the state is the workflow starting state, its data input is the workflow data input.&lt;/li&gt;
&lt;li&gt;When workflow execution ends, the data output of the last executed state becomes the workflow data output.&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%2F7l1x9qakox35xmxw4icr.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%2F7l1x9qakox35xmxw4icr.png" alt="Information passing between states" width="800" height="974"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, we have two tables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;instances&lt;/li&gt;
&lt;li&gt;instance_states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Marketplace Order Workflow execution can be stored like this:&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%2Fohein5an3d9bdun5zfmt.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%2Fohein5an3d9bdun5zfmt.png" alt="Table instances" width="800" height="128"&gt;&lt;/a&gt;&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%2F91x54cc12jbbyateu154.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%2F91x54cc12jbbyateu154.png" alt="Table instance_states" width="800" height="116"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Retrying actions
&lt;/h2&gt;

&lt;p&gt;If a state returning an error, we can leave it as the final result or define a retry policy.&lt;br&gt;
For example, we have a Chance of Success Workflow.&lt;/p&gt;

&lt;p&gt;Directory structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
└── chance-of-success
    ├── definition.sw.yaml
    └── scripts
        └── chance.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;chance.js&lt;/code&gt; will randomize a boolean. If true, returns data. If false, returns error:&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;chance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;chance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nc"&gt;Chance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&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;isTrue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;likelihood&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;likelihood&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isTrue&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;failed&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;success&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the workflow definition contains a retry definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chanceofsuccess&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;
&lt;span class="na"&gt;specVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.7"&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Chance of Success Workflow&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Try your chance of success. Retry if failed.&lt;/span&gt;
&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TakeAChance&lt;/span&gt;
&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chanceFunction&lt;/span&gt;
    &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mocobaas://chance-of-success#chance&lt;/span&gt;
&lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chanceRetryStrategy&lt;/span&gt;
    &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PT10S&lt;/span&gt;
    &lt;span class="na"&gt;maxAttempts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;span class="na"&gt;states&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TakeAChance&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;operation&lt;/span&gt;
    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;functionRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chanceFunction&lt;/span&gt;
        &lt;span class="na"&gt;retryRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chanceRetryStrategy&lt;/span&gt;
    &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that retry definition, the runtime will perform this mechanism:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The maximum attempts is 3 times.&lt;/li&gt;
&lt;li&gt;There is 10 second delay between retries.&lt;/li&gt;
&lt;li&gt;If we get data before maxAttempts, there will be no more retries.&lt;/li&gt;
&lt;li&gt;If maxAttempts is reached, there will be no more retries, regardless of the result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we can use the delay duration, it needs to be parsed. For example, I use &lt;a href="https://github.com/sosodev/duration" rel="noopener noreferrer"&gt;sosodev/duration&lt;/a&gt; and it works well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diagram visualization
&lt;/h2&gt;

&lt;p&gt;Generating a diagram visualization from the workflow definition is really helpful, especially when you have complex workflows.&lt;br&gt;
One way is that you can use the &lt;a href="https://serverlessworkflow.io/editor.html" rel="noopener noreferrer"&gt;Web Editor in the official website&lt;/a&gt;. It can generate diagram from JSON or YAML, but the linter in the text editor will always expect JSON.&lt;/p&gt;

&lt;p&gt;For VS Code users, there's an &lt;a href="https://marketplace.visualstudio.com/items?itemName=serverlessworkflow.serverless-workflow-vscode-extension" rel="noopener noreferrer"&gt;official extension&lt;/a&gt;, But as of this writing, it is outdated, only supports spec v0.6.&lt;br&gt;
A better alternative is to use an &lt;a href="https://marketplace.visualstudio.com/items?itemName=redhat.vscode-extension-serverless-workflow-editor" rel="noopener noreferrer"&gt;extension from Red Hat&lt;/a&gt;. It supports spec v0.8. It also works well with spec v0.7. The only requirement is you must name the definition files to &lt;code&gt;*.sw.json&lt;/code&gt;, &lt;code&gt;*.sw.yaml&lt;/code&gt; or &lt;code&gt;*.sw.yml&lt;/code&gt;.&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%2Frcleq66k8j81cv2bexhw.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%2Frcleq66k8j81cv2bexhw.gif" alt="VS Code extension by Red Hat" width="480" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Caveat:&lt;br&gt;
Looks like those tools use the same generator, as they produce the same diagram visualization. I noticed that they can only visualize the flow, but don't include other details, such as functions or retries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Workflow is quite a huge feature. And as you can see, Serverless Workflow offers a great flexibility between standard and customization. But if you need more training wheels in using a workflow system, there may be better solutions out there.&lt;/p&gt;

&lt;p&gt;We haven't implemented most of the Serverless Workflow features yet.&lt;br&gt;
For example, the workflow expressions I mentioned above. Using a library like &lt;a href="https://github.com/itchyny/gojq" rel="noopener noreferrer"&gt;itchyny/gojq&lt;/a&gt; looks promising, although I haven't tried it.&lt;br&gt;
But at least this little effort is enough for a minimal functioning system.&lt;/p&gt;

&lt;p&gt;Well, hope you enjoyed this article and found it useful 😉&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>workflow</category>
      <category>go</category>
      <category>node</category>
    </item>
    <item>
      <title>Sistem Yang Aman Dan Berkualitas</title>
      <dc:creator>Yogi khoirul Anwar</dc:creator>
      <pubDate>Wed, 20 Jul 2022 04:11:09 +0000</pubDate>
      <link>https://dev.to/hash-id/sistem-yang-aman-dan-berkualitas-5cf2</link>
      <guid>https://dev.to/hash-id/sistem-yang-aman-dan-berkualitas-5cf2</guid>
      <description>&lt;p&gt;Sistem adalah suatu kumpulan atau himpunan dari suatu unsur, komponen, atau variabel yang terorganisasi, saling berinteraksi, saling tergantung satu sama lain dan terpadu” Menurut Sutabri (2012:3). Sebuah sistem banyak digunakan oleh perusahaan-perusahaan di Indonesia maupun dunia mulai dari perusahaan kecil hingga perusahaan raksasa. Namun sebuah sistem yang dimiliki tiap perusahaan memiliki keamanan dan kualitas yang berbeda-beda. Hal tersebut dipengaruhi oleh dana yang dianggarkan oleh perusahaan untuk mendanai keamanan dan kualitas dari sistem mereka. Semakin tinggi dana yang dianggarkan,maka sistem akan semakin berkualitas. Namun dari segi keamanan belum tentu sebuah sistem benar-benar aman. Banyak hacker-hacker handal yang sangat&lt;br&gt;
mudahnya membajak sebuah situs yang dikenal sangat aman, seperti yang dikutip dari &lt;a href="https://www.cnnindonesia.com/nasional/20210223131459-20-609784/hendropriyonousul-tni-rekrut-remaja-ri-pembobol-situs-nasa.%E2%80%9C"&gt;&lt;/a&gt; yaitu bocah berusia 16 tahun asal Tangerang bernama Putera Adji Adhari yang sempat membobol situs keamanan NASA. Diketahui, Putera memiliki keterampilan di bidang komputerisasi secara otodidak. Ia terampil mencari kelemahan sistem dari suatu instansi, lalu menginformasikannya ke instansi terkait agar dapat memperbaikinya. &lt;/p&gt;

&lt;p&gt;Dilansir dari &lt;a href="https://antariksa.republika.co.id/posts/72049/anggaran-nasa-tahun2022-rp-354-triliun-apa-saja-programnya-"&gt;&lt;/a&gt; , DPR dan Senat Amerika Serikat telah menyelesaikan rancangan anggaran pengeluaran omnibus untuk tahun fiskal 2022. Mereka akan memberi NASA anggaran sebesar 24 miliar dan 760 juta dolar AS di bawah anggaran pemerintah. Jika dirupiahkan, anggaran Badan Antariksa Amerika itu untuk tahun ini sebesar Rp 354 triliun . &lt;/p&gt;

&lt;p&gt;Dalam berita tersebut, dapat diketahui setinggi apapun dana yang dikeluarkan untuk mengamankan sebuah sistem, tidak akan pernah ada sistem yang 100 persen aman kecuali sistem tersebut digunakan secara offline. &lt;/p&gt;

</description>
      <category>systems</category>
      <category>security</category>
    </item>
    <item>
      <title>[ID] Otentikasi Skema Hybrid: JWT + userinfo</title>
      <dc:creator>Muhammad Yahya Muhaimin</dc:creator>
      <pubDate>Mon, 23 May 2022 01:32:02 +0000</pubDate>
      <link>https://dev.to/hash-id/id-otentikasi-skema-hybrid-jwt-userinfo-5aba</link>
      <guid>https://dev.to/hash-id/id-otentikasi-skema-hybrid-jwt-userinfo-5aba</guid>
      <description>&lt;p&gt;Sebagai pengembang backend, barangkali Anda pernah membangun sendiri sistem otentikasi untuk aplikasi yang Anda buat. Jika Anda mengelola session, misalnya dengan memanfaatkan database, maka itu disebut stateful.&lt;br&gt;
Kebalikan dari stateful adalah stateless. Beberapa standar otentikasi stateless yang umum di antaranya JWT dan SAML.&lt;sup&gt;[1]&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Di artikel ini kita akan mengupas bagaimana JWT digunakan dan mengapa kita tidak bisa mengandalkan dia seutuhnya.&lt;/p&gt;

&lt;p&gt;Aplikasi demo yang akan kita buat berbasis framework &lt;a href="https://gofiber.io" rel="noopener noreferrer"&gt;Fiber&lt;/a&gt;.&lt;br&gt;
Jika Anda punya preferensi lain, secara prinsip soal JWT dan otentikasi skema hybrid ini harusnya sama.&lt;/p&gt;
&lt;h2&gt;
  
  
  Development Tools
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://go.dev/dl/" rel="noopener noreferrer"&gt;Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; + &lt;a href="https://marketplace.visualstudio.com/items?itemName=golang.Go" rel="noopener noreferrer"&gt;ekstensi Go&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postman.com/downloads/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Cara Kerja JWT
&lt;/h2&gt;

&lt;p&gt;JWT (dibaca "jot") merupakan sebuah standar internet untuk pertukaran informasi, dituangkan dalam dokumen &lt;a href="https://datatracker.ietf.org/doc/html/rfc7519" rel="noopener noreferrer"&gt;RFC 7519&lt;/a&gt;.&lt;br&gt;
JSON Web Token, sesuai namanya, dia membawa payload dalam bentuk objek JSON. Setiap properti di dalamnya disebut dengan &lt;strong&gt;claim&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Langsung saja kita mulai dengan membuat sebuah folder, misalnya dengan nama &lt;code&gt;how-to-hybrid-auth&lt;/code&gt;. Kemudian masuk ke folder tersebut, buka terminal dan ketik:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go mod init how-to-hybrid-auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maka akan muncul file &lt;code&gt;go.mod&lt;/code&gt;.&lt;br&gt;
File ini bersama dengan file &lt;code&gt;go.sum&lt;/code&gt; nantinya digunakan untuk mencatat berbagai dependency project. Kecuali project yang zero dependency, hanya membutuhkan standard packages, maka hanya akan ada file &lt;code&gt;go.mod&lt;/code&gt;, tanpa file &lt;code&gt;go.sum&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Di tutorial ini struktur projectnya seperti ini:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── go.mod
├── go.sum
├── main.go
└── pkg
    ├── authenticate
    │   └── handler.go
    ├── getaccess
    │   └── handler.go
    ├── getprofile
    │   └── handler.go
    ├── jwt
    │   └── jwt.go
    └── store
        ├── seeder.go
        └── store.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Data contoh: users
&lt;/h3&gt;

&lt;p&gt;Misalnya ada data profil pengguna sebagai dasar untuk mengidentifikasi pengguna aplikasi. Di sini kita simpan datanya dalam file JSON.&lt;/p&gt;

&lt;p&gt;Isi file &lt;code&gt;pkg/store/store.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Item&lt;/span&gt; &lt;span class="k"&gt;map&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="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="k"&gt;map&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;Item&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storeName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Read file content.&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storeName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;".json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Parse it.&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmarshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storeName&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;data&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;overwrite&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Serialize the data.&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MarshalIndent&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="s"&gt;""&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;overwrite&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Write file (overwrite existing file).&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storeName&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;".json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModePerm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Check if file doesn't exist.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storeName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;".json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNotExist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Write file.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storeName&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;".json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ModePerm&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="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fungsi &lt;code&gt;Set&lt;/code&gt; untuk memformat data menjadi JSON dan menulisnya ke file, sedangkan fungsi &lt;code&gt;Get&lt;/code&gt; untuk membaca file JSON dan mengonversinya menjadi data.&lt;/p&gt;

&lt;p&gt;Dan isi file &lt;code&gt;pkg/store/seeder.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Seed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"rI3sMqctRUv9Cv9CdvJIV"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Agni"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Owner"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"ELjvSoCYudoMnlEYlhCjP"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Bisma"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Maintainer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"5hkcRZcrOWTxaeFhq3EdD"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Catur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Developer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fungsi &lt;code&gt;Seed&lt;/code&gt; akan membuatkan data awal jika belum ada.&lt;/p&gt;

&lt;p&gt;Sekarang gunakan fungsi tersebut di entry point aplikasi, yaitu file &lt;code&gt;main.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/store"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Seed some data.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Seed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&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;Jika kita jalankan aplikasi dengan perintah:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maka akan muncul file &lt;code&gt;users.json&lt;/code&gt;, yang berisi data awal seperti definisi di atas.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Membuat Access Token
&lt;/h3&gt;

&lt;p&gt;Di sini kita mulai menggunakan Fiber, diawali dengan sebuah endpoint API untuk membuat Access Token.&lt;/p&gt;

&lt;p&gt;Ubah file &lt;code&gt;main.go&lt;/code&gt; seperti berikut:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/gofiber/fiber/v2"&lt;/span&gt;

    &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/getaccess"&lt;/span&gt;
    &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/store"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Seed some data.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Seed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Create Fiber app.&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// External endpoints.&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/get-access"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getaccess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":3000"&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;Untuk memfungsikan endpoint tersebut, pertama siapkan satu fungsi khusus untuk membuat token JWT di file &lt;code&gt;pkg/jwt/jwt.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;jwtutil&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/golang-jwt/jwt/v4"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;SigMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"HS256"&lt;/span&gt;
    &lt;span class="n"&gt;SigKey&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2022terceS"&lt;/span&gt;
    &lt;span class="n"&gt;Issuer&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"localhost"&lt;/span&gt;
    &lt;span class="n"&gt;Audience&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"localhost"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Sign creates a token from the specified claims.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;claims&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapClaims&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expiresIn&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&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;claims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"iss"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Issuer&lt;/span&gt;
    &lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"aud"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Audience&lt;/span&gt;

    &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&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="s"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unix&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="s"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unix&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewWithClaims&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetSigningMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SigMethod&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;out&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SignedString&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;SigKey&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;out&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Di sini kita menggunakan signing method yang mudah dulu, yaitu &lt;strong&gt;HS256&lt;/strong&gt;, di mana signing key yang digunakan sifatnya &lt;strong&gt;simetris&lt;/strong&gt;: Key yang digunakan &lt;em&gt;sama persis&lt;/em&gt; untuk signing dan verifikasi JWT.&lt;sup&gt;[2]&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Issuer = Pihak yang menerbitkan JWT. Audience = Pihak yang menggunakan JWT.&lt;br&gt;
Jika dibuat sama, bisa bermaksud bahwa JWT diterbitkan dan digunakan sendiri. Meskipun itu bukan berarti JWT tersebut tidak akan berpindah tangan.&lt;br&gt;
Setidaknya antara aplikasi server dan aplikasi client sudah termasuk dua pihak yang berbeda. JWT diterbitkan oleh server, kemudian "dipinjamkan" ke client, tetapi hanya server yang berhak untuk memverifikasinya.&lt;/p&gt;

&lt;p&gt;Setelah itu buat fungsi handler yang dipasangkan ke endpoint &lt;code&gt;/get-access&lt;/code&gt; itu tadi, yaitu di file &lt;code&gt;pkg/getaccess/handler.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;getaccess&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gofiber/fiber/v2"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/golang-jwt/jwt/v4"&lt;/span&gt;

    &lt;span class="n"&gt;jwtutil&lt;/span&gt; &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/jwt"&lt;/span&gt;
    &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/store"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;findUser&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`form:"id"`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;accessResponse&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AccessToken&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"access_token"`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Find user by ID.&lt;/span&gt;
    &lt;span class="n"&gt;fUser&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;findUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BodyParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Generate access token.&lt;/span&gt;
    &lt;span class="n"&gt;accessClaims&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapClaims&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"sub"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;accessClaims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jwtutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessClaims&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Minute&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessResponse&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fungsi &lt;code&gt;getaccess.Handle&lt;/code&gt; akan mencari user berdasarkan ID. Jika ada, maka ambil semua informasi dari user tersebut menjadi claim-claim JWT, kemudian operkan ke fungsi &lt;code&gt;jwtutil.Sign&lt;/code&gt; untuk digabungkan dengan beberapa claim khusus.&lt;br&gt;
Untuk demo ini, expiresIn (masa berlaku JWT) kita buat 5 menit saja, yang kira-kira cukup untuk melihat perubahan dari valid menjadi expired.&lt;br&gt;
Terakhir dilakukan signing, sehingga menghasilkan token JWT.&lt;/p&gt;

&lt;p&gt;Karena sampai di bagian ini kita sudah menggunakan beberapa dependency ekstra (3rd party packages), antara lain &lt;code&gt;gofiber/fiber/v2&lt;/code&gt; dan &lt;code&gt;golang-jwt/jwt/v4&lt;/code&gt;, maka kita perlu panggil perintah berikut:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go mod tidy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dengan begitu semua dependency tersebut menjadi tersedia untuk project ini.&lt;/p&gt;

&lt;p&gt;Sekarang jalankan lagi aplikasi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fxtzfzt3y9ajankgb958v.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%2Fxtzfzt3y9ajankgb958v.png" alt="Fiber startup message" width="334" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Itu menunjukkan bahwa aplikasi ini berjalan dengan Fiber, berupa server HTTP di port 3000.&lt;/p&gt;

&lt;p&gt;Kita coba panggil endpoint yang dibuat tadi.&lt;br&gt;
Tambahkan satu request di Postman seperti berikut:&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%2Feqouc19wz1kyz7ptyej9.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%2Feqouc19wz1kyz7ptyej9.png" alt="Postman - POST /get-access" width="751" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Method: POST&lt;/li&gt;
&lt;li&gt;URL: &lt;a href="http://localhost:3000/get-access" rel="noopener noreferrer"&gt;http://localhost:3000/get-access&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Body: x-www-form-urlencoded

&lt;ul&gt;
&lt;li&gt;id: { salah satu ID user }&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Setelah klik Send, maka akan dapat jawaban seperti berikut:&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;"access_token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJsb2NhbGhvc3QiLCJleHAiOjE2NTI5NzA2MzYsImlhdCI6MTY1Mjk3MDMzNiwiaXNzIjoibG9jYWxob3N0IiwibmFtZSI6IkNhdHVyIiwicm9sZSI6IkRldmVsb3BlciIsInN1YiI6IjVoa2NSWmNyT1dUeGFlRmhxM0VkRCJ9.Qo70QaMH0NVo11i8u1uXgJssor1iAtGruXPiWG2PGF0"&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;h3&gt;
  
  
  3. Bedah Singkat Token JWT
&lt;/h3&gt;

&lt;p&gt;Coba salin jawaban &lt;code&gt;access_token&lt;/code&gt; di atas, atau yang Anda tes sendiri, ke website &lt;a href="https://jwt.io" rel="noopener noreferrer"&gt;jwt.io&lt;/a&gt;.&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%2Fxw4dzbvpi5t1q41aoe0b.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%2Fxw4dzbvpi5t1q41aoe0b.png" alt="Copas token JWT ke jwt.io" width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Di situ kita bisa mengintip isi dari token, ada tiga bagian: Header, Payload dan Signature.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bagian Header tidak saya jelaskan.&lt;/li&gt;
&lt;li&gt;Bagian Payload berisi semua claim yang sudah disiapkan sebelumnya.&lt;/li&gt;
&lt;li&gt;Bagian Signature digunakan untuk verifikasi. Bisa coba salin signing key yang didefinisikan di source code: "2022terceS".&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%2Fcnanaqctbarttgmd4syk.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%2Fcnanaqctbarttgmd4syk.png" alt="Signature Verified di jwt.io" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sekarang status di pojok kiri-bawah menjadi "Signature Verified".&lt;/p&gt;

&lt;p&gt;Nah, token yang bisa di-parse sebelum diverifikasi seperti itu berarti dia termasuk jenis &lt;strong&gt;JWS&lt;/strong&gt; (&lt;strong&gt;J&lt;/strong&gt;SON &lt;strong&gt;W&lt;/strong&gt;eb &lt;strong&gt;S&lt;/strong&gt;ignature).&lt;br&gt;
Jenis lainnya adalah &lt;strong&gt;JWE&lt;/strong&gt; (&lt;strong&gt;J&lt;/strong&gt;SON &lt;strong&gt;W&lt;/strong&gt;eb &lt;strong&gt;E&lt;/strong&gt;ncryption), yang isinya terenkripsi dan membutuhkan sebuah secret key untuk membukanya.&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%2Fr7dpi5knbn3yr9mf3jbx.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%2Fr7dpi5knbn3yr9mf3jbx.png" alt="JWT berjenis JWS atau JWE" width="800" height="688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Jadi sebuah token JWT pasti berbentuk salah satu dari dua jenis tersebut.&lt;br&gt;
Yang biasanya digunakan adalah JWS, berisi tiga bagian yang disebutkan di atas. Sedangkan JWE berisi lima bagian, dan salah satu metode enkripsinya adalah AES256-GCM.&lt;sup&gt;[3]&lt;/sup&gt;&lt;sup&gt;[4]&lt;/sup&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  4. JWT Middleware (Parse + Verifikasi)
&lt;/h3&gt;

&lt;p&gt;Middleware untuk JWT termasuk &lt;a href="https://github.com/gofiber/fiber#-external-middleware" rel="noopener noreferrer"&gt;salah satu&lt;/a&gt; yang dibuat oleh para maintainer Fiber, jadi kita tinggal memanfaatkannya saja.&lt;/p&gt;

&lt;p&gt;Isi file &lt;code&gt;pkg/authenticate/handler.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;authenticate&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gofiber/fiber/v2"&lt;/span&gt;
    &lt;span class="n"&gt;jwtware&lt;/span&gt; &lt;span class="s"&gt;"github.com/gofiber/jwt/v3"&lt;/span&gt;

    &lt;span class="n"&gt;jwtutil&lt;/span&gt; &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/jwt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&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;jwtware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwtware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SigningMethod&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;jwtutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SigMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SigningKey&lt;/span&gt;&lt;span class="o"&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;jwtutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SigKey&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ContextKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"auth"&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;Supaya project mengenali dependency yang baru ditambahkan, panggil lagi perintah berikut:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go mod tidy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Signing method dan signing key yang digunakan adalah yang ditetapkan di file &lt;code&gt;pkg/jwt/jwt.go&lt;/code&gt;.&lt;br&gt;
Dengan konfigurasi minimal seperti di atas, maka otomatis dilakukan verifikasi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signature.&lt;/li&gt;
&lt;li&gt;Expiration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jika butuh memverifikasi hal lain, misalnya Audience, maka harus ditulis secara custom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;authenticate&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/gofiber/fiber/v2"&lt;/span&gt;
    &lt;span class="n"&gt;jwtware&lt;/span&gt; &lt;span class="s"&gt;"github.com/gofiber/jwt/v3"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/golang-jwt/jwt/v4"&lt;/span&gt;

    &lt;span class="n"&gt;jwtutil&lt;/span&gt; &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/jwt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handler&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;jwtware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwtware&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SigningMethod&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;jwtutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SigMethod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SigningKey&lt;/span&gt;&lt;span class="o"&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;jwtutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SigKey&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ContextKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;SuccessHandler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Locals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&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;claims&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Claims&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapClaims&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c"&gt;// Verify audience.&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VerifyAudience&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwtutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Audience&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid JWT audience. Expected: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jwtutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Audience&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusUnauthorized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&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;Middleware ini belum bisa digunakan jika belum ada endpoint yang dituju. Jadi kita langsung beranjak ke poin berikutnya.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Meminta Data Profil Pengguna
&lt;/h3&gt;

&lt;p&gt;Buat fungsi handler di file &lt;code&gt;pkg/getprofile/handler.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;getprofile&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gofiber/fiber/v2"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/golang-jwt/jwt/v4"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reservedClaims&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;map&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="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}{&lt;/span&gt;
    &lt;span class="s"&gt;"iss"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"aud"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"exp"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"nbf"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"iat"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"sub"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"jti"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Locals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&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;claims&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Claims&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapClaims&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Extract profile from JWT claims.&lt;/span&gt;
    &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;reservedClaims&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&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;Profil pengguna diekstrak dari claim-claim JWT, dengan mengeliminasi tujuh claim khusus (jika ada) yang disebut dengan &lt;strong&gt;reserved claims&lt;/strong&gt;.&lt;sup&gt;[5]&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Dan ubah file &lt;code&gt;main.go&lt;/code&gt; seperti berikut:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/gofiber/fiber/v2"&lt;/span&gt;

    &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/authenticate"&lt;/span&gt;
    &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/getaccess"&lt;/span&gt;
    &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/getprofile"&lt;/span&gt;
    &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/store"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Seed some data.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Seed&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Create Fiber app.&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// External endpoints.&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/get-access"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getaccess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// JWT middleware.&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authenticate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="c"&gt;// Internal endpoints.&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/get-profile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getprofile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":3000"&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;Jadi umumnya JWT middleware itu ditempatkan setelah endpoint-endpoint eksternal dan sebelum endpoint-endpoint internal.&lt;/p&gt;

&lt;p&gt;Jalankan lagi aplikasi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kemudian ke Postman dan tambahkan satu request lagi:&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%2F2a4p2t3dh1hgnszz15mc.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%2F2a4p2t3dh1hgnszz15mc.png" alt="Postman - POST /get-profile" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Method: POST&lt;/li&gt;
&lt;li&gt;URL: &lt;a href="http://localhost:3000/get-profile" rel="noopener noreferrer"&gt;http://localhost:3000/get-profile&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Body: none&lt;/li&gt;
&lt;li&gt;Authorization: Bearer Token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isikan Access Token ke kolom yang tersedia.&lt;/p&gt;

&lt;p&gt;Jika token tidak valid atau sudah expired, maka akan dapat jawaban "Invalid or expired JWT".&lt;/p&gt;

&lt;p&gt;Sedangkan jika valid, maka akan dapat jawaban seperti berikut:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Catur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Developer"&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;h2&gt;
  
  
  Keterbatasan JWT
&lt;/h2&gt;

&lt;p&gt;JWT memang dirancang untuk pertukaran informasi. Namun satu problem yang jelas adalah ketika misalnya data profil diubah dan kita butuh agar perubahan tersebut langsung berdampak terhadap penggunaan aplikasi, maka pada kondisi tersebut JWT tidak bisa diandalkan.&lt;br&gt;
Jika untuk melihat perubahan itu harus melakukan penerbitan ulang JWT, maka sepertinya itu skenario yang terlalu memaksakan, tidak praktis.&lt;/p&gt;

&lt;p&gt;Fakta yang ada di lapangan, oleh berbagai sistem penyedia identitas (identity provider), yaitu antara dua kemungkinan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Token JWT bukan sebagai Access Token, melainkan hanya sebagai ID Token. Ini yang dilakukan oleh Apple.&lt;sup&gt;[6]&lt;/sup&gt;
&lt;/li&gt;
&lt;li&gt;Pihak provider menyediakan endpoint &lt;code&gt;/userinfo&lt;/code&gt; dan butuh Access Token untuk memanggilnya. Ini yang dilakukan oleh Auth0.&lt;sup&gt;[7]&lt;/sup&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dalam kasus Apple, mereka tidak peduli terhadap perubahan profil pengguna dan cukup ID Token itu yang dijadikan patokan, selama masih valid.&lt;br&gt;
Dari sisi aplikasi, bisa menetapkan durasi expiration, seperti yang ada di library &lt;code&gt;albenik-go/apple-sign-in&lt;/code&gt; pada fungsi &lt;a href="https://pkg.go.dev/github.com/albenik-go/apple-sign-in#Client.ValidateCode" rel="noopener noreferrer"&gt;&lt;code&gt;Client.ValidateCode&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Dalam kasus Auth0, Access Token yang digunakan untuk memanggil endpoint &lt;code&gt;/userinfo&lt;/code&gt; bersifat opaque (tidak transparan). Artinya ketika aplikasi memperoleh Access Token, tidak untuk di-parse, sebab bukan JWT.&lt;/p&gt;
&lt;h2&gt;
  
  
  Userinfo: Tantangan dan Solusinya
&lt;/h2&gt;

&lt;p&gt;Kita tahu bahwa urusan otentikasi dapat dikelola secara internal maupun eksternal.&lt;/p&gt;

&lt;p&gt;Meskipun di atas sudah disebutkan tentang keterbatasan pada JWT dan keunggulan adanya endpoint &lt;code&gt;/userinfo&lt;/code&gt;, tapi kadang kita dihadapkan pada tantangan ketika aplikasinya harus mendukung beberapa penyedia identitas, contohnya Google dan Facebook.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google punya endpoint &lt;code&gt;/userinfo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Facebook punya endpoint &lt;code&gt;/me&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kalau hanya satu sumber data, kita bisa mengandalkannya. Tapi kalau lebih dari satu, itu juga menjadi masalah.&lt;br&gt;
Mungkin endpoint tersebut hanya berguna untuk membantu pendaftaran pengguna, untuk mengisi profil di awal. Yang berarti aplikasi harus menyimpan data profil secara lokal.&lt;br&gt;
Dalam kasus yang seperti ini, tidak ada bedanya antara penyedia identitas menggunakan endpoint &lt;code&gt;/userinfo&lt;/code&gt; atau ID Token, karena hanya diakses sekali untuk masing-masing pengguna.&lt;/p&gt;

&lt;p&gt;Jadi dapat diambil kesimpulan bahwa harusnya selalu ada data profil lokal aplikasi, baik itu otentikasinya secara internal maupun eksternal.&lt;/p&gt;

&lt;p&gt;Sekarang untuk penyesuaian yang diperlukan di aplikasi demo kita:&lt;/p&gt;

&lt;p&gt;Di file &lt;code&gt;pkg/getaccess/handler.go&lt;/code&gt;, baris 35-49:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Generate access token.&lt;/span&gt;
    &lt;span class="n"&gt;accessClaims&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapClaims&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"sub"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jwtutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accessClaims&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Di situ kita hanya membutuhkan informasi ID pengguna dan tidak menyertakan informasi lainnya untuk pembuatan Access Token.&lt;/p&gt;

&lt;p&gt;Kemudian di file &lt;code&gt;pkg/getprofile/handler.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;getprofile&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/gofiber/fiber/v2"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/golang-jwt/jwt/v4"&lt;/span&gt;

    &lt;span class="s"&gt;"how-to-hybrid-auth/pkg/store"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reservedClaims&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;map&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="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}{&lt;/span&gt;
    &lt;span class="s"&gt;"iss"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"aud"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"exp"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"nbf"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"iat"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"sub"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="s"&gt;"jti"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Locals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&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;claims&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Claims&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapClaims&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Extract user ID from JWT claims.&lt;/span&gt;
    &lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Find user by ID.&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;userID&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fiber&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusNotFound&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&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;Di situ pertama kita memastikan bahwa JWT berisi claim "sub", yang itu adalah ID pengguna. Setelah itu kita mencari data pengguna berdasarkan ID tersebut.&lt;/p&gt;

&lt;p&gt;Dengan begitu, hasil yang didapat akan selalu sesuai dengan data profil saat ini.&lt;/p&gt;

&lt;p&gt;Jawaban pertama:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Catur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Developer"&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;Jawaban kedua setelah data berubah:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Catur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Maintainer"&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;h2&gt;
  
  
  Referensi
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://doubleoctopus.com/security-wiki/network-architecture/stateless-authentication/" rel="noopener noreferrer"&gt;https://doubleoctopus.com/security-wiki/network-architecture/stateless-authentication/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtu.be/XfjQ2qO4ca8" rel="noopener noreferrer"&gt;https://youtu.be/XfjQ2qO4ca8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2020/12/21/beginners-guide-to-jwt" rel="noopener noreferrer"&gt;https://developer.okta.com/blog/2020/12/21/beginners-guide-to-jwt&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.facilelogin.com/jwt-jws-and-jwe-for-not-so-dummies-b63310d201a3" rel="noopener noreferrer"&gt;https://medium.facilelogin.com/jwt-jws-and-jwe-for-not-so-dummies-b63310d201a3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-claims" rel="noopener noreferrer"&gt;https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-claims&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple" rel="noopener noreferrer"&gt;https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/docs/secure/tokens/access-tokens" rel="noopener noreferrer"&gt;https://auth0.com/docs/secure/tokens/access-tokens&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Bacaan/Tontonan Lanjutan
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Bagaimana cara logout ketika menggunakan JWT: &lt;a href="https://medium.com/devgorilla/how-to-log-out-when-using-jwt-a8c7823e8a6" rel="noopener noreferrer"&gt;https://medium.com/devgorilla/how-to-log-out-when-using-jwt-a8c7823e8a6&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Mengenal OpenID Connect: &lt;a href="https://www.youtube.com/watch?v=6DxRTJN1Ffo&amp;amp;list=PLKCk3OyNwIzuD_jxWu-JddooM2yjX5q99&amp;amp;index=12" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=6DxRTJN1Ffo&amp;amp;list=PLKCk3OyNwIzuD_jxWu-JddooM2yjX5q99&amp;amp;index=12&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>go</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
