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
...
endThe 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 withPlug.Conn.resp/3and its headers, not send one.MishkaCmsCoreWeb.AuthControllerfires it for the browser's routes, before anything is sent.
Summary
Functions
Puts the headers of a refusal that says to wait seconds on conn.
Functions
@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.