<?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: Alex Savage</title>
    <description>The latest articles on DEV Community by Alex Savage (@savagealex).</description>
    <link>https://dev.to/savagealex</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F807295%2Ff1e62fa9-2153-4701-9051-a0375090bf2b.jpeg</url>
      <title>DEV Community: Alex Savage</title>
      <link>https://dev.to/savagealex</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/savagealex"/>
    <language>en</language>
    <item>
      <title>API provider contract testing for all with Portman, OpenAPI and Postman</title>
      <dc:creator>Alex Savage</dc:creator>
      <pubDate>Wed, 10 May 2023 15:08:00 +0000</pubDate>
      <link>https://dev.to/oneadvanced/api-provider-contract-testing-for-all-with-portman-openapi-and-postman-4ll1</link>
      <guid>https://dev.to/oneadvanced/api-provider-contract-testing-for-all-with-portman-openapi-and-postman-4ll1</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;APIs are everywhere and power almost everything we use today.&lt;br&gt;
OpenAPI has revolutionized the way APIs are designed, documented, deployed... Its kind of a big thing.&lt;/p&gt;

&lt;p&gt;Making a great OpenAPI definition is helped with amazing tools such as &lt;a href="https://stoplight.io/open-source/spectral"&gt;Stoplight Spectral&lt;/a&gt; which can help you ensure that your definition is not only valid against the OpenAPI specification but also your own API standards through custom linting rules.&lt;/p&gt;

&lt;p&gt;How do you ensure that what you create and deploy matches your definition? Do you write all your tests by hand? Do you probe with Postman and hope for the best?&lt;/p&gt;

&lt;p&gt;Maybe you do today but this blog will help you try something new... Welcome to the world of &lt;em&gt;contract testing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;By the end of this blog you will have completed your first contract test (against my mocked API) and you should be free to use this knowledge to find gaps in your or any API out there subject to permission!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Rather than spending too long explaining what contract testers are, where they came from etc... Lets use one! (It's free so what is stopping you?)&lt;/p&gt;

&lt;p&gt;We will be using &lt;a href="https://github.com/apideck-libraries/portman"&gt;apideck Portman&lt;/a&gt;. You are welcome to follow their readme.MD and come back later or follow the instructions here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Portman
&lt;/h3&gt;

&lt;p&gt;Install Portman&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ npm install -g @apideck/portman&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Initialize your CLI&lt;/p&gt;

&lt;p&gt;&lt;code&gt;portman --init&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a contract test to run in Postman
&lt;/h3&gt;

&lt;p&gt;Make a new directory and download this example portman configuration file and store it there&lt;br&gt;
&lt;a href="https://api.oneadvanced.com/portman/configurations/portman-config.adv.json"&gt;https://api.oneadvanced.com/portman/configurations/portman-config.adv.json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Download this openAPI definition from SwaggerHub (the unresolved YAML works fine)&lt;br&gt;
Link to SwaggerHub: &lt;a href="https://app.swaggerhub.com/apis/AdvancedComputerSoft/Contract-Breaker/1.0.0"&gt;https://app.swaggerhub.com/apis/AdvancedComputerSoft/Contract-Breaker/1.0.0&lt;/a&gt;&lt;br&gt;
Direct link to YAML: &lt;a href="https://api.swaggerhub.com/apis/AdvancedComputerSoft/Contract-Breaker/1.0.0"&gt;https://api.swaggerhub.com/apis/AdvancedComputerSoft/Contract-Breaker/1.0.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the directory that has both files and run the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;portman -c portman-config.adv.json -l AdvancedComputerSoft-Contract-Breaker-1.0.0-swagger.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foxvmbwzlpm0a8s1dhnof.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foxvmbwzlpm0a8s1dhnof.png" alt="Terminal output from a successful creation of tests using Portman ready to import manually into Postman" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Portman will use the configuration file to port the openAPI definition to a Postman collection you can now import into Postman and start running tests!&lt;/p&gt;

&lt;h3&gt;
  
  
  Run the test
&lt;/h3&gt;

&lt;p&gt;Import the Postman collection file from the tmp/converted folder that Portman created as part of the process.&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevq4lni1vzgj64otiqwn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevq4lni1vzgj64otiqwn.png" alt="Postman application with the manually imported collection that was created by Portman" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the List Countries request and press send!&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8zqrjssrcskdl69pergb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8zqrjssrcskdl69pergb.png" alt="Postman test result showing failed tests" width="800" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You ran a test and you got some failed tests. (Hopefully)&lt;br&gt;
Don't worry, that was supposed to happen.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happened?
&lt;/h3&gt;

&lt;p&gt;Lets go back and look at the collection.&lt;br&gt;
At first glance, it looks like any other collection. Check the tests tab and you should see something new.&lt;/p&gt;

&lt;p&gt;This is where Portman has done its magic and created contract tests for you to run against the API. The OpenAPI definition was used to generate these tests.&lt;/p&gt;


&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lrwnllwrd5mfbu916k2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7lrwnllwrd5mfbu916k2.png" alt="Postman tests that were generated by Portman and included in the collection. Details below" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see Postman will now test to ensure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The status code matches 200&lt;/li&gt;
&lt;li&gt;The response is application/json&lt;/li&gt;
&lt;li&gt;The response has a body which is an object&lt;/li&gt;
&lt;li&gt;The response body validates against the schema from the OpenAPI&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Bonus - The response is received within 2000ms (This is not taken from the OpenAPI definition but from the Portman configuration&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this and you did not need to write anything. No human error, no missed tests. All of it was auto-generated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interpreting the responses
&lt;/h3&gt;


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

&lt;p&gt;Postman has reported that 3 out of 5 tests have failed:&lt;/p&gt;

&lt;p&gt;1) &lt;code&gt;[GET]::/countries - Response status code is 200 | AssertionError: expected 201 to equal 200&lt;/code&gt; &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This error was raised by the status code check as the API incorrectly returned a 201 response code instead of a 200 which it was expecting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;2) &lt;code&gt;[GET]::/countries - Schema is valid | AssertionError: expected data to satisfy schema but found following errors: data.data[0] should have required property 'countryId', data.data[0].population should be integer, data.data[1] should NOT have additional properties, data.data[1].countryId should be string, data.data[1].population should be integer, data.pagination should NOT have additional properties, data.pagination.offset should be integer&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This error was contains all the json schema validation issues. There are missing required properties, wrong types and additionProperties (these are caused by misspelled properties or those that were not documented)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What you should find is this isn't an API that you would want to release! Contract testing lets you find where your deployed API (or your one that you are developing) doesn't match the OpenAPI definition. Catch things now before you release!&lt;/p&gt;

&lt;h2&gt;
  
  
  More tests
&lt;/h2&gt;

&lt;p&gt;Portman has lots of other functionality to explore.&lt;/p&gt;

&lt;p&gt;I would recommend to first look at overrides to try manipulating the collection. I started by making tests that removed the security and tried to call the API. You can stipulate which response from the OpenAPI you wish to receive. I was expecting a 401 to be returned. The Portman documentation has details of all of the functionality on offer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;APIs need to match their documentation. Whether or not you start with Design in OpenAPI or start with code, contract testing can ensure the 2 are in sync so your consumers don't get surprises and raise issues.&lt;/p&gt;

</description>
      <category>api</category>
      <category>openapi</category>
      <category>testing</category>
      <category>postman</category>
    </item>
    <item>
      <title>JWT.io signature validation</title>
      <dc:creator>Alex Savage</dc:creator>
      <pubDate>Wed, 09 Feb 2022 15:59:51 +0000</pubDate>
      <link>https://dev.to/oneadvanced/jwtio-signature-validation-4dhk</link>
      <guid>https://dev.to/oneadvanced/jwtio-signature-validation-4dhk</guid>
      <description>&lt;p&gt;Have you ever wondered how &lt;a href="https://jwt.io/" rel="noopener noreferrer"&gt;jwt.io&lt;/a&gt; gives you the Signature Verified badge when you paste a new JWT?&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.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%2Ftu7fxk1b37z4a553tm6i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftu7fxk1b37z4a553tm6i.png" alt="Signature verified with a tick from jwt.io"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Note: This only works for asymmetric signing algorithms such as RS256 signed JWTs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The JWT was signed using a private key which is safely inside the issuer but there is a public key available so that any recipient of the token can validate if it is valid or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This blog is focused on using a familiar and popular tool as a worked example for how automatic signature verification of JWTs can happen. It is not about recommending you to or not to use tools like this. I do however want to pause for a second to remind you that &lt;a href="https://jwt.io/" rel="noopener noreferrer"&gt;jwt.io&lt;/a&gt; is an online tool. It does state that it runs in the client, but as ever you should never automatically trust online tools and always consider the risk / threat they pose. &lt;/p&gt;

&lt;p&gt;As a minimum, I would suggest never putting production tokens into sites like this (or anywhere you don't control) and recommend you find a locally runnable alternative. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.jvt.me/" rel="noopener noreferrer"&gt;Jamie Tanna&lt;/a&gt; who is a member of my favorite API community (&lt;a href="https://apisyouwonthate.com/community" rel="noopener noreferrer"&gt;APIs you wont hate&lt;/a&gt;), wrote a fantastic blog actively discouraging the use of tools like this: &lt;a href="https://www.jvt.me/posts/2020/09/01/against-online-tooling/" rel="noopener noreferrer"&gt;Why I Actively Discourage Online Tooling like jwt.io&lt;/a&gt;. This might be a strange link in a blog that explains how this tool works but it is a popular tool that many take for granted and has some interesting functionality to share.&lt;/p&gt;

&lt;p&gt;Hopefully the introduction has not put you off (that was not the intention). If not, lets explain how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;Inside the decoded JWT payload is the &lt;code&gt;iss&lt;/code&gt; property (Issuer of the token). This will be a URI that can be used to find the &lt;strong&gt;well-known endpoint&lt;/strong&gt; and from there the &lt;code&gt;jwks_uri&lt;/code&gt; which is the location where the public key(s) can be retrieved.&lt;/p&gt;

&lt;p&gt;Important note: JWTs are untrusted input so you should not directly trust what is located inside! This means if you are relying on the issuer to retrieve the public key, make sure you are validating the issuer before calling it to retrieve a public key. It might not be your issuer! Similarly the token could tell you where the public keys are via the &lt;code&gt;jku&lt;/code&gt; parameter to send your verify function to retrieve keys an attacker controls.&lt;/p&gt;

&lt;p&gt;Below is an example of an access token that is a JWT in encoded and decoded form using &lt;a href="https://jwt.io" rel="noopener noreferrer"&gt;https://jwt.io&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.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%2Fz2tcwndlp23mym86oxav.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fz2tcwndlp23mym86oxav.png" alt="An example JWT from https://jwt.io in encoded and decoded form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a well-known endpoint?
&lt;/h2&gt;

&lt;p&gt;Thanks to &lt;a href="https://datatracker.ietf.org/doc/html/rfc8414" rel="noopener noreferrer"&gt;RFC8414 - OAuth 2.0 Authorization Server Metadata&lt;/a&gt;, authorization servers provide a &lt;strong&gt;well-known&lt;/strong&gt; endpoint that returns information about how to interact with the service at: &lt;/p&gt;
&lt;p&gt;&lt;br&gt;
 &lt;code&gt;http://service-url.com/.well-known/openid-configuration&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It returns information such as the OAuth token and authorization endpoints, which OAuth grants are available, scopes and many more. In order to retrieve the public keys we need to validate our token, we need the &lt;strong&gt;jwks_uri&lt;/strong&gt;&lt;/p&gt;

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

// Example response from a well-known endpoint provided by Auth0.com
// Some items are removed to reduce the overall height of this code block
{
  "issuer": "https://dev-spu08zrz.us.auth0.com/",
  "authorization_endpoint": "https://dev-spu08zrz.us.auth0.com/authorize",
  "token_endpoint": "https://dev-spu08zrz.us.auth0.com/oauth/token",
  "jwks_uri": "https://dev-spu08zrz.us.auth0.com/.well-known/jwks.json"
  "scopes_supported":
    [
      "openid",
      "profile",
      "offline_access",
      "name",
      "email"
    ],
  "code_challenge_methods_supported": ["S256", "plain"],
  "response_modes_supported": ["query", "fragment", "form_post"],
  "subject_types_supported": ["public"],
  "id_token_signing_alg_values_supported": ["HS256", "RS256"],
  "token_endpoint_auth_methods_supported":
    ["client_secret_basic", "client_secret_post"],
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  jwks_uri
&lt;/h2&gt;

&lt;p&gt;The jwks_uri returns the JSON Web Key Set (List of JSON web keys). There is often more than one to allow for key rotation. Each key in the set contains the public key that can be used to verify the signature of a the JWT.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-key-sets" rel="noopener noreferrer"&gt;Auth0 has a detailed write up on JSON Web Keys here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is an example response from a jwks_uri. You can see there are 2 keys currently available. It contains the required information for a token verifier to locate the correct public key that matches the &lt;code&gt;kid&lt;/code&gt; (key id) from the token header and pass it to the verifying function.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Key length has been shorted to make this code block more readable.
{
  "keys": [
    {
      "alg": "RS256", // This is the algorithm that was used
      "kty": "RSA", // This is the key type: RSA being asymmetric
      "use": "sig", // This is the usage: sig = signing, enc = encrypting
      "n": "xi9SZLDzHULsG_ab9zBO2....", //This is the modulo of the public key
      "e": "AQAB", // This is the exponent of the public key (used with the modulo)
      "kid": "uMSsz8nx8OEuXhEbnXcoH", // This is the Key Id needed to match the header
      "x5t": "k1uZJUy2G7i-acZ36Yg....", // This is the thumbprint of the x509 cert
      "x5c": [
        "MIIDDTCCAfWgAwIBAgIJEd...." // This is the x509 certificate chain
      ]
    },
    {
      "alg": "RS256",
      "kty": "RSA",
      "use": "sig",
      "n": "yBMPFvevpfesWmtdmt8V....",
      "e": "AQAB",
      "kid": "oHf3mAsLQ2vaXgxMRP0qh",
      "x5t": "jN_H055zOjrXkQSdiunn....",
      "x5c": [
        "MIIDDTCCAfWgAwIBAgIJej6...."
      ]
    }
  ]
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Verifying JWTs
&lt;/h2&gt;

&lt;p&gt;Using the kid and alg in the JWT header (JOSE Header from &lt;a href="https://datatracker.ietf.org/doc/html/rfc7515" rel="noopener noreferrer"&gt;RFC7515 - JSON Web Signature (JWS)&lt;/a&gt;) and the public keys from the authorization servers jwks_uri, we can verify the signature. &lt;/p&gt;


&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.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%2Fzpjw7mh641s47ruom0j6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fzpjw7mh641s47ruom0j6.png" alt="An example JWT's JOSE header from https://jwt.io in decoded form. It contains the details of how the token is signed, its type and which key was used to sign it."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify the signature, &lt;a href="https://jwt.io/" rel="noopener noreferrer"&gt;jwt.io&lt;/a&gt; uses the JWK’s: key id, e and n values which represent the public key.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
    "alg": "RS256",
    "kty": "RSA",
    "use": "sig",
    "n": "xi9SZLDzHULsG_ab9zBO2b1L...",
    "e": "AQAB",
    "kid": "uMSsz8nx8OEuXhEbnXcoH",
    "x5t": "k1uZJUy2G7i-acZ36YgCw6...",
    "x5c": [
        "MIIDDTCCAfWgAwIBAgIJEd8o..."
    ]
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you want to manually verify signatures with &lt;a href="https://jwt.io/" rel="noopener noreferrer"&gt;jwt.io&lt;/a&gt;, you will need to paste in a JWK (as above) into the box in the bottom right (normally automatically completed) and it will verify the signature. &lt;/p&gt;

&lt;h3&gt;
  
  
  What about PEMs?
&lt;/h3&gt;

&lt;p&gt;Some verify functions such as the popular &lt;a href="https://github.com/auth0/node-jsonwebtoken/blob/master/verify.js" rel="noopener noreferrer"&gt;Auth0 node-jsonwebtoken&lt;/a&gt; library requires the public key in PEM format. There are many libraries available to do this conversation.&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br&gt;
Here is an example of a JWK converted into PEM format: (Its not a real one)&lt;/p&gt;


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

&lt;p&gt;-----BEGIN PUBLIC KEY-----&lt;br&gt;
MIIBIjANBgkqhkBAQEFAAOCAQ8AMIIAQEAi3gtrfA&lt;br&gt;
MtCEFTqRWF9wxfnGA2VYVsI7jByBK7/ulIdHI64B2&lt;br&gt;
IQgct0hLRPcPFHOhBxjwa8Ls7pvNyUPD4srMrzW27&lt;br&gt;
EVeShS09Z5F4/tBWFpxzi+Cdpd8mKAqJyQYebgfBE&lt;br&gt;
Kv61IrhNOPRypWJG40+YTO5rDAkKsF0HXZ+beuabD&lt;br&gt;
vWiIwV5A76866FOn3PKL73yTdGEjiXuB7o9Aak8k/&lt;br&gt;
2wIDAQAB&lt;br&gt;
-----END PUBLIC KEY-----&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Summary&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;If you are using JWTs today you have probably come across JWT.io, but perhaps have never given a second thought to how the validation works. I hadn't, but found it interesting and felt the need to share with others in case they did too.&lt;/p&gt;

&lt;p&gt;Perhaps you are using them today but had not looked into the detail of the various components you are or could be using.&lt;/p&gt;

&lt;p&gt;I hope you found this helpful to gain some more knowledge but as always, be careful and keep your applications safe. They are many ways to open up security holes by misconfiguration or out of date libraries. Do your research and lots of testing.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Safely Handling JWTs</title>
      <dc:creator>Alex Savage</dc:creator>
      <pubDate>Wed, 02 Feb 2022 13:58:54 +0000</pubDate>
      <link>https://dev.to/oneadvanced/safely-handling-jwts-5d49</link>
      <guid>https://dev.to/oneadvanced/safely-handling-jwts-5d49</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foat95cmbis0kmoccmry8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foat95cmbis0kmoccmry8.png" alt="MEME with character Mugatu from film Zoolander played by Will Farrel  saying to a colleague, JWTs, so hot right now. He is carrying a small white dog dressed in clothes and a tie matching his own." width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This article is focused on helping people already using JWTs today.&lt;br&gt;
I did not want this article to try and persuade you to or not to use JWTs, there are plenty of those already. What I struggled to find was a summary for existing users. So if you are using them, let's help make sure you are handling them safely.&lt;/p&gt;
&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What they are
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;JWTs - JSON Web Tokens pronounced as “JOT”.&lt;/li&gt;
&lt;li&gt;User input so treat them as untrusted.&lt;/li&gt;
&lt;li&gt;Start with &lt;code&gt;eyJ&lt;/code&gt; (base64d JSON object).&lt;/li&gt;
&lt;li&gt;Have 3 parts: header.payload.signature separated by dots.&lt;/li&gt;
&lt;li&gt;Able to be signed by a variety of algorithms including none (no signature at all).&lt;/li&gt;
&lt;li&gt;Hard to get right! Even some of the best get it wrong: &lt;a href="https://insomniasec.com/blog/auth0-jwt-validation-bypass"&gt;Auth0's 2020 validation bypass&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Come in different types: An example being an &lt;code&gt;access token&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The problems they can solve
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Exist to allow claims to be transferred between two parties&lt;/li&gt;
&lt;li&gt;Could be used to support solutions secured by OAuth2.0 and OpenID, to allow 3rd party applications to call the API on behalf of the user without the 3rd party having the users credentials (depending on the implementation). Meaning the user may not have an active session with the 1st party application but the API can still be called.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  What you need to do
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;em&gt;one&lt;/em&gt; algorithm and stick to ONLY ONE such as RS256. Turn off the rest including the option for none. &lt;/li&gt;
&lt;li&gt;Use an identity provider to create JWTs.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Decode != verified&lt;/em&gt; you need to do both before acting on the information.&lt;/li&gt;
&lt;li&gt;Don't write your own decode or verify functions, use a library like &lt;a href="https://github.com/auth0/node-jsonwebtoken"&gt;https://github.com/auth0/node-jsonwebtoken&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If it doesn't decode, &lt;strong&gt;reject it&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If it doesn't verify &lt;strong&gt;reject it&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Verify functions often have a lot of options. Use them!

&lt;ul&gt;
&lt;li&gt;Algorithm&lt;/li&gt;
&lt;li&gt;Issuer&lt;/li&gt;
&lt;li&gt;Audience&lt;/li&gt;
&lt;li&gt;Issued at isn't in the future&lt;/li&gt;
&lt;li&gt;Not before isn't in the future&lt;/li&gt;
&lt;li&gt;Bonus: expiry clock tolerance (max 30 seconds)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Check the scopes&lt;/li&gt;
&lt;li&gt;Check the claims&lt;/li&gt;
&lt;li&gt;Bonus: If you can apply a type to the token. Check it's an access/ bearer token. &lt;/li&gt;
&lt;li&gt;If you are rotating signing certificates &lt;em&gt;which you should&lt;/em&gt;, check if you have the public key matching the &lt;code&gt;kid&lt;/code&gt; in the header before trying to validate.

&lt;ul&gt;
&lt;li&gt;Check the issuer is trusted before attempting to retrieve new certificates. If it's not, &lt;strong&gt;reject it&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Ideally your services would already have the latest / current key through another means after the rotation. &lt;/li&gt;
&lt;li&gt;Do not recklessly trust the issuer field and retrieve new public keys from it's JWKS_Uri!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;If you're using a symmetric algorithm like HS256, make sure the secret is changed to something unique and long enough. (At least as long as the resulting token)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  TLDR not enough?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What is a JWT?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc7519"&gt;RFC7519 - JSON Web Token (JWT)&lt;/a&gt; defines a JWT (Often pronounced “jot”) as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties.  The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.&lt;br&gt;
&lt;/p&gt;




&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;They have 3 parts separated by dots (note the colours in the figure below). &lt;/li&gt;
&lt;li&gt;Each are base64 encoded separately.&lt;/li&gt;
&lt;li&gt;They are instantly recognizable as they start with eyJ… (the first 2 parts are JSON objects when when base64 encoded become eyJ).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may have seen them in cookies or Authorization headers with the word “bearer” in front of them. &lt;/p&gt;

&lt;p&gt;Below is an example of a access token that is a JWT in encoded and decoded form using &lt;a href="https://jwt.io"&gt;https://jwt.io&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxzt8a8mop6ew392ydod.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxzt8a8mop6ew392ydod.png" alt="An example JWT from https://jwt.io" width="800" height="664"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The 3 sections are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first section shown in red is the &lt;code&gt;Header&lt;/code&gt; (JOSE Header) which tells the receiving party how it's signed and which key has been used (if appropriate).&lt;/li&gt;
&lt;li&gt;The second section shown in purple is the &lt;code&gt;Payload&lt;/code&gt; with some standard properties based on the RFC but can also include custom properties/ claims.&lt;/li&gt;
&lt;li&gt;The third section shown in in blue is the &lt;code&gt;Signature&lt;/code&gt; for consumers to use to validate that the JWT was issued by the trusted authority.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The access token can be likened to a passport that states some claims and is certified by a trusted authority. Like a passport, they can be tampered with so we need to ensure we properly validate and verify the contents using the tools available and protect ourselves from the numerous vulnerabilities that have been discovered and treat all input as untrusted user input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signing algorithms
&lt;/h3&gt;

&lt;p&gt;Your authorization server will let you choose which algorithm you want your tokens signed with.&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br&gt;
Choose one, use one and only one if you can. The verify function will need to be told which you support and will reject anything else. You could even check before you verify and if you receive a token with anything else, reject it.&lt;/p&gt;

&lt;p&gt;Algorithm names are usually split into 2 parts:&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqm4ovm3m9izsgxw374k7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqm4ovm3m9izsgxw374k7.png" alt="Signing algorithm diagram showing that the first 2 characters relate to the signature algorithm and the remaining characters relate to the hashing algorithm. The example shown is for RS256" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signature algorithm: In our example, RS means RSASSA-PKCS1-v1_5 (typically)&lt;/li&gt;
&lt;li&gt;Hashing algorithm In our example, 256 means SHA-256
&lt;p&gt;
Together it's known as “RS256”, a very popular JWT signing algorithm. It has been the default for Auth0 since around 2019.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: The latest signing algorithm “EdDSA” doesn't follow the same naming setup as the others (as does the “none” algorithm).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://datatracker.ietf.org/doc/html/rfc7518#section-3"&gt;RFC7518&lt;/a&gt; defines a list of 13 options (One being “none” where the JWT has no signature at all!) &lt;/p&gt;


&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgz4fhnhiky2abi2n60o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgz4fhnhiky2abi2n60o.png" alt="List of supported JWT signing algorithms taken from RFC7518" width="539" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition to the RFC, there are others available which you can find on the iana registry: &lt;a href="https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms"&gt;JSON Object Signing and Encryption (JOSE)&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What should I use?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;RS256&lt;/strong&gt; and &lt;strong&gt;HS256&lt;/strong&gt; are the two most popular signing algorithms and will have the best support from libraries you might use. Two others to note are ES256 and PS256 which are mandated by OpenBanking so are gaining popularity. Another new algorithm to take note of is EdDSA which is recommended by cryptographic experts but today lacks the universal library support offered by some of the older options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RS256&lt;/strong&gt; - Asymmetric (Private Key to sign and Public key to verify) using RSASSA-PKCS1-v1_5 &lt;a href="https://datatracker.ietf.org/doc/html/rfc3447"&gt;RFC 3447 - Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1)&lt;/a&gt; with a SHA-256 hashing&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HS256&lt;/strong&gt; - Symmetric (shared secret used by signer and verifier) using SHA-256 hashing&lt;/p&gt;

&lt;p&gt;Both are as cryptographically safe as each other but RS* type algorithms have the advantage of easier implementation and maintenance as you do not have to do secrets management in your consuming service. This has the added benefit of a stronger trust level as no shared secret is ever exposed. Not exposed = reduces chance of it leaking and tokens from rogue actors being received.&lt;/p&gt;

&lt;p&gt;The other possible negative of &lt;strong&gt;HS256&lt;/strong&gt; or any symmetric algorithms  is that because you have to distribute the secret to any verifying services, it has a much higher chance of being leaked (it’s a private key that should not normally ever leave the authorization server or a certificate store). This means the trust level is reduced as you cannot say 100% that the token was signed by the authorization server you are expecting.&lt;/p&gt;

&lt;p&gt;A final note on symmetric algorithms is that the secret used to create the signature needs to be unique, sufficiently large and complex to prevent it being brute forced. There have also been instances where people have forgotten to change the default secret included with the package or example code they used. These have been compiled into helpful word-lists such as &lt;a href="https://github.com/wallarm/jwt-secrets"&gt;Wallarm's JWT Secrets list&lt;/a&gt;. This allows attackers to quickly check if your JWT is signed with something from an example rather than a traditional brute force.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decode
&lt;/h2&gt;

&lt;p&gt;As JWTs are base64 encoded, they need to be decoded before the information inside can be reviewed and the JWT verified. Important node: &lt;strong&gt;Decode != Verify&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The library you are using may have a decode function included as well. &lt;/p&gt;

&lt;p&gt;Before running the verify function you may choose to validate some of the tokens claims such as the issuer if you need to support more than a few. Most verify functions will take in a list of issuers but you may have too many to pass. It may be better to verify the issuer yourself and then pass the issuer in as an additional option for the verify function.&lt;/p&gt;

&lt;p&gt;If you are doing RS256 you will want to check if the key Id that is present in the header matches the key Id you have. If it doesn't then the verify function will fail. There may have been a genuine key rotation meaning new tokens have a new key you don't have (or it's a bad token).  Retrieve the keys from the JWKS URI and check if the key matches, if multiple keys are present but neither matches, reject the token. If one does match, optionally save it, then send it to the verify function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not act on the information in the token until it's verified.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;Verification ensures that the token is signed correctly and that it's currently valid for use. &lt;/p&gt;

&lt;p&gt;Where verification often fails due to missing or incorrect configuration: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Algorithms are not defined. As above there should only be one algorithm in use which you can pass to the verify function.&lt;/li&gt;
&lt;li&gt;Issuers are not checked. You know who can issue tokens for your service. Ensure that you are passing that issuer into the function as it will be able to check if that was the claim versus your expectation. You may have already checked this as well once it was decoded.&lt;/li&gt;
&lt;li&gt;Audience isn't checked. You may have multiple APIs. Ensure that the tokens received are for the API that is being called.&lt;/li&gt;
&lt;li&gt;Scopes are not checked. You may have scopes providing coarse grained authorization on the API. Check that the scope(s) for the resource that is being requested are present.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can read what Auth0 has to say about validation here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://auth0.com/docs/secure/tokens/json-web-tokens/validate-json-web-tokens"&gt;Validate JSON Web Tokens&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/docs/secure/tokens/access-tokens/validate-access-tokens"&gt;Validate Access Tokens&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Rotate your keys
&lt;/h2&gt;

&lt;p&gt;You should rotate your keys to prevent brute force attackers from cracking the key or secret used to sign the tokens. For asymmetrically signed tokens, this should be fairly trivial as you will likely have implemented a key retrieval mechanism. If you receive a new key, you would need to trigger that.&lt;/p&gt;

&lt;p&gt;For symmetrically signed tokens this is much harder as you need to provide the secret to all services via a secure back channel. Note that some identity providers use symmetrically signed tokens for refresh tokens as they are the sole consumer of them so already have access to the secret.&lt;/p&gt;

&lt;p&gt;Be careful to provide a long enough overlap depending on the lifespan of your tokens. You wouldn't want to reject a legitimate token that has not expired yet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.okta.com/docs/concepts/key-rotation/"&gt;Okta's fantastic write up on key rotation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Read the TLDR&lt;/li&gt;
&lt;li&gt;Choose a single algorithm&lt;/li&gt;
&lt;li&gt;Decode and validate&lt;/li&gt;
&lt;li&gt;Validate issuers against a known list or a rule set.&lt;/li&gt;
&lt;li&gt;Don't act until it's validated&lt;/li&gt;
&lt;li&gt;Test it in dev and prod&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipcfvhyx2aodgo662712.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipcfvhyx2aodgo662712.png" alt="Picture of Sergeant Phil Esterhaus's (played by Michael Conrad) morning roll call catchphrase in 'Hill Street Blues'. Text reads Let's Be Careful Out there" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Research links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://redthunder.blog/2017/06/08/jwts-jwks-kids-x5ts-oh-my/"&gt;JWTs? JWKs? ‘kid’s? ‘x5t’s? Oh my! | Red Thunder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.scottbrady91.com/jose/jwts-which-signing-algorithm-should-i-use"&gt;JWTs: Which Signing Algorithm Should I Use? | Scott Brady&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/blog/navigating-rs256-and-jwks/"&gt;Navigating RS256 and JWKS | Auth0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://curity.io/resources/learn/jwt-best-practices/#5-always-check-the-audience"&gt;JWT Security Best Practices | Curity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://42crunch.com/7-ways-to-avoid-jwt-pitfalls/"&gt;7 Ways to Avoid JWT Security Pitfalls | 42Crunch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/docs/secure/tokens/access-tokens/validate-access-tokens"&gt;Validate Access Tokens | Auth0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://auth0.com/docs/secure/tokens/json-web-tokens/validate-json-web-tokens"&gt;Validate JSON Web Tokens | Auth0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/docs/concepts/key-rotation/"&gt;Key Rotation | Okta&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>api</category>
      <category>jwt</category>
    </item>
  </channel>
</rss>
