Elixir OAuth2.0 Неверные учетные данные - PullRequest
0 голосов
/ 19 сентября 2019

Я использую клиент Elixir OAuth2 и использую Образец аутентификации Elixir Google Cloud Platform в качестве общей базы.

Код выглядит примерно так:

mix.exs


...

  def application do
    [
      mod: {ExampleApp.Application, []},
      extra_applications: [:logger, :runtime_tools, :oauth2]
    ]
  end


...


...
  defp deps do
  ...
      {:oauth2, "~> 2.0"}
  ...
...

Создание файла env

  1. Создать файл .env: nano .env
  2. Добавить .env в .gitignore: echo .env >> .gitignore env
#!/bin/bash
export GOOGLE_CLIENT_ID="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com"
export GOOGLE_CLIENT_SECRET="xxxxxxxxxxxxxxxxxxxxxxxx"
export GOOGLE_REDIRECT_URI="http://localhost:4000/auth/callback" 

source ./.env

OAuth Модель Google

lib / example_app_web / oauth / google.ex

defmodule Google do
  use OAuth2.Strategy

  # Public API
  def client do
    OAuth2.Client.new([
      strategy: __MODULE__,
      client_id: System.get_env("GOOGLE_CLIENT_ID"),
      client_secret: System.get_env("GOOGLE_CLIENT_SECRET"),
      redirect_uri: System.get_env("GOOGLE_REDIRECT_URI"),
      site: "https://accounts.google.com",
      authorize_url: "https://accounts.google.com/o/oauth2/auth",
      token_url: "https://oauth2.googleapis.com/token"
    ])
  end

  def authorize_url!(params \\ []) do
    OAuth2.Client.authorize_url!(client(), params)
  end

  # you can pass options to the underlying http library via `opts` parameter
  def get_token!(params \\ [], headers \\ [], opts \\ []) do
    OAuth2.Client.get_token!(client(), params, headers, opts)
  end

  # Strategy Callbacks
  def authorize_url(client, params) do
    OAuth2.Strategy.AuthCode.authorize_url(client, params)
  end

  def get_token(client, params, headers) do
    client
    |> put_param(:client_secret, client.client_secret)
    |> put_header("accept", "application/json")
    |> OAuth2.Strategy.AuthCode.get_token(params, headers)
  end
end

контроллер авторизации

lib / example_app_web / controllers / auth_controller.ex

defmodule ExampleApp.AuthController do
  use ExampleAppWeb, :controller

  @doc """
  This action is reached via `/auth/callback` is the the callback URL that
  Google's OAuth2 provider will redirect the user back to with a `code` that will
  be used to request an access token. The access token will then be used to
  access protected resources on behalf of the user.
  """
  def callback(conn, %{"code" => code}) do
    # Exchange an auth code for an access token
    client = Google.get_token!(code: code)

    # Request the user's data with the access token
    scope = "https://www.googleapis.com/plus/v1/people/me/openIdConnect"
    %{body: user} = OAuth2.Client.get!(client, scope)
    current_user = %{
      name: user["name"],
      avatar: String.replace_suffix(user["picture"], "?sz=50", "?sz=400")
    }

    # Store the user in the session under `:current_user` and redirect to /.
    # In most cases, we'd probably just store the user's ID that can be used
    # to fetch from the database. In this case, since this example app has no
    # database, I'm just storing the user map.
    #
    # If you need to make additional resource requests, you may want to store
    # the access token as well.
    conn
    |> put_session(:current_user, current_user)
    |> put_session(:access_token, client.token.access_token)
    |> redirect(to: "/")
  end

  @doc """
  This action is reached via `/auth` and redirects to the Google OAuth2 provider.
  """
  def index(conn, _params) do
    redirect conn, external: Google.authorize_url!(
      scope: "https://www.googleapis.com/auth/userinfo.email"
    )
  end

  def delete(conn, _params) do
    conn
    |> put_flash(:info, "You have been logged out!")
    |> configure_session(drop: true)
    |> redirect(to: "/")
  end
end

router.ex

defmodule ExampleApp.Router do
  use ExampleApp.Web, :router

  pipeline :browser do
    ...
    plug :assign_current_user
    ...
  end

  ...

  scope "/auth", ExampleApp do
    pipe_through :browser

    get "/", AuthController, :index
    get "/callback", AuthController, :callback
    delete "/logout", AuthController, :delete
  end

  ...

  # Fetch the current user from the session and add it to `conn.assigns`. This
  # will allow you to have access to the current user in your views with
  # `@current_user`.
  defp assign_current_user(conn, _) do
    assign(conn, :current_user, get_session(conn, :current_user))
  end
end

Добавить кнопку в шаблон

lib / example_app_web / templates / page / index.html.eex

<section class="phx-hero">
      <%= if @current_user do %>
        <h2>Welcome, <%= @current_user.name %>!</h2>
        <p><img src="<%= @current_user.avatar %>" class="img-circle"/></p>
        <p><%= link "Logout", to: Routes.auth_path(@conn, :delete), method: :delete, class: "btn btn-danger" %></p>
      <% else %>
        <br>
        <br>

        <a class="btn btn-primary btn-lg" href="<%= Routes.auth_path @conn, :index %>">
          <i class="fa fa-google"></i>
          Sign in with Google
        </a>
      <% end %>
</section>


При отправке я получаю сообщение об ошибке "Неверные учетные данные", похожее на это SO

Ошибка в консоли выглядит следующим образом:

[debug] Processing with ExampleApp.AuthController.callback/2
  Parameters: %{"authuser" => "0", "code" => "4/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "prompt" => "consent", "scope" => "email https://www.googleapis.com/auth/userinfo.email openid", "session_state" => "920caea4175b647de7feea0fc7cf230fcdb45a22..0f99"}
  Pipelines: [:browser]
[info] Sent 500 in 399ms
[error] #PID<0.3262.0> running ExampleAppWeb.Endpoint (connection #PID<0.3261.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: GET /auth/callback?code=4%2FblahblaheblahblahCWoINhOouSLIvt38H4Hoy5p5343r5B6HC4qVadotDjobRcXKDs&scope=email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid&authuser=0&session_state=920caea4175b647de7feea0fc7cf230fcdb45a22..0f99&prompt=consent
** (exit) an exception was raised:
    ** (OAuth2.Error) Server responded with status: 401

Headers:

vary: X-Origin
www-authenticate: Bearer realm="https://accounts.google.com/", error=invalid_token
content-type: application/json; charset=UTF-8
date: Thu, 19 Sep 2019 06:15:21 GMT
expires: Thu, 19 Sep 2019 06:15:21 GMT
cache-control: private, max-age=0
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
server: GSE
accept-ranges: none
vary: Origin,Accept-Encoding
transfer-encoding: chunked
alt-svc: quic=":443"; ma=2592000; v="46,43,39"

Body:

"{\n \"error\": {\n  \"errors\": [\n   {\n    \"domain\": \"global\",\n    \"reason\": \"authError\",\n    \"message\": \"Invalid Credentials\",\n    \"locationType\": \"header\",\n    \"location\": \"Authorization\"\n   }\n  ],\n  \"code\": 401,\n  \"message\": \"Invalid Credentials\"\n }\n}\n"

        (oauth2) lib/oauth2/request.ex:65: OAuth2.Request.request!/6
        (example_app) lib/example_app_web/controllers/auth_controller.ex:25: ExampleApp.AuthController.callback/2
        (example_app) lib/example_app_web/controllers/auth_controller.ex:1: ExampleApp.AuthController.action/2
        (example_app) lib/example_app_web/controllers/auth_controller.ex:1: ExampleApp.AuthController.phoenix_controller_pipeline/2
        (phoenix) lib/phoenix/router.ex:288: Phoenix.Router.__call__/2
        (example_app) lib/example_app_web/endpoint.ex:1: ExampleAppWeb.Endpoint.plug_builder_call/2
        (example_app) lib/plug/debugger.ex:122: ExampleAppWeb.Endpoint."call (overridable 3)"/2
        (example_app) lib/example_app_web/endpoint.ex:1: ExampleAppWeb.Endpoint.call/2
        (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:42: Phoenix.Endpoint.Cowboy2Handler.init/4
        (cowboy) /Users/bigbassroller2017/Sites/gapi-elixir/example_app/deps/cowboy/src/cowboy_handler.erl:41: :cowboy_handler.execute/2
        (cowboy) /Users/bigbassroller2017/Sites/gapi-elixir/example_app/deps/cowboy/src/cowboy_stream_h.erl:296: :cowboy_stream_h.execute/3
        (cowboy) /Users/bigbassroller2017/Sites/gapi-elixir/example_app/deps/cowboy/src/cowboy_stream_h.erl:274: :cowboy_stream_h.request_process/3
        (stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3

Вот некоторые аспекты ввода-вывода из контроллера аутентификации

"Scope: ======================"
"https://www.googleapis.com/plus/v1/people/me/openIdConnect"
"======================"
"Client: ======================"
%OAuth2.Client{
  authorize_url: "https://accounts.google.com/o/oauth2/auth",
  client_id: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
  client_secret: "xxxxxxxxxxxxxxxxxxxxxxxxx",
  headers: [],
  params: %{},
  redirect_uri: "http://localhost:4000/auth/callback",
  ref: nil,
  request_opts: [],
  serializers: %{},
  site: "https://accounts.google.com",
  strategy: Google,
  token: %OAuth2.AccessToken{
    access_token: "{\n  \"access_token\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\",\n  \"expires_in\": 3600,\n  \"scope\": \"https://www.googleapis.com/auth/userinfo.email openid\",\n  \"token_type\": \"Bearer\",\n  \"id_token\": \"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n}",
    expires_at: nil,
    other_params: %{},
    refresh_token: nil,
    token_type: "Bearer"
  },
  token_method: :post,
  token_url: "https://oauth2.googleapis.com/token"
}
"======================"
"Code: ======================"
"4/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"======================"

Params Параметры authuser "0" код "4 / xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" приглашение "область" согласия "электронная почта https://www.googleapis.com/auth/userinfo.email openid" session_state "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

для меня это паравечеров.

Есть идеи, что не так?

...