Using the Management & Impersonation API Through JSON Web Tokens (JWT)

This article describes the steps needed in order to fully automate management of the product's licenses and public API through the use of JSON Web Tokens

JSON Web Tokens are an open industry standard formalized through RFC 7519 that can be used with Delve's APIs in order to automate license management (Management API) and regular product activity through its OpenAPI (Impersonation API).

In order to obtain access to the API JWT infrastructure, you will need to:

  • Let us know that you require JWT token enablement for your instance.
  • Generate two secure sets of keys on your own, one for each API.
  • Send us the public part of both keys and store & manage the private parts securely.
  • Obtain the issuer and cid strings from us (setup after we receive your public key and JWT setup request).
  • Use both these in conjunction with the corresponding private keys to sign JWTs needed to use the APIs.

Generating a secure set of keys

The key format that we require for your JWT is ECDSA keys using curve secp521r1.

As an example you can generate these using the following openssl commands, but make sure to adapt this to your own key management policy so that they are handled in a secure way (in this example the private key will be generated on disk, in the directory that you are in).

openssl ecparam -genkey -name secp521r1 -noout -out mgt-api-private.pem

openssl ec -in mgt-api-private.pem -pubout -out mgt-api-public.pem

These commands will need to be repeated for the impersonation API to obtain a secondary set of public/private keys.

Send the public key file to Delve.

Once you've managed to generate the set of keys, you should be sending us the public part only, in that example that would be the license-api-public.pem file.

You can send this keypart directly by email to delve-support@delvesecurity.com or in a new ticket's attachment.

Obtain an issuer and cid from Delve

Once obtained, we will setup your public key file on our end, and generate two specific base64-encoded data elements that you will need to use in your code as part of your token signature mechanism. These two elements are the issuer (very long) and the cid (relatively short).

Use the issuer and cid to sign API messages

Using JWT is pretty well documented with tons of language-specific examples out there.

The following very basic example is provided in Python as a starting mechanism for you to validate your use case before implementing it in your production system.

Note that there are two examples, one for each API (impersonation API and management API), with distinct parameters and keys.

Placeholders that you have to fill in are provided between brackets <>.

 

Example using the Impersonation API:

import jwt

# Configuration
with open("/path/to/mgt-api-private.pem", "r") as fp:
sign_key = fp.read()
issuer = "<Base 64 string provided by Delve>"
client_id = "<Base 64 string provided by Delve>"
instance_name = "<your instance name>" # Instance name is the subdomain in your URL
impersonation_user_id = 123 # Specific user id we will perform the request as (impersonate)

token = jwt.encode({
"iss": issuer,
"iat": time(),
"exp": time() + 60,
"aud": instance_name,
"sub": "user:" + impersonation_user_id,
"cid": client_id,
"scp": ["read_only", "full_access"],
}, sign_key, algorithm="ES512").decode("utf-8")

# ...

headers = {"Authorization": f"Bearer {token}"}

# Do the request

The API Endpoints that can be targeted are exactly the same as provided in the Swagger documentation file of your own instance, accessible at

https://<YOUR INSTANCE>.wardenscanner.com/api/v2/spec#/

 

Example using the Management API:

import jwt

# Configuration
with open("/path/to/impersonation-api-private.pem", "r") as fp:
sign_key = fp.read()
issuer = "<Base 64 string provided by Delve>"
client_id = "<Base 64 string provided by Delve>"
instance_name = "<your instance name>" # Instance name is the subdomain in your URL

current_user = "..." # To be included in [performed by ...] in the audit logs

token = jwt.encode({
"iss": issuer,
"iat": time(),
"exp": time() + 60,
"aud": instance_name,
"pby": current_user,
"cid": client_id,
"scp": ["management"],
}, sign_key, algorithm="ES512").decode("utf-8")
# ...
headers = {"Authorization": f"Bearer {token}"}

The management API has different endpoints not listed in the Swagger documentation.

The management API endpoint and JSON data input is understandable by looking at the corresponding call in Panopticon, using the following translation table:

Description

Type

Panopticon API call

JWT Management API call

Instance Status

GET

/{id}

/api/status

Get Instance Usage GET /{id}/usage /api/usage?date_begin={date_begin}&date_end={date_end}
Get Organization Usage GET /{id}/usage/{org_id} /api/usage/{organization}?{request.query_string}
List Instance Users GET /{id}/users /api/users
Search Instance Users PATCH /{id}/users /api/users
Update User POST /{id}/users/{user_id} /api/users/{user_id}
Login to Instance Organization POST /{id}/users/{user_id}/login-as /api/users/{user_id}/login-as
List Instance Organizations GET /{id}/organizations /api/organizations
Edit Organization POST /{id}/organizations/{org_id} /api/organizations/{organization_id}
Search Organizations PATCH /{id}/organizations /api/organizations
Create Organization POST /{id}/organizations /api/organizations
Create Team POST /{id}/organizations/{org_id}/teams /api/organizations/{organization_id}/teams
Create Users POST /{id}/organizations/{org_id}/users /api/organizations/{organization_id}/users
Add User To Team POST /{id}/teams/{team_id}/grants /api/teams/{team_id}/grants