Реализовать аутентификацию в клиенте Elixir Phoenix Absinthe GraphIQL?
Я использую встроенный интерфейс GraphiQL в Absinthe. Следующее:
pipeline :browser do
plug RemoteIp, headers: ~w[x-forwarded-for], proxies: ~w[]
plug :accepts, ["html", "json"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
scope "/graphiql" do
pipe_through :browser # Use the default browser stack
forward "/", Absinthe.Plug.GraphiQL,
schema: ApiWeb.Schema,
default_headers: {__MODULE__, :graphiql_headers},
context: %{pubsub: ApiWeb.Endpoint}
end
def graphiql_headers(conn) do
%{
"X-CSRF-Token" => Plug.CSRFProtection.get_csrf_token(),
}
end
Мне нужен конечный пользователь, чтобы вставить Authentication: Bearer <JWT>
в интерфейсе, а затем нужно развернуть его для заголовка sub:, который содержит мой идентификатор пользователя, который мне нужно использовать в средствах разрешения.
Пользователь может настроить собственные заголовки, это не проблема. Если он затем выполнит запрос GraphSQL, интерфейс выдаст POST для конечной точки /graphiql. Именно в этот момент я хочу вызвать несколько плагинов, которые проверяют JWT и получают информацию о пользователе.
Я думал, что смогу использовать опцию default_headers, но, похоже, она вызывается только во время запросов GET.
Кажется, мне нужны разные конвейеры для GET и POST к конечной точке /graphiql, как мне это сделать? Я должен делать что-то не так...
Обратите внимание, что если я использую один и тот же конвейер для GET и POST, JWT уже будет проверяться при посещении конечной точки в браузере, что мне не нужно.
1 ответ
Да, на самом деле я сделал следующее:
pipeline :authenticate_on_post_only do
plug ApiWeb.Plugs.Authenticate, post_only: true
end
scope "/graphiql" do
pipe_through [:browser, :authenticate_on_post_only]
forward "/", Absinthe.Plug.GraphiQL,
schema: ApiWeb.GraphQL,
socket: ApiWeb.GraphQLSocket
end
в сочетании с:
defmodule ApiWeb.Plugs.Authenticate do
use Plug.Builder
alias ApiWeb.Helpers.JWT
plug Joken.Plug, verify: &JWT.verify/0, on_error: &JWT.error/2
plug ApiWeb.Plugs.Subject
plug Backend.Plug.Session
def call(%Plug.Conn{method: "POST"} = conn, opts) do
conn = super(conn, opts) # calls the above plugs
put_private(conn, :absinthe, %{context: conn}) # for absinthe (GraphQL), for resolvers to re-use
end
def call(conn, opts) do
if opts[:post_only] do
conn
else
super(conn, opts) # calls the above plugs
end
end
end
Конечно, вы можете использовать любые собственные плагины аутентификации вместо тех, что я перечислил.
У меня также был REST API в том же модуле, который я использовал следующим образом:
scope "/v1", ApiWeb do
pipe_through :api
<my resources here>
done
с конвейером API, определяемым как:
pipeline :api do
plug :put_resp_content_type, "application/json"
plug :accepts, ["json"]
plug ApiWeb.Plugs.Authenticate
end
который будет аутентифицироваться при любом типе HTTP-запроса.