AQUILA - Atlassian Jira Integration through Oauth 2.0(3LO) and API Key
What are API Token Scopes?
Scopes define what actions an API token is allowed to perform in Atlassian apps such as Jira and Confluence. They enhance security by limiting permissions to only what's needed (e.g., read-only access to audit logs). Always use scoped tokens for AQUILA integrations—unscoped tokens are deprecated for most apps and may not support fine-grained access. For audit logs (events like user actions, config changes, or security incidents), use the specific scopes listed below. Broader scopes may be needed for other integrations (e.g., content indexing) but stick to these for basic monitoring to minimize risk.
Prerequisites
- Atlassian Admin Email Account
- Atlest have a read rights linux privilege
- Broswer (firefox,chrome)
Creating an API Token with Scopes
- Log in to https://id.atlassian.com/manage-profile/security/api-tokens.
- Select "Create API token with scopes".
- Enter a descriptive name for the token (e.g., "AQUILA- Audit Logs Monitoring").
Choose an expiration date for the token (between 1 and 365 days; consider shorter for security).
- Select the application Jira.
- Select the scopes or permissions the token should have:
- For Jira (audit logs): Classic:
manage:jira-configuration orto access /rest/api/3/auditing/record.Granular:Granular: read:audit-log:jira,read:user:jira
- For Jira (audit logs): Classic:
- Click "Create".
- Copy the token and save it securely. You cannot view it again after this step. If lost, generate a new one. Share only with trusted integrations like AQUILA—revoke if compromised.
OAuth 2.0(3LO)apps
OAuth 2.0 (3LO) (also known as "three-legged OAuth" or "authorization code grants") apps. OAuth 2.0 (3LO) allows external applications
and services to access Atlassian product APIs on a user's behalf. OAuth 2.0 (3LO) apps are created and managed in the developer console.
Enabling OAuth 2.0(3LO)
1. Select your profile icon in the top-right corner, and from the dropdown, select Developer console.
2. Select your app from the list (or create one if you don't already have one).
3. Select Permissions in the left menu.
4. Next to the API you want to add, select Configure or Add.
6. Next to OAuth 2.0 (3LO), select Configure or Add.
7. Enter the Callback URL. Set this to any URL that is accessible by the app. When you implement OAuth 2.0 (3LO) in your app (see next section),
The redirect_uri must match this URL.
8. Click Save changes.
10. Copy the URL and paste in text editor (notepad).
Example of a copied URL (the boxed section contains the authorization code).
Exchange authorization code for access token
ChangePaste allthis fields:Curl command into terminal.
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"}'
Change all fields:
client_id: (required) Set this to the Client ID for your app. Find this in Settings for your app in the developer console.client_secret: (required) Set this to the Secret for your app. Find this in Settings for your app in the developer console.code: (required) Set this to the authorization code received from the initial authorize call (described above).redirect_uri: (required) Set this to the callback URL configured for your app in the developer console.
If successful, this call returns an access token similar to this:
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": <string>,
"expires_in": <expiry time of access_token in second>,
"scope": <string>
}
Make calls to the API using the access token Get the cloudid for your site
Your app now has an access token that it can use to authorize requests to the APIs for the Atlassian site. To make requests, do the following:
- Get the
cloudidfor your site. - Construct the request URL using the
cloudid. - Call the API, using the access token and request URL.
Get the cloud_id for your site
Make a GET request to https://api.atlassian.com/oauth/token/accessible-resources passing the access token as a bearer token in the header of the request. For example:
Replace "ACCESS_TOKEN" with your newly generated token.
curl --request GET \
--url https://api.atlassian.com/oauth/token/accessible-resources \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--header 'Accept: application/json'
This will retrieve the sites that have scopes granted by the token (see Check site access for the app below for details). Find your site in the response and copy the id. This is the cloud_id for your site.
sample output: a Jira site:
[
{
"id": "1324a887-45db-1bf4-1e99-ef0ff456d421",
"name": "Site name",
"url": "https://your-domain.atlassian.net",
"scopes": [
"write:jira-work",
"read:jira-user",
"manage:jira-configuration"
],
"avatarUrl": "https:\/\/site-admin-avatar-cdn.prod.public.atl-paas.net\/avatars\/240\/flag.png"
}
]
Creating the Bash Wrapper Script:
The wrapper script manages event collection, logging, and ensures only one instance runs at a time.
- Create required directories for data, logs:
Create a directory named jira (for this example):
mkdir jira
2. Create the wrapper script file:
sudo nano /usr/local/bin/jira_audit.sh
3. Add the following content to the file:
Replace the file path FLAT_FILE, as well as WATCHDOG_FILECLOUD_ID, USER_EMAIL, and API_KEY, with their actual values.
#!/bin/bash
set -euo pipefail
# ----------------------------
# Config
# ----------------------------
FLAT_FILE="/home/testing-jira/jira/flattened.json"
LOCK_FILE="/var/lock/jira-events.lock"
CLOUD_ID="PASTE_YOUR_CLOUD_ID"
USER_EMAIL="PASTE_YOUR_EMAIL"
API_KEY="PASTE_YOUR_API_KEY"
# Ensure only one instance runs at a time
exec 200>"$LOCK_FILE"
flock -n 200 || exit 1
# ----------------------------
# Call Jira API
# ----------------------------
RESPONSE=$(curl -s --request GET \
--url "https://api.atlassian.com/ex/jira/$CLOUD_ID/rest/api/3/auditing/record" \
--user "$USER_EMAIL:$API_KEY" \
--header "Accept: application/json")
# ----------------------------
# Validate JSON
# ----------------------------
if ! echo "$RESPONSE" | jq . >/dev/null 2>&1; then
echo "Error: API did not return valid JSON"
exit 1
fi
# ----------------------------
# Flatten directly to file
# ----------------------------
echo "$RESPONSE" | jq -c '
.offset as $offset
| .total as $total
| (.records // [])[]
| .offset = $offset
| .total = $total
' > "$FLAT_FILE"
# ----------------------------
# Count records
# ----------------------------
count=$(echo "$RESPONSE" | jq '(.records // []) | length')
echo "Flattened $count records into $FLAT_FILE"
4. Make the script executable:
sudo chmod +x /usr/local/bin/jira_audit.sh
5. Set ownership to the dedicated user:user:
Replace the "user:group" in this example both testing-jira:testing-jira.
sudo chown testing-jira:testing-jira /usr/local/bin/jira_audit.sh
Creating the Systemd Service File:
The systemd service ensures the event collection runs continuously and restarts automatically on failure.
1. Create the service file:
sudo nano /etc/systemd/system/jira-audit.service
2. Add the following content to the file:
Replace WorkingDirectory file path.
[Unit]
Description=Jira Audit Log Fetcher
After=network.target
Wants=network-online.target
[Service]
Type=simpleoneshot
ExecStart=/usr/local/bin/jira_audit.sh
Restart=always
RestartSec=600
StartLimitBurst=10
StartLimitIntervalSec=600
# Watchdog: restart if hung
WatchdogSec=30s
NotifyAccess=all
# Run as your user
User=testing-jiracato
WorkingDirectory=/home/testing-jira/cato/jira
# Logging
StandardOutput=journal
StandardError=journal
# Security (optional but recommended)
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
Create a Systemd Timer to handle looping and run automatically in the background:
A systemd timer is a feature of systemd used to schedule tasks to run automatically at specific times or intervals.
1. Create the timer file:
sudo nano /etc/systemd/system/jira-audit.timer
2. Add the following content to the file:
[unit]
Description=Run Jira Audit every 10 minutes
[Timer]
OnBootSec=60
OnUnitActiveSec=600
Persistent=true
Unit=jira-audit.service
[Install]
WantedBy=timers.target
Configuring Log Rotation
To manage log file sizes and prevent disk space issues, configure log rotation for Jira Audit Logs.
Step 1: Create a logrotate configuration file:
sudo nano /etc/logrotate.d/jira_audit
Step 2: Add the following configuration to the file:
Replace /home/your_user/ with your actual path, and update user_owner and user_group to match the correct user and group.
/home/your_user/jira/flattened.json {
daily
rotate 7
missingok
notifempty
copytruncate
compress
compressoptions -1
delaycompress
dateext
dateformat -%Y%m%d-%H%M%S
create 0640 user_owner user_group
}
Enabling and Starting the Service
Step 1: Reload systemd to recognize the new service file:
sudo systemctl daemon-reload
Step 2: Enable the service to start automatically on boot:
sudo systemctl enable --now jira-audit.servicetimer
Step 3: Start the service:
sudo systemctl start jira-audit
Step 4: Verify the service is running:
sudo systemctl status jira-auditlist-timers
You should see something like this.
Monitoring Live Logs:
To view real-time logs from the Cato event feedjira-audit service:
journalctl -u jira-audit -f
Please provide the following information to CyTech.
- Jira User Identifier: Your Atlassian email address (must be linked to an admin account as noted above).
- Jira API Token: The scoped token created above.
- Cloud_ID: The ID retrieved from accessible-resources api call











