<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>livn</title>
    <link>https://livn.dev/</link>
    <description>liang, ivan</description>
    <pubDate>Thu, 09 Apr 2026 19:50:56 +0000</pubDate>
    <item>
      <title>Implementing Photoshop&#39;s Content-Aware Scaling </title>
      <link>https://livn.dev/seam-carving-project?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[I once took a photograph of a lighthouse by the beach. I had carefully planned the composition of my photo and was pleased with the result. Later that day, I went to go post it on Instagram, only to realize that the top of the lighthouse was cropped out by Instagram&#39;s max aspect ratio of 4:5. If only I had known of content-aware scaling!&#xA;&#xA;Content-aware scaling is a computational photography technique made possible by an algorithm called seam carving. But before we get into the details, let&#39;s talk about traditional methods of image scaling. &#xA;&#xA;!--more--&#xA;&#xA;Normally, we want to shrink or enlarge an image to fit a desired format. For example, I wanted to shrink my lighthouse photo from its original aspect ratio of 2:3 to 4:5. &#xA;&#xA;Common aspect ratios&#xA;&#xA;The traditional methods of image resizing are scaling and cropping. Scaling is used to shrink or enlarge an image. However, if the aspect ratio is locked, then scaling results in an image of the same aspect ratio — making it useless for my goal of making the lighthouse fit in Instagram&#39;s crop. If the aspect ratio is unlocked, meaning the width or height can change independently, then the image will become distorted (like those funhouse mirrors). Cropping simply removes pixels from the image periphery, which may alter the main subject of the image. Both methods are unsatisfactory because of how they affect the image content.&#xA;&#xA;Content-aware scaling aims to avoid these limitations by resizing an image while preserving the image content. The technique shrinks or enlarges an image by adding or removing unimportant pixels. This exact feature is implemented in Photoshop as Content-Aware Scaling. Now that we&#39;ve discussed the purpose of this technique, let us dive into the algorithm.&#xA;&#xA;Seam carving algorithm&#xA;&#xA;The seam carving algorithm was first published in 2007 and later refined in 2008 (see references below). The 2008 paper extended content-aware scaling to video and introduced an improved forward-energy criterion to the algorithm. This article will only discuss the application to images. &#xA;&#xA;A seam is defined as a monotonic and connected path of pixels to &#34;carve&#34; out. Monotonic means that there can be one and only one pixel in each row or column of the image. Connected here means the pixels on the path are horizontally, vertically, or diagonally adjacent. A seam can go either left-to-right or top-to-bottom, depending on which direction we want to resize the image.&#xA;&#xA;The steps of the algorithm are:&#xA;&#xA;Find the energy of each pixel.&#xA;Find the path of the optimal seam.&#xA;Remove (or insert) the seam, and repeat the process from step 1 until the desired size has been achieved.&#xA;&#xA;Find the energy of each pixel &#xA;&#xA;A pixel&#39;s energy is how much its intensity value changes from neighboring pixels. A large change indicates a sharp contrast between  neighboring pixels. This tells us where the edges of objects in the image lie. Our goal is to remove pixels that are low-energy and blend into their surroundings, thus leaving the high-energy edge pixels untouched. &#xA;&#xA;Horizontal and vertical image gradients&#xA;&#xA;We define an energy function to find the edges in an image by combining the horizontal and vertical gradients of an image. The image gradient tells us the magnitude and direction of changing values. First, we compute the convolution of our image with the Sobel operator. This computation gives us the x- and y- image gradients, which we then take the absolute values of and sum together. After summing together, we get the final energy image depicting the edges of objects within the image. &#xA;&#xA;Edges of steam valve&#xA;&#xA;Find the path of the optimal seam&#xA;&#xA;With the energy image obtained, we can now target pixels for deletion. For each row of the image, we calculate the cumulative minimum energy of the connected path it took to get each pixel. I.e., at each pixel, we choose the adjacent pixel from the previous row with the minimum value, sum it with the current pixel&#39;s value from the energy image, and store it for the next row to use. &#xA;&#xA;The optimal seam to remove has the minimum cost among all other seams, and we define the cost of a seam as the cumulative energy of all pixels on the seam. &#xA;&#xA;At the end of this dynamic programming procedure, the minimum value in the last row holds the tail of our optimal seam. By backtracking from this tail pixel, we can find the entire path of this optimal seam. &#xA;&#xA;Remove (or insert) the seam&#xA;&#xA;Seam removal and insertion work similarly; in fact, seam insertion relies on seam removal first. Recall that we continuously loop the process of finding the optimal seam and removing the seam. Thus, the image either shrinks or grows larger by 1 pixel in each iteration. &#xA;&#xA;With NumPy&#39;s Boolean array indexing, we can create a mask over the image that is the same shape and size minus the optimal seam. Then, we can copy the values of the image under the mask to a new matrix that represents the new image. We repeat this process to remove as many seams as desired. The code can be optimized by using matrix operations in NumPy and optionally Numba&#39;s JIT compiler. It is not recommended for performance reasons to use naive, nested for-loops in your code because it will take hours to process (don&#39;t ask me how I know this).&#xA;&#xA;For insertion, we do the same process in reverse. Having obtained the optimal seam path, we want to duplicate that seam and add 1 pixel to the overall width or height of the image instead of subtracting. This will require keeping track of the indices of the original image, because after insertion all pixels to the right of the seam will be shifted over by 1.&#xA;&#xA;Forward-energy criterion&#xA;&#xA;One final consideration is the improved seam carving algorithm published in the 2008 paper. The authors noticed that the original seam carving algorithm was prone to producing visual artifacts in the results. The culprit they discovered is that the original algorithm doesn&#39;t take into account energy introduced into the new image when a seam is removed. When a seam is removed, pixels that were previously separate now became adjacent. In some cases, this would create pixels with higher energy than before the removal — leading to visual artifacts in the result such as jaggedness. &#xA;&#xA;The fix was to modify the cumulative minimum cost function and consider the energy cost that would be introduced by seam removal or insertion. I.e., we look forward in time to see which pixels will become adjacent as a result of seam carving and add that to the pixel&#39;s cumulative cost. The rest of the code remains exactly the same. &#xA;&#xA;New edges created by seam carving&#xA;&#xA;Results&#xA;&#xA;The original image is a photograph of the Statue of Liberty. There are areas of water on the side that have less image content, so  we want the seam carving algorithm to target those areas over the middle of the image with the statue. &#xA;&#xA;Statue of Liberty&#xA;&#xA;Below are the images generated using the original (backward-energy) criterion and the improved forward-energy criterion. The images with red lines show the seams that were removed or inserted into the original image. &#xA;&#xA;Backward-energy seam removal:&#xA;&#xA;Backward-energy seam removal&#xA;Backward-energy seam removal with red overlay&#xA;&#xA;Forward-energy seam removal:&#xA;&#xA;Forward-energy seam removal&#xA;Forward-energy seam removal with red overlay&#xA;&#xA;Backward-energy seam insertion:&#xA;&#xA;Backward-energy seam insertion&#xA;Backward-energy seam insertion with red overlay&#xA;&#xA;Forward-energy seam insertion:&#xA;&#xA;Forward-energy seam insertion&#xA;Forward-energy seam insertion with red overlay&#xA;&#xA;Both methods produce good results, but look closely and you&#39;ll notice that the base of the statue pedestal is less distorted in the forward-energy seam removal and insertion. From the overlay of red seams, we can see that the seam carving algorithm completely avoids impacting the statue. Traditional scaling or cropping would not be able to produce similar results, and this demonstrates the usefulness of content-aware scaling.&#xA;&#xA;---&#xA;&#xA;References&#xA;&#xA;Avidan, S., &amp;#38; Shamir, A. (2007). Seam carving for content-aware image resizing. iACM SIGGRAPH 2007 Papers/i, 10–es. https://doi.org/10.1145/1275808.1276390&#xA;&#xA;Rubinstein, M., Shamir, A., &amp;#38; Avidan, S. (2008). Improved seam carving for video retargeting. iACM Transactions on Graphics/i, i27/i(3), 1–9. https://doi.org/10.1145/1360612.1360615]]&gt;</description>
      <content:encoded><![CDATA[<p>I once took a photograph of a lighthouse by the beach. I had carefully planned the composition of my photo and was pleased with the result. Later that day, I went to go post it on Instagram, only to realize that the top of the lighthouse was cropped out by Instagram&#39;s max aspect ratio of 4:5. If only I had known of content-aware scaling!</p>

<p>Content-aware scaling is a computational photography technique made possible by an algorithm called <strong>seam carving</strong>. But before we get into the details, let&#39;s talk about traditional methods of image scaling.</p>



<p>Normally, we want to shrink or enlarge an image to fit a desired format. For example, I wanted to shrink my lighthouse photo from its original aspect ratio of 2:3 to 4:5.</p>

<p><img src="https://i.snap.as/HRpf3Il4.png" alt="Common aspect ratios"/></p>

<p>The traditional methods of image resizing are <em>scaling</em> and <em>cropping</em>. Scaling is used to shrink or enlarge an image. However, if the aspect ratio is locked, then scaling results in an image of the same aspect ratio — making it useless for my goal of making the lighthouse fit in Instagram&#39;s crop. If the aspect ratio is unlocked, meaning the width or height can change independently, then the image will become distorted (like those funhouse mirrors). Cropping simply removes pixels from the image periphery, which may alter the main subject of the image. Both methods are unsatisfactory because of how they affect the image content.</p>

<p>Content-aware scaling aims to avoid these limitations by resizing an image while <strong>preserving the image content</strong>. The technique shrinks or enlarges an image by adding or removing <strong>unimportant pixels</strong>. This exact feature is implemented in Photoshop as Content-Aware Scaling. Now that we&#39;ve discussed the purpose of this technique, let us dive into the algorithm.</p>

<h2 id="seam-carving-algorithm" id="seam-carving-algorithm">Seam carving algorithm</h2>

<p>The seam carving algorithm was first published in 2007 and later refined in 2008 (see references below). The 2008 paper extended content-aware scaling to video and introduced an improved forward-energy criterion to the algorithm. This article will only discuss the application to images.</p>

<p>A <strong>seam</strong> is defined as a <em>monotonic</em> and <em>connected</em> path of pixels to “carve” out. Monotonic means that there can be one and only one pixel in each row or column of the image. Connected here means the pixels on the path are horizontally, vertically, or diagonally adjacent. A seam can go either left-to-right or top-to-bottom, depending on which direction we want to resize the image.</p>

<p>The steps of the algorithm are:</p>
<ol><li>Find the <em>energy</em> of each pixel.</li>
<li>Find the path of the optimal seam.</li>
<li>Remove (or insert) the seam, and repeat the process from step 1 until the desired size has been achieved.</li></ol>

<h3 id="find-the-energy-of-each-pixel" id="find-the-energy-of-each-pixel">Find the energy of each pixel</h3>

<p>A pixel&#39;s energy is how much its intensity value changes from neighboring pixels. A large change indicates a sharp contrast between  neighboring pixels. This tells us where the edges of objects in the image lie. Our goal is to remove pixels that are <em>low-energy</em> and blend into their surroundings, thus leaving the <em>high-energy</em> edge pixels untouched.</p>

<p><img src="https://i.snap.as/npixDeap.png" alt="Horizontal and vertical image gradients"/></p>

<p>We define an <strong>energy function</strong> to find the edges in an image by combining the horizontal and vertical gradients of an image. The image gradient tells us the magnitude and direction of changing values. First, we compute the convolution of our image with the <a href="https://en.wikipedia.org/wiki/Sobel_operator" rel="nofollow">Sobel operator</a>. This computation gives us the x- and y- image gradients, which we then take the absolute values of and sum together. After summing together, we get the final energy image depicting the edges of objects within the image.</p>

<p><img src="https://i.snap.as/hZXZk5JJ.png" alt="Edges of steam valve"/></p>

<h3 id="find-the-path-of-the-optimal-seam" id="find-the-path-of-the-optimal-seam">Find the path of the optimal seam</h3>

<p>With the energy image obtained, we can now target pixels for deletion. For each row of the image, we calculate the <strong>cumulative minimum energy</strong> of the connected path it took to get each pixel. I.e., at each pixel, we choose the <em>adjacent</em> pixel from the previous row with the minimum value, sum it with the current pixel&#39;s value from the energy image, and store it for the next row to use.</p>

<p>The optimal seam to remove has the minimum cost among all other seams, and we define the cost of a seam as the cumulative energy of all pixels on the seam.</p>

<p>At the end of this dynamic programming procedure, the minimum value in the last row holds the tail of our optimal seam. By backtracking from this tail pixel, we can find the entire path of this optimal seam.</p>

<h3 id="remove-or-insert-the-seam" id="remove-or-insert-the-seam">Remove (or insert) the seam</h3>

<p>Seam removal and insertion work similarly; in fact, seam insertion relies on seam removal first. Recall that we continuously loop the process of finding the optimal seam and removing the seam. Thus, the image either shrinks or grows larger by 1 pixel in each iteration.</p>

<p>With NumPy&#39;s <a href="https://numpy.org/devdocs/reference/arrays.indexing.html#boolean-array-indexing" rel="nofollow">Boolean array indexing</a>, we can create a <a href="https://jakevdp.github.io/PythonDataScienceHandbook/02.06-boolean-arrays-and-masks.html#Boolean-Arrays-as-Masks" rel="nofollow">mask</a> over the image that is the same shape and size minus the optimal seam. Then, we can copy the values of the image under the mask to a new matrix that represents the new image. We repeat this process to remove as many seams as desired. The code can be optimized by using matrix operations in NumPy and optionally <a href="https://numba.pydata.org/" rel="nofollow">Numba&#39;s JIT compiler</a>. It is not recommended for performance reasons to use naive, nested for-loops in your code because it will take hours to process (don&#39;t ask me how I know this).</p>

<p>For insertion, we do the same process in reverse. Having obtained the optimal seam path, we want to duplicate that seam and <em>add</em> 1 pixel to the overall width or height of the image instead of subtracting. This will require keeping track of the indices of the original image, because after insertion all pixels to the right of the seam will be shifted over by 1.</p>

<h3 id="forward-energy-criterion" id="forward-energy-criterion">Forward-energy criterion</h3>

<p>One final consideration is the improved seam carving algorithm published in the 2008 paper. The authors noticed that the original seam carving algorithm was prone to producing visual artifacts in the results. The culprit they discovered is that the original algorithm doesn&#39;t take into account energy <em>introduced</em> into the new image when a seam is removed. When a seam is removed, pixels that were previously separate now became adjacent. In some cases, this would create pixels with higher energy than before the removal — leading to visual artifacts in the result such as jaggedness.</p>

<p>The fix was to modify the cumulative minimum cost function and consider the energy cost that would be introduced by seam removal or insertion. I.e., we look <em>forward</em> in time to see which pixels will become adjacent as a result of seam carving and add that to the pixel&#39;s cumulative cost. The rest of the code remains exactly the same.</p>

<p><img src="https://i.snap.as/7BXXy9su.png" alt="New edges created by seam carving"/></p>

<h2 id="results" id="results">Results</h2>

<p>The original image is a photograph of the Statue of Liberty. There are areas of water on the side that have less image content, so  we want the seam carving algorithm to target those areas over the middle of the image with the statue.</p>

<p><img src="https://i.snap.as/2zJ0LLHL.jpg" alt="Statue of Liberty"/></p>

<p>Below are the images generated using the original (backward-energy) criterion and the improved forward-energy criterion. The images with red lines show the seams that were removed or inserted into the original image.</p>

<p><strong>Backward-energy seam removal:</strong></p>

<p><img src="https://i.snap.as/6aa55Y3Y.jpg" alt="Backward-energy seam removal"/>
<img src="https://i.snap.as/9oQqftV7.jpg" alt="Backward-energy seam removal with red overlay"/></p>

<p><strong>Forward-energy seam removal:</strong></p>

<p><img src="https://i.snap.as/PhCl7VLU.jpg" alt="Forward-energy seam removal"/>
<img src="https://i.snap.as/HeLF1UvQ.jpg" alt="Forward-energy seam removal with red overlay"/></p>

<p><strong>Backward-energy seam insertion:</strong></p>

<p><img src="https://i.snap.as/i42iS7EY.jpg" alt="Backward-energy seam insertion"/>
<img src="https://i.snap.as/G8smZPQV.jpg" alt="Backward-energy seam insertion with red overlay"/></p>

<p><strong>Forward-energy seam insertion:</strong></p>

<p><img src="https://i.snap.as/u35gVvkR.jpg" alt="Forward-energy seam insertion"/>
<img src="https://i.snap.as/t5BEVM5U.jpg" alt="Forward-energy seam insertion with red overlay"/></p>

<p>Both methods produce good results, but look closely and you&#39;ll notice that the base of the statue pedestal is less distorted in the forward-energy seam removal and insertion. From the overlay of red seams, we can see that the seam carving algorithm completely avoids impacting the statue. Traditional scaling or cropping would not be able to produce similar results, and this demonstrates the usefulness of content-aware scaling.</p>

<hr/>

<h2 id="references" id="references">References</h2>

<p>Avidan, S., &amp; Shamir, A. (2007). Seam carving for content-aware image resizing. <i>ACM SIGGRAPH 2007 Papers</i>, 10–es. <a href="https://doi.org/10.1145/1275808.1276390" rel="nofollow">https://doi.org/10.1145/1275808.1276390</a></p>

<p>Rubinstein, M., Shamir, A., &amp; Avidan, S. (2008). Improved seam carving for video retargeting. <i>ACM Transactions on Graphics</i>, <i>27</i>(3), 1–9. <a href="https://doi.org/10.1145/1360612.1360615" rel="nofollow">https://doi.org/10.1145/1360612.1360615</a></p>
]]></content:encoded>
      <guid>https://livn.dev/seam-carving-project</guid>
      <pubDate>Fri, 08 Apr 2022 23:25:25 +0000</pubDate>
    </item>
    <item>
      <title>Self-Piloting Spaceship Simulation</title>
      <link>https://livn.dev/asteroids-project?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[The goal is to simulate a self-piloting spaceship that can navigate through an asteroid field without collisions. I implemented a Kalman filter that estimates the state of the system — the asteroids’ future positions. Knowing where each asteroid is heading, I then programmed the spaceship to choose the best heading and velocity at each time step as not to crash into any asteroids.&#xA;&#xA;!--more--&#xA;&#xA;Estimating future locations of asteroids is the most important part of the project and is calculated through Kalman filtering. This is a technique to estimate the state of a system given measurements over time. For this project, the measurements are the asteroids’ coordinates (with measurement noise) and the state we estimate are the coordinates of each asteroid in the next time step. &#xA;&#xA;The Pilot class controls the spaceship’s flight path and contains three methods:&#xA;&#xA;observeasteroids is called once per time step and informs the spaceship of the latest asteroid measurements.&#xA;estimateasteroidlocs predicts the location of each asteroid in the next time step after observing asteroids.&#xA;nextmove chooses the best move given the current state of the spaceship and system.&#xA;&#xA;The Pilot class is provided an array of asteroid coordinates that are currently in the field. In a real-life scenario, we can imagine the spaceship having a sensor or radar to make these measurements. All measurements, including the one passed into the Pilot class, come with noise from the sensors, meaning these measurements may not be 100% accurate. We have to take this error into account as Gaussians when calculating our Kalman filter.&#xA;&#xA;Kalman filter cycle&#xA;&#xA;In a Kalman filter, every new measurement, observeasteroids, makes us more confident about the asteroid coordinates. However, every prediction step, estimateasteroidlocs, throws some uncertainty into the equation. The Kalman filter continually cycles through these two steps to produce the best estimate of where each asteroid could be at the current time step, but this is merely a guess and can be off. &#xA;&#xA;Asteroid measurements&#xA;&#xA;The recording above shows red dots floating around the asteroids. These red dots are estimates that are too far away from the asteroid’s actual location to be accurate. Notice how the estimates get more accurate over time as state measurements accumulate.&#xA;&#xA;To mitigate uncertainty, I have my spaceship wait at the starting area for a few time steps while the Kalman filter calibrates. Once I am confident that I have localized all the asteroids in the field to a certain margin of error, I let loose the spaceship to fly itself. The Pilot then calls nextmove at each time step while considering the coordinate estimates given by the Kalman filter.&#xA;&#xA;Test flight&#xA;&#xA;These test runs show exciting results as the spaceship zips around, narrowly avoiding catastrophic collisions.]]&gt;</description>
      <content:encoded><![CDATA[<p>The goal is to simulate a self-piloting spaceship that can navigate through an asteroid field without collisions. I implemented a <a href="https://en.wikipedia.org/wiki/Kalman_filter" rel="nofollow">Kalman filter</a> that estimates the state of the system — the asteroids’ future positions. Knowing where each asteroid is heading, I then programmed the spaceship to choose the best heading and velocity at each time step as not to crash into any asteroids.</p>



<p>Estimating future locations of asteroids is the most important part of the project and is calculated through Kalman filtering. This is a technique to estimate the state of a system given measurements over time. For this project, the measurements are the asteroids’ coordinates (with measurement noise) and the state we estimate are the coordinates of each asteroid in the next time step.</p>

<p>The <code>Pilot</code> class controls the spaceship’s flight path and contains three methods:</p>
<ol><li><code>observe_asteroids</code> is called once per time step and informs the spaceship of the latest asteroid measurements.</li>
<li><code>estimate_asteroid_locs</code> predicts the location of each asteroid in the next time step after observing asteroids.</li>
<li><code>next_move</code> chooses the best move given the current state of the spaceship and system.</li></ol>

<p>The <code>Pilot</code> class is provided an array of asteroid coordinates that are currently in the field. In a real-life scenario, we can imagine the spaceship having a sensor or radar to make these measurements. All measurements, including the one passed into the <code>Pilot</code> class, come with noise from the sensors, meaning these measurements may not be 100% accurate. We have to take this error into account as <a href="https://en.wikipedia.org/wiki/Normal_distribution" rel="nofollow">Gaussians</a> when calculating our Kalman filter.</p>

<p><img src="https://i.snap.as/wrfwQmZW.png" alt="Kalman filter cycle"/></p>

<p>In a Kalman filter, every new measurement, <code>observe_asteroids</code>, makes us more confident about the asteroid coordinates. However, every prediction step, <code>estimate_asteroid_locs</code>, throws some uncertainty into the equation. The Kalman filter continually cycles through these two steps to produce the best estimate of where each asteroid could be at the current time step, but this is merely a guess and can be off.</p>

<p><img src="https://i.snap.as/ikINb5Xi.gif" alt="Asteroid measurements"/></p>

<p>The recording above shows red dots floating around the asteroids. These red dots are estimates that are too far away from the asteroid’s actual location to be accurate. Notice how the estimates get more accurate over time as state measurements accumulate.</p>

<p>To mitigate uncertainty, I have my spaceship wait at the starting area for a few time steps while the Kalman filter calibrates. Once I am confident that I have localized all the asteroids in the field to a certain margin of error, I let loose the spaceship to fly itself. The <code>Pilot</code> then calls <code>next_move</code> at each time step while considering the coordinate estimates given by the Kalman filter.</p>

<p><img src="https://i.snap.as/hFOdI87U.gif" alt="Test flight"/></p>

<p>These test runs show exciting results as the spaceship zips around, narrowly avoiding catastrophic collisions.</p>
]]></content:encoded>
      <guid>https://livn.dev/asteroids-project</guid>
      <pubDate>Tue, 26 Oct 2021 02:01:05 +0000</pubDate>
    </item>
    <item>
      <title>JWT Authentication for Single Page Applications</title>
      <link>https://livn.dev/jwt-auth-spa?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Last updated: April 9, 2021&#xA;&#xA;A set of notes made when I was learning about web authentication. Like with anything else in software development, there are tradeoffs between using sessions vs. JWTs. At the end of this post, I highlight the security concerns that need to be taken into consideration when using JWT authentication.&#xA;&#xA;Traditional client-server interactions were straightforward request-response cycles. Modern Single-Page Application (SPA) interactions can be more complex, and can involve multiple clients and servers.&#xA;&#xA;!--more--&#xA;&#xA;Traditional server-side authentication&#xA;&#xA;In server-side web applications, the user signs in and their credentials are sent to the server. The server checks these credentials against a database, and if everything matches, a session is created on the server. The session is a piece of data that identifies the user. After the session is created, a cookie gets sent back to the browser with the sessionid of the user. The cookie is saved in localhost and anytime the user makes a request, the cookie is automatically sent along with the request to the server. The server extracts the sessionid from the cookie and verifies that the session exists in the database. This is how users remain authenticated over multiple, separate requests, and is an example of stateful authentication. &#xA;&#xA;What is a session?&#xA;&#xA;In general terms, a session is a way to preserve a desired state. For both server-side and client-side authentication, this piece of state determines whether the user is authenticated.&#xA;&#xA;In a session, this data is stored in memory on the server (or database). For server-side authentication, this is the identification (session_id) for the user, which is used to make a determination about the user&#39;s authentication status. Keeping sessions in this manner is stateful.&#xA;&#xA;In client-side authentication, the SPA has no way to know whether a user is authenticated or not. We can&#39;t store a session like the traditional manner, because the SPA is decoupled from the backend.&#xA;&#xA;Client-side authentication&#xA;&#xA;The client-side authentication flow starts off similar to the server-side flow. The user submits their credentials, which are sent to the server and checked against the database. If everything matches, a token is created and signed instead of a session. The token is sent back to the user and saved in the browser, usually in local storage or a cookie. The token is then attached to the Authorization header on every subsequent HTTP request.&#xA;&#xA;When the request is received on the backend, the token is verified by the secret key stored on the server. The payload is checked and the server looks at the claims on the token. If the token is valid, the requested resource is returned, else a 401 is returned.&#xA;&#xA;RESTful APIs have a formal constraint that they should be stateless, so traditional authentication doesn&#39;t conform to those standards. Authentication with tokens can be stateless and is a good approach for authentication with a SPA and RESTful API. &#xA;&#xA;SPAs no longer rely on the server to do authentication. Instead, the client claims to the server that it is authenticated with the token. The backend can now receive requests from multiple clients and it only cares if the token is valid. The backend acts as a decoupled API serving up resources, which means there is no need for additional user access lookups because this information can be included right in the payload.&#xA;&#xA;What&#39;s in a JWT?&#xA;&#xA;I&#39;ve been vague about what a token is so far, so let&#39;s get into the details. By token, I am referring to a JSON Web Token.&#xA;&#xA;JWTs are a method for communicating between computers in a secure way, through a JSON payload. A web browser can send a claim (assertions) with a JWT to a server that asserts something about the identity (user). The client says &#34;believe me, and here&#39;s the proof&#34;. The token is digitally signed, and contains the proof that this client is who they say they are. All this information exists in the self-contained, compact JWT. This is a great method for performing stateless authentication.&#xA;&#xA;Stateless authentication means that the client and server don&#39;t need to know too much about one another. The token contains the necessary information to identify the user, whereas stateful authentication uses sessions to identify users. These sessions must be stored in the database, hence stateful.&#xA;&#xA;Example JWT and its three sections&#xA;&#xA;This is the structure of a basic JWT and its three main components. The header contains meta information about the token, the payload contains the information you want to pass along, and the signature is what makes the JWT secure. The JWT is hashed with this secret key and makes the token &#34;unchangeable&#34;, in that changes would produce a different hashed JWT.&#xA;&#xA;It&#39;s important to note that JWTs are not encrypted, meaning that if someone were to get a hold of a JWT, they could easily decode it and extract the payload from it. This is why you should not put sensitive information in the payload, just enough to identify the user. However, if a JWT is modified, it is immediately invalidated because the hashing algorithm will produce a completely new JWT.&#xA;&#xA;The payload contains claims about the entity for which it was issued. The JWT standard describes a set of reserved claims: its, sub, aud, exp, nbf, int, jti. You can see above in the picture that sub is a type of user ID and can be used to identify the user. You can add any arbitrary claim, such as name.&#xA;&#xA;Implementation details about JWT authentication &#xA;&#xA;Because JWT authentication is stateless, the best method for determining the user&#39;s authentication status is to go by the  JWT&#39;s expiry time. If the JWT is expired, it can&#39;t be used to access protected resources.&#xA;&#xA;When the user logs in, we provide an application-wide &#34;flag&#34; to indicate the user is logged in, by putting the token in  local storage. At any point in the application&#39;s lifecycle the token&#39;s exp value can be checked against the current time. An example of a lifecycle event is the route changing. If the token expires, we change the &#34;flag&#34; (remove from local storage) to indicate the user is logged out.&#xA;&#xA;To conditionally render content based on the user&#39;s authentication status, we can implement a function to check if the user is authenticated or not. We do this by grabbing the decoded token&#39;s expiry date and comparing to the current time. We can then store this isAuthenticated flag on the global state.&#xA;&#xA;User information in the JWT payload&#xA;&#xA;The JWT payload is what makes the JWT useful as it contains a &#34;summary&#34; of the user. We want to use the information in the payload to feed our profile view. A JavaScript library that can decode the JWT payload is jwt-decode.&#xA;&#xA;Payload best practices&#xA;&#xA;It may be tempting to store a whole profile object in the payload, but we shouldn&#39;t do this. It&#39;s important to keep the JWT small because it is sent on every single request. Furthermore, because the JWT is decodable, we don&#39;t want to store any sensitive information in there.&#xA;&#xA;So, what should be in the payload? Basic user information such as email, name, and picture are good things to keep in the payload that can build a simplified user profile. Consider providing a separate endpoint which retrieves a user profile object if you need more profile data.&#xA;&#xA;Protecting API resources&#xA;&#xA;The whole point of implementing authentication in an app is to restrict resource access to users who own those resources. We can think of the different levels of access:&#xA;&#xA;Publicly Accessible: Open to anyone&#xA;Limited to Authenticated Users: Open to anyone logged in&#xA;Limited to a Single Authenticated User: Open only to the user logged in&#xA;Limited to a Subset of Authenticated Users: Open to anyone of a specific privilege&#xA;&#xA;JWT middleware&#xA;&#xA;We can create API endpoints that require an authentication check. To pass the check, a valid JWT must be present. If it&#39;s valid, access is granted for the resource.&#xA;&#xA;To make this JWT check available for multiple endpoints, we can create a custom middleware that will extract the incoming Bearer tokens and verify it with the server&#39;s secret key. We can also specify the scope or levels of access for a specific endpoint by using a library like express-jwt-authz, which will validate against the scope value from a JWT payload.&#xA;&#xA;Making authenticated requests&#xA;&#xA;Sending authenticated requests from the client to the server requires us to first retrieve the JWT from local storage and attaching it in the Authorization header. Or, if we choose to store the token in a cookie, it will automatically be attached to every request by the browser.&#xA;&#xA;const token = await getToken();&#xA;const response = await fetch(&#39;/protected-route&#39;, {&#xA;&#x9;headers: {&#xA;&#x9;&#x9;Authorization: Bearer ${token}&#xA;&#x9;}&#xA;})&#xA;&#xA;The Bearer scheme is coming from the OAuth 2.0 specification.&#xA;&#xA;Protecting client-side routes&#xA;&#xA;In your client-side application, you may want to protect certain routes from unauthenticated users. In traditional web apps, the server can verify the user&#39;s session before serving up the requested page. In SPAs, however, we don&#39;t have a server protecting our routes and the entire client-side is bundled and loaded on the first visit. We can protect API resources using the JWT check, but how will we protect client-side routes?&#xA;&#xA;Protecting client-side routes can be tricky because the user can modify the expiration time or scope in their JWT. Moreover, we can&#39;t verify the JWT on the client-side because the secret only lives on the server. &#xA;&#xA;Does it matter if the user accesses protected client-side routes? In the end, even if a user hacks their way into the protected route, they will not receive any information because protected resources live on the server. The server is responsible for feeding data into the client view, so as long as sensitive information is stored correctly on the server, there is no issue with a simple check on the client-side. &#xA;&#xA;Given what we discussed above, we can safely use the expiration time and scope claims from the JWT to decide what to render to the user. We can build out a PrivateRoute component to wrap routes we want to protect. &#xA;&#xA;Important considerations&#xA;&#xA;Nothing is 100% secure, and JWTs are no exception. There are several attack vectors on JWT authentication. If you store your token in local storage, you are vulnerable to cross-site scripting (XSS). If you do store your token in local storage, you should be protecting your site against XSS by ensuring you are following protocols. If you store your token in a cookie, you are vulnerable to cross-site request forgery (CSRF). Cookies can be set as httpOnly to prevent JavaScript access. It is commonly recommended to store the token in a httpOnly cookie.&#xA;&#xA;There are also man-in-the-middle attacks (MITM), which is prevalent on public networks. Always serve your application and API over HTTPS.&#xA;&#xA;Because JWTs should be short-lived (around 1h), we need to implement refresh tokens to provide a better user experience. A refresh token generates new tokens for the user when their tokens expire or are about to expire. These refresh tokens are long-lived and stored on the server. They should be kept secret and never stored on the client.&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p><em>Last updated: April 9, 2021</em></p>

<p>A set of notes made when I was learning about web authentication. Like with anything else in software development, there are tradeoffs between using sessions vs. JWTs. At the end of this post, I highlight the security concerns that need to be taken into consideration when using JWT authentication.</p>

<p>Traditional client-server interactions were straightforward request-response cycles. Modern Single-Page Application (SPA) interactions can be more complex, and can involve multiple clients and servers.</p>



<h2 id="traditional-server-side-authentication" id="traditional-server-side-authentication">Traditional server-side authentication</h2>

<p>In server-side web applications, the user signs in and their credentials are sent to the server. The server checks these credentials against a database, and if everything matches, a session is created on the server. The session is a piece of data that identifies the user. After the session is created, a cookie gets sent back to the browser with the <code>session_id</code> of the user. The cookie is saved in localhost and anytime the user makes a request, the cookie is automatically sent along with the request to the server. The server extracts the <code>session_id</code> from the cookie and verifies that the session exists in the database. This is how users remain authenticated over multiple, separate requests, and is an example of <em>stateful</em> authentication.</p>

<h3 id="what-is-a-session" id="what-is-a-session">What is a session?</h3>

<p>In general terms, a session is a way to preserve a desired state. For both server-side and client-side authentication, this piece of state determines whether the user is authenticated.</p>

<p>In a session, this data is stored in memory on the server (or database). For server-side authentication, this is the identification (<code>session_id</code>) for the user, which is used to make a determination about the user&#39;s authentication status. Keeping sessions in this manner is stateful.</p>

<p>In client-side authentication, the SPA has no way to know whether a user is authenticated or not. We can&#39;t store a session like the traditional manner, because the SPA is decoupled from the backend.</p>

<h2 id="client-side-authentication" id="client-side-authentication">Client-side authentication</h2>

<p>The client-side authentication flow starts off similar to the server-side flow. The user submits their credentials, which are sent to the server and checked against the database. If everything matches, a <em>token</em> is created and signed instead of a session. The token is sent back to the user and saved in the browser, usually in local storage or a cookie. The token is then attached to the <code>Authorization</code> header on every subsequent HTTP request.</p>

<p>When the request is received on the backend, the token is verified by the secret key stored on the server. The payload is checked and the server looks at the claims on the token. If the token is valid, the requested resource is returned, else a 401 is returned.</p>

<p>RESTful APIs have a formal constraint that they should be stateless, so traditional authentication doesn&#39;t conform to those standards. Authentication with tokens <em>can be</em> stateless and is a good approach for authentication with a SPA and RESTful API.</p>

<p>SPAs no longer rely on the server to do authentication. Instead, the client claims to the server that it is authenticated with the token. The backend can now receive requests from multiple clients and it only cares if the token is valid. The backend acts as a decoupled API serving up resources, which means there is no need for additional user access lookups because this information can be included right in the payload.</p>

<h3 id="what-s-in-a-jwt" id="what-s-in-a-jwt">What&#39;s in a JWT?</h3>

<p>I&#39;ve been vague about what a token is so far, so let&#39;s get into the details. By token, I am referring to a JSON Web Token.</p>

<p>JWTs are a method for communicating between computers in a secure way, through a JSON payload. A web browser can send a claim (assertions) with a JWT to a server that asserts something about the identity (user). The client says “believe me, and here&#39;s the proof”. The token is digitally signed, and contains the proof that this client is who they say they are. All this information exists in the self-contained, compact JWT. This is a great method for performing <em>stateless</em> authentication.</p>

<p>Stateless authentication means that the client and server don&#39;t need to know too much about one another. The token contains the necessary information to identify the user, whereas stateful authentication uses sessions to identify users. These sessions must be stored in the database, hence stateful.</p>

<p><img src="https://i.snap.as/YlmuSoMx.png" alt="Example JWT and its three sections"/></p>

<p>This is the structure of a basic JWT and its three main components. The header contains meta information about the token, the payload contains the information you want to pass along, and the signature is what makes the JWT secure. The JWT is hashed with this secret key and makes the token “unchangeable”, in that changes would produce a different hashed JWT.</p>

<p>It&#39;s important to note that JWTs are <strong>not encrypted</strong>, meaning that if someone were to get a hold of a JWT, they could easily decode it and extract the payload from it. This is why you should not put sensitive information in the payload, just enough to identify the user. However, if a JWT is modified, it is immediately invalidated because the hashing algorithm will produce a completely new JWT.</p>

<p>The payload contains claims about the entity for which it was issued. The JWT standard describes a set of reserved claims: <code>its</code>, <code>sub</code>, <code>aud</code>, <code>exp</code>, <code>nbf</code>, <code>int</code>, <code>jti</code>. You can see above in the picture that <code>sub</code> is a type of user ID and can be used to identify the user. You can add any arbitrary claim, such as name.</p>

<h2 id="implementation-details-about-jwt-authentication" id="implementation-details-about-jwt-authentication">Implementation details about JWT authentication</h2>

<p>Because JWT authentication is stateless, the best method for determining the user&#39;s authentication status is to go by the  JWT&#39;s expiry time. If the JWT is expired, it can&#39;t be used to access protected resources.</p>

<p>When the user logs in, we provide an application-wide “flag” to indicate the user is logged in, by putting the token in  local storage. At any point in the application&#39;s lifecycle the token&#39;s <code>exp</code> value can be checked against the current time. An example of a lifecycle event is the route changing. If the token expires, we change the “flag” (remove from local storage) to indicate the user is logged out.</p>

<p>To conditionally render content based on the user&#39;s authentication status, we can implement a function to check if the user is authenticated or not. We do this by grabbing the decoded token&#39;s expiry date and comparing to the current time. We can then store this isAuthenticated flag on the global state.</p>

<h3 id="user-information-in-the-jwt-payload" id="user-information-in-the-jwt-payload">User information in the JWT payload</h3>

<p>The JWT payload is what makes the JWT useful as it contains a “summary” of the user. We want to use the information in the payload to feed our profile view. A JavaScript library that can decode the JWT payload is <code>jwt-decode</code>.</p>

<h3 id="payload-best-practices" id="payload-best-practices">Payload best practices</h3>

<p>It may be tempting to store a whole profile object in the payload, but we shouldn&#39;t do this. It&#39;s important to keep the JWT small because it is sent on every single request. Furthermore, because the JWT is decodable, we don&#39;t want to store any sensitive information in there.</p>

<p>So, what should be in the payload? Basic user information such as email, name, and picture are good things to keep in the payload that can build a simplified user profile. Consider providing a separate endpoint which retrieves a user profile object if you need more profile data.</p>

<h3 id="protecting-api-resources" id="protecting-api-resources">Protecting API resources</h3>

<p>The whole point of implementing authentication in an app is to restrict resource access to users who own those resources. We can think of the different levels of access:</p>
<ul><li>Publicly Accessible: Open to anyone</li>
<li>Limited to Authenticated Users: Open to anyone logged in</li>
<li>Limited to a Single Authenticated User: Open only to the user logged in</li>
<li>Limited to a Subset of Authenticated Users: Open to anyone of a specific privilege</li></ul>

<h3 id="jwt-middleware" id="jwt-middleware">JWT middleware</h3>

<p>We can create API endpoints that require an authentication check. To pass the check, a valid JWT must be present. If it&#39;s valid, access is granted for the resource.</p>

<p>To make this JWT check available for multiple endpoints, we can create a custom middleware that will extract the incoming <code>Bearer</code> tokens and verify it with the server&#39;s secret key. We can also specify the <code>scope</code> or levels of access for a specific endpoint by using a library like <code>express-jwt-authz</code>, which will validate against the <code>scope</code> value from a JWT payload.</p>

<h3 id="making-authenticated-requests" id="making-authenticated-requests">Making authenticated requests</h3>

<p>Sending authenticated requests from the client to the server requires us to first retrieve the JWT from local storage and attaching it in the <code>Authorization</code> header. Or, if we choose to store the token in a cookie, it will automatically be attached to every request by the browser.</p>

<pre><code class="language-js">const token = await getToken();
const response = await fetch(&#39;/protected-route&#39;, {
	headers: {
		Authorization: `Bearer ${token}`
	}
})
</code></pre>

<p>The <code>Bearer</code> scheme is coming from the OAuth 2.0 specification.</p>

<h3 id="protecting-client-side-routes" id="protecting-client-side-routes">Protecting client-side routes</h3>

<p>In your client-side application, you may want to protect certain routes from unauthenticated users. In traditional web apps, the server can verify the user&#39;s session before serving up the requested page. In SPAs, however, we don&#39;t have a server protecting our routes and the entire client-side is bundled and loaded on the first visit. We can protect API resources using the JWT check, but how will we protect client-side routes?</p>

<p>Protecting client-side routes can be tricky because the user can modify the expiration time or scope in their JWT. Moreover, we can&#39;t verify the JWT on the client-side because the secret only lives on the server.</p>

<p>Does it matter if the user accesses protected client-side routes? In the end, even if a user hacks their way into the protected route, they will not receive any information because protected resources live on the server. The server is responsible for feeding data into the client view, so as long as sensitive information is stored correctly on the server, there is no issue with a simple check on the client-side.</p>

<p>Given what we discussed above, we can safely use the expiration time and scope claims from the JWT to decide what to render to the user. We can build out a <code>PrivateRoute</code> component to wrap routes we want to protect.</p>

<h2 id="important-considerations" id="important-considerations">Important considerations</h2>

<p>Nothing is 100% secure, and JWTs are no exception. There are several attack vectors on JWT authentication. If you store your token in local storage, you are vulnerable to cross-site scripting (XSS). If you do store your token in local storage, you should be protecting your site against XSS by ensuring you are following protocols. If you store your token in a cookie, you are vulnerable to cross-site request forgery (CSRF). Cookies can be set as <code>httpOnly</code> to prevent JavaScript access. It is commonly recommended to store the token in a <code>httpOnly</code> cookie.</p>

<p>There are also man-in-the-middle attacks (MITM), which is prevalent on public networks. Always serve your application and API over HTTPS.</p>

<p>Because JWTs should be short-lived (around 1h), we need to implement refresh tokens to provide a better user experience. A refresh token generates new tokens for the user when their tokens expire or are about to expire. These refresh tokens are long-lived and stored on the server. They should be kept secret and never stored on the client.</p>
]]></content:encoded>
      <guid>https://livn.dev/jwt-auth-spa</guid>
      <pubDate>Sat, 12 Dec 2020 20:52:39 +0000</pubDate>
    </item>
  </channel>
</rss>