A member was asking for my implementation around LiveView. So I share here. There’s nothing much LiveView though. For the posterity.
I’m using the button API at the moment, not the OAuth API. The button POST
s a JWT to /access
. The only LiveView thing here is phx-update="ignore"
. It prevents LiveSocket connection removing the button.
# button component
def google_oauth(assigns) do
~H"""
<button id="google_oauth" phx-update="ignore">
<div
id="g_id_onload"
data-client_id={OAuth.id(:google)}
data-login_uri={url(~p"/access")}
data-auto_prompt="false"
>
</div>
<div class="g_id_signin" data-type="standard" data-size="medium" data-text="continue_with">
</div>
</button>
"""
end
To get around the CSRF token, I’ve pipe_through :api
them. I check CSRF token from google instead. You can skip this part if you use the OAuth API within Phoenix. The button API requires it because requests are forged in the nested HTML in the button where Phoenix have no control.
# router
pipeline :api do
plug :accepts, ["json"]
end
scope "/", Feder do
pipe_through :api
post "/access", Auth.Conn, :sign_in
delete "/access", Auth.Conn, :sign_out
end
Process them in a controller of your choice.
# actions
@doc """
Signs account in with JWT.
"""
def sign_in(conn, params) do
with conn <- fetch_cookies(conn),
true <- conn.cookies["g_csrf_token"] == params["g_csrf_token"],
{:ok, %{"email" => email}} <- OAuth.verify(params["credential"]),
%{token: token} <- Access.grant(email),
id when is_integer(id) <- Access.get_account_id_by_token(token),
cookie_name <- Access.token_cookie() |> Keyword.get(:name),
cookie_opts <- Access.token_cookie() |> Keyword.drop([:name]) do
conn
|> put_resp_cookie(cookie_name, token, cookie_opts)
|> redirect(to: ~p"/")
end
end
@doc """
Signs the account out. Clears all session data.
"""
def sign_out(conn, _params) do
with conn <- fetch_session(conn),
token <- get_session(conn, Access.token_key()),
socket <- get_session(conn, :live_socket_id) do
Access.delete_by_token(token)
Feder.Endpoint.broadcast(socket, "disconnect", %{})
conn
|> configure_session(renew: true)
|> clear_session()
|> delete_resp_cookie(Access.token_cookie()[:name])
|> redirect(to: ~p"/")
end
end