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


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="olivermt" data-post="6" data-topic="58465">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/olivermt/48/2354_2.png" class="avatar"> olivermt:</div>
<blockquote>
<p>I do wonder why Ash has such a hangup on using uuids. I am not a big fan of using uuids.</p>
</blockquote>
</aside>
<p>Mostly security, as they don’t expose guessable identifiers, which is especially important in APIs, but also they can give away things like order of magnitude or even close to exact number of users (just signup and you get the next ID and that’s roughly the count).</p>
<p>As you point out uuids can be generated on the client also (think of a complex mesh of objects that you want to ideompotently update or create, e.g. an upsert).</p>
<p>Another consideration for your database is they don’t require as much sequence coordination across nodes if running a cluster, eg a citus postgresql cluster supports uuid (previously citus only supported ints with clever handling of sequences in the citus coordinator).</p>
<p>Sidenote, you can scale to petabytes with Postgres using the Citus extension which makes Postgres a most compelling solution initially, and well beyond unicorn status.</p>
<p>I normally don’t reach for integer IDs, so I personally saw this a positive sensible default in Ash as I stopped using integer IDs around 2012.</p>
<p>I do accept that if you prefer to map integer IDs that is also quite a valid approach to addressing security <strong>provided</strong> it is actually done securely and efficiently. Sadly that library <strong>does not use established cryptographic primitives</strong>, a better approach would be to use a well known encryption algorithm (eg AES) to encrypt and decrypt IDs and map the encrypted bits to an alphabet such as the <a href="https://github.com/ai/nanoid" rel="noopener nofollow ugc">nano id alphabet</a> (A-Za-z0-9_-) on the server.</p>
<aside class="quote no-group" data-username="olivermt" data-post="6" data-topic="58465">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/olivermt/48/2354_2.png" class="avatar"> olivermt:</div>
<blockquote>
<p>The liveview usage segment intrigues me a bit, I do find typing out all the changeset blabla boilerplate in my service modules (context modules for you who like that nomenclature) to be a bit tedious, so I like that that is all taken care of. Including all the form helper stuff from lv 0.18+.</p>
</blockquote>
</aside>
<p>Excitingly it seems there is a new mix task for generating liveviews on main…</p>
<aside class="onebox githubfolder" data-onebox-src="https://github.com/ash-project/ash_phoenix/tree/main/lib/mix/tasks">
  <header class="source">
      <img src="https://github.githubassets.com/favicons/favicon.svg" class="site-icon" alt="" width="32" height="32">

      <a href="https://github.com/ash-project/ash_phoenix/tree/main/lib/mix/tasks" target="_blank" rel="noopener nofollow ugc">github.com</a>
  </header>

  <article class="onebox-body">
    <h3><a href="https://github.com/ash-project/ash_phoenix/tree/main/lib/mix/tasks" target="_blank" rel="noopener nofollow ugc">ash_phoenix/lib/mix/tasks at main · ash-project/ash_phoenix</a></h3>

  <p><a href="https://github.com/ash-project/ash_phoenix/tree/main/lib/mix/tasks" target="_blank" rel="noopener nofollow ugc">main/lib/mix/tasks</a></p>

  <p><span class="label1">Utilities for integrating Ash and Phoenix. Contribute to ash-project/ash_phoenix development by creating an account on GitHub.</span></p>

  </article>

  <div class="onebox-metadata">
    
    
  </div>

  <div style="clear: both"></div>
</aside>

<aside class="quote no-group" data-username="olivermt" data-post="1" data-topic="58465">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/olivermt/48/2354_2.png" class="avatar"> olivermt:</div>
<blockquote>
<p>Making <a class="mention" href="/u/kip" rel="nofollow">@kip</a> and <a class="mention" href="/u/zachdaniel" rel="nofollow">@zachdaniel</a> get together on slack to make ash_double_entry default (or at least more fully support) using ex_money so that the framework does not expose such a big foot gun to people doing something else than dollars.</p>
</blockquote>
</aside>
<p>Awesome. That was something I noticed also with the out of the box Ash types in general, no built in support for money (currency and amount), but it was something I planned to implement as a custom type when I implement billing.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="302169" data-batch-url="/posts/batch_likers">
                        5
                      </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/a-toy-project-in-a-non-toy-framework-observe-my-journey-as-a-phoenix-veteran-trying-out-ash-for-the-first-time/58465/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-302169" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="302169"
                     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 #11"></div>
  </section>
</div>
    <div class="postbit" id="302170" data-post-id="302170">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="fceruti" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/fceruti/120/28850_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  fceruti
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="adw632" data-post="12" data-topic="58465">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/9d8465/48.png" class="avatar"> adw632:</div>
<blockquote>
<p>Mostly security, as they don’t expose guessable identifiers</p>
</blockquote>
</aside>
<p>Or shamefully low ids.</p>
<p>If these are the only reasons why you reach for uuid (speaking to an audience now), then I have an alternative:</p>
<p>Use this ecto type:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule HashedIdType do
  use Ecto.Type

  @type t() :: String.t()
  @hasher Hashids.new(salt: "123", min_len: 13, alphabet: "123456789abcdefghijklmnopqrstuvwxyz")

  @impl true
  def type, do: :id

  @impl true
  def cast(id) when is_integer(id) do
    {:ok, encode_id(id)}
  end

  def cast(hashed_id) when is_binary(hashed_id) do
    {:ok, hashed_id}
  end

  def cast(_), do: :error

  @impl true
  def load(id) when is_integer(id) do
    hashed_id = encode_id(id)
    {:ok, hashed_id}
  end

  @impl true
  def dump(hashed_id) when is_binary(hashed_id) do
    id = decode_id(hashed_id)
    {:ok, id}
  end

  def dump(_), do: :error

  def encode_id(id) when is_integer(id) do
    Hashids.encode(@hasher, id)
  end

  def decode_id(hashed_id) when is_binary(hashed_id) do
    case Hashids.decode(@hasher, hashed_id) do
      {:ok, [id]} -&gt; id
      _ -&gt; nil
    end
  end
end
</code></pre>
<p>put this on every schema of yours (you can do this by replacing <code>use Ecto.Schema</code> with <code>use MyApp.Schema</code></p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule MyApp.Schema do
  defmacro __using__(_) do
    quote do
      use Ecto.Schema

      @primary_key {:id, HashedIdType, autogenerate: true}
      @foreign_key_type HashedIdType
    end
  end
end
</code></pre>
<p>and with this in place, you’ll never have to think about this again, and your users will never know they are user <span class="hashtag-raw">#42</span>.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="302170" 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/a-toy-project-in-a-non-toy-framework-observe-my-journey-as-a-phoenix-veteran-trying-out-ash-for-the-first-time/58465/13">Post #12</a>
	                </div>
	            </div>
              <div id="likers-container-302170" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="302170"
                     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 #12"></div>
  </section>
</div>
    <div class="postbit" id="302178" data-post-id="302178">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Everything is fine, but the hashid implementation is not cryptographiclly secure and possibly slower than AES.</p>
<p>Replace the hashids with AES:</p>
<p>Example encrypt/decrypt from <a href="https://hexdocs.pm/ex_crypto/ExCrypto.html#decrypt/3" rel="noopener nofollow ugc">here</a></p>
<p>Setup a key for your application ONCE (don’t change it once in production).</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes)
</code></pre>
<p>Encrypt the db id:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">{:ok, {init_vec, cipher_text}} = ExCrypto.encrypt(aes_256_key,  &lt;&lt;db_id::integer-size(64)&gt;&gt;)
param_id = encode_id_string(cipher_text)
</code></pre>
<p>Decrypt the param id to get the original db id:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">cipher_text = decode_id_string(param_id)
{:ok, val} = ExCrypto.decrypt(aes_256_key, init_vec, cipher_text)
db_id = &lt;&lt;val::integer-size(64)&gt;&gt;
</code></pre>
<p>The functions encode_id_string(cipher_text) and   decode_id_string(param_id) convert the binary cipher text to the url safe alphabet as discussed in my previous message.</p>
<p>Again, I prefer uuids in the database vs mapping them everywhere as they are also uniformly distributed and shard friendly.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="302178" data-batch-url="/posts/batch_likers">
                        2
                      </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/a-toy-project-in-a-non-toy-framework-observe-my-journey-as-a-phoenix-veteran-trying-out-ash-for-the-first-time/58465/14">Post #13</a>
	                </div>
	            </div>
              <div id="likers-container-302178" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="302178"
                     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 #13"></div>
  </section>
</div>
    <div class="postbit" id="302192" data-post-id="302192">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zachdaniel" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachdaniel/120/31980_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zachdaniel
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Ash</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I’d also suggest that this is a purely representational concern and should probably not affect the type of your id.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">uuid_primary_key :id do
  private? true
end

calculations do
  # add a public_id calculation for display
  calculate :public_id, :string, MyApp.Calculations.PublicId

  calculate :matches_public_id, :boolean, MyApp.Calculations.MatchesPublicId do
    argument :public_id, :string, allow_nil?: false
  end
end

defmodule MyApp.Calculations.PublicId do
  use Ash.Calculation

  # this kind of calculation can't be automatically filtered on
  def calculate(records, _, _) do
    Enum.map(records, fn record -&gt; &amp;hash_id/1)
  end
end

defmodule MyApp.Calculations.PublicIdMatches do
  use Ash.Calculation
  require Ash.Expr

  # this kind can, because it happens in the data layer if necessary
  def expression(_, args) do
    id = unhash(args.public_id)
    Ash.Expr.expr(id == ^id)
  end
end
</code></pre>
<p>You could write a small <code>HashedId</code> extension that would add these calculations and the attribute to any resource.</p>
<p>EDIT: it would make hooking up certain <code>get_by_id</code> actions a bit more tedious as you won’t be able to use the default behavior of “primary key equals X”. You’ll end up with stuff like</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">read :by_public_id do
  argument :id, :string, allow_nil?: false
  filter expr(public_id_matches(public_id: arg(:id)))
end
</code></pre>
<p>and then to hook that up to something like <code>ash_graphql</code></p>
<pre data-code-wrap="elixir"><code class="lang-elixir">mutations do
  update :update_action,
    read_action: :by_public_id, # look the record up using the by public id action
    identity: false # don't automatically add an input for id &amp; filter by it
end
</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="302192" data-batch-url="/posts/batch_likers">
                        2
                      </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/a-toy-project-in-a-non-toy-framework-observe-my-journey-as-a-phoenix-veteran-trying-out-ash-for-the-first-time/58465/15">Post #14</a>
	                </div>
	            </div>
              <div id="likers-container-302192" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="302192"
                     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 #14"></div>
  </section>
</div>
    <div class="postbit" id="302195" data-post-id="302195">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="derek-zhou" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/derek-zhou/120/19943_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  derek-zhou
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="adw632" data-post="14" data-topic="58465">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/9d8465/48.png" class="avatar"> adw632:</div>
<blockquote>
<p>Everything is fine, but the hashid implementation is not cryptographiclly secure and possibly slower than AES.</p>
</blockquote>
</aside>
<p>Can you substantiate this claim? I’d think nothing is cryptically secure for small integers, since collision attack can be done easily. If someone broke my hashids and recover the integer ids, what is the problem other than shameful low ids?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="302195" 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/a-toy-project-in-a-non-toy-framework-observe-my-journey-as-a-phoenix-veteran-trying-out-ash-for-the-first-time/58465/16">Post #15</a>
	                </div>
	            </div>
              <div id="likers-container-302195" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="302195"
                     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 #15"></div>
  </section>
</div>
    <div class="postbit" id="302196" data-post-id="302196">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zachdaniel" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachdaniel/120/31980_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zachdaniel
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Ash</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I think maybe we should find a way to branch off of this discussion. It will turn into the kitchen sink as OP continues his writing on Ash and we respond with anything interesting <img src="https://forum.elixirforum.com/images/emoji/apple/laughing.png?v=15" title=":laughing:" class="emoji" alt=":laughing:" loading="lazy" width="20" height="20"> Just a thought.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="302196" data-batch-url="/posts/batch_likers">
                        7
                      </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/a-toy-project-in-a-non-toy-framework-observe-my-journey-as-a-phoenix-veteran-trying-out-ash-for-the-first-time/58465/17">Post #16</a>
	                </div>
	            </div>
              <div id="likers-container-302196" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="302196"
                     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 #16"></div>
  </section>
</div>
    <div class="postbit" id="302206" data-post-id="302206">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I don’t really want to have this side thread in here either, but I suggest you read the code… <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"> it doesn’t use a cryptographically strong cipher, its merely a weak obfuscation mechanism.  The output from strong ciphers can’t be reversed without the secret key and are effectively indistinguishable from random noise. You cannot learn anything about the plain text (db id) from the ciphertext (the id in the url) or guess the private key.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="302206" 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/a-toy-project-in-a-non-toy-framework-observe-my-journey-as-a-phoenix-veteran-trying-out-ash-for-the-first-time/58465/18">Post #17</a>
	                </div>
	            </div>
              <div id="likers-container-302206" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="302206"
                     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 #17"></div>
  </section>
</div>
    <div class="postbit" id="302237" data-post-id="302237">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="olivermt" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/olivermt/120/2354_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  olivermt
                    <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">
								<h2><a name="p-302237-part-3-1" class="anchor" href="#p-302237-part-3-1" aria-label="Heading link" rel="nofollow"></a>Part 3</h2>
<p>Up up and away to Auth0 we go!</p>
<p>I’d just like to do a quick detour and say the following:</p>
<ul>
<li>HashIDs are not a security tool, it’s a visual makeup tool if you need / want to obfuscate your IDs for UX or PR purposes. Like I said in my initial comment and someone else repeated above. Going to uuids because you dont want your first big customer to see that their Company PK field is 4 is the wrong reason to use an UUID.</li>
<li>UUIDv4 is practically un-enumerable, but a very determined attacker with a large botnet can still conceivably hit a correct UUID, so you still need to follow the same best practices around row level security protecting against integer based enumeration attacks. To me it’s a dangerous crutch with the potential of being a foot gun because you remove an attack vector that is important to always protect against. For example a user accidentally pasting a sensitive URL somewhere. ACL is always needed regardless. This means the “upside” shrinks enough that it does not in any way outweigh the multiple downsides of uuids as PKs. Postgres is not Oracle, Postgres uses the operating system to handle all the disk stuff through pages, and random uuids are detrimental to such page loading. It will be interesting to see some numbers once UUIDv7 with the time-bits starts to see some use though.</li>
<li>If you really want to have uuids to be external facing, the idiomatic solution is to keep the int as a PK and then add .marketing_uid or whatever if you need a uuid to pump into google for a ecommerce product or whatever.</li>
<li>Going to uuids because they are a good shard key is a very strange argument. I so happened to work on selling CitusDB for said petabyte databases when I worked at Microsoft (both as an architect and later at pre-sales, better comp earlier in the pipe :D) and I have never ever recommended to use an uuid PK as a distribution column. Sure you want even distribution, but you also want to plan for co-location of shards for any complex joining you do and if you want to shard by tenant id for example (in a multi tenancy scenario) then it doesn’t really matter if you use uuids or not because it all gets overriden by tenant affinity.</li>
</ul>
<p>If any interested parties want to read more, the people over at Citus has an excellent readme on data sharding here: <a href="https://docs.citusdata.com/en/stable/sharding/data_modeling.html" class="inline-onebox" rel="noopener nofollow ugc">Choosing Distribution Column — Citus 13.0.1 documentation</a><br>
Beware of the lure of Citus though. Only Azure has it as a managed option after it was aqui-hired by Microsoft and you can go extremely far with some judicious usage of partitions and materialized views to simulate the realtime aggregates before you should reach for Citus. Even the managed version can feel like a chore to manage, with loads of foot guns <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
<p>I was never involved in anything under 500TB when Citus was discussed, just to give a baseline on what kind of database size you should have before you should look towards that solution <img src="https://forum.elixirforum.com/images/emoji/apple/smiley.png?v=15" title=":smiley:" class="emoji" alt=":smiley:" loading="lazy" width="20" height="20"> Typically the competing tech was BigTable or Databricks engine (Python on Spark and an SQL translator on top of Azure datalake/S3)</p>
<p>I did say in my intro post that I would talk about not only Ash, but also other topics that could be relevant for people who want to learn how to architect something and not just “code it”, but I think we have covered uuids enough now. I think it is the wrong decision to default to them in Ash but I don’t think it strongly enough to not recommend using Ash over it. Especially if “hey you can also use integers in this easy way” makes its way into the documentation <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"> PostgreSQL itself heavily recommends sticking with bigints for the PK after all.</p>
<p>Now, Auth0!</p>
<p>This is an interesting one, because I have architected (and implemented) a ton of Openid and Auth0 usage over the years and from the elixir perspective my firm belief these days is that no matter if you are on liveview, exposing a graphql endpoint or other REST the auth0 flowchart should always go directly from Openid → Elixir. As in skip any Auth0-js stuff for the SPA and all such stuff. I have made a comprehensive OpenID module that I bring along from project to project which has convenience functions to handle a full PKCE-flow (tamper proof, and encoded state for easy post-login or post-logout redirects) without any external deps other than Joken and JWKS for JWT verification.</p>
<p>Auth0 has a ton of functionality to create password reset tickets you can use in invite mails to facilitate an invite-only system and you can simply disable open registration in the login flow. For the purpose of this app I will just assume we allow open login and that a user goes through the regular auth0 verification loop.</p>
<p>Essentially you are looking at a flow more or less going:</p>
<ol>
<li>Use internal code to generate a login-url with whatever state and/or redirect info you want</li>
<li>Go to auth0 login either by the SPA sending you there, or more convenient in liveview, simply <code>redirect/2</code> there.</li>
<li>Login as usual</li>
<li>Callback is set to /auth/callback_handler (or something like that)</li>
<li>Elixir parses IdToken and syncs the user to the db. Check that email_verified is true and reject the callback, redirecting to a page saying why if the user is not email verified.</li>
<li>If IDToken parses fine, pass off the auth and refresh token to the UserSession service that verifies the auth token. Auth token will also hold information about if the user is an admin or not, if you prefer to manage that in auth0.</li>
<li>UserSessionService writes the auth and refresh token to the UserSession table and the usersession ID to Plug.Session</li>
<li>Subsequent requests loads the usersession from db based on the ID, verifies the auth token (for time validity). If expired (or within a minute or two), use refresh token to grab new set of auth and refresh token from the OpenID token endpoint. This will let auth0 control token validity etc without you having to think about invalidating things for other than application logic reasons. It is also a checkbox to tick off if you need to SOC2 I believe <img src="https://forum.elixirforum.com/images/emoji/apple/smiley.png?v=15" title=":smiley:" class="emoji" alt=":smiley:" loading="lazy" width="20" height="20"></li>
</ol>
<p>Important note: If you don’t create an “api” in auth0 and ask for that “api” (I just call it literally “api”) in the claims request when redirecting in, auth0 will simply give you an opaque string as the auth token and you lose the ability to JWT.verify it for time claims etc.</p>
<p>Important note2: Don’t fall for the temptation to just pass the IDToken around, that big boy is heavy and should only be used at initial login <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
<p>I have created a tenant, extracted the various config points and I am now curious what Ash actually does under the hood. I suspect / fear that it is not what I outlined above, but hopefully I will be pleasantly surprised! After reading the auth0 guide that is placed at the top I however see that no concept of session is mentioned and I also realize that even though the Auth0 is at the top, I should probably start with “Getting started with auhtentication”.</p>
<p>Meaning I go from <a href="https://ash-hq.org/docs/guides/ash_authentication/latest/tutorials/auth0-quickstart" rel="noopener nofollow ugc">https://ash-hq.org/docs/guides/ash_authentication/latest/tutorials/auth0-quickstart</a> to <a href="https://ash-hq.org/docs/guides/ash_authentication/latest/tutorials/getting-started-with-authentication" rel="noopener nofollow ugc">https://ash-hq.org/docs/guides/ash_authentication/latest/tutorials/getting-started-with-authentication</a></p>
<p>Bingo! One of the first things it shows is a <code>Token</code> resource, this makes me happy. ..Oh it’s if you need to encode stuff that “doesn’t fit in your Token resource”. As in wont be easily encoded in Phoenix.Token maybe? I don’t know. I read on in the docs that start to feel a bit like a rabbit hole in Alice in Wonderland now and move on to the Ash Authentication + Phoenix guide. I think my experience so far is more a problem with the jumbled way Ash displays all these different guides in the sidebar more than “bad documentation”, just so that is said.</p>
<p>Landing safely at <a href="https://ash-hq.org/docs/guides/ash_authentication_phoenix/latest/tutorials/getting-started-with-ash-authentication-phoenix" rel="noopener nofollow ugc">https://ash-hq.org/docs/guides/ash_authentication_phoenix/latest/tutorials/getting-started-with-ash-authentication-phoenix</a> I follow the guide there now, adding all the stuff they ask me to:</p>
<ul>
<li>deps</li>
<li>helpers in the web macro module (would really have liked a <em>why</em> here :D)</li>
<li>tailwind stuff “if you plan on using our default tailwind components” (would also have liked a link to some explanation here, because I have no idea what the ash components referenced are, but it sounds cool and I am going FULL KOOL AID in this toy project, so I’m adding it!)</li>
<li>Repo stuff is a repeat of part 2 (I still don’t have any extensions even after following the guide, just so thats said <a class="mention" href="/u/zachdaniel" rel="nofollow">@zachdaniel</a> :D)</li>
<li>Supervisor for the authentication system. Small nitpick here, the docs should probably show the <code>Endpoint</code> thing last in the list so that you stick to the good practice of not starting the Endpoint (inherently starting to serve requests) before everything else is done.</li>
</ul>
<p>Then we start digging into the <code>Accounts</code> stuff and define the ash APIs needed.</p>
<p>First hiccup is when adding the user.ex resource as described. It has a <code>hashed_password</code> attribute, has a <code>authentication</code> section that defines a password strategy. This does not feel like what I want to do and I hop back to the Auth0 guide to read a bit more.</p>
<p>Bingo, the section that shows the User even says “assuming you have everything else setup” shows me to add:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">authentication do
  strategies do
    auth0 do
      client_id MyApp.Secrets
      redirect_uri MyApp.Secrets
      client_secret MyApp.Secrets
      site MyApp.Secrets
    end
  end
end
</code></pre>
<p>I grab this snippet and hop back to the ash auth phoenix guide.<br>
However, I now have a token section that I have no idea WTF does.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">tokens do
  enabled? true
  token_resource Example.Accounts.Token

  signing_secret Example.Accounts.Secrets
end
</code></pre>
<p>Why do I want this?<br>
The auth0 guide talks about <code>AshAuthentication.GenerateTokenChange</code>.<br>
I google this and end up at the following page:<br>
</p><div class="lightbox-wrapper"><a class="lightbox" href="https://forum.elixirforum.com/uploads/default/original/3X/f/6/f639718ff54ea5f20e7fe212f798cfd6fcfe19b0.jpeg" data-download-href="https://forum.elixirforum.com/uploads/default/f639718ff54ea5f20e7fe212f798cfd6fcfe19b0" title="image" rel="nofollow"><img src="https://forum.elixirforum.com/uploads/default/original/3X/f/6/f639718ff54ea5f20e7fe212f798cfd6fcfe19b0.jpeg" alt="image" data-base62-sha1="z8cwRDobsB86G5kye7KsfuntgwE" width="690" height="500" data-dominant-color="C5C7CC"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">image</span><span class="informations">1124×816 64.6 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div><br>
I am thoroughly impressed that an Elixir framework managed to send me back to 2008 and coding Enterprise Java Beans <img src="https://forum.elixirforum.com/images/emoji/apple/joy.png?v=15" title=":joy:" class="emoji" alt=":joy:" loading="lazy" width="20" height="20"><p></p>
<p>I am obviously knowledgeable enough about the various moving pieces here to start to unravel this and look under the hood tomorrow, but this is going to be <strong>very</strong> confusing for a noob who just wants to login and figure out how to do this with Auth0 without Growth as a sample app to look at because you need to hop guides back and forth so much.</p>
<p>Todays questions and suggestions:</p>
<ol>
<li>Auth0 guide needs to stand a lot more on its own legs. I would just duplicate the everliving hell out of everything. It means you need to work harder to keep stuff up to date, but I hope that at 3.X Ash is mature enough that there won’t be overly large changes needed to something as core as authentication?</li>
<li>What is it that actually goes on when using Oauth2 / OpenID in Ash? The strategy page doesn’t really say other than explaining what the various configuration options do.</li>
<li>I do like how easy it is to do an openid provided upsert allowing for registration, while at the same time also letting you easily deny non-invited users coming in from auth0.</li>
<li>What is it <code>change AshAuthentication.GenerateTokenChange</code> in <a href="https://hexdocs.pm/ash_authentication/AshAuthentication.Strategy.OAuth2.html" class="inline-onebox" rel="noopener nofollow ugc">AshAuthentication.Strategy.OAuth2 — ash_authentication v4.14.1</a> actually does? I have tried to dig for like 10min now and have no idea whats going on <img src="https://forum.elixirforum.com/images/emoji/apple/smiley.png?v=15" title=":smiley:" class="emoji" alt=":smiley:" loading="lazy" width="20" height="20"></li>
</ol>
<p>I hoped I’d have gotten auth done in my two hours post-baby time today, but it seems my Auth0 ventures will continue tomorrow.</p>
<p>Edit: And just so its said, there is a substantial non-zero likelyhood that I will simply keep authentication outside Ash and just plug into AshAuthentication Policies (which do look very nice), but with all the authorization and verification ceremony happening outside Ash. It will be a nice excersize in utilizing the fabled simple to use escape hatches <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
<p>Edit2: I also realized that ash_authentication replaces mix phx.gen.auth and I just want all the nice policy stuff, so yes, tomorrow will be good old OpenID on Phoenix before I circle back to Ash <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="302237" data-batch-url="/posts/batch_likers">
                        13
                      </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/a-toy-project-in-a-non-toy-framework-observe-my-journey-as-a-phoenix-veteran-trying-out-ash-for-the-first-time/58465/19">Post #18</a>
	                </div>
	            </div>
              <div id="likers-container-302237" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="302237"
                     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 #18"></div>
  </section>
</div>
    <div class="postbit" id="302239" data-post-id="302239">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zachdaniel" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachdaniel/120/31980_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zachdaniel
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Ash</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Its absolutely the right idea to start with users instead of pushing the problem til later, but FWIW auth0 is a lesser used path for AshAuthentication, and isn’t nearly as plug and play as something like Oauth. This is obviously something we need to fix, <em>especially</em> how difficult it is to follow the guide. I just want to point out that you’ve probably started out with one of the most complicated pieces you could find <img src="https://forum.elixirforum.com/images/emoji/apple/laughing.png?v=15" title=":laughing:" class="emoji" alt=":laughing:" loading="lazy" width="20" height="20"> I’ll reach out to users on the discord and see if anyone who has set up auth0 or if <a class="mention" href="/u/jimsynz" rel="nofollow">@jimsynz</a> has some input.</p>
<ol>
<li>Yes, agreed <img src="https://forum.elixirforum.com/images/emoji/apple/+1.png?v=15" title=":+1:" class="emoji" alt=":+1:" loading="lazy" width="20" height="20"> We’ll work on that. As part of our 3.0 plans in the next few months, we will be revisiting and reworking <em>every</em> piece of documentation in the framework to make it cohesive, with a focus not on “information density” but on the experience of setting things up, and the ability to discover things.</li>
<li>I’ll leave this up to <a class="mention" href="/u/jimsynz" rel="nofollow">@jimsynz</a> or whoever else has set up auth0 with Ash.</li>
<li>noice.</li>
</ol>
<p>For the GenerateTokenChange, it’s first important to understand what a <code>change</code> is. It’s a <code>plug</code> but for <code>Ash.Changeset</code>. They are meant to be a way to share some modification of an action/build your application. So that <code>change GenerateTokenChange</code> points to a module that has a <code>change/3</code> function, that takes a changeset and returns a changeset.</p>
<p>The meat of that implementation is this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">    Changeset.after_action(changeset, fn changeset, result -&gt;
      {:ok, strategy} = Info.find_strategy(changeset, context, options)

      if Info.authentication_tokens_enabled?(result.__struct__) do
        {:ok, generate_token(changeset.context[:token_type] || :user, result, strategy)}
      else
        {:ok, result}
      end
    end)
</code></pre>
<p>So that change adds an after action hook that will generate a token and set it as metadata on the resource. That token would be the token that would get set in the session. AshAuthentication stores this in the session in the <code>AuthController</code></p>
<pre data-code-wrap="elixir"><code class="lang-elixir">  def success(conn, _activity, user, _token) do
    return_to = get_session(conn, :return_to) || ~p"/"

    conn
    |&gt; delete_session(:return_to)
    |&gt; store_in_session(user)
    |&gt; assign(:current_user, user)
    |&gt; redirect(to: return_to)
  end
</code></pre> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="302239" data-batch-url="/posts/batch_likers">
                        6
                      </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/a-toy-project-in-a-non-toy-framework-observe-my-journey-as-a-phoenix-veteran-trying-out-ash-for-the-first-time/58465/20">Post #19</a>
	                </div>
	            </div>
              <div id="likers-container-302239" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="302239"
                     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 #19"></div>
  </section>
</div>
    <div class="postbit" id="302240" data-post-id="302240">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="zachdaniel" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/zachdaniel/120/31980_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  zachdaniel
                  </h3>
		          </div>
						
			          <div class="user-title">
									<span>Creator of Ash</span>
			          </div>
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>oh, and for the UUID thing, I’d like to add some context: I don’t personally think encrypting your identifiers matters all that much, if you have good security it shouldn’t matter if I can guess at another id that might exist in your system <img src="https://forum.elixirforum.com/images/emoji/apple/person_shrugging.png?v=15" title=":person_shrugging:" class="emoji" alt=":person_shrugging:" loading="lazy" width="20" height="20"></p>
<p>We chose UUIDs only because we had to choose something simple and it wasn’t going to be auto incrementing integers because not all data layers support that, but UUIDs are supported everywhere.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="302240" data-batch-url="/posts/batch_likers">
                        7
                      </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/a-toy-project-in-a-non-toy-framework-observe-my-journey-as-a-phoenix-veteran-trying-out-ash-for-the-first-time/58465/21">Post #20</a>
	                </div>
	            </div>
              <div id="likers-container-302240" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="302240"
                     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 #20"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/58465/load_more?page=3">Load more posts (7 remaining)</a>
</div></template></turbo-stream>