# OAuth and third-party app grants viewable and individually revocable

- **Pattern:** `ab-000730` (`community-privacy-controls.consent.oauth-app-revocation`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000730
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000730)

## 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`:

```ts
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:

  ```tsx
  // 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
- owasp A01
- gdpr Art. 7(3)
- nist AC-2(9)

Taxons: access-control, privacy-consent

HTML version: https://auditbuffet.com/patterns/ab-000730
