Opinionated authentication for Phoenix LiveView using WorkOS AuthKit.
def deps do
[
{:sgiath_auth, github: "sgiath/auth"}
]
end# config/runtime.exs
config :sgiath_auth,
workos_client_id: System.fetch_env!("WORKOS_CLIENT_ID"),
workos_secret_key: System.fetch_env!("WORKOS_SECRET_KEY"),
callback_url: "https://yourapp.com/auth/callback"# lib/my_app/application.ex
children = [
SgiathAuth.Supervisor
]# lib/my_app_web/router.ex
scope "/auth", SgiathAuth do
pipe_through [:browser]
get "/sign-in", Controller, :sign_in
get "/sign-up", Controller, :sign_up
get "/sign-out", Controller, :sign_out
get "/callback", Controller, :callback
get "/refresh", Controller, :refresh
post "/refresh", Controller, :refresh
end
import SgiathAuth
pipeline :browser do
plug :fetch_session
plug :fetch_current_scope
end
pipeline :require_authenticated do
plug :require_authenticated_user
end# lib/my_app_web.ex
def live_view do
quote do
use Phoenix.LiveView, layout: {MyAppWeb.Layouts, :app}
on_mount {SgiathAuth, :mount_current_scope}
# or: on_mount {SgiathAuth, :require_authenticated}
end
endThe refresh endpoint accepts GET or POST and supports optional organization_id for org switching.
Use return_to to send users back to their current page after refresh.
# In a HEEx template
<form method="post" action="/auth/refresh">
<input type="hidden" name="_csrf_token" value={Plug.CSRFProtection.get_csrf_token()} />
<input type="hidden" name="return_to" value={@return_to} />
<input type="hidden" name="organization_id" value={@organization_id} />
<button type="submit">Switch org</button>
</form>For LiveView, trigger a full HTTP POST via a client hook (recommended):
// assets/js/app.js
let Hooks = {};
Hooks.AuthRefresh = {
mounted() {
this.handleEvent("auth:refresh", ({ return_to, organization_id }) => {
const form = document.createElement("form");
form.method = "post";
form.action = "/auth/refresh";
form.appendChild(this.input("_csrf_token", this.csrfToken()));
form.appendChild(this.input("return_to", return_to));
if (organization_id)
form.appendChild(this.input("organization_id", organization_id));
document.body.appendChild(form);
form.submit();
});
},
input(name, value) {
const input = document.createElement("input");
input.type = "hidden";
input.name = name;
input.value = value;
return input;
},
csrfToken() {
const meta = document.querySelector("meta[name='csrf-token']");
return meta ? meta.getAttribute("content") : "";
},
};# In your LiveView
push_event(socket, "auth:refresh", %{return_to: "/dashboard", organization_id: @organization_id})Implement SgiathAuth.Profile to load app-specific profile/admin data, then set:
config :sgiath_auth, profile_module: MyApp.ProfileSee usage-rules.md for flow, hooks, and behavior notes.