ASVS 5.0 V10.6.2 (Level 2) requires the OpenID Provider to prevent denial of service through forced logout. Either ask the end user to confirm, or, when the relying party initiates logout, validate parameters in the request such as id_token_hint.
Why it matters
If hitting the end session endpoint signs a user out with no questions, any web page can embed it in an image tag or a redirect and keep users logged out of every app that relies on that provider. It is a nuisance attack, but at scale it is a real outage for users.
How to implement it
On the end session endpoint, check the request before ending anything.
@app.route("/logout", methods=["GET", "POST"])
def end_session():
hint = request.values.get("id_token_hint")
if hint:
claims = verify_id_token_issued_by_us(hint, allow_expired=True)
client = db.get_client(claims["aud"])
target = request.values.get("post_logout_redirect_uri")
if target and target not in client.post_logout_redirect_uris:
abort(400)
return end_session_for(claims["sid"], redirect=target)
return render_template("confirm_logout.html") # explicit user click, with CSRF token
The confirmation page must submit a POST protected by a CSRF token. Only redirect to registered post logout URIs.
Why the hint matters
The id_token_hint proves that the request refers to a real session issued by this provider. Validate its signature and issuer, and confirm the audience matches a registered client. Accept expired hints, since users often log out long after the token was issued, but check everything else. Use the sid claim to find which session to end, and end only that one, not every session the user has, unless the user chose that.
Redirect handling
The post logout redirect is an open redirect risk. Compare it exactly against the list registered for the client. Reject anything else, and fall back to a plain "you are signed out" page of your own. Never build the target from a parameter without checking.
The confirmation page
When no valid hint arrives, show a page asking "Do you want to sign out?" with a button. That button must submit a POST with a CSRF token tied to the user's session. Without a token, an attacker's page could submit the form for the user and bring the attack back.
<form method="post" action="/logout">
<input type="hidden" name="csrf" value="{{ csrf_token }}">
<button type="submit">Sign out</button>
</form>
Back and front channel logout
If your provider notifies relying parties of logout, those notifications are a separate flow with their own checks (see V10.5.5). Do not let the end session endpoint trigger a fan out to many clients on an unauthenticated request, because that multiplies the effect of a forced logout.
How to test it
Load the logout URL from a page on another origin, with no hint. You should see a confirmation prompt, not a silent logout. Send a request with a tampered or foreign id_token_hint and expect rejection. Send an unregistered redirect URI and expect an error.
Monitoring
Count logout requests per source. A sudden spike in confirmation page views or rejected hints is a sign someone is probing. Alert on it, and consider rate limits on the endpoint.
Common mistakes
- GET logout that acts at once.
- Treating any JWT as a valid hint.
- Open redirects through the post logout parameter.
Quick check
Name the two accepted defences.
Explicit confirmation from the user, or validation of request parameters such as the id_token_hint.