Skip to main content

Access requests and approvals

The request, review and revoke workflow end to end: the exact states, the attestation an approver must write, and who can now actually approve one since safe-scoped, self-approval-refusing authorisation replaced the old role-attestation gate.

Written for whoever runs IT12 min readUpdated

An access request is how a person asks to use a privileged credential, and an approval is how somebody else agrees. This is one of the more complete parts of the privileged access module: the states are real, the attestation is mandatory, and a grant can only be spent once.

The workflow, end to end

  1. Someone raises a request

    They name the credential and write a justification. Both are required — omit either and the request is refused with credential_id and justification are required. They may also state how long they need; the default is 60 minutes.

  2. The request is created as pending, and approvers are notified

    A pending request is pushed to connected console clients in real time, so an approver who has the console open sees it appear without refreshing.

  3. An approver reviews it

    They either approve or deny. Approving requires an attestation — a sentence saying what they checked and why they agree. Denying does not require one.

  4. The request becomes approved once quorum is met

    By default that is one approval. It stays pending, with each approval recorded, until the required count is reached. Break-glass requests need two — see Break-glass access.

  5. The requester starts a session, once

    The approval is checked again at that moment and bound to the session it opens. It cannot open a second one.

  6. Or the approval is revoked

    An approved request can be revoked. Only approved requests can be revoked; anything else returns access request not found or not in approved status. If a session is running under that grant right now, revoking ends it immediately, in the same action. There is a section on this below.

The exact states, and one that never appears

Every value the status field can hold, and what writes it. Note the last row.
StateWhat it meansWritten when
pendingRaised, awaiting the required number of approvals.On creation.
approvedQuorum reached. Usable to start exactly one session, until the grant expiry passes.When the number of recorded approvals reaches the required count.
deniedAn approver declined it. Terminal.On a deny review.
revokedAn approved grant was withdrawn. Terminal. If a session was open under it, that session was terminated at the same moment.On revoke.
expiredDefined in the data model. No code ever writes it.Never. Expiry is enforced when a session is started, not by changing the row.

Two other fields have fixed vocabularies. The review action is exactly approve or deny; anything else is rejected, though currently with a 500 rather than a 400, which is a defect rather than a judgement about your request. And the requested action — what the grant is for — has one value in practice:

The requested_action field
requested_action: "session.start"

session.start is the only value the platform produces, and it is the default when the field is omitted. The column is free text and is not validated against a list, so a client can post any string and it will be stored and returned verbatim. If you are integrating, send session.start and nothing else — a different value will not unlock a different behaviour, it will just make your audit trail inconsistent with everyone else's.

Justification and attestation: two different fields, two different people

These get confused, and the distinction is the whole point of the workflow.

`justification` — written by the requester
Why they need the access. Mandatory. This is the field an auditor reads first, so it is worth setting a house standard: a ticket reference and a sentence, not the word work. Nothing in the platform enforces quality, only presence.
`attestation` — written by the approver
What the approver checked before agreeing. Mandatory on approve and refused without: approver attestation is required, returned with status 400. It is stored on the approval record alongside the approver's identity, claimed role and the timestamp.

The reason attestation is mandatory rather than optional is worth stating, because it is the design idea that makes this workflow useful at all. An approval button with no text field produces a log line that says somebody clicked yes. An approval with a required sentence produces a record of a decision, made by a named person, with a reason that can be read back and disagreed with later. The first is a formality. The second is a control.

A request can also carry an optional ticket system and reference — a third piece of context alongside justification and attestation, shown to the approver in the review modal alongside the ticket's recorded state. It is optional on the form because whether it is actually required is a per-safe server-side setting the form cannot know in advance. See Requiring a ticket before privileged access for what that control checks and what happens when nothing is connected to check it against.

Who can approve, and how many

Every credential lives in a Safe, and approving a request against it now requires the authorize_requests safe permission on that safe — the same fifteen-permission model Safes and safe members covers in full. Two ways to hold it: an explicit pam_safe_members row naming authorize_requests, resolved directly on the user or through an IAM group or role; or the admin override, which is holding pam:credentials.delete — the one permission every migration since this module's first release treats as admin-only, so it doubles as is a PAM administrator here. A reviewer with neither is refused with ErrInvalidApproval, at the review step itself, not only at the route.

How many approvals a request needs comes from the dual-control workflow governing the credential's safe: the safe's own workflow if an administrator configured one, else the organisation's default workflow, else a built-in one-approver, twenty-four-hour fallback so an organisation that has configured nothing is not permanently stuck. A break-glass request always needs at least two, raised from whatever the resolved workflow asks for if that number is already lower — see Break-glass access.

One older limitation remains, worth naming precisely: reaching the review route at all still needs the separate, older pam:access_requests.write permission, which member holds — so a member account can still open the review screen for a request they have no safe permission over. What changed is that doing so no longer lets them approve it; safeAccess refuses the actual review call regardless of what the route-level permission allowed through.

What is unchanged: a second approval from the same account on the same request is refused with approval record is invalid for this session, so quorum cannot be met by one person clicking twice, and only a pending request can be reviewed at all — reviewing anything else returns 409 with access request is not in pending status.

Revoking a grant, and the live session it kills

Revoking is how you take back an approval you have already given. It applies to the access request — the record that granted the access in the first place — and it is a stronger action than it used to be.

The confirmation in the console says so rather than leaving you to find out. It states that revoking loses the account its privileged access immediately, including any session that is open right now; that the original request and the revocation both stay in the audit trail; and that the person can ask for access again if they need it. That last clause matters operationally — revoking is not a punishment or a permanent block, it is the withdrawal of one grant, and the ordinary way back is a new request.

Three outcomes are possible, and the console tells you which one you got rather than reporting a generic success:

What the console reports after a revoke, and how to read each result.
What you are toldWhat happened
Access revoked, and a live session was terminatedThe grant is withdrawn and the session running under it has been ended. Nothing further to do.
Access revoked, with a warning attachedThe grant is withdrawn, but the session could not be looked up, or could not be stopped. The access is gone and the session may still be live — go to the sessions screen and terminate it directly. You are told this plainly rather than being left to assume the best.
Access revokedThere was no live session under the grant. Not having one is not a problem and is not reported as one.

Both halves are recorded. The revoke itself, and — separately — the session termination it triggered, are each written to the tamper-evident hash chain, along with the identity of whoever performed it. Earlier releases wrote the revoke event with an empty payload and no actor, so the trail said a grant had been withdrawn without saying by whom; that is fixed, and there is no longer a reason to note the person in your own change record alongside it.

Timeouts and expiry

There is one clock and it starts early. The grant expiry is set when the request is created, as the request time plus the requested duration. Approval does not restart it — the approval simply copies the expiry that was already fixed.

The practical consequence is worth an example. A sixty-minute request raised at 09:00 and approved at 09:45 gives fifteen usable minutes. Requesters who submit and then go to lunch will find their approval already spent. Ask for a duration that covers the approval wait as well as the work, within whatever your policy allows.

The default requested duration is 60 minutes. A policy on the credential can cap it, and a request over the cap is refused with both numbers in the message so nobody has to guess:

action violates privilege elevation policy: requested duration 240 min exceeds policy max 60 min

Two limits of the timing model to be aware of. The cap is applied before the sixty-minute default is filled in, so a request that omits the duration entirely is checked against zero and then becomes sixty minutes — meaning a cap below 60 does not constrain such a request. And nothing terminates a session when its grant expires; the expiry gates starting a session, not continuing one. The only automatic bound on a live session is the gateway ceiling of eight hours.

What an approver sees

The pending request presents the facts needed to make the decision without leaving the page:

  • Who is asking, and their justification in their own words.
  • Which credential, and therefore which account on which machine.
  • The requested action and duration, and when the grant will expire.
  • How many approvals are required, which roles are nominated, and which approvals have already been recorded — with each earlier approver's attestation.
  • Whether this is a break-glass request.
  • Where Sentinel-Q is enabled, a recommendation with its reasoning and cited evidence.

That recommendation is advice and nothing else. It is worth being precise about why, because it is one of the platform's structural limits rather than a setting:

The workflow engine: no longer decorative, still not multi-stage

There is a second engine underneath this workflow that supports staged approval, and earlier guidance here said its answers were computed and then thrown away, with a single hardcoded workflow shared by every organisation. That is fixed. RequestAccess now registers the resolved workflow — the one described in who can approve, and how many above — onto this engine and starts a real instance; ReviewAccessRequest persists the post-submit and post-reject state rather than discarding it. See Dual-control approval workflows for how a workflow is configured and resolved; this section is only about what the underlying engine does and does not guarantee.

One honest limit remains, and it is defence-in-depth rather than a security hole: the persisted instance table is written to but not read back on a cold start, so a process restart still loses the engine's own in-memory progress for a request that was mid-flight. This does not weaken any check that actually matters — self-approval, the safe permission, and the distinct-approver headcount are all enforced by the main request path described above, independently of this engine's own state, and none of them depended on surviving a restart before either. Full instance recovery from the persisted table is still future work.

What this engine still does not do is genuine multi-stage approval — several sequential steps, each with its own role gate. A workflow today has one step with a minimum approver count; it does not chain steps. If you need that, it is a real extension to what is here now, not an invented one.

What is recorded

Three audit actions cover the workflow: pam.access_request.create, pam.access_request.review and pam.access_request.revoke. Session start and termination are audited separately, and a session ended automatically by the duration reaper is recorded as pam.session.expire rather than as a termination — deliberately, so that does this platform enforce session duration limits is a question you answer with a query. Break-glass status is carried explicitly in the metadata of both the request creation and the session start, so you can find every break-glass activation without inference.

Secrets cannot reach the audit trail. That is enforced twice — a strip list in code covering nine field names that carry secrets, and a database constraint that rejects a row containing any of them. The two lists are not identical: the database covers five of the nine and the remaining four are caught in code only. Belt and braces on this one is deliberate even where the braces are shorter than the belt, because an audit trail that occasionally contains a password is worse than one that occasionally lacks context.

What to do next

  1. Grant authorize_requests deliberately, on the safes that need approvers beyond your admins — nobody else can approve anything against a safe with no members.
  2. Bind a dual-control workflow to your most sensitive safes if the built-in one-approver fallback is not enough for them.
  3. Set the policy that decides whether approval is required at all.
  4. Read what break-glass actually does before an emergency, because it is not what the name suggests.

Was this article wrong?

If a procedure here does not match what you see, or a limit we described has changed, tell us and we will fix the page. Email us about this article, or see how to get help if you need an answer rather than a correction.

Everything in privileged access