## Why there is no error

This is the part that wastes the afternoon. A blocked form action produces no exception, no flash, no
failed request in the network tab and nothing in the Rails log. The browser simply declines to
navigate. The only evidence is a console warning, which is easy to miss if the console is noisy.

## Both conditions have to hold

**The host has to be in `form-action`.** Not `connect-src`, not `default-src`. `form-action` applies
to the entire navigation the form begins, including every redirect along the way, so an OAuth flow
that bounces through two hosts needs both listed.

**The form needs `data-turbo="false"`.** Turbo checks the form and the submit button, so either works.
Without it, Turbo intercepts the submission and issues a `fetch`, which turns a navigation into an
XHR, which `connect-src` then blocks. Same dead button, different rule.

## Fix and pin

```bash
bin/rspec spec/requests/content_security_policy_spec.rb spec/requests/cross_origin_forms_spec.rb
```

Both specs exist so this failure cannot come back silently. Add the host to
`config/initializers/content_security_policy.rb`, add the example to
`spec/requests/content_security_policy_spec.rb`, and the next person who tightens the policy gets a
red build instead of a broken sign-in button.

## The same trap, three other places

Any redirect off-site has this shape. Stripe's hosted checkout, the Stripe billing portal, and any
third-party consent screen all need the host in `form-action` and the form marked
`data-turbo="false"`. If one of those buttons stops working after a policy change, this is why.

## Check the console before anything else

Open devtools, click the button, and read the one console line. It names the directive and the
blocked URL, which turns a guess into a two-minute fix. That line is the entire diagnostic, and it is
the only place the failure is visible.
