Permissions and Security
Copyright 2026 Matify. All Rights Reserved.
This page explains why Access Auditor for Jira asks for the Jira permission it does, how it decides who is allowed to use it, what every declared scope is for, and what security properties the app is built to hold. It is written for the administrator who has to answer their own security team's questions before approving the install.
Why the app needs the Jira administrator permission
Access Auditor audits site-wide permission configuration: every permission scheme, every project's role membership, every group, and every shared filter and dashboard. Jira does not expose that scope of configuration to a non-administrator, so the product only makes sense for someone who already holds the Jira Administer Jira global permission.
Authorisation model
Every screen and every action in Access Auditor goes through the same check before it reads or returns anything: the app asks Jira, using your own credentials (asUser() against GET /rest/api/3/mypermissions), whether you hold the ADMINISTER global permission. Jira — not the app — makes that decision. Only after Jira confirms it does the app read configuration data with its own app-level credentials.
Background code paths have no such option, because they have no user to check. The daily scheduled trigger, the installation trigger and the asynchronous scan worker all run with the app's own credentials by design and never attempt a per-user permission check. This was confirmed on a real Jira site: calling asUser() from a background trigger fails outright with AUTH_TYPE_UNAVAILABLE, so there is no code path where a background job could be tricked into acting as a particular person.
If you use Access Auditor without the Administer Jira permission, every screen shows an explanation instead of data (error MAA-E401). See Troubleshooting.md.
The app is read-only
Access Auditor declares zero write scopes, zero administration scopes, and zero manage:* scopes. It issues only HTTP GET requests against the Jira Cloud REST API. It never creates, edits or deletes anything in Jira. The only things it writes are its own audit history, findings, settings and your acknowledgement notes, and all of that is written to Forge's Atlassian-hosted app storage, never back to Jira.
Declared scopes, grouped by purpose
Access Auditor declares 22 scopes. Every one of them is either used directly by a feature, or required by Jira as part of the fixed set of scopes an operation demands — Jira Cloud's granular scopes are cumulative, not alternatives, so an endpoint can require a scope the app never reads data through. Both kinds are listed here, and neither kind grants write access.
Scopes the audit reads data through
| Scope | Used for |
|---|---|
read:permission-scheme:jira | Reading every permission scheme and its grants — the core of the audit |
read:permission:jira | Reading grants, and confirming you are a Jira administrator via /mypermissions |
read:project:jira | Every project-scoped read the audit performs |
read:project-role:jira | Resolving a project-role grant to the people who actually hold it |
read:group:jira | Listing groups and resolving a group grant to its members |
read:user:jira | Telling an active account from a deactivated one (finding R4) |
read:application-role:jira | Identifying which groups grant Jira product access, one of the three privileged-group signals |
read:field:jira | Naming the custom field behind a custom-field grant (finding R11) |
read:issue-security-scheme:jira | Detecting a project with no issue security scheme (finding R8) |
read:issue-security-level:jira | Required by Jira alongside the scheme scope for the same per-project check |
read:filter:jira | Detecting shared filters that expose issue data beyond the team (finding R3) |
read:jql:jira | Required by Jira to list filters at all; the JQL body of a filter is never read |
read:dashboard:jira | Detecting shared dashboards that expose data beyond the team (finding R3) |
read:project-category:jira | Required by Jira as part of the cumulative scope set for project, scheme and role reads |
storage:app | Storing audit history — snapshots, findings, the change list, acknowledgements and settings — in Atlassian-hosted app storage |
Scopes required only because Jira bundles them into an operation the audit needs
These are not used by the product for their own data. Jira's granular scopes are cumulative: removing any one of them causes the operation the app depends on to fail with HTTP 401. None of them lets the app read anything beyond configuration metadata.
| Scope | Bundled into |
|---|---|
read:avatar:jira | /group/member, /user/bulk and related lookups — no avatar is ever read, stored or displayed |
read:issue-type-hierarchy:jira | /project/search, /filter/search, /dashboard/search |
read:issue-type:jira | /project/search, /dashboard/search |
read:project-version:jira | /project/search |
read:project.component:jira | /project/search |
read:project.property:jira | /project/search |
Every scope's customer impact is the same: read access to configuration metadata only. No issue content, no attachments and no email addresses are ever read through any declared scope. The full technical derivation of this table — including which Jira REST operation required which scope — is available from Matify on request for anyone who wants to verify it scope by scope, and every scope listed here can also be read directly from the app's own published manifest in the Atlassian Marketplace.
The scope Atlassian requires of any app that stores personal data
| Scope | Used for |
|---|---|
report:personal-data | Reporting the Atlassian account identifiers the app has stored back to Atlassian once a week, so Atlassian can tell the app which of them have changed or been closed. Nothing is read through this scope and nothing leaves Atlassian; it is the platform obligation described under "Keeping stored account data current" in Privacy_and_Data.md. |
What the app does not request
The app does not request manage:jira-configuration or any other administration scope. An earlier build considered reading Jira's audit log to reconstruct global permission grants; that scope was removed before release. See Known_Limitations.md for why.
Tenant isolation
Forge storage is scoped to your installation; one site's data is never addressable from another site's install of the app. The app adds a second guarantee on top of that: nothing is held in memory across requests. Forge can reuse a running container between invocations, so the app re-reads storage on every single request rather than caching a value that might have been set by a different installation's turn on that container.
Secrets
The app holds no secrets. It has no API token, no OAuth client secret and no external credential of any kind, because it never talks to a service outside Atlassian. Nothing is stored with Forge's secret storage. No credential appears anywhere in the app: not in the source, the manifest, the bundled frontend, the logs, this documentation, or the Marketplace listing.
Nothing is cached between requests
Every resolver call re-reads whatever it needs from Forge storage or from Jira. There is no in-memory cache of permission data, group membership or settings that could persist from one request to the next, or from one tenant's request to another's.