Search

Dark theme | Light theme
Showing posts with label HTTP Client. Show all posts
Showing posts with label HTTP Client. Show all posts

March 6, 2024

IntelliJ HTTP Client: Parsing JSON Web Tokens

The IntelliJ HTTP Client is very useful for testing APIs. We can use Javascript to look at the response and write tests with assertions about the response. If an API returns a JSON Web Token (JWT), we can use a Javascript function to decode the token and extract information from it. For example we can then assert that fields of the token have the correct value. There is no built-in support in IntelliJ HTTP Client to decode a JWT, but we can write our own Javascript function to do it. We then use the function in our Javascript response handler to decode the token.

In the following HTTP request file we simulate a call to get an response with a field containing an JWT. We use the function decodeJwt from the file jwt-utils.js to decode the token and extract information from it.

### Simulate a call to get an response with a field containing an JWT.
POST https://examples.http-client.intellij.net/anything
Content-Type: application/json

// Original token before it is base64 encoded:
// {
//     "sub": "1234567890",
//     "upn": "hubert@mrhaki.com",
//     "name": "mrhaki",
//     "groups": ["Blogger"],
//     "iat": 1516239022
// }
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwidXBuIjoiaHViZXJ0QG1yaGFraS5jb20iLCJuYW1lIjoibXJoYWtpIiwiZ3JvdXBzIjpbIkJsb2dnZXIiXSwiaWF0IjoxNTE2MjM5MDIyfQ.9E2gYNFogs3K8pJH9JiJYISv403EtCm4tRzQWZi1CXM"
}

> {%
    import {decodeJwt} from './scripts/jwt-utils';

    // The token is in the response body and we get it
    // using the path `json.token`.
    // We store it as variable `token` so we can use in the next step.
    const token = decodeJwt(response.body.json.token);

    // We can write assertions on the token contents.
    client.test("Check fields in token", function () {
        client.assert(token.upn === "hubert@mrhaki.com");
        client.assert(token.name === "mrhaki");
        client.assert(token.groups.includes("Blogger"));
        client.assert(token.sub === "1234567890");
        client.assert(token.iat === 1516239022);
    });
%}

The function decodeJwt is defined in the file jwt-utils.js:

// File: ./scripts/jwt-utils.js
export function decodeJwt(token) {
    var base64EncodedPayload = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
    var jsonPayload = decodeURIComponent(decodeBase64(base64EncodedPayload)
        .split('')
        .map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
        .join(''))
        // Remove any NUL characters at the end of the string.
        .replace(/\0+$/g, '');
    return JSON.parse(jsonPayload);
}

function decodeBase64(input) {
    const _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    let output = "";
    let chr1, chr2, chr3;
    let enc1, enc2, enc3, enc4;
    let i = 0;
    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
    while (i < input.length) {
        enc1 = _keyStr.indexOf(input.charAt(i++));
        enc2 = _keyStr.indexOf(input.charAt(i++));
        enc3 = _keyStr.indexOf(input.charAt(i++));
        enc4 = _keyStr.indexOf(input.charAt(i++));
        chr1 = (enc1 << 2) | (enc2 >> 4);
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        chr3 = ((enc3 & 3) << 6) | enc4;
        output = output + String.fromCharCode(chr1);
        if (enc3 !== 64) {
            output = output + String.fromCharCode(chr2);
        }
        if (enc4 !== 64) {
            output = output + String.fromCharCode(chr3);
        }
    }
    return decodeURI(output);
}

Written with IntelliJ IDEA 2023.3.4.

February 11, 2024

IntelliJ HTTP Client: Using In-Place Variables

The built-in IntelliJ HTTP Client is very useful for testing HTTP requests and responses. If we want to define a variable in our .http file that is only used in this file and will not change per environment we can define it using the following syntax: @<variable name> = variable value. The variable is an in-place variable and the scope of the variable in the current .http file. The variable is immutable and can only be defined with a value once and cannot be overwritten. To refer to the variable we use the syntax {{<variable name>}}.

In the following example we define a variable with the name base-url and the value https://ijhttp-example.jetbrains.com:

# We define an in-place variable "base-url"
# to be used in this file.
@base-url = https://ijhttp-examples.jetbrains.com

### GET HTML page
GET {{base-url}}/html

### GET XML page
GET {{base-url}}/xml

### GET JSON page
GET {{base-url}}/json

### GET UUID4 response
GET {{base-url}}/uuid

Written with IntelliJ 2023.3.3.

January 13, 2024

IntelliJ HTTP Client: Allowing Insecure HTTPS Requests

Sometimes we want to send HTTP requests to servers that use HTTPS with self-signed certificates. We then need to tell HTTP Client to not check the certificate of the server. This is like running the curl command with the --insecure or '-k' flag. To disable the certificate verification for HTTP Client we need to adjust the http-client.private.env.json file. For the environment we want to disable the certificate verification we must add a SSLConfiguration section. In the SSLConfiguration section we add the verifyHostCertificate property with value 'true'.

In the following example http-client.private.env.json file we have the environments development and production with different passwords. For the development environment we disable the certificate verification and we keep the certificate verification for the production environment.

{
  "development": {
    "password": "mrhaki",
    "SSLConfiguration": {
      "verifyHostCertificate": false
    }
  },
  "production": {
    "password": "mrhaki42"
  }
}

Written with IntelliJ 2023.3.2

November 5, 2023

IntelliJ HTTP Client: Using External Files As JSON Payload

The built-in IntelliJ HTTP Client is very useful for testing HTTP requests and responses. We can use it to test for example a REST API that works with JSON data. If an endpoint expects a JSON payload we can specify the payload in our HTTP Client request file. But if we have a lot of endpoints and large payload the request file can get big and messy. Instead of having the payload in the request file directly we can specify an external JSON file with the payload and use it for a request body. We must use the < operator and give the name of the file with our JSON payload. The IntelliJ HTTP Client will read the contents of that file and use it as the request body. The payload may also contain (dynamic) variables and those variables will be replaced with correct values when the request is executed.

In the following example we have a POST request with a JSON payload from an external file. The payload in the file contains variables. We use an endpoint from the test API at https://ijhttp-examples.jetbrains.com that expects a JSON payload. The response will contain the payload with request payload where all variables are replaced in the json property.

### POST to /anything with payload from external file
POST https://ijhttp-examples.jetbrains.com/anything
Content-Type: application/json

< ./sample-payload.json

# Content of sample-playload.json:
#{
#  "id": "{{$random.uuid}}",
#  "username": "{{username}}",
#}

# Content of http-client.env.json with value for variable username:
#{
#  "test": {
#    "username": "mrhaki"
#  }
#}

> {%
    client.test("Response is OK", function () {
        client.assert(response.status === 200);
    });
    client.test("Response has username set", function () {
        client.assert(response.body.json.username === "mrhaki");
    });
    client.test("Response has id with value", function () {
        const regexExp = /^[0-9a-f]{8}\b-[0-9a-f]{4}\b-[0-9a-f]{4}\b-[0-9a-f]{4}\b-[0-9a-f]{12}$/gi;
        client.assert(regexExp.test(response.body.json.id));
    });
%}

Written with IntelliJ IDEA 2023.2.4.