ASVS 5.0 V10.7.3 (Level 2) requires that a user can review, modify and revoke the consents they have given, through the authorization server. Three verbs matter: see them, change them, remove them.
Why it matters
Consent given two years ago to an app the user forgot about is a quiet risk. A management page lets users clean up, and it gives them a way to respond when an app is compromised.
How to implement it
Build an account page that lists consents with app name, scopes, granted date and last used date. Allow a user to remove individual scopes, and to revoke an app entirely. Revoking must also invalidate the refresh tokens and reference access tokens linked to that consent, as covered in V10.4.9.
@app.post("/account/consents/<consent_id>")
@login_required
@csrf_protect
def update_consent(consent_id):
consent = db.get_consent(consent_id)
if consent is None or consent.user_id != current_user.id:
abort(404)
keep = set(request.form.getlist("scope")) & set(consent.scopes)
if not keep:
revoke_consent_and_tokens(consent)
else:
db.set_scopes(consent.id, sorted(keep))
revoke_tokens_exceeding_scopes(consent, keep)
audit.log("consent_changed", user=current_user.id, consent=consent.id)
return redirect("/account/consents")
Note the intersection with the existing scopes: a user can narrow a consent but cannot widen it by editing a form.
What the page should show
For each consent, show the application name and whether it is verified, the list of permissions in plain language, the date granted and the date last used, and how long it stays valid. Add a clear button for each action: remove a permission, or remove access entirely. Add a short explanation of what removal does, such as "This app will be signed out and will ask again if it needs access."
Data model
Keep consents in their own table, separate from tokens, so that the page and the revocation logic have one source of truth.
CREATE TABLE consents (
id uuid PRIMARY KEY,
user_id uuid NOT NULL,
client_id text NOT NULL,
scopes text[] NOT NULL,
granted_at timestamptz NOT NULL DEFAULT now(),
last_used timestamptz,
expires_at timestamptz,
UNIQUE (user_id, client_id)
);
Each token row stores the consent ID it came from, so changing a consent can find and revoke the affected tokens in one query.
Modification means narrowing
A user can reduce the scopes of a consent. Widening requires a new authorization flow that shows the new permission and asks again. Make the narrowing take effect on existing tokens at once, not only for new ones.
Access to the page
Protect the page behind a recent login. For high risk accounts, ask for step up authentication before allowing a revoke all, to stop a stolen session from tampering with it. Also link to the page from the consent screen and from account settings, so users can find it.
How to test it
Grant a consent, then open the page and confirm it appears. Remove one scope and confirm tokens lose that access. Revoke it and confirm tokens fail. Try to modify another user's consent ID and expect 404. Try adding a scope through a crafted form and confirm it is ignored.
Admin view
Support staff and administrators benefit from the same data. Give them a read only view of a user's consents for investigations, and require a logged reason before they revoke on someone's behalf.
Common mistakes
- A page that deletes the consent row but leaves tokens working.
- No ownership check.
- Letting a form add scopes that were never granted.
Quick check
What must happen to tokens when a consent is revoked?
Related refresh tokens and reference access tokens must stop working.