Merge pull request #7946 from blockscout/np-api-v2-token-changes

API v2 rate limit: Put token to cookies & change /api/v2/key method
pull/7954/head
Victor Baranov 1 year ago committed by GitHub
commit 10f7fff0bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 3
      apps/block_scout_web/config/config.exs
  3. 4
      apps/block_scout_web/lib/block_scout_web/api_key_v2_router.ex
  4. 20
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/api_key_controller.ex
  5. 17
      apps/block_scout_web/lib/block_scout_web/views/access_helper.ex

@ -4,6 +4,7 @@
### Features
- [#7946](https://github.com/blockscout/blockscout/pull/7946) - API v2 rate limit: Put token to cookies & change /api/v2/key method
- [#7888](https://github.com/blockscout/blockscout/pull/7888) - Add token balances info to watchlist address response
- [#7898](https://github.com/blockscout/blockscout/pull/7898) - Add possibility to add extra headers with JSON RPC URL
- [#7836](https://github.com/blockscout/blockscout/pull/7836) - Improve unverified email flow

@ -12,7 +12,8 @@ config :block_scout_web,
cookie_domain: System.get_env("SESSION_COOKIE_DOMAIN"),
# 604800 seconds, 1 week
session_cookie_ttl: 60 * 60 * 24 * 7,
invalid_session_key: "invalid_session"
invalid_session_key: "invalid_session",
api_v2_temp_token_key: "api_v2_temp_token"
config :block_scout_web,
admin_panel_enabled: System.get_env("ADMIN_PANEL_ENABLED", "") == "true"

@ -9,8 +9,6 @@ defmodule BlockScoutWeb.APIKeyV2Router do
plug(Logger, application: :api_v2)
plug(:accepts, ["json"])
plug(CheckApiV2)
plug(:fetch_session)
plug(:protect_from_forgery)
end
scope "/", as: :api_v2 do
@ -18,6 +16,6 @@ defmodule BlockScoutWeb.APIKeyV2Router do
alias BlockScoutWeb.API.V2
get("/", V2.APIKeyController, :get_key)
post("/", V2.APIKeyController, :get_key)
end
end

@ -2,10 +2,17 @@ defmodule BlockScoutWeb.API.V2.APIKeyController do
use BlockScoutWeb, :controller
alias BlockScoutWeb.AccessHelper
alias Plug.Crypto
@api_v2_temp_token_key Application.compile_env(:block_scout_web, :api_v2_temp_token_key)
action_fallback(BlockScoutWeb.API.V2.FallbackController)
plug(:fetch_cookies, signed: [@api_v2_temp_token_key])
@doc """
Function to handle POST requests to `/api/v2/key` endpoint. It expects body with `recaptcha_response`. And puts cookie with temporary API v2 token. Which is handled here: https://github.com/blockscout/blockscout/blob/cd19739347f267d8a6ad81bbba2dbdad08bcc134/apps/block_scout_web/lib/block_scout_web/views/access_helper.ex#L170
"""
@spec get_key(Plug.Conn.t(), nil | map) :: {:recaptcha, any} | Plug.Conn.t()
def get_key(conn, params) do
helper = Application.get_env(:block_scout_web, :captcha_helper)
ttl = Application.get_env(:block_scout_web, :api_rate_limit)[:api_v2_token_ttl_seconds]
@ -14,11 +21,14 @@ defmodule BlockScoutWeb.API.V2.APIKeyController do
{:recaptcha, false} <- {:recaptcha, is_nil(recaptcha_response)},
{:recaptcha, true} <- {:recaptcha, helper.recaptcha_passed?(recaptcha_response)} do
conn
|> put_resp_cookie(@api_v2_temp_token_key, %{ip: AccessHelper.conn_to_ip_string(conn)},
max_age: ttl,
sign: true,
same_site: "Lax",
domain: Application.get_env(:block_scout_web, :cookie_domain)
)
|> json(%{
key:
Crypto.sign(conn.secret_key_base, conn.secret_key_base, %{ip: AccessHelper.conn_to_ip_string(conn)},
max_age: ttl
)
message: "OK"
})
end
end

@ -11,7 +11,7 @@ defmodule BlockScoutWeb.AccessHelper do
alias BlockScoutWeb.WebRouter.Helpers
alias Explorer.AccessHelper
alias Explorer.Account.Api.Key, as: ApiKey
alias Plug.{Conn, Crypto}
alias Plug.Conn
alias RemoteIp
@ -77,7 +77,7 @@ defmodule BlockScoutWeb.AccessHelper do
ip_string = conn_to_ip_string(conn)
plan = get_plan(conn.query_params)
token = get_ui_v2_token(conn, conn.query_params, ip_string)
token = get_ui_v2_token(conn, ip_string)
user_agent = get_user_agent(conn)
@ -167,18 +167,19 @@ defmodule BlockScoutWeb.AccessHelper do
to_string(:inet_parse.ntoa(ip))
end
defp get_ui_v2_token(conn, %{"token" => token}, ip_string) do
case is_api_v2_request?(conn) && Crypto.verify(conn.secret_key_base, conn.secret_key_base, token) do
{:ok, %{ip: ^ip_string}} ->
token
defp get_ui_v2_token(conn, ip_string) do
api_v2_temp_token_key = Application.get_env(:block_scout_web, :api_v2_temp_token_key)
conn = Conn.fetch_cookies(conn, signed: [api_v2_temp_token_key])
case is_api_v2_request?(conn) && conn.cookies[api_v2_temp_token_key] do
%{ip: ^ip_string} ->
conn.req_cookies[api_v2_temp_token_key]
_ ->
nil
end
end
defp get_ui_v2_token(_conn, _params, _ip_string), do: nil
defp get_user_agent(conn) do
case Conn.get_req_header(conn, "user-agent") do
[agent] ->

Loading…
Cancel
Save