在 Elixir Phoenix Absinthe GraphIQL 客户端中实现身份验证?
implementing authentication in Elixir Phoenix Absinthe GraphIQL client?
我在 Absinthe 中使用 built-in GraphiQL 界面。如下:
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
我需要end-user在界面中插入一个Authentication: Bearer <JWT>
,然后需要解包为子:header,其中包含我需要的用户ID在解析器中使用。
用户可以配置自定义 header,这没问题。如果他随后执行 GraphSQL 查询,接口将向 /graphiql 端点发出 POST。在 this 点我想调用一些检查 JWT 和检索用户信息的插件。
我想我可以使用 default_headers 选项,但它似乎只在 GET 请求期间被调用。
我似乎需要不同的 GET 管道和 POST 到 /graphiql 端点,我该如何实现?我一定是做错了什么...
请注意,如果我对 GET 和 POST 使用相同的管道,则仅在浏览器中访问端点时就会检查 JWT,这是我不想要的。
是的,实际上我做了以下事情:
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 请求进行身份验证。
我在 Absinthe 中使用 built-in GraphiQL 界面。如下:
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
我需要end-user在界面中插入一个Authentication: Bearer <JWT>
,然后需要解包为子:header,其中包含我需要的用户ID在解析器中使用。
用户可以配置自定义 header,这没问题。如果他随后执行 GraphSQL 查询,接口将向 /graphiql 端点发出 POST。在 this 点我想调用一些检查 JWT 和检索用户信息的插件。
我想我可以使用 default_headers 选项,但它似乎只在 GET 请求期间被调用。
我似乎需要不同的 GET 管道和 POST 到 /graphiql 端点,我该如何实现?我一定是做错了什么...
请注意,如果我对 GET 和 POST 使用相同的管道,则仅在浏览器中访问端点时就会检查 JWT,这是我不想要的。
是的,实际上我做了以下事情:
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 请求进行身份验证。