· Paul Crossland
Eight-Hour Tokens and Type-Scoped Kill Switches
GitHub's type-scoped revocation and eight-hour OAuth tokens, plus Cloudflare's pre-registered MCP clients, make credential class operational for fetch fleets.
A pipeline that pulls from a SaaS API every fifteen minutes wakes up to a wall of 401s. Nothing in the fetch log says whether the token expired on schedule, was revoked by an admin, lost a scope, or had its SSO authorization removed. The retry controller guesses "transient," the extraction team guesses "blocked," and the on-call engineer manually replays a login flow that was never the problem.
This week moved the answer twice. On August 18, 2026, GitHub announced credential revocation and deauthorization by token type: enterprise and organization admins can now revoke every credential of one class — personal access tokens, SSH keys, OAuth app tokens, or GitHub App user access tokens — for a single user or across the enterprise, through the UI and the REST APIs, where the previous kill switch removed all of a user's credentials at once. On August 14, 2026, GitHub shipped multiple redirect URIs and token refresh for OAuth apps: OAuth apps can opt into an eight-hour access token backed by a six-month refresh token, register up to ten callback URIs, and control per-URI wildcard matching that was previously an invisible legacy default. The same day, Cloudflare explained how it detects MCP traffic and secures it, including MCP Portals moving to pre-registered OAuth clients as the 2026-07-28 MCP specification deprecates dynamic client registration.
The thesis: credential class — not the individual secret — is becoming the operational unit of API access. Platforms are converging on short-lived, refresh-backed, pre-registered, and selectively revocable credentials, so fetch pipelines need a credential-class inventory, refresh orchestration, class-aware failure classification, and rehearsed revocation drills.
This is defensive operations, not evasion guidance. Revocation is the source platform exercising its rights; nothing here helps traffic survive a denial. The goal is to interpret lifecycle events correctly, contain incidents faster, and stop pipelines from misreading a scheduled expiry as a block, or a deauthorization as a parser bug.
The repeated angle to avoid
Earlier posts here covered browser-side authorization as a fetch boundary and agent identity resolution. The unit there was the browser session or the agent principal. The unit here is different: the server-side credential class on the API you pull from — its lifetime, its refresh path, its revocation granularity. A crawl that stores "we have a token" is about to be as inadequate as one that stores "we have a cookie."
Source map
Fresh primary evidence from the last seven days:
| Source | Date | What it contributes |
|---|---|---|
| Credential revocation and deauthorization by token type | 2026-08-18 | Revocation granularity is now per class and per user, at enterprise and organization level, in UI and REST API, with audit log capture and user notification. |
| Multiple redirect URIs and token refresh for OAuth apps | 2026-08-14 | Eight-hour access tokens with six-month refresh tokens, opt-in via offline_access, on by default for new apps; ten redirect URIs; visible, controllable wildcard matching. |
| How Cloudflare detects MCP traffic and helps secure it | 2026-08-14 | MCP Portals adopt pre-registered OAuth clients as dynamic client registration is deprecated — registration models are tightening, not loosening. |
Older background: the stateless MCP 2026-07-28 specification, whose deprecation of dynamic registration motivated Cloudflare's portal change. GitHub notes the OAuth changes also ship in GitHub Enterprise Server 3.23, so self-hosted sources inherit them too.
Why class, not secret, is the unit
Three properties of a credential class drive pipeline behavior:
Lifetime. An eight-hour access token is a scheduled outage every eight hours unless refresh is automated. The refresh flow has its own failure modes: concurrent workers racing to refresh one token, refresh tokens invalidated by rotation, and clock skew deciding whether a token is honored. The changelog is explicit that apps can force short-lived tokens for all clients and that new apps default to them — meaning a source can flip this on for you.
Revocation granularity. Type-scoped revocation changes incident math in both directions. If your pipeline's token class is revoked, your pipeline stops — cleanly, without taking the account's SSH keys with it. If a different class is revoked during an incident, your pipeline keeps running while the account burns down around it. Your monitoring must not treat "our fetches still succeed" as "our credentials are fine."
Registration topology. Multiple redirect URIs and wildcard matching change where authorization codes can land. For multi-environment pipelines (staging, prod, regional egress), that is convenient; for any host that serves user-controlled content, Cloudflare's and GitHub's own warnings about redirect hygiene apply. Know which of your callback URIs have wildcards on — including the legacy single-URI default that is only now becoming visible.
A failure taxonomy for 401s
Classify before retrying. Record the class in the fetch log, not just the status code:
| Signal | Likely credential event | Correct pipeline response |
|---|---|---|
| Token older than its documented lifetime | Scheduled expiry | Refresh and retry once; alert if refresh fails |
| Refresh returns invalid_grant | Refresh token rotated, revoked, or expired | Re-run authorization flow; quarantine, do not retry |
| Valid token, 403 with scope language | Scope reduced or policy changed | Stop and review scope; never re-auth escalate |
| Works after re-consent prompt absent | SSO deauthorization of the token class | Flag as deauthorized; audit-log correlation |
| All classes fail simultaneously | Account or enterprise kill switch | Page the data-source owner; this is an incident, not a retry |
| Only one class fails | Type-scoped revocation | Contain: switch job to an approved class or pause |
The exact response bodies differ per API, so verify each row with a probe against a canary credential you own — and store the observed signature alongside the taxonomy so classification is grounded, not assumed.
Build the credential-class inventory
For every source your fleet authenticates to, record one row per class in use:
| Field | Example |
|---|---|
| class | OAuth app access token (8h) |
| lifetime / refresh | 8 hours; 6-month refresh token |
| revocation path | Admin, per-type, UI or REST |
| blast radius if revoked | All org pulls for source X |
| environments | prod-eu, prod-us (redirect URIs, wildcard on/off) |
| approved for production | yes / no / canary only |
Then extend fetch logs with: credential_class, credential_id_hash, token_age_bucket, refresh_count, last_refresh_status, scope_hash, and failure_class from the taxonomy above. These fields make expiry curves and revocation events visible as fleet-wide trends instead of scattered mystery 401s.
Four drills worth rehearsing
- Expiry drill. Point a canary job at a token with a deliberately short lifetime. Measure time-to-detect, refresh success rate under concurrent workers, and whether any worker retries with a dead token.
- Revocation drill. Ask the source admin to revoke only the canary's class. Confirm detection latency, that the failure is classified as revocation — not retried as transient — and that other classes' jobs are untouched.
- Refresh race test. Run N workers against one expiring token simultaneously. Assert exactly one refresh succeeds and losers adopt the new token without a re-authorization.
- Redirect audit. Enumerate every registered callback URI across environments; find wildcard matching you did not know was on; decide per URI whether tenants justify it.
Re-run the drills when the source's platform ships identity changes — this week proved those land quietly in changelogs, not in API error messages.
The decision rule
When a credentialed fetch starts failing, resolve in this order: token age, then refresh status, then failure class, then scope, then account-level state. Only after all five are clean may a failure be treated as a source-side block or a reason to change crawling strategy. A pipeline that cannot answer "which class, how old, refreshed when" is not observing its own authentication — it is guessing at it, and this week's changes make the guesses wrong more often.