Skip to main content
Version: 1.2.0

Jira Reader

Connects to a Jira instance via the REST API to read requirements stored as Jira issues (Epics, Stories, Tasks, etc.).

When to use: Your requirements are managed in Jira and you want live access without manual exports.

Tested Jira versions

DeploymentVersion
Jira Cloudlatest
Jira Data Center11.3
Jira Data Center10.3
Jira Data Center9.4

Other versions may work but are not officially supported.

Installation

Install the Jira extra:

pip install testbench-requirement-service[jira]

Setup

  1. Ensure the Jira account has the required permissions.
  2. Configure the Jira server URL and authentication type.
  3. Provide credentials via config.toml, a .env file, or environment variables.
  4. Start the service.

Jira user permissions

The Jira account needs the following permissions:

  • Browse Projects — required to list projects, search issues, read changelogs and field metadata.
  • Create Issues — required to fetch per-project field metadata. This is used when querying user-defined attributes or when baseline_field is set to a custom field name other than fixVersions or sprint.

Minimal configuration

# config.toml
[testbench-requirement-service]
reader_class = "JiraRequirementReader"

[testbench-requirement-service.reader_config]
server_url = "https://example.atlassian.net/"
auth_type = "basic"
username = "my-user@example.com" # (or set JIRA_USERNAME as environment variable)
password = "my-api-token" # (or set JIRA_PASSWORD as environment variable)

Set credentials via environment variables, e.g. run in the terminal:

export JIRA_USERNAME=my-user@example.com
export JIRA_PASSWORD=my-api-token

Or in a .env file:

JIRA_USERNAME=my-user@example.com
JIRA_PASSWORD=my-api-token

Configuration

The configuration can be added directly to config.toml under [testbench-requirement-service.reader_config] (recommended) or in a separate .toml file without a section prefix.

Connection settings

SettingTypeDescriptionRequiredDefault
server_urlStringBase URL of the Jira instance (e.g. https://your-company.atlassian.net)Yes(none)
auth_typeStringAuthentication method: basic, token, oauth1, oauth2 2LO (service account), or oauth2 3LO (user account). The short forms oauth2 2LO and oauth2 3LO are accepted as aliases.Nobasic
timeoutIntegerHTTP request timeout in seconds (1–300)No30
max_retriesIntegerMax retries for failed API requests (0–10)No3
cache_ttlFloatCache time-to-live in seconds. 0 = disable caching.No300.0
proxy_urlStringHTTP(S) proxy for all Jira API requests (e.g. http://proxy.example.com:8080). See Proxy settings.No(none)

Authentication methods

Pick the authentication flow that matches your Jira deployment. Credentials can be set in the config file or via environment variables.

auth_typeWhen to useRequired values
basicJira Cloud and Data Center with username + password/API tokenusername + password (or JIRA_USERNAME + JIRA_PASSWORD)
tokenJira Server/Data Center with Personal Access Tokenstoken (or JIRA_BEARER_TOKEN)
oauth1Enterprise instances requiring OAuth 1.0aoauth1_access_token, oauth1_access_token_secret, oauth1_consumer_key, oauth1_key_cert_path (or matching env vars)
oauth2 2LO (service account)Jira Cloud (or Data Center instances that support the client_credentials grant), unattended service-to-service access without a user accountoauth2_client_id + oauth2_client_secret (or JIRA_OAUTH2_CLIENT_ID + JIRA_OAUTH2_CLIENT_SECRET)
oauth2 3LO (user account)Jira Cloud or Jira Data Center, delegated access on behalf of a useroauth2_client_id + oauth2_client_secret, plus a refresh token seeded via the setup wizard

Whether the OAuth 2.0 flows talk to Jira Cloud or Jira Data Center is detected automatically at startup — see OAuth 2.0 auth (Jira Data Center).

warning

The OAuth 2.0 values must be typed exactly as shown, including spaces, capitalization and parentheses — e.g. auth_type = "oauth2 3LO (user account)". The short forms "oauth2 2LO" and "oauth2 3LO" are accepted as aliases, but any other spelling (such as the older "oauth2") is rejected with a validation error at startup. Use testbench-requirement-service init / configure to pick the value from a list instead of typing it.

Basic authentication (auth_type = "basic")

SettingTypeDescriptionEnv var
usernameStringJira account username (e-mail for Cloud)JIRA_USERNAME
passwordStringPassword or API token (Cloud requires API token)JIRA_PASSWORD

Token authentication (auth_type = "token")

SettingTypeDescriptionEnv var
tokenStringPersonal Access Token (PAT)JIRA_BEARER_TOKEN

OAuth1 authentication (auth_type = "oauth1")

SettingTypeDescriptionEnv var
oauth1_access_tokenStringOAuth1 access tokenJIRA_OAUTH1_ACCESS_TOKEN
oauth1_access_token_secretStringOAuth1 access token secretJIRA_OAUTH1_ACCESS_TOKEN_SECRET
oauth1_consumer_keyStringOAuth1 consumer keyJIRA_OAUTH1_CONSUMER_KEY
oauth1_key_cert_pathStringPath to RSA private key file (.pem)JIRA_OAUTH1_KEY_CERT_PATH

OAuth2 2LO authentication (auth_type = "oauth2 2LO (service account)")

Two-legged OAuth: the service authenticates as itself using the client_credentials grant. No user consent and no refresh token are involved — access tokens are minted from the client ID/secret and re-minted automatically when they expire.

SettingTypeDescriptionEnv var
oauth2_client_idStringOAuth 2.0 client ID (required)JIRA_OAUTH2_CLIENT_ID
oauth2_client_secretStringOAuth 2.0 client secret (required)JIRA_OAUTH2_CLIENT_SECRET

OAuth2 3LO authentication (auth_type = "oauth2 3LO (user account)")

Three-legged OAuth: a user grants consent once, and the resulting refresh token is used to obtain short-lived access tokens at runtime.

SettingTypeDescriptionEnv var
oauth2_client_idStringOAuth 2.0 client ID (required)JIRA_OAUTH2_CLIENT_ID
oauth2_client_secretStringOAuth 2.0 client secret (required)JIRA_OAUTH2_CLIENT_SECRET
oauth2_refresh_tokenStringRefresh token from the 3LO consent flow. Not written to config.toml — the wizard stores it in tmp/oauth2_tokens.toml. Required unless a token is already cached there.JIRA_OAUTH2_REFRESH_TOKEN
oauth2_expires_atIntegerOptional UNIX timestamp for when the current access token expires. Rarely needed; the service tracks expiry itself.JIRA_OAUTH2_EXPIRES_AT

See OAuth 2.0 (3LO) auth below for the full consent flow and the required scopes.

Proxy settings

Only needed when the service cannot reach Jira (or auth.atlassian.com) directly and has to go through a forward proxy — typical in corporate networks with egress filtering.

SettingTypeDescriptionDefaultEnv var
proxy_urlStringProxy URL used for both HTTP and HTTPS Jira API requests, e.g. http://proxy.example.com:8080. Credentials can be embedded as http://user:password@proxy.example.com:8080.(none)
# config.toml
[testbench-requirement-service.reader_config]
server_url = "https://example.atlassian.net/"
proxy_url = "http://proxy.example.com:8080"

Notes:

  • proxy_url has no environment variable. When it is not set, the underlying requests library still honors the standard HTTP_PROXY/HTTPS_PROXY/NO_PROXY environment variables; setting proxy_url overrides them for Jira API calls.
  • proxy_url covers the Jira REST API calls, the Atlassian Cloud ID lookup (/_edge/tenant_info) and attachment/image downloads. The OAuth 2.0 token requests (to https://auth.atlassian.com/oauth/token for Jira Cloud, or {server_url}/rest/oauth2/1.0/token for Jira Data Center) are made through a separate HTTP client that only honors the HTTPS_PROXY environment variable — set that as well when using OAuth 2.0 behind a proxy.
  • A proxy that terminates TLS (SSL inspection) presents its own certificate. In that case also set ssl_ca_cert_path to the proxy's CA bundle, rather than disabling verify_ssl.
Security

Embedding proxy credentials in proxy_url stores them in plain text in config.toml. Prefer a proxy that does not require authentication, or supply the credentialed URL via HTTPS_PROXY in the service environment.

SSL / TLS settings

SSL verification (all auth types)

SettingTypeDescriptionDefaultEnv var
verify_sslBooleanEnable SSL certificate verification. Only set to false in dev/test.trueJIRA_VERIFY_SSL
ssl_ca_cert_pathStringPath to CA certificate bundle (.pem/.crt) for self-signed or corporate CAs(none)JIRA_SSL_CA_CERT_PATH

Mutual TLS client certificate (all auth types)

SettingTypeDescriptionEnv var
client_cert_pathStringPath to client certificate file (.pem or .crt)JIRA_CLIENT_CERT_PATH
client_key_pathStringPath to client private key (only needed if separate from cert)JIRA_CLIENT_KEY_PATH

Requirements & baselines settings

SettingTypeDescriptionDefault
baseline_fieldStringJira field used to identify baselines (e.g. fixVersions, sprint, or custom field ID)fixVersions
baseline_jqlStringJQL template for fetching issues of a baseline. Placeholders: {project}, {baseline}project = "{project}" AND fixVersion = "{baseline}" AND issuetype in standardIssueTypes()
current_baseline_jqlStringJQL template for the current/active baseline. Placeholder: {project}project = "{project}" AND issuetype in standardIssueTypes()
requirement_group_typesList[String]Issue types treated as requirement groups/folders["Epic"]
major_change_fieldsList[String]Fields whose changes count as a major version bump["fixVersions"]
minor_change_fieldsList[String]Fields whose changes count as a minor version bump["summary", "description", "affectsVersions", "status"]
owner_fieldStringJira field used as the requirement ownerassignee
rendered_fieldsList[String]Fields to render as HTML in TestBench (must be multiline text in Jira)[]

Project-specific overrides

All requirement and baseline settings can be overridden per project.

Inline in config.toml: Add a [testbench-requirement-service.reader_config.projects.<project>] section.

Separate config file: Add a [projects.<project>] section in your reader config file.

SettingDescriptionDefault
baseline_fieldProject-specific baseline fieldInherits from global
baseline_jqlProject-specific baseline JQL templateInherits from global
current_baseline_jqlProject-specific current baseline JQLInherits from global
requirement_group_typesProject-specific group typesInherits from global
major_change_fieldsProject-specific major change fieldsInherits from global
minor_change_fieldsProject-specific minor change fieldsInherits from global
ownerProject-specific owner fieldInherits from global
rendered_fieldsProject-specific rendered fieldsInherits from global

Authentication

Basic auth (Jira Cloud)

Recommended for Jira Cloud. Uses your Atlassian account email and an API token.

# config.toml
[testbench-requirement-service.reader_config]
auth_type = "basic"
username = "your-email@company.com"
password = "your-api-token"

Generate an API token at https://id.atlassian.com/manage-profile/security/api-tokens.

Token auth (Jira Data Center)

Uses a Personal Access Token (PAT) generated in your Jira Data Center profile.

# config.toml
[testbench-requirement-service.reader_config]
auth_type = "token"
token = "your-personal-access-token"
note

Personal Access Tokens expire based on the duration set in your Jira Data Center profile. If the service stops authenticating unexpectedly, check whether the token has expired and generate a new one.

OAuth 2.0 (3LO) auth (Jira Cloud)

Uses an OAuth 2.0 access token obtained via the Atlassian 3-Legged OAuth (3LO) flow. This is recommended when your Atlassian app is registered in the Atlassian developer console and you need delegated user access — every Jira request is made on behalf of the user who granted consent.

# config.toml
[testbench-requirement-service.reader_config]
auth_type = "oauth2 3LO (user account)"
oauth2_client_id = "your-client-id"
oauth2_client_secret = "your-client-secret"

How to obtain an OAuth 2.0 access token

Step 1 — Direct the user to the Atlassian authorization URL

Send the user to the following URL in a browser (GET request). You can construct it manually or copy it from Authorization → OAuth 2.0 (3LO) → Configure in the developer console:

https://auth.atlassian.com/authorize?
audience=api.atlassian.com&
client_id=YOUR_CLIENT_ID&
scope=read%3Ajira-work%20read%3Ajira-user%20write%3Ajira-work%20offline_access&
redirect_uri=https://YOUR_APP_CALLBACK_URL&
state=requirement-service&
response_type=code&
prompt=consent
ParameterRequiredDescription
audienceYesAlwaysapi.atlassian.com.
client_idYesClient ID from your app's Settings in the developer console.
scopeYesSpace-separated list of scopes (URL-encoded as%20). Only choose scopes already added to your app. See Required scopes below.
redirect_uriYesCallback URL configured inAuthorization for your app.
stateYes (security)An opaque string to prevent CSRF, e.g.requirement-service.
response_typeYesMust becode.
promptYesMust beconsent to show the access-grant screen.

If the user grants access, Atlassian redirects to redirect_uri with an ?code=... query parameter.

Step 2 — Exchange the authorization code for an access token

curl --request POST \
--url 'https://auth.atlassian.com/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"grant_type": "authorization_code",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"code": "YOUR_AUTHORIZATION_CODE",
"redirect_uri": "https://YOUR_APP_CALLBACK_URL"
}'

A successful response returns:

{
"access_token": "<string>",
"refresh_token":"<string>",
"expires_in": 3600,
"scope": "<string>"
}

Run the setup wizard, choose Enter a refresh token (Jira Cloud) when asked how to provide OAuth2 authorization, and enter the returned refresh_token. The wizard stores that token in tmp/oauth2_tokens.toml, not in config.toml. The service uses the refresh token to request short-lived access tokens at runtime and updates the cache automatically when they expire.

Persist only the OAuth client credentials in your configuration (or provide them via environment variables):

  • oauth2_client_id (or JIRA_OAUTH2_CLIENT_ID) = your client ID
  • oauth2_client_secret (or JIRA_OAUTH2_CLIENT_SECRET) = your client secret
note

Do not store OAuth2 access tokens or refresh tokens in config.toml or .env files. If the refresh token is revoked or expires, re-run the setup wizard to seed a new tmp/oauth2_tokens.toml cache. Refresh tokens are single-use and expire after 90 days without use.

OAuth 2.0 (2LO) auth (Jira Cloud)

Uses the Atlassian 2-Legged OAuth (2LO) flow, i.e. the OAuth 2.0 client_credentials grant. The service authenticates as itself (a service account) instead of on behalf of a user. Choose this for unattended deployments where no user should have to grant consent and no long-lived refresh token has to be maintained.

# config.toml
[testbench-requirement-service.reader_config]
auth_type = "oauth2 2LO (service account)"
oauth2_client_id = "your-client-id"
oauth2_client_secret = "your-client-secret"

Or via environment variables:

export JIRA_OAUTH2_CLIENT_ID=your-client-id
export JIRA_OAUTH2_CLIENT_SECRET=your-client-secret

How it differs from 3LO:

2LO (service account)3LO (user account)
Grant typeclient_credentialsrefresh_token (after authorization_code)
Browser consent stepNot neededRequired once
Refresh tokenNot usedRequired, stored in tmp/oauth2_tokens.toml
Wizard prompts for a tokenNoYes
Token cache on diskNo — access tokens are held in memory onlyYes
Acts asThe app itselfThe consenting user
Required settingsoauth2_client_id, oauth2_client_secretoauth2_client_id, oauth2_client_secret, refresh token

Access tokens are requested from https://auth.atlassian.com/oauth/token on first connect and re-minted automatically shortly before they expire, so no manual token rotation is needed. Because nothing is persisted, a restart simply mints a new token.

note

2LO requires an Atlassian app that is permitted to use the client_credentials grant, with the required scopes granted to the app itself. If Atlassian rejects the grant, the service fails at startup with Jira OAuth2 client credentials are not configured or an authorization error — verify the app type and its scopes in the developer console.

OAuth 2.0 auth (Jira Data Center)

Both OAuth 2.0 flows also work against Jira Data Center. The service detects the deployment type automatically at startup: it first tries the Atlassian Cloud ID lookup ({server_url}/_edge/tenant_info). If no Cloud ID is found, the instance is treated as Jira Data Center and the service:

  • requests tokens from {server_url}/rest/oauth2/1.0/token (with form-encoded bodies) instead of https://auth.atlassian.com/oauth/token, and
  • sends API requests directly to the configured server_url instead of going through the Atlassian API gateway.

The configuration is the same as for Jira Cloud — auth_type, oauth2_client_id and oauth2_client_secret. The client ID and secret come from an incoming OAuth 2.0 application link that a Jira administrator configures in Jira Data Center (Administration → Applications → Application links).

3LO on Jira Data Center

Jira Data Center issues authorization codes with PKCE, so the initial consent flow differs from Jira Cloud. Obtain an authorization code by directing a user to your instance's OAuth 2.0 authorization endpoint with a PKCE code challenge, as described in the Atlassian Data Center OAuth 2.0 documentation. Then run the setup wizard (testbench-requirement-service init / configure) and choose Exchange an authorization code (Jira Data Center) when asked how to provide OAuth2 authorization. The wizard prompts for:

  1. the authorization code returned to your redirect URI,
  2. the PKCE code_verifier used to create the code challenge, and
  3. the redirect URI used in the authorization request.

It exchanges these at {server_url}/rest/oauth2/1.0/token and stores the resulting refresh token in tmp/oauth2_tokens.toml, exactly like the Jira Cloud flow. Authorization codes are single-use and short-lived — if the exchange fails with HTTP 400/401, generate a fresh code and let the wizard retry.

2LO on Jira Data Center

oauth2 2LO (service account) works against Jira Data Center only when the instance supports the client_credentials grant. Vanilla Jira Data Center application links only offer authorization-code flows, so 2LO typically requires an additional gateway or plugin that provides that grant. If the grant is not supported, use oauth2 3LO (user account) or token (Personal Access Token) instead.

Required OAuth scopes

These scopes apply to Jira Cloud apps. Jira Data Center application links use their own scope model (READ, WRITE, …) configured on the incoming application link — grant at least READ, plus WRITE unless the service runs with readonly = true.

The minimum Jira Cloud scopes needed by the Requirement Service:

ScopePurpose
read:jira-workRead projects, issues, fields, changelogs
read:jira-userRead user/account information
write:jira-workCreate and update issues, field metadata

For readonly = true deployments, write:jira-work can be omitted.

Refer to the Atlassian REST API documentation to confirm which scopes individual endpoints require:


Example configurations

# config.toml
[testbench-requirement-service]
reader_class = "JiraRequirementReader"

[testbench-requirement-service.reader_config]
server_url = "https://example.atlassian.net/"
auth_type = "basic"

# Credentials (alternative to env vars)
# username = "my-user@example.com"
# password = "my-api-token-or-password"

# Connection tuning
# timeout = 30
# max_retries = 3
# cache_ttl = 300.0

# Forward proxy for all Jira API requests (optional)
# proxy_url = "http://proxy.example.com:8080"

# Requirement & baseline settings
baseline_field = "fixVersions"
baseline_jql = 'project = "{project}" AND fixVersion = "{baseline}" AND issuetype in standardIssueTypes()'
current_baseline_jql = 'project = "{project}" AND issuetype in standardIssueTypes()'
requirement_group_types = ["Epic"]
major_change_fields = ["fixVersions"]
minor_change_fields = ["summary", "description", "affectsVersions", "status"]
owner_field = "assignee"
rendered_fields = ["Acceptance Criteria", "Technical Specification"]

[testbench-requirement-service.reader_config.projects."Project A"]
baseline_field = "fixVersions"
baseline_jql = 'fixVersion = "{baseline}"'
current_baseline_jql = 'project = "{project}" AND fixVersion = "{baseline}"'
requirement_group_types = ["Initiative"]
owner = "creator"

Separate config file

# config.toml
[testbench-requirement-service]
reader_class = "JiraRequirementReader"
reader_config_path = "jira_config.toml"
# jira_config.toml (no section prefix)
server_url = "https://example.atlassian.net/"
auth_type = "basic"
# ... same settings as inline example

[projects."Project A"]
baseline_field = "fixVersions"
requirement_group_types = ["Initiative"]

.env file

# Basic authentication (Jira Cloud)
JIRA_USERNAME=my-user@example.com
JIRA_PASSWORD=my-api-token

# Token authentication (Jira Server/Data Center)
# JIRA_BEARER_TOKEN=my-personal-access-token

# OAuth1 authentication
# JIRA_OAUTH1_ACCESS_TOKEN=my-access-token
# JIRA_OAUTH1_ACCESS_TOKEN_SECRET=my-access-token-secret
# JIRA_OAUTH1_CONSUMER_KEY=my-consumer-key
# JIRA_OAUTH1_KEY_CERT_PATH=/path/to/private-key.pem

# OAuth2 authentication (2LO and 3LO)
# JIRA_OAUTH2_CLIENT_ID=my-client-id
# JIRA_OAUTH2_CLIENT_SECRET=my-client-secret

# Mutual TLS (optional)
# JIRA_CLIENT_CERT_PATH=/path/to/client.crt
# JIRA_CLIENT_KEY_PATH=/path/to/client.key

# Forward proxy for the OAuth2 token endpoint (auth.atlassian.com).
# Jira API requests use the `proxy_url` reader setting instead.
# HTTPS_PROXY=http://proxy.example.com:8080
warning

Do not put JIRA_OAUTH2_REFRESH_TOKEN in a .env file for normal operation — the refresh token rotates on every use and belongs in the runtime cache (tmp/oauth2_tokens.toml) that the setup wizard seeds. Only use the environment variable for one-off seeding in automated deployments.

Testing

Smoke test

  1. Set your Jira credentials (via environment variables or config):

    export JIRA_USERNAME=my-user@example.com
    export JIRA_PASSWORD=my-api-token
  2. Start the server:

    testbench-requirement-service start
  3. Call the projects endpoint:

    curl -u "ADMIN_USERNAME:PASSWORD" http://127.0.0.1:8020/projects
  4. Verify that the expected Jira projects are returned.

Troubleshooting

ProblemCauseSolution
ModuleNotFoundErrorMissing [jira] dependenciesRun pip install testbench-requirement-service[jira]
Connection refusedWrong server_urlVerify the URL is reachable and includes the protocol (https://)
401 / 403 from JiraInvalid or missing credentialsCheck that the env vars or config match the selected auth_type
SSL errorsSelf-signed or corporate CA certificateSet ssl_ca_cert_path to your CA bundle, or set verify_ssl = false for testing only
Timeout errorsSlow Jira instance or networkIncrease timeout and max_retries in config
Validation error on auth_typeValue not spelled exactly as an allowed literalUse basic, token, oauth1, oauth2 2LO (service account) / oauth2 2LO, or oauth2 3LO (user account) / oauth2 3LO — see Authentication methods
Connection works, but OAuth2 token refresh times outEgress to the token endpoint is blocked; proxy_url does not cover itSet the HTTPS_PROXY environment variable for the service process
Jira OAuth2 refresh token is not configured3LO refresh token missing or expired (90 days unused, or revoked)Re-run testbench-requirement-service configure and enter a fresh refresh token
Jira OAuth2 client credentials are not configuredoauth2_client_id / oauth2_client_secret missing or still placeholdersSet both in the config or via JIRA_OAUTH2_CLIENT_ID / JIRA_OAUTH2_CLIENT_SECRET
Jira OAuth2 token endpoint not found at '…/rest/oauth2/1.0/token' (HTTP 404)The instance was detected as Jira Data Center but has no incoming OAuth 2.0 application link — or server_url is wrongConfigure an incoming OAuth 2.0 application link in Jira Data Center, or verify server_url. For a Jira Cloud site, ensure {server_url}/_edge/tenant_info is reachable so the service can detect Cloud.
Wizard: Token exchange failed (HTTP 400/401)Authorization code already used, expired, or the PKCE code_verifier does not matchGenerate a fresh authorization code and retry the exchange — codes are single-use and short-lived
Jira Cloud site treated as Data Center (OAuth2)The Cloud ID lookup at {server_url}/_edge/tenant_info failed (blocked egress, proxy)Make the tenant-info endpoint reachable, e.g. via proxy_url — see Proxy settings
Connection refused / timeout in a corporate networkDirect egress to Jira blockedSet proxy_url to your forward proxy — see Proxy settings