Where to put code that should run after sign_in?

When a user signs up, then I want to run some checks before I let him into the app.
E.g. check if he is already affiliated with an existing Company.

Do I write a plug or do I do something else? :slight_smile:

I’m using auth.gen for authentication.

Best,
Martin

Are these things that would block signing up or not? Based on your example, I’ll assume they’re not.

In one of our apps, we perform similar tasks and really like using Oban to handle these background jobs. We enqueue the jobs in Accounts.create_user/1, which manages everything within an %Ecto.Multi{}. For your case it would be something like this:

defmodule MyApp.Users.CheckUsersExistingCompaniesWorker do
  use Oban.Worker,
    queue: :default,
    priority: 3,
    max_attempts: 3,
    tags: ["users"],
    unique: [period: 30]

  @impl Oban.Worker
  def perform(%Oban.Job{args: %{"user_id" => user_id}}) do
    # do the check, maybe update db

    # :ok | {:ok, value} | {:cancel, reason} | {:error, error} | {:snooze, seconds}
  end
end

defmodule MyApp.Users do
  def enqueue_existing_companies_job(user_id) do
    %{user_id: user_id}
    |> __MODULE__.CheckUsersExistingCompaniesWorker.new()
    |> Oban.insert()
  end
end

You could choose your preferred method of spawning processes, but if you want an easy and solid solution, then I’d recommend going with a robust library like Oban or a similar one.

But if you’re talking about things that would block signing up or in, then you’d want to do this in a Plug or an on_mount handler for LiveView.

1 Like

A plug for this use case would be something along the lines of this:

defmodule MyAppWeb.Plugs.CheckUserCompany do
  import Plug.Conn

  def init(_opts), do: %{}

  def call(conn, _opts) do
    case MyApp.SomeContext.affiliated_with_existing_company?(conn.assigns.current_user) do
      true ->
        conn
        |> put_flash(:error, "Not allowed")
        |> redirect(to: "/")

      false ->
        conn
    end
  end
end

And an on_mount handler like this:

defmodule MyAppWeb.Live.OnMountHandlers do
  def on_mount(:affiliated_with_existing_company, _params, _session, socket) do
    case MyApp.SomeContext.affiliated_with_existing_company?(socket.assigns.current_user) do
      true -> {:halt, redirect(socket, to: "/")}
      false -> {:cont, socket}
    end
  end
end

Or whatever the logic is behind that check.

1 Like

Thanks for pointing me in the right direction! :slight_smile: