Packages
phoenix_kit
1.7.169
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/phoenix_kit/integrations/encryption.ex
defmodule PhoenixKit.Integrations.Encryption do
@moduledoc """
AES-256-GCM encryption for sensitive integration credentials.
Encrypts fields like `access_token`, `refresh_token`, `client_secret`,
`api_key`, `bot_token`, `secret_key` before storing in the database.
Decrypts them when reading.
Uses the application's `secret_key_base` as the root key, deriving a
dedicated integration encryption key via PBKDF2.
## Configuration
Encryption is enabled by default when `secret_key_base` is configured.
To disable, set:
config :phoenix_kit, integration_encryption_enabled: false
"""
@sensitive_fields ~w(
access_token refresh_token client_secret
api_key bot_token secret_key
)
# Prefix to identify encrypted values
@encrypted_prefix "enc:v1:"
@doc """
Returns the list of field keys that are encrypted.
"""
@spec sensitive_fields() :: [String.t()]
def sensitive_fields, do: @sensitive_fields
@doc """
Encrypt sensitive fields in an integration data map before saving.
Non-sensitive fields and nil/empty values are left unchanged.
Already-encrypted values (with `enc:v1:` prefix) are not re-encrypted.
"""
@spec encrypt_fields(map()) :: map()
def encrypt_fields(data) when is_map(data) do
case encryption_key() do
nil -> data
key -> do_encrypt_fields(data, key)
end
end
@doc """
Decrypt sensitive fields in an integration data map after reading.
Only values with the `enc:v1:` prefix are decrypted.
Non-encrypted values are returned as-is for backwards compatibility.
"""
@spec decrypt_fields(map()) :: map()
def decrypt_fields(data) when is_map(data) do
case encryption_key() do
nil -> data
key -> do_decrypt_fields(data, key)
end
end
def decrypt_fields(other), do: other
@doc """
Check if encryption is available and enabled.
"""
@spec enabled?() :: boolean()
def enabled? do
encryption_key() != nil
end
# ---------------------------------------------------------------------------
# Private
# ---------------------------------------------------------------------------
defp do_encrypt_fields(data, key) do
Enum.reduce(@sensitive_fields, data, fn field, acc ->
case Map.get(acc, field) do
nil ->
acc
"" ->
acc
value when is_binary(value) ->
if String.starts_with?(value, @encrypted_prefix) do
# Already encrypted
acc
else
Map.put(acc, field, encrypt_value(value, key))
end
_ ->
acc
end
end)
end
defp do_decrypt_fields(data, key) do
Enum.reduce(@sensitive_fields, data, fn field, acc ->
case Map.get(acc, field) do
value when is_binary(value) and value != "" ->
maybe_decrypt_field(acc, field, value, key)
_ ->
acc
end
end)
end
defp maybe_decrypt_field(acc, field, value, key) do
if String.starts_with?(value, @encrypted_prefix) do
case decrypt_value(value, key) do
{:ok, plaintext} -> Map.put(acc, field, plaintext)
{:error, _} -> acc
end
else
acc
end
end
defp encrypt_value(plaintext, key) do
iv = :crypto.strong_rand_bytes(12)
{ciphertext, tag} = :crypto.crypto_one_time_aead(:aes_256_gcm, key, iv, plaintext, "", true)
encoded = Base.encode64(iv <> tag <> ciphertext)
@encrypted_prefix <> encoded
end
defp decrypt_value(@encrypted_prefix <> encoded, key) do
with {:ok, binary} <- Base.decode64(encoded),
<<iv::binary-12, tag::binary-16, ciphertext::binary>> <- binary do
case :crypto.crypto_one_time_aead(:aes_256_gcm, key, iv, ciphertext, "", tag, false) do
plaintext when is_binary(plaintext) -> {:ok, plaintext}
:error -> {:error, :decryption_failed}
end
else
_ -> {:error, :invalid_format}
end
end
defp decrypt_value(_, _key), do: {:error, :not_encrypted}
defp encryption_key do
if Application.get_env(:phoenix_kit, :integration_encryption_enabled, true) do
case PhoenixKit.Config.get(:secret_key_base) do
{:ok, secret} when is_binary(secret) and secret != "" -> derive_key(secret)
_ -> nil
end
else
nil
end
end
defp derive_key(secret) do
# Derive a dedicated 32-byte key for integration encryption
:crypto.hash(:sha256, "phoenix_kit_integrations:" <> secret)
end
end