dwyl / elixir-auth-google

👤Minimalist Google OAuth Authentication for Elixir Apps. Tested, Documented & Maintained. Setup in 5 mins. 🚀
https://github.com/dwyl/elixir-auth-google-demo
GNU General Public License v2.0
253 stars 42 forks source link

Does this work with barebones Elixir / no Phoenix? #123

Open hawkyre opened 5 months ago

hawkyre commented 5 months ago

I'm trying to use this in a project without Phoenix and, with the same exact credentials, it will work on the Phoenix project but not on the Elixir one. It keeps throwing the "invalid_client" and "The OAuth client was not found." error after I request the token. I am running the server and client separately.

Here's the code:

post "/" do
  conn = Plug.Conn.fetch_session(conn)

  code = Map.get(conn.body_params, "code", "")

  {:ok, token} =
    ElixirAuthGoogle.get_token(
      code,
      "#{Application.get_env(:root, :frontend_url)}"
    )
    |> IO.inspect()

  {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) |> IO.inspect()

  ...
end

Am I doing something wrong? This is the only relevant code to the project, and it is throwing after ElixirAuthGoogle.get_token/2

nelsonic commented 5 months ago

Great question. ❓ ❤️ The quick answer is yes. 👍 But we only tend to use it with Phoenix so we haven't written an example without it. 🙃 Please confirm your Elixir App has access to the required environment variable. 🔑

hawkyre commented 5 months ago

Yes, it does. The request is done successfully and I've compared the payload and it seems to be the same in both the phoenix and the bare elixir, but the phoenix one does return the proper profile data while the elixir can't exchange the user code for the token. Could it be the JSON parsing somehow being different? I'm also using HTTPoison.

Btw, if it's any helpful, I was testing yesterday and even though the variables were being added as I could see in the request, I modified the module code to have the client id and secret be static and that instead throws a redirect_uri_mismatch even though the redirect uri is added to the GC project.

I also wrote a different function with the same structure as the module and this one does throw the redirect_uri_mismatch as well. Here's the code:

def get_token(code) do
  # Build the request body
  body =
    %{
      client_id: "raw client id",
      client_secret: "raw secret",
      redirect_uri: "http://localhost:4000/auth/google/callback",
      grant_type: "authorization_code",
      code: code
    }
    |> Jason.encode!()

  headers = []

  # Make the HTTP POST request
  case HTTPoison.post(@google_token_url, body, headers) do
    {:ok, %HTTPoison.Response{status_code: 200, body: response_body}} ->
      # Handle the response, typically converting JSON to an internal format
      case Jason.decode(response_body) do
        {:ok, decoded} -> {:ok, decoded}
        {:error, reason} -> {:error, reason}
      end

    {:ok, %HTTPoison.Response{status_code: status} = res} ->
      IO.inspect(res)
      {:error, "Failed to retrieve token, status code: #{status}"}

    {:error, reason} ->
      {:error, reason}
  end
end

Here are the response's request objects in both projects, copy pasted straight from the raw response (I'm pretty sure they are the same):

Bare elixir:

request: %HTTPoison.Request{
    method: :post,
    url: "https://oauth2.googleapis.com/token",
    headers: [],
    body: "{\"code\":\"code\",\"client_id\":\"client-id\",\"client_secret\":\"client-secret\",\"grant_type\":\"authorization_code\",\"redirect_uri\":\"http://localhost:4000/auth/google/callback\"}",
    params: %{},
    options: []
  }

Phoenix

request: %HTTPoison.Request{
     method: :post,
     url: "https://oauth2.googleapis.com/token",
     headers: [],
     body: "{\"code\":\"code\",\"client_id\":\"client-id\",\"client_secret\":\"client-secret\",\"redirect_uri\":\"http://localhost:4000/auth/google/callback\",\"grant_type\":\"authorization_code\"}",
     params: %{},
     options: []
   }

I can also provide how I'm treating the code in the Next.js frontend (I'm using a popup instead of a redirect):

const googleLogin = useGoogleLogin({
  flow: 'auth-code',
  redirect_uri: `${process.env.NEXT_PUBLIC_SERVER_URL}/auth/google/callback`,
  ux_mode: 'popup',
  onSuccess: async (codeResponse) => {
    console.log(codeResponse);

    const tokens = await getAxiosInstance().post(
      `${process.env.NEXT_PUBLIC_SERVER_URL}/auth/google/callback`,
      {
        code: codeResponse.code,
      }
    );

    console.log(tokens);
  },
  onError: (errorResponse) => console.log(errorResponse),
});