What you build yourself
- List your networks. The office ranges and the VPN address pool, both IPv4 and IPv6. Include only networks where you control who gets in: a managed device, a VPN sign-in, a building pass.
-
Restrict
/connect/authorizeon the proxy. Only this route starts a sign-in. An NGINX example with documentation address ranges, to add alongside the configuration from Behind a reverse proxy:Veriqa accepts this route regardless of case and with a trailing slash, so the block matches it the same way:~*ignores case, and/?allows the slash. An exactlocation =would let/CONNECT/AUTHORIZEthrough the generallocation /. The query string does not affect the match. If Veriqa runs under a path prefix, include the prefix in the expression and let it repeat:^(/auth/veriqa)+/connect/authorize/?$. When your general block trims the prefix,/auth/veriqa/auth/veriqa/connect/authorizereaches Veriqa as/auth/veriqa/connect/authorize, and Veriqa still treats it as this route. NGINX checks regular expressions in the order they appear, so put this block before the otherlocation ~blocks. Make sure nolocation ^~orlocation =block covers this path: NGINX then skips regular expressions. In this blockproxy_passforwards the path unchanged, because NGINX does not allow a URI inproxy_passinside a regular-expressionlocation. Veriqa still accepts the prefixed path even if your general block trims the prefix. Use the same address as your general block, for example the sameupstream, so that these requests reach your instances the same way as all others. -
Leave the other routes open. The phone opens the pages under
/auth/and/r/over mobile data, the messaging platforms call the webhooks under/api/channels/, and your backend calls/connect/tokenfrom wherever it runs. The browser that has already passed the check opens the sign-in page and its WebSocket. The phone also uses the routes under/connect/authorize/callback, for example to submit the form that asks for a missing profile field, so do not restrict them. The block above does not match them. -
Check the client’s address. NGINX checks the address of whoever connects to it. If a
load balancer or a CDN sits in front of NGINX, restore the client address from its header first
with the
set_real_ip_fromandreal_ip_headerdirectives of the realip module; otherwise every request comes from the load balancer, and they all pass or all fail together. On the Veriqa side, configure trust in the proxy itself in theForwardedHeaderssection — see self-hosted, step 6. -
Tell the person why. Outside your networks the browser gets a 403. Serve your own page for that
response (
error_page 403in the samelocation) saying that sign-in works only from the office network or over the VPN.
Why this works
A forwarded-QR-code attack starts when the attacker’s own browser opens the sign-in page. From outside your networks that browser gets a 403 instead of the page, and there is no QR code to send. To start a sign-in, the attacker needs a device inside the network — the RFC puts it as “the attacker needs to have access to a device on the network”. In BCP 247 terms, this is the Trusted Networks mitigation (RFC 10027 §6.1.8). The list lives in your proxy rather than in Veriqa, which is why Veriqa, BCP 247 and the threat model marks the row as not covered in the core and points here for what you can do today. The same list of networks can also pick the surface in the Place and identity scenario.What to keep in mind
- The rule applies to every application on the instance. They share the
/connect/authorizeroute. The proxy can tell which application starts a sign-in only from theclient_idquery parameter, and the same parameter can be written in several ways, so a proxy rule based on it is easy to get around. If one Veriqa instance serves both an internal and a public application, give the internal one an instance of its own, with its own host name and issuer. - It does not suit consumer applications. Your customers sign in from networks you do not control; the RFC itself notes this limitation.
- The network is only as trusted as the way into it. Guest Wi-Fi in the same range, a personal device on the VPN, or malware on an office computer is already inside. The RFC advises combining this control with others (§6.1.8, SHOULD), for example with the Passkeys alongside Veriqa scenario.
- Remote work goes through the VPN. An employee outside the office signs in only with the VPN on, and a VPN outage stops their sign-ins too.