Skip to content

Instantly share code, notes, and snippets.

@qntoni
Created October 16, 2025 11:04
Show Gist options
  • Select an option

  • Save qntoni/3d7f4c4ff64cf81df4ffa101982e9215 to your computer and use it in GitHub Desktop.

Select an option

Save qntoni/3d7f4c4ff64cf81df4ffa101982e9215 to your computer and use it in GitHub Desktop.
Understand the workflow of JWT authentication with passbolt

Understanding how JWT authentication works

Generating a challenge

In order to begin, you need to create a unique challenge which is a simple JSON message containing: the API version which is a static value, a unique random token (UUID), the domain of your passbolt server and an expiry timestamp which is usually about ten minutes in the future.

Below is an example on how the payload should look like:

{
	version: "1.0.0",
	domain: "{{API_BASE_URL}}",
	verify_token: "e5bbc6da-22eb-43c4-82b3-137f47577766"
	verify_token_expiry: 1741771173
}

Encrypting the challenge

The challenge payload that we’ve just created needs to be encrypted using the passbolt server’s public GPG key which can be obtained in body.keydata when performing a GET {{API_BASE_URL}}/auth/verify.json

It’s important to note that you also need to sign the encrypted challenge with your own private GPG key. This is the private key related to your passbolt’s account. In case you don’t know where it’s stored, it can be downloaded from the user interface in Profile > Keys Inspector > Private. This signing step proves your identity to the server. Sending the encrypted challenge To proceed further, we need to send this encrypted and signed challenge to the API using POST {{API_BASE_URL}}/auth/jwt/login.json along with your user_id. If you are unsure about your user_id, you can either obtain it through the database e.g., SELECT username, active, id FROM users WHERE username = “YOUR_EMAIL”\G or through the users workspace in the user interface. When clicking on your name, you should see the URI being {{API_BASE_URL}}/app/users/view/{{USER_ID}}

The payload should then be similar than below:

{
  "user_id": "8bb80df5-700c-48ce-b568-85a60fc3c8f2",
  "challenge": "-----BEGIN PGP MESSAGE-----"
}

Server response

The passbolt server decrypts your challenge using its own private key, verifying your signature to confirm your identity. Then, if the verification succeeds, passbolt responds by sending back its own encrypted message which is a new challenge containing your JWT token.

{
  "header": {
    "id": "7ff2828c-1092-4897-8e0a-1dc64ada889f",
    "status": "success",
    "servertime": 1721207029,
    "action": "4d0c0996-ce30-4bce-9918-9062ab35c542",
    "message": "The operation was successful.",
    "url": "/auth/jwt/login.json",
    "code": 200
  },
  "body": {
    "challenge": "-----BEGIN PGP MESSAGE-----"
  }
}

Decrypting the server’s JWT token

When you’ve copied the content out of body.challenge from the server’s response. You need to decrypt it using your private GPG key. Inside the decrypted response, you’ll find your JWT tokens: access_token to authentication subsequent API requests refresh_token to renew your access without repeating the login process frequently

Using JWT tokens for API requests

Now that you’ve been able to successfully authenticate, don’t forget to include the JWT access token in the headers: Authorization: Bearer {{JWT_TOKEN}} for each API request.

Tokens are short-lived but can be refreshed regularly using the refresh token!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment