OAuth and third-party app grants viewable and individually revocable
Why it matters
OAuth app grants that cannot be individually revoked give third-party applications permanent access to a user's account unless the user changes their password — a drastic action that breaks all legitimate sessions. GDPR Art. 7(3) requires withdrawal of consent to be as easy as giving it; if consent was given by authorizing an OAuth app, revocation must be available at the same granularity. CWE-285 and OWASP A01:2021 classify unrevocable authorization grants as access control failures. A user who suspects an app is misusing their data has no recourse short of a full password reset.
Severity rationale
Low because exploitation requires a third-party app to already have a valid grant, but permanent irrevocable access grants violate GDPR Art. 7(3) and create lasting exposure if an app is compromised.
Remediation
Build a connected apps management page in account settings. List all active OAuth grants with app name, granted scopes, and grant date. Provide a per-app revoke button that immediately invalidates the access token in your database. In src/app/settings/connected-apps/page.tsx:
async function revokeOAuthGrant(appId: string, userId: string) {
await db.oauthGrant.deleteMany({
where: { appId, userId }
});
// Invalidate any active tokens for this grant
await db.oauthToken.deleteMany({
where: { appId, userId }
});
}
Display the scopes each app was granted in plain language (e.g., 'Read your posts', 'Send messages on your behalf') — not raw OAuth scope strings — so users can make informed revocation decisions.
Detection
-
ID:
oauth-app-revocation -
Severity:
low -
What to look for: Enumerate every relevant item. If the platform supports OAuth (connecting third-party apps like Twitter clients, Slack integrations, or mobile apps), check whether users can see a list of authorized applications. Verify each app grant can be individually revoked from settings. Look for scope disclosures (what permissions each app has).
-
Pass criteria: At least 1 of the following conditions is met. Settings page lists all authorized OAuth apps with grant date and scopes. Each app has a "revoke" button. Revocation immediately invalidates the app's access token.
-
Fail criteria: No list of authorized apps. Users cannot see or revoke app access. Third-party apps have permanent access.
-
Skip (N/A) when: The platform does not support third-party OAuth integrations.
-
Detail on fail: Describe the limitation. Example:
"No management page for connected apps. Third-party integrations cannot be revoked without changing password." -
Remediation: Implement OAuth grant management:
// Settings: Connected Apps export function ConnectedAppsSettings({ userId }) { const [apps, setApps] = useState([]); useEffect(() => { fetch(`/api/oauth/apps?userId=${userId}`) .then(r => r.json()) .then(setApps); }, []); const revokeApp = async (appId: string) => { await fetch(`/api/oauth/revoke`, { method: 'POST', body: JSON.stringify({ appId }) }); setApps(apps.filter(a => a.id !== appId)); }; return ( <div> <h3>Connected Apps</h3> {apps.map(app => ( <div key={app.id}> <div>{app.name}</div> <div>Scopes: {app.scopes.join(', ')}</div> <div>Connected: {new Date(app.grantedAt).toLocaleDateString()}</div> <button onClick={() => revokeApp(app.id)}>Revoke Access</button> </div> ))} </div> ); }
External references
- cwe · CWE-285 — Improper Authorization
- owasp:2021 · A01 — Broken Access Control
- gdpr · Art. 7(3) — Right to withdraw consent — app access revocation
- nist:rev5 · AC-2(9) — Account Management — prohibit use of shared accounts
Taxons
History
- 2026-04-18·v1.0.0·Initial import from community-privacy-controls·automated