kevinschweikert

kevinschweikert

Hi there,

is there a recommended way to encrypt/decrypt fields of a resource to the database?

I’ve used Cloak before, but i struggle to understand, how this would integrate into Ash.
It would be really nice if it worked like Cloak and with that I mean, that it encrypts/decrypts the data automatically when inserting/reading.

Showing Posts 1 to 7

zachdaniel

zachdaniel

Creator of Ash

In AshHq we use cloak to encrypt data. We do it with a combination of changes and calculations.

For instance:

attribute :encrypted_name, :string
attribute :encrypted_address, :string

Then, in actions we write to those attributes using arguments:

    update :update_merch_settings do
      argument :address, :string
      argument :name, :string

      accept [:shirt_size]
      change set_attribute(:encrypted_address, arg(:address))
      change set_attribute(:encrypted_name, arg(:name))
    end
  end

We don’t write to the encrypted attributes directly to prevent double encryption.

This is what our encryption change looks like:

defmodule AshHq.Changes.Encrypt do
  @moduledoc "Encrypt attributes that are being changed before submitting the action"
  use Ash.Resource.Change

  def change(changeset, opts, _) do
    Ash.Changeset.before_action(changeset, fn changeset ->
      Enum.reduce(opts[:fields], changeset, fn field, changeset ->
        case Ash.Changeset.fetch_change(changeset, field) do
          {:ok, value} when is_binary(value) ->
            new_value =
              value
              |> AshHq.Vault.encrypt!()
              |> Base.encode64()

            Ash.Changeset.force_change_attribute(changeset, field, new_value)

          _ ->
            changeset
        end
      end)
    end)
  end
end

And we apply it on every action like so:

  changes do
    ...
    change {AshHq.Changes.Encrypt, fields: [:encrypted_address, :encrypted_name]}
  end

Then, we can decrypt it on demand using calculations:

  calculations do
    calculate :address, :string, {Decrypt, field: :encrypted_address}
    calculate :name, :string, {Decrypt, field: :encrypted_name}
  end

And that calculation looks like this:

defmodule AshHq.Calculations.Decrypt do
  @moduledoc "Decrypts a given value on demand"
  use Ash.Calculation

  @impl Ash.Calculation
  def calculate(records, opts, _) do
    {:ok,
     Enum.map(records, fn record ->
       record
       |> Map.get(opts[:field])
       |> case do
         nil ->
           nil

         value ->
           value
           |> Base.decode64!()
           |> AshHq.Vault.decrypt!()
       end
     end)}
  end

  @impl Ash.Calculation
  def load(_, opts, _) do
    [opts[:field]]
  end
end

This was a very early addition to AshHq. I’d love to see an encryption extension at some point :slight_smile:

zachdaniel

zachdaniel

Creator of Ash

We initially implemented this as an Ash.Type, but it actually gets a bit strange further down the line. You can’t cast_input the value that you get back from cast_input and so you have to be careful not to accidentally double encrypt things. This way keeps things very clean.

jimsynz

jimsynz

Ash Core Team

We also use this calculation based approach in a client app and it’s very handy because you have to explicitly ask for the decrypted value (via a load) and due to our policy configuration you have to provide an actor which also gives us a place to add audit logging at a later date.

kevinschweikert

kevinschweikert OP

Thank you @zachdaniel! That worked like a charm! I slowly begin to understand how Ash works :slight_smile:

mudspot

mudspot

@zachdaniel Hi! Is this still the recommended way of doing this in 2025?

I now have the following actions definition. I am hoping for something like that in the read action

create :create do
  accept [....]
  change EncryptValue
end

read :read do
  before_action DecryptValue
end

Is something like before_action possible in the read action to handle the decryption?
Of course, I’m using calculations at the moment.

zachdaniel

zachdaniel

Creator of Ash

Take a look at AshCloak: Home — ash_cloak v0.3.1

mudspot

mudspot

Brilliant!

I managed to implement it with my custom Vault (without Cloak)

Thank you!

— All posts loaded —

Where Next? Top

Trending in Questions Top

water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New

Other Trending Topics Top

budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews