<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="151471" data-post-id="151471">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="wolfiton" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wolfiton/120/15884_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  wolfiton
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thanks</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="151471" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/authentication-and-authorization-demystified-by-example-and-experience/26980/32">Post #31</a>
	                </div>
	            </div>
              <div id="likers-container-151471" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="151471"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #31"></div>
  </section>
</div>
    <div class="postbit" id="151474" data-post-id="151474">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="amnu3387" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  amnu3387
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Usually I start from this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Repo.Migrations.CreateUsersTable do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :email,              :string, null: false
      add :temp_email,         :string # for changing emails until confirmed
      
      add :password_hash,      :string, null: false
      add :temp_password,      :string # for changing passwords until confirmed
      
      add :name,               :string # or divided into first, last, etc
      add :profile_image,      :string

      add :verification_token, :string # for the signup and email verification - this can easily be other table just for the verifications
      add :verified,           :boolean, default: false
      add :locked,             :boolean, default: false

      #add :last_login,         :utc_datetime
      #add :logins,             :map, default: %{}, or {:array, :map}, default []

      timestamps(type: :utc_datetime)
    end

    create unique_index(:users, ["(lower(email))"], name: "users_email_index") #lower case in order to prevent different cased emails, same name as if it was the regular index on a field
  end
end

</code></pre>
<p>Then I add other relevant bits depending on what is needed.</p>
<p>The schema itself has at least two additional two additional virtual fields if using password based accounts:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule User do
  use Ecto.Schema

  schema "users" do
    field :email,                 DowncasedString # this would probably be more resilient if done at the db level but if it's only you working with the db through the elixir app and changesets it's enough
    field :temp_email,            DowncasedString
    
    field :password_hash,         :string
    field :temp_password,         :string
    
    field :password,              :string, virtual: true
    field :password_confirmation, :string, virtual: true

    field :verification_token,    :string

    field :name,                  :string
    field :profile_image,         :string

    field :verified,              :boolean
    field :locked,                :boolean


    timestamps(type: :utc_datetime)
  end
end
</code></pre>
<p><code>password</code> and <code>password_confirmation</code> as virtual fields for easier handling on changesets and so that the forms/payload can simply have those two fields in them.</p>
<p>For managing sessions I’ve played with mnesia and this works for having multiple nodes connected and sharing a store of valid &amp; invalid tokens - not sure if it scales though, or if it’s the best approach, but wanted to do some testing with it. This is for a game, usually I wouldn’t implement it for more normal web apps.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule SessionsManager do
  use GenServer
  require Logger

  alias Core.Runtime

  @cleanup_interval 120_000

  def start_link(_) do
    GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
  end

  def init(_) do
    Process.send_after(self(), :cleanup, @cleanup_interval)
    {:ok, %{}}
  end

  def add_sibling_tokens(login_tuple) do
    GenServer.cast(__MODULE__, {:add_sibling_tokens, login_tuple})
  end

  def add_invalid_tokens(token, socket_token) do
    GenServer.cast(__MODULE__, {:add_invalid_tokens, token, socket_token})
  end

  def handle_info(:cleanup, state) do
    Task.start(&amp;clean_up/0)
    Process.send_after(self(), :cleanup, @cleanup_interval)
    {:noreply, state}
  end


  def handle_cast({:add_invalid_tokens, token, socket_token}, state) do
    invalidate_tokens(token, socket_token)
    {:noreply, state}
  end

  def handle_cast({:add_sibling_tokens, {%{id: id}, token, socket_token}}, state) do
    fun = fn() -&gt;
      now = :erlang.system_time(:second)
      :mnesia.write({:sessions_siblings, id, {token, socket_token}, now})
      :mnesia.write({:sessions_siblings, token, id, now})
      :mnesia.write({:sessions_siblings, socket_token, id, now})
    end

    case :mnesia.transaction(fun) do
      {:atomic, _} -&gt; :ok
      {:aborted, reason} -&gt;
        Logger.error("SessionsManager Error adding sibling tokens: #{inspect reason}")
    end
    {:noreply, state}
  end

  @spec invalidate_tokens(String.t(), String.t()) :: boolean()
  def invalidate_tokens(token, socket_token) do
    fun = fn() -&gt;
      now = :erlang.system_time(:second)
      :mnesia.write({:sessions_invalid, token, now})
      :mnesia.write({:sessions_invalid, socket_token, now})
      case :mnesia.wread({:sessions_siblings, token}) do
        [] -&gt; false
        [{_, id, _, _}] -&gt;
          :mnesia.delete({:sessions_siblings, id})
          :mnesia.delete({:sessions_siblings, token})
          :mnesia.delete({:sessions_siblings, socket_token})
      end
    end

    case :mnesia.transaction(fun) do
      {:atomic, _} -&gt; :ok
      {:aborted, reason} -&gt;
        Logger.error("SessionsManager Error invalidating tokens: #{inspect reason}")
    end
  end

  def clean_up do
    case GenServer.whereis(SessionsManager) do
      nil -&gt; :ok

      pid -&gt;
        fun = fn() -&gt;
          case :mnesia.select(:sessions_invalid, match_spec(:invalid)) do
            [] -&gt; :ok
            
            tokens -&gt;
                Enum.each(:lists.flatten(tokens), fn(token) -&gt;
                  :mnesia.delete({:sessions_invalid, token})
                end)
          end
          case :mnesia.select(:sessions_siblings, match_spec(:siblings)) do
            [] -&gt; :ok
              
            tokens -&gt;
                Enum.each(tokens, fn([id, {token, socket_token}]) -&gt;
                  :mnesia.delete({:sessions_siblings, id})
                  :mnesia.delete({:sessions_siblings, token})
                  :mnesia.delete({:sessions_siblings, socket_token})
                end)
          end
        end

        case :mnesia.transaction(fun) do
          {:atomic, _} -&gt; :ok
          {:aborted, reason} -&gt;
            Logger.error("SessionsManager Error cleaning up tokens: #{inspect reason}")
        end
    end
  end

  def match_spec(:invalid) do
    token_life = Runtime.token_validity()
    ttl_threshold = :erlang.system_time(:second) - token_life 
    [{{:_, :"$2", :"$3"}, [{:"=&lt;", :"$3", ttl_threshold}], [:"$2"]}]
  end

  def match_spec(:siblings) do
    token_life = Runtime.token_validity() + 20
    ttl_threshold = :erlang.system_time(:second) - token_life
    [{{:_, :"$1", :"$2", :"$3"}, [{:"=&lt;", :"$3", ttl_threshold}, {:is_tuple, :"$2"}], [[:"$1", :"$2"]]}]
  end
    
end
</code></pre>
<p>And I have like a basic plug for it, in this case it’s more convuluted because of the session tokens house keeping, usually I just use Phoenix.Tokens, authorize header.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule Authorize.Plug do
  import Plug.Conn, only: [get_req_header: 2, assign: 3]

  alias Authorize.Helpers
  alias Core.Runtime
  
  def init(opts) do
    opts
  end

  @spec call(%Plug.Conn{}, any()) :: %Plug.Conn{}
  def call(conn, _) do
    case get_req_header(conn, "authorisation") do
      [] -&gt; assign(conn, :user, false)
      ["Bearer " &lt;&gt; header] -&gt;
        case valid_token(header) &amp;&amp; Phoenix.Token.verify(conn, Helpers.salt(), header, max_age: Runtime.token_validity()) do
          {:ok, user} -&gt; assign_valid_tokens(conn, user)
          {:error, code} -&gt; assign_valid_tokens(conn, code)
          false -&gt; assign_valid_tokens(conn, :invalid)
        end
    end
  end

  @spec assign_valid_tokens(%Plug.Conn{}, {:admin | :player, integer(), String.t()} | :expired | :invalid | false) :: %Plug.Conn{}
  def assign_valid_tokens(conn, {type, id, username}) do
    case get_sibling_tokens(id) do
      :invalid -&gt; assign_valid_tokens(conn, :invalid)
      {token, socket_token} -&gt;
        conn
        |&gt; assign(:user, {type, id, username})
        |&gt; assign(:token, token)
        |&gt; assign(:socket_token, socket_token)
    end
  end

  def assign_valid_tokens(conn, reason) do
    conn |&gt; assign(:user, reason) |&gt; assign(:token, reason) |&gt; assign(:socket_token, reason)
  end

  @spec valid_token(String.t()) :: boolean()
  def valid_token(token) do
    case :mnesia.dirty_read({:sessions_invalid, token}) do
      [] -&gt; true
      _ -&gt; false
    end
  end

  @spec get_sibling_tokens(integer()) :: :invalid | {String.t(), String.t()}
  def get_sibling_tokens(id) do
    case :mnesia.dirty_read({:sessions_siblings, id}) do
      [] -&gt; :invalid
      [{_, _, {token, socket_token}, _}] -&gt; {token, socket_token}
    end
  end

end
</code></pre>
<p>Then both login and logout do calls to the sessions manager gen_server. Expired tokens that a user hasn’t logout explicitly from the interface end up being rejected once their TTL expires.</p>
<p>For the mnesia part I now always create a “bootstrap”(per) in the umbrella, that is the only app started on the release, that sets up everything and connects nodes, and only then starts the remaining parts of the actual “application” (that are set to be :loaded, but not started on the release definition - in fact I have started doing that even if not using mnesia as it allows to control the startup flow).</p>
<p>The token storing like I said not sure how it works in terms of production ready, but all other things pretty much have worked fine and are quite simple.</p>
<p>(this was copy pasta and slightly changed so some things might not be 100% correct)</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="151474" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/authentication-and-authorization-demystified-by-example-and-experience/26980/33">Post #32</a>
	                </div>
	            </div>
              <div id="likers-container-151474" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="151474"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #32"></div>
  </section>
</div>
    <div class="postbit" id="151477" data-post-id="151477">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="wolfiton" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wolfiton/120/15884_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  wolfiton
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thanks for providing you experience to this talk and also the use of gen servers.</p>
<p>But if I am allowed i would like to ask why not use agents in this case for the state of the tokens expiration?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="151477" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/authentication-and-authorization-demystified-by-example-and-experience/26980/34">Post #33</a>
	                </div>
	            </div>
              <div id="likers-container-151477" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="151477"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #33"></div>
  </section>
</div>
    <div class="postbit" id="151495" data-post-id="151495">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="amnu3387" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  amnu3387
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I usually default to GenServers, to be sincere I don’t use agents that much, or gen_statem if it’s going to have a public “server” like interface but then do complex flows based on it. Agents are designed to work on their own state as you provide a function that takes the state and returns the new one - in this case the state is in mnesia. In the current stage the code I posted is, it wouldn’t even need to be encapsulated in a process - instead be just function calls since mnesia deals with locks, etc. But I was planning to add some more functionality to it.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="151495" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/authentication-and-authorization-demystified-by-example-and-experience/26980/35">Post #34</a>
	                </div>
	            </div>
              <div id="likers-container-151495" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="151495"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #34"></div>
  </section>
</div>
    <div class="postbit" id="151516" data-post-id="151516">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="wolfiton" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wolfiton/120/15884_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  wolfiton
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thank you for the explanation and also for sharing your code with me.</p>
<p>Will you make it an auth library in the future?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="151516" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/authentication-and-authorization-demystified-by-example-and-experience/26980/36">Post #35</a>
	                </div>
	            </div>
              <div id="likers-container-151516" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="151516"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #35"></div>
  </section>
</div>
    <div class="postbit" id="151692" data-post-id="151692">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="amnu3387" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  amnu3387
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hmm, there’s already a few done and they seem to work fine - plus - authorization is a bit of a custom thing and the best approach depends on a lot of factors - then with like, Phoenix tokens, plug parsers, the bcrypt lib, ecto, I found it straightforward to implement it and then just re-use it with the tweaks I need. Doing a library that handles all those tweaks is fairly more complex though. If there’s interest I would gladly write a blog post on it - bearing in mind the way I do it is mostly used in the context of an elixir backend talking to a decoupled frontend, so the front-end just needs to store the necessary token(s) (and share them across browser tabs) and then include an “authorization” header in all requests - that way the backend can easily see if the user is logged in or not.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="151692" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/authentication-and-authorization-demystified-by-example-and-experience/26980/37">Post #36</a>
	                </div>
	            </div>
              <div id="likers-container-151692" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="151692"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #36"></div>
  </section>
</div>
    <div class="postbit" id="151694" data-post-id="151694">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="wolfiton" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/wolfiton/120/15884_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  wolfiton
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I would be most definitely interested in a blog post on this and any other:</p>
<ul>
<li>otp</li>
<li>genserver stuff implementation in a phoenix application.</li>
</ul>
<p>My current interest is because of the following topics and technologies:</p>
<ul>
<li>
<p>Absinthe</p>
</li>
<li>
<p>Dataloader</p>
</li>
<li>
<p>PWA</p>
</li>
<li>
<p>SSR</p>
</li>
<li>
<p>SEO</p>
</li>
</ul>
<p>So if you could make a detailed guide how the back-end and front-end send data for your auth example.<br>
I would  really appreciate it.</p>
<p>Thanks for the follow up on my comment</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="151694" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/authentication-and-authorization-demystified-by-example-and-experience/26980/38">Post #37</a>
	                </div>
	            </div>
              <div id="likers-container-151694" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="151694"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-last-post cat-last-post" title="Last post!"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <span class="all-loaded">— All posts loaded —</span>
</div></template></turbo-stream>