Skip to content

Auditor Portal

Path: /auditor

The Auditor Portal gives an external auditor a scoped, read-only view of a single assessment without giving them an account, an org membership, or a third-party identity provider. An admin invites the auditor against one specific assessment, the auditor opens an accept link and receives a short-lived session token, and they land in a workspace that shows only that assessment's controls, evidence, and findings — nothing else in the tenant. The scoped token is the session; there is no user record to create, manage, or off-board.

Key Elements

  • Auditors section on the assessment — Lives on the assessment detail page. An admin invites an auditor by email, picks a role, and gets back a copy-able accept URL. The same section lists outstanding grants with their status and a Revoke button.
  • Accept link — A one-time URL (/auditor?token=...) the auditor opens. Accepting it redeems the token for a scoped session token.
  • Auditor workspace (/auditor) — A single read-only page showing the assessment summary, its controls/findings, and its evidence. It is pinned to one assessment and cannot reach any other data in the org.

Inviting an External Auditor

An invite (an access grant) is created against one assessment:

POST /api/v1/orgs/{org_id}/assessments/{assessment_id}/auditor-grants

The body sets the auditor_email (required), an optional auditor_name, and the role (defaults to auditor_readonly). The response returns the serialized grant plus a one-time accept_token and a ready-to-send accept_url.

  1. Open the assessment and find the Auditors section.
  2. Enter the auditor's email and (optionally) name.
  3. Choose the role — auditor_readonly or auditor_commenter.
  4. Invite. The accept URL appears in the panel; copy it and send it to the auditor.

Email is log-only on the demo — the accept URL is in the response

The demo has no SMTP relay, so the invite is not emailed. Instead the raw accept_token and the full accept_url ({base}/auditor?token=...) are returned in the API response and written to the audit log. This is intentional — it mirrors an air-gapped invite flow where you hand the link to the auditor out-of-band. The base URL prefers the APP_BASE_URL setting and falls back to the request origin.

The grant is created with status pending and an expires_at 14 days out, so the auditor has a sensible window in which to accept.

Roles

Role Value Meaning
Read-only auditor_readonly View the scoped workspace. The default.
Commenter auditor_commenter Read-only plus the intent to leave comments.

The role is recorded on the grant and carried into the scoped token as a claim, so it travels with the auditor's session.

Accepting the Invite

The auditor opens the accept URL, which redeems the one-time token:

POST /api/v1/auditor/accept

The body is just { "token": "..." }. On success the endpoint mints a short-lived, assessment-scoped JWT and returns it as access_token (plus token_type, expires_in, and the resolved assessment_id, organization_id, role, and auditor identity).

There is no third-party IdP and no user account — the scoped JWT is the session. The frontend stores it for the browser session and uses it as a Bearer token for the workspace call. Accepting consumes the one-time token; the grant flips to active and records accepted_at.

The Scoped Workspace

GET /api/v1/auditor/workspace

The workspace is delivered as one read-only payload, pinned to the JWT's assessment_id:

  • assessment — the framework, version, status, examination period, and the passing/failing control counts.
  • controls / findings — each control's id, name, status, severity, disposition, evidence summary, remediation, and evidence count.
  • evidence — the evidence references for the assessment, each flagged as downloadable or not.
  • auditor — the auditor's email, name, and role, so the UI can label the session.

Because the response is built from the token's assessment_id, the auditor sees exactly one assessment's data and has no path to anything else in the tenant.

Security Model

The portal uses a scoped JWT rather than reusing the normal user session, because an external auditor is not an org member.

  • Distinct scope — The minted token carries scope: "auditor_grant". The workspace dependency rejects any token whose scope is not this value, so a normal user token cannot be used on the auditor endpoints and vice-versa.
  • Assessment pin — The token carries assessment_id (and org_id, grant_id, grant_role) as claims. The workspace is built strictly from that assessment_id, which is how per-assessment scoping is enforced — there is no parameter the auditor can change to widen access.
  • Re-validation on every call — The workspace does not trust the JWT alone. On each request it re-loads the backing grant and rejects the token unless the grant is still active and not past expires_at. A revoked or expired grant cuts access immediately, even within the JWT's lifetime.

Two clocks: a 14-day grant and a 12-hour session

The grant (and its one-time accept token) is valid for 14 days — long enough to work through an audit. The scoped JWT minted on accept lives for 12 hours, so a leaked session token ages out quickly. Pending grants that pass their expiry are lazily marked expired so the grant list reflects reality.

Revoking Access

DELETE /api/v1/orgs/{org_id}/assessments/{assessment_id}/auditor-grants/{grant_id}

Revoking sets the grant's status to revoked. Because the workspace re-validates the grant on every request, the auditor's scoped JWT stops working immediately — there is no waiting for the token to expire.

Who Can Do What

Action Required permission
List auditor grants on an assessment Assessment read
Invite / revoke an auditor Assessment update
Accept an invite None — the one-time token authorizes it
Open the scoped workspace The scoped auditor JWT (not an org login)

Inviting and revoking are admin/manager actions gated on the assessment-update permission. The accept and workspace endpoints are a separate, public auth path: they are authorized solely by the one-time token and the scoped JWT, so an external auditor never needs an org login.