<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel>
    <title>Kévin THÉRAGE | Symfony Lead Developer (Expert Symfony 7 Certified) - 2022</title>
    <description><![CDATA[Kévin THÉRAGE – Symfony Lead Developer, Expert Symfony 7 Certified. Technical blog on Symfony, PHP, web development with tutorials, best practices and expert advice for developers.]]></description>
    <lastBuildDate>2026-08-28T15:17:50+00:00</lastBuildDate>
    <link href="https://ktherage.github.io/blog/2022/feed.xml" rel="self" type="application/rss+xml" />
    <link href="https://ktherage.github.io/blog/2022/" rel="alternate" type="text/html" />
    <item>
      <guid>https://ktherage.github.io/blog/2022/postman-automatic-jwt-authentication-with-expired-token-refresh/</guid>
      <title>Postman : Automatic JWT authentication with expired token refresh</title>
      <pubDate>2022-12-20T00:00:00+00:00</pubDate>
      <link href="https://ktherage.github.io/blog/2022/postman-automatic-jwt-authentication-with-expired-token-refresh/" rel="alternate" type="text/html" />
      <description><![CDATA[<p>You’ve always wondered how to get automatically authenticated toward your JWT API. I’ll tell you how I achieved this in this article.</p>
<hr>
<h2 id="short-introduction">Short Introduction</h2>
<p>You may not know what is Postman, so I'll describe it to you.
You may use any IDE (like PHPStorm, VSCode,…) to help you during the development phase of your project with things like autocompletion, test runs, debugging, and so on.</p>
<p>Postman is quite the same as an IDE but designed especially to create APIs calls. It's a lot more pleasant to use than the plain old curl command. Learn more about Postman by visiting their website <a href="https://www.postman.com/" rel="noopener noreferrer">https://www.postman.com/</a>.</p>
<p>What about JSON Web Tokens (shortened to JWT in the rest of this article)? JWT is an open-source industry standard that defines a self-contained (i.e. information are held by the token) way for securely transmitting information between parties as a JSON object.
In our case, they'll be used to give us access to an API. Learn more about JSON Web Tokens by visiting their website <a href="https://jwt.io/" rel="noopener noreferrer">https://jwt.io/</a>.</p>
<hr>
<h2 id="the-journey-to-this-magical-world">The journey to this magical world</h2>
<h3 id="in-the-begining-was-the-manual-request">In the begining, was the manual request</h3>
<p>As a Web Developer, I'm used to call APIs (mine or third party APIs) that requires a JWT, and I usually test them using Postman.
For many months, or maybe years, calling an endpoint on those kinds of APIs led me to manually apply the following workflow:</p>
<ol>
<li>Get a token through a saved request attached to my Postman collection (basically authenticating toward my API)</li>
<li>Copy the token string</li>
<li>Create an "Authorization" header</li>
<li>Paste the token string as a "Bearer" token as value for my "Authorization" header (header example: "Authorization:Bearer pastedTokenString")</li>
<li>Finally, request my endpoint</li>
</ol>
<p>This workflow also had me to update manually the JWT token when it has expired. By the time, I got a bit smarter by using Postman's environment variables in order to have the right token string stored in the proper environment (like having a JWT for the staging environment and one for the production).</p>
<p>But some months ago I was wondering if, as lazy as I am, there was a better way to make it? I also heard legends about some dev who calls his API calls without worrying about Authentication. So why not me?</p>
<h3 id="then-was-the-automatic-request">Then was the automatic request</h3>
<p>So like any good (or not 😛) developer, my journey has started on… (already guessed it 😉?) Stack Overflow!</p>
<p>I found this article there, and even if it was not the right solution, it gave me a clearer idea of where to search. In fact, and to be honest, I was searching for a piece of code that I could quickly copy and paste eyes closed 😅.</p>
<p>So I continued my quest, and I found Utkarsha Baksh's medium article "Using Postman Pre-request Script to Automatically Set Token" which has a perfect (and not too long) step by step tutorial. It details how to make a simple automatic login request before calling the desired API endpoint using small Postman's pre-request script. If you are new to that "pre-request script" concept, really shall read this article or take a look at Postman's documentation.</p>
<p>Amazing plus, there's a piece of code! So I copy/pasted it, followed the steps, adapted it to my use cases, and it worked 🎊 🎉 🪩!</p>
<p>I never got back on it until… I lost this pre-request script because of a Postman reinstallation and not having a paid version 🫣 😩.</p>
<p>This leads us to this day (2022/12/12 😉) where I was again doing the same manual routine and remembered that good old time when it was automatic.</p>
<h3 id="finally-was-the-consecration">Finally was the consecration</h3>
<p>So another time, I packed my bag and got again on that journey to that wonderful land. The trip was a lot faster that time 😅 and I found a new piece of code that looked more detailed and more respectful of JWT's refresh mechanism by not always authenticating toward the API when the token is still valid (I mean not expired).</p>
<p>This code could be found there: <a href="https://gist.github.com/Glideh/0f24b8973bb7d79ae8124fa160966df1" rel="noopener noreferrer">https://gist.github.com/Glideh/0f24b8973bb7d79ae8124fa160966df1</a></p>
<p>The only cons I've found using it directly is that it was not taking advantage of the JWT's refresh token. So I copy/pasted it and made some changes to allow an automatic refresh of the token.</p>
<hr>
<h2 id="the-code">The code</h2>
<p>To get a full tutorial on how to define a the following code as a Postman's pre-request script read Utkarsha Baksh's medium article: "Using Postman Pre-request Script to Automatically Set Token"</p>
<pre><code class="language-javascript hljs javascript" translate="no"><span class="hljs-comment">/**
 * fill in the blanks
 */</span>
<span class="hljs-keyword">const</span> TOKEN_ENV_VAR_NAME = <span class="hljs-string">'token_client'</span>
<span class="hljs-keyword">const</span> LOGIN_URL = pm.environment.get(<span class="hljs-string">"host"</span>) + <span class="hljs-string">"/api/v2/authenticate"</span>
<span class="hljs-keyword">const</span> LOGIN_BODY={
    <span class="hljs-string">"app_id"</span>: pm.environment.get(<span class="hljs-string">"app_id"</span>),
    <span class="hljs-string">"app_secret"</span>: pm.environment.get(<span class="hljs-string">"app_secret"</span>)
}

<span class="hljs-keyword">const</span> REFRESH_TOKEN_ENV_VAR_NAME = <span class="hljs-string">'refresh_token_client'</span>
<span class="hljs-keyword">const</span> REFRESH_URL = pm.environment.get(<span class="hljs-string">"host"</span>) + <span class="hljs-string">"/api/v2/authenticate/refresh"</span>
<span class="hljs-keyword">const</span> REFRESH_BODY={
    <span class="hljs-string">"refreshToken"</span>: pm.environment.get(REFRESH_TOKEN_ENV_VAR_NAME)
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">isExpiredToken</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> jwt = pm.environment.get(TOKEN_ENV_VAR_NAME)
    <span class="hljs-keyword">const</span> payload = <span class="hljs-built_in">JSON</span>.parse(atob(jwt.split(<span class="hljs-string">'.'</span>)[<span class="hljs-number">1</span>]));
    <span class="hljs-comment">// Expiration timestamp (in seconds) is located in the `exp` key</span>
    <span class="hljs-keyword">const</span> millisecBeforeExpiration = (payload.exp * <span class="hljs-number">1000</span>) - (<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()).getTime();
    <span class="hljs-keyword">if</span> (millisecBeforeExpiration &lt;= <span class="hljs-number">0</span>) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Token is expired"</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Token is still valid ! Expiring in <span class="hljs-subst">${millisecBeforeExpiration <span class="hljs-regexp">/ 1000} seconds`);
    return false;
}

function tokensExists() {
    const token = pm.environment.get(TOKEN_ENV_VAR_NAME)
    const refreshToken = pm.environment.get(REFRESH_TOKEN_ENV_VAR_NAME)
    if (!token) {
        console.log("Token not found");
        return false;
    }

    if (!refreshToken) {
        console.log("Refresh token not found");
        return false;
    }

    return true;
}

function login() {
    console.log("Authenticating")
    const body = JSON.stringify(LOGIN_BODY);
    const request = {
        url: LOGIN_URL,
        method: "POST",
        header: {
            "Content-Type": "application/</span>json<span class="hljs-string">",
            "</span>Accept<span class="hljs-string">": "</span>application<span class="hljs-regexp">/json",
        },
        body,
    };

    pm.sendRequest(request, (err, res) =&gt; {
        if (err || res.code !== 200) {
            console.log("Login failed:");
            console.log(err);
            console.log(res);

            throw new Error('Login failed, check postman\'s console for details')
        }
        pm.environment.set(TOKEN_ENV_VAR_NAME, res.json().token);
        console.log("Token saved");
        pm.environment.set(REFRESH_TOKEN_ENV_VAR_NAME, res.json().refreshToken);
        console.log("Refresh Token saved");
    });
}

function refresh() {
    console.log("Refreshing token")
    const body = JSON.stringify(REFRESH_BODY);
    const request = {
        url: REFRESH_URL,
        method: "POST",
        header: {
            "Content-Type": "application/</span>json<span class="hljs-string">",
            "</span>Accept<span class="hljs-string">": "</span>application<span class="hljs-regexp">/json",
        },
        body,
    };

    pm.sendRequest(request, (err, res) =&gt; {
        if (res.code === 498) {
            console.log('Refresh token has expired or is invalid')
            login()
            return
        }
        if (err || res.code !== 200) {
            console.log("Refreshing token failed:");
            console.log(err);
            console.log(res);

            throw new Error('Refreshing token failed, check postman\'s console for details')
        }
        console.log("Token refreshed");
        pm.environment.set(TOKEN_ENV_VAR_NAME, res.json().token);
    });
}

if (tokensExists()) {
    if (!isExpiredToken()) {
        return
    }

    refresh()
} else {
    login()
}</span></span></span></code></pre>
<hr>
<h2 id="thanks">Thanks</h2>
<p>Postman for producing tools that ease API developer's life</p>
<p>Stack overflow and his community for being the developer's life-buoy</p>
<p>Pierre de LESPINAY for the Gist that inspired me this piece of code and the current article</p>
<p>SensioLabs' Team for their help and their reviews on this article</p>]]></description>
    </item>
  </channel>
</rss>
