Why per client, not globally
A relaxation enabled globally weakens the check for every application — including those that send conformant requests, and those that will appear in the configuration tomorrow. Strictness stops being a property of the deployment and becomes a property of its weakest participant. So a quirk is addressed byClientId: the client with the broken plugin gets the relaxation, every
other client stays strict. That also keeps the relaxation visible — the configuration says which
application is allowed what beyond the RFC.
Enabling
Enabling takes two steps: listing the quirk on the client is not enough, the deployment gate has to be open as well. While the gate is closed, the client’s quirk set is intersected with the empty core-level set and no relaxation applies.appsettings.json
The two steps exist so that a single client’s configuration cannot weaken the check on its own: the
overall ceiling belongs to whoever owns the deployment, the specific application to its client entry.
A profile instead of a list of keys
Rather than writing the keys out one by one, you can name a profile — the engine and plugin you actually run. A profile expands into the very same keys and passes the same validation, the same gate and the same ceiling: it is a shorthand, not a second mechanism.appsettings.json
A profile and an explicit list can be combined — the keys are unioned. An unknown profile name stops
startup with a message listing the allowed names, exactly as an unknown quirk key does: a typo cannot quietly
leave the relaxation switched off.
A profile may carry relaxations of the request form and nothing else. Nothing that changes claims
can be enabled by one: switching on compatibility with a broken client must not also change what the
token says about who signed in.
Disabling
Remove the key from that client’sCompatibilityQuirks — the relaxation stops applying after the next
restart. To drop every quirk in the deployment at once without touching client entries, close the
gate: CompatibilityQuirksAllowApplicationOverride: false.
Nothing needs disabling to begin with: the default is off for everyone. A fresh deployment
applies no quirk until you open the gate and list a key explicitly.
What Veriqa reports at startup
- A quirk is in effect — a warning listing “client → enabled quirks”. This noise is intended: an enabled relaxation must not be silent.
- A quirk is listed but not in effect — a warning with the reason. If the gate is closed it says
so, and opening the gate is the fix. If the gate is open and the set is still empty, the gate is
not the cause: the client entry never reached the resolver, most often because a second entry with
the same
ClientIdshadows it. - An unknown key — startup does not continue: the configuration picks a key from a closed
registry, and a typo here would mean a silently unapplied relaxation. The message lists the
allowed keys. The comparison is case-sensitive —
Drop-Scope-On-Code-Exchangeis rejected just like a typo.
The configuration selects a key, never a type or an assembly name: no reflection is ever
performed on a configured string. The right to edit configuration does not grant code execution
inside the identity provider.
Quirk registry
drop-scope-on-code-exchange
What it does. Drops the scope parameter from a token request made with
grant_type=authorization_code.
Why. RFC 6749 §4.1.3 does not define scope in that request, and the strict check rejects it as
invalid_request. On a code exchange the submitted scope has no effect anyway — the granted set
comes from the authorization code — so dropping the parameter is equivalent to the client never
having sent it. No rights are widened.
Who needs it. The openid-connect-generic plugin for WordPress puts scope into the code
exchange request. The symptom: sign-in reaches the code exchange and fails with
wp-login.php?login-error=invalid_request&message=The+'scope'+parameter+is+not+valid+in+this+context.
With the quirk enabled the same request passes the form check, and the log gains an entry about the
dropped parameter — carrying the ClientId and the dropped value. No site-side change is needed:
sign-in works on the stock plugin, with no patch installed on the site.
What is untouched. The grant_type=refresh_token path: there scope is allowed by RFC 6749 §6
and narrows the granted rights. The quirk leaves that path alone — narrowing rights is both
legal and useful.
Removal condition. The upstream fix
oidc-wp/openid-connect-generic#497
is released and rolled out by the integrators running the plugin. After that the key is retired
along with its handler. While the issue is open, the quirk is needed.
drop-resource-parameter
What it does. Drops the resource parameter (RFC 8707)
from the authorization request and from the token request.
Why. Veriqa registers no resources, so the strict check rejects any value of that parameter with
invalid_target and the sign-in fails before it starts. Nothing here consumes the parameter: with no
registered resources the granted rights come from the scopes alone, so dropping it yields exactly the
token a client that never sent it would receive. The quirk grants no audience and narrows none — it
is not support for resource indicators.
Who needs it. The auth_oidc plugin for Moodle keeps the Microsoft Entra ID dialect it was
written for and sends resource on every request, in its generic mode too; there is no setting to
switch that off. The symptom: sign-in stops at the authorization request with
ClientId and the dropped value.
What is untouched. The default. Without the key enabled for that specific client_id, resource
is rejected with invalid_target.
Removal condition. Moodle’s auth_oidc stops sending resource in its generic (non-Entra) mode.
There is no upstream ticket for that.
Next
Configuration reference
Every key, client ones included.
Preparing for production
What to check before shipping to production.