GitHub Proof of Presence can prove that a person recently satisfied an identity-provider challenge. It cannot prove that an AI agent is allowed to execute this exact proposal. Keep fresh authentication, action approval, workload credentials and execution read-back as four separate records.
What GitHub Shipped, And Where It Stops
GitHub's new control raises the identity bar for sensitive account actions, but its published scope is narrower than an agent authorization system.
GitHub released Proof of Presence on September 24, 2026 as a public preview. The release announcement scopes the preview to Enterprise Managed User enterprises on github.com and GHEC-DR that use Microsoft Entra ID through SAML or OIDC. When a member attempts a protected action, GitHub redirects that person to the identity provider and proceeds only after the configured policy is satisfied.
The protected examples are consequential: creating a token, editing webhooks, changing organization security settings and viewing recovery codes. GitHub lets an enterprise require reauthentication or MFA for the challenge.
The important operating detail is session reuse. Proof of Presence extends GitHub's sudo-mode model. GitHub currently documents a two-hour sudo-mode timeout, with a sensitive action during that window resetting the timer. That is reasonable for an interactive administrator. It is not an action-specific authorization record.
GitHub also says support before pull request merges is coming soon. Do not design a current merge-control claim around an announced future boundary.
Presence And Approval Answer Different Questions
Fresh authentication should be an input to approval, never the approval itself.
An identity-provider challenge answers: is this the expected person, at an acceptable assurance level, now? An action approval answers a different set of questions:
- Which exact tool call is proposed?
- Which organization, repository and resource will it affect?
- What before-and-after state did the reviewer inspect?
- Which policy caused the pause?
- Is this decision reusable, editable, rejectable or one-time?
- What credential will execute the effect?
- What state was observed after the provider accepted the call?
Those distinctions matter even when the reviewer and executor both live inside GitHub. A person can enter a fresh sudo-mode session to create a token. That does not make every token scope acceptable. A person can prove presence before editing a webhook. That does not establish which endpoint, event set or secret rotation plan the business approved.
The same separation applies to coding agents. A reviewer may be freshly authenticated while inspecting a proposal, but the model can still mutate the arguments after review, target the wrong repository, reuse an old decision or execute with a credential broader than the proposal.
Our canonical guide to human approval gates for AI agents covers the full pause, persist, review and resume state machine. Proof of Presence strengthens the reviewer evidence inside that state machine. It does not replace the state machine.

Bind The Human Decision To One Proposal
Hash the canonical proposal before review, and accept the decision only for that exact hash.
The smallest useful proposal record contains the action, owner, repository, resource, intended effect and arguments. Canonicalization must be deterministic. If the action changes after the reviewer sees it, the hash changes and the gate denies execution.
This dependency-free reference gate implements that rule:
import { createHash } from "node:crypto";
function proposalHash(proposal) {
const canonical = JSON.stringify({
action: proposal.action,
owner: proposal.owner,
repository: proposal.repository,
resource: proposal.resource,
effect: proposal.effect,
arguments: proposal.arguments,
});
return createHash("sha256").update(canonical).digest("hex");
}
export function authorizeAgentAction({
proposal,
approval,
credential,
nowMs,
consumedApprovalIds,
maxApprovalAgeMs,
}) {
if (approval.proposalHash !== proposalHash(proposal)) {
return { kind: "deny", reason: "proposal_changed" };
}
if (consumedApprovalIds.has(approval.id)) {
return { kind: "deny", reason: "approval_replayed" };
}
if (approval.decision !== "approve") {
return { kind: "deny", reason: "not_approved" };
}
if (!approval.reviewerId || !approval.authenticatedAtMs) {
return { kind: "deny", reason: "missing_reviewer_evidence" };
}
if (nowMs - approval.authenticatedAtMs > maxApprovalAgeMs) {
return { kind: "deny", reason: "approval_stale" };
}
if (credential.expiresAtMs <= nowMs) {
return { kind: "deny", reason: "credential_expired" };
}
if (!credential.repositories.includes(proposal.repository)) {
return { kind: "deny", reason: "repository_out_of_scope" };
}
if (!credential.permissions.includes(proposal.requiredPermission)) {
return { kind: "deny", reason: "permission_out_of_scope" };
}
return {
kind: "allow_once",
approvalId: approval.id,
proposalHash: approval.proposalHash,
reviewerId: approval.reviewerId,
credentialId: credential.id,
};
}The reference artifact uses an illustrative 15-minute approval age. That is a demonstration policy, not a GitHub default or a recommendation for every buyer. Set the age from the effect's risk, the review latency your operators can sustain and the identity-provider policy. For a destructive or privilege-changing action, a business may require a new challenge for every proposal. For a bounded reversible change, the accepted window may differ.
The local artifact passes 10 of 10 cases: exact proposal, post-review mutation, replay, rejection, stale evidence, expired credential, wrong repository, missing permission, missing read-back and contradictory read-back. Passing those cases proves the reference logic follows its stated contract. It does not prove the policy fits a buyer's workflow.
Do Not Turn Freshness Into Prompt Fatigue
Challenge frequency is an operating choice, and more prompts are not automatically safer.
Microsoft Entra's every-time sign-in policy requires full reauthentication when a session is evaluated. Microsoft also factors in five minutes of clock skew so a person is not prompted more often than once in that interval. Its documentation warns that excessive reauthentication can create fatigue and increase exposure to phishing.
That warning matters for agent review queues. If every low-risk proposal demands MFA, reviewers learn to clear prompts rather than inspect effects. Put the strongest challenge on the actions that change authority, credentials, external integrations or irreversible state. Keep low-risk read-only work outside the approval queue, and use policy plus later sampling where delayed correction is acceptable.
The reviewer UI should show the proposal before the identity challenge. Otherwise the person proves presence without knowing what decision will follow. After the challenge returns, re-check the proposal hash before enabling the approval control. Identity freshness and proposal integrity must meet at the same decision boundary.
Scope The Agent Credential Separately
Execute with a workload credential that cannot exceed the approved repository and permission.
GitHub App installation access tokens expire after one hour. The token request can narrow access to selected repositories and to a subset of the app's installed permissions. Use both controls. A short lifetime without a narrow scope still leaves an unnecessarily powerful credential during that hour.
The credential record should retain:
| Field | Release check |
|---|---|
credential_id | One retained identifier without storing the secret in the approval log |
repositories | Contains the approved repository and no unrelated target |
permissions | Contains the required permission and no unnecessary write class |
issued_at and expires_at | Valid at dispatch and short enough for the job |
GitHub's permission model makes a useful distinction. A user access token is constrained by both the app's permissions and the user's permissions. An installation token depends on the app's permissions. Neither fact says that the current business owner approved this specific effect. The application must retain that approval separately.
This is also why handing an agent the reviewer's personal token is a poor shortcut. It collapses human identity, workload identity and business authority into one credential. The system then cannot tell whether a call was made by the person, by the agent on the person's behalf, or by a replay after the review context changed.
Reconcile The Effect After GitHub Accepts It
A successful API response is an acceptance receipt, not proof that the intended state now exists.
After the call, read the authoritative resource and compare the observed repository, resource and effect with the approved proposal. Retain three distinct outcomes:
observed: the canonical state matches the exact proposal.uncertain: the provider accepted the request, but read-back is unavailable or incomplete.divergent: the canonical state contradicts the approved effect.
Do not replay an uncertain write merely because the approval and credential are still valid. First reconcile by business-effect key or provider state. A blind retry can turn one approved intent into two effects.
This is the same discipline behind an AI coding-agent pilot release gate: task acceptance, denied actions, human merge control and rollback need evidence from the actual runtime. Here the decisive evidence is narrower. The approved proposal, dispatched credential and observed GitHub state must describe the same effect.

Ship The Control As Four Records
The clean implementation keeps four records and refuses to infer one from another.
Record fresh presence
Store the reviewer identity, authentication time, identity-provider policy and challenge result. Do not store the factor secret or copy the browser session into the agent runtime.
Bind one proposal
Canonicalize the target and arguments, hash them, show the diff, then record approve, edit, reject or escalate against that hash. Mark an approval consumed after dispatch.
Mint narrow workload authority
Request a GitHub App token for only the repository and permission needed by the approved proposal. Keep the credential identifier and scope in the execution record, never the token value.
Observe the provider state
Read back the authoritative resource. Close the action only on a canonical match; route uncertainty and divergence to a named owner without automatic replay.
The position is simple: enable Proof of Presence where its current scope fits, but do not label the agent path approved because the reviewer passed MFA. A fresh person, an unchanged proposal, a narrow workload credential and a matching read-back are four independent gates.
Does GitHub Proof of Presence apply to pull request merges?
Not yet according to GitHub's September 24 release announcement. GitHub says support before pull request merges is coming soon, so teams should not treat it as a current merge gate.
Is MFA the same as approving an AI agent action?
No. MFA establishes identity assurance for a person at a time. Agent approval must bind that person to one exact proposal, target, policy and effect.
How long does GitHub Proof of Presence last?
Proof of Presence uses GitHub's sudo-mode session model. GitHub currently documents a two-hour timeout, with protected actions during the session resetting that timer.
Should an agent use a person's GitHub token after approval?
Prefer a separate, narrowly scoped workload credential. That preserves the difference between who reviewed the action, what the business approved and which process executed it.








