# `MishkaCmsCore.Runtime.Plugs.AuthRateLimitPlug`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/plugs/auth_rate_limit_plug.ex#L5)

The JSON:API side of `MishkaCmsCore.Accounts.AuthRateLimit`: hands every action the address
`conn.remote_ip` names (the endpoint's `plug RemoteIp` has read it) as the
`ash_authentication_request` context, marked `api?: true` so the actions ask no captcha
(`MishkaCmsCore.Accounts.CaptchaCheck`), and gives a `429` AshJsonApi wrote for a refused sign-in
or request its headers.

    pipeline :api do
      plug :accepts, ["json"]
      plug MishkaCmsCore.Runtime.Plugs.ApiTenantLookupPlug
      plug MishkaCmsCore.Runtime.Plugs.ApiCorsPlug
      plug MishkaCmsCore.Runtime.Plugs.AuthRateLimitPlug
      ...
    end

## The refusal

`retry-after` in seconds, `x-error-code: rate_limited` and `cache-control: no-store`, both headers
exposed to a frontend on an origin `ApiCorsPlug` allowed, on the JSON:API errors document:

    {"errors": [{"status": "429", "code": "rate_limited", "title": "Too many sign-in attempts",
                 "detail": "Too many sign-in attempts. Try again in 15 minutes.",
                 "meta": {"retry_after": 900, "action": "sign_in", "limited_by": "account"}}],
     "jsonapi": {"version": "1.0"}}

## Events

- `auth:request:rate_limited` — the refused conn, with `%{action: action, key: :ip | :account,
  retry_after: seconds, site: site}` as its meta. The response is written by then: a plugin may
  change it with `Plug.Conn.resp/3` and its headers, not send one. `MishkaCmsCoreWeb.AuthController`
  fires it for the browser's routes, before anything is sent.

# `call`

# `init`

# `put_refusal_headers`

```elixir
@spec put_refusal_headers(Plug.Conn.t(), pos_integer()) :: Plug.Conn.t()
```

Puts the headers of a refusal that says to wait `seconds` on `conn`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
