JWT-based authentication allows scripted wget requests to access protected HTTP and HTTPS APIs without interactive logins. Automated backups, batch imports, and monitoring jobs can include a signed token with each call so that the remote service can apply role-based access control instead of relying on brittle session cookies.

A JSON Web Token is issued by an authorization service after successful authentication or another grant flow, encoded as three base64url segments and signed with a shared secret or key pair. APIs usually expect the token in an Authorization header that uses the Bearer scheme, while token endpoints accept credentials or refresh tokens in a JSON payload and respond with a JSON document containing the access token and expiry metadata. On Linux, wget combines these patterns through the header and post-data options so that both token acquisition and authenticated calls can remain fully non-interactive.

Short token lifetimes, transport security, and careful handling of secrets are essential when wiring JWTs into scripts. Access tokens should only be sent to trusted endpoints over HTTPS, refresh tokens require even tighter protection, and shell history or verbose logging can easily leak credentials if commands and outputs are not kept minimal. Parsing the JSON response with tools such as jq, storing tokens in environment variables or short-lived shell variables, and avoiding unnecessary echoing of full token values reduces exposure risk in operational environments.

Steps to authenticate using JWT in wget:

  1. Open a terminal in a directory used for JWT testing output.
    $ pwd
    /home/user
  2. Request a JWT access token from the authentication endpoint using wget with a JSON login payload.
    $ wget --header="Content-Type: application/json" \
           --post-data='{"username":"user","password":"pass"}' \
           https://api.example.com/auth/login \
           --output-document=-
    {
      "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.##### snipped #####",
      "expiresIn": 3600,
      "tokenType": "Bearer"
    }

    The token endpoint accepts credentials or another grant and returns a signed accessToken together with metadata such as expiresIn and tokenType.

  3. Store the access token from the JSON response in a shell variable using jq.
    $ ACCESS_TOKEN=$(wget --quiet \
                          --header="Content-Type: application/json" \
                          --post-data='{"username":"user","password":"pass"}' \
                          https://api.example.com/auth/login \
                          --output-document=- | jq -r '.accessToken')
    $ echo "${ACCESS_TOKEN}" | cut -d'.' -f1 | base64 --decode
    {"alg":"HS256","typ":"JWT"}

    Printing full JWT values, copying them into tickets, or leaving them in shared terminals exposes credentials until expiry and can grant the same permissions as the original account.

  4. Call a protected resource endpoint using wget and send the token in the Authorization header with the Bearer scheme.
    $ wget --header="Authorization: Bearer ${ACCESS_TOKEN}" \
           https://api.example.com/api/metrics \
           --output-document=metrics.json
    $ head -n 5 metrics.json
    {
      "status": "ok",
      "metrics": {
    ##### snipped #####

    A valid token and sufficient privileges usually produce a 200 OK response and the expected JSON payload from the protected endpoint.

  5. Inspect the HTTP response headers and status when access fails with 401 or 403 codes.
    $ wget --server-response \
           --header="Authorization: Bearer ${ACCESS_TOKEN}" \
           https://api.example.com/api/metrics \
           --output-document=metrics.json
      HTTP/2 401
      content-type: application/json
      content-length: 128
    ##### snipped #####

    Status 401 Unauthorized often indicates an expired, malformed, or revoked token, whereas 403 Forbidden points to a valid token without sufficient authorization for the requested resource.

  6. Exchange a refresh token for a new access token when the current JWT expires.
    $ wget --header="Content-Type: application/json" \
           --post-data='{"refreshToken":"YOUR_REFRESH_TOKEN"}' \
           https://api.example.com/auth/refresh \
           --output-document=-
    {
      "accessToken": "NEW_ACCESS_JWT_VALUE",
      "expiresIn": 3600,
      "tokenType": "Bearer"
    }

    Refresh endpoints typically accept a long-lived refreshToken and issue a fresh short-lived access token without resending the original username and password.

  7. Automate token retrieval and authenticated requests using a reusable shell script.
    jwt-wget.sh
    #!/usr/bin/env bash
    set -euo pipefail
     
    AUTH_URL="https://api.example.com/auth/login"
    API_URL="https://api.example.com/api/metrics"
     
    get_access_token() {
      wget --quiet \
        --header="Content-Type: application/json" \
        --post-data='{"username":"user","password":"pass"}' \
        "${AUTH_URL}" \
        --output-document=- | jq -r '.accessToken'
    }
     
    ACCESS_TOKEN="$(get_access_token)"
     
    wget --header="Authorization: Bearer ${ACCESS_TOKEN}" \
         "${API_URL}" \
         --output-document=metrics.json

    Keeping token acquisition and usage inside a script reduces repeated credential entry and limits exposure of raw tokens in interactive shell commands.

  8. Verify successful authentication by checking the HTTP status and an expected field in the response body.
    $ wget --server-response \
           --header="Authorization: Bearer ${ACCESS_TOKEN}" \
           https://api.example.com/api/metrics \
           --output-document=metrics.json 2>&1 | grep "HTTP/"
      HTTP/2 200
    $ jq -r '.status' metrics.json
    ok

    Confirming both a 200 status line and an application-level flag such as a JSON status field provides strong evidence that the protected API accepted the JWT.

Discuss the article:

Comment anonymously. Login not required.