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.
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.
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.
ID: community-privacy-controls.consent.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>
);
}