Packages

phoenix_kit

1.7.97
1.7.208 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
phoenix_kit lib phoenix_kit migrations postgres v16.ex
Raw

lib/phoenix_kit/migrations/postgres/v16.ex

defmodule PhoenixKit.Migrations.Postgres.V16 do
@moduledoc """
PhoenixKit V16 Migration: OAuth Providers System & Magic Link Registration
This migration introduces OAuth authentication support for external providers
like Google, Apple, GitHub, and others using the Ueberauth framework.
It also modifies the tokens table to support magic link registration.
## Changes
### OAuth Providers System
- Adds phoenix_kit_user_oauth_providers table for OAuth provider linking
- Supports multiple OAuth providers per user (Google, Apple, GitHub, etc.)
- Stores OAuth tokens with encryption support
- Account linking by email address
- Audit trail with insertion timestamps
### Magic Link Registration Support
- Allows null user_id in phoenix_kit_users_tokens for magic_link_registration context
- Adds check constraint to ensure user_id is required for non-registration contexts
### New Tables
- **phoenix_kit_user_oauth_providers**: OAuth provider associations with users
## PostgreSQL Support
- Supports PostgreSQL prefix for schema isolation
- Optimized indexes for performance
- Unique constraints to prevent duplicate provider links
- JSONB field for flexible OAuth data storage
"""
use Ecto.Migration
@doc """
Run the V16 migration to add OAuth providers system and magic link registration support.
"""
def up(%{prefix: prefix} = _opts) do
# Modify tokens table to allow null user_id for magic link registration
alter_tokens_table_user_id_nullable(prefix)
add_user_id_check_constraint(prefix)
# Create OAuth providers table
create_if_not_exists table(:phoenix_kit_user_oauth_providers, prefix: prefix) do
# Foreign key to users table
add :user_id,
references(:phoenix_kit_users,
prefix: prefix,
on_delete: :delete_all,
on_update: :update_all
),
null: false
# OAuth provider name (google, apple, github, etc.)
add :provider, :string, null: false
# OAuth provider's unique user identifier
add :provider_uid, :string, null: false
# Email address from OAuth provider (for audit trail)
add :provider_email, :string, null: true
# OAuth access token (should be encrypted in production)
add :access_token, :text, null: true
# OAuth refresh token (should be encrypted in production)
add :refresh_token, :text, null: true
# Token expiration timestamp
add :token_expires_at, :utc_datetime_usec, null: true
# JSONB field for additional OAuth data (avatar_url, locale, etc.)
add :raw_data, :map, null: true, default: %{}
# Timestamps for tracking record creation/update
timestamps(type: :utc_datetime_usec)
end
# Unique constraint: one provider per user (user can't have multiple Google accounts)
create_if_not_exists unique_index(:phoenix_kit_user_oauth_providers, [:user_id, :provider],
prefix: prefix,
name: :phoenix_kit_oauth_providers_user_provider_idx
)
# Unique constraint: provider_uid must be unique per provider
create_if_not_exists unique_index(
:phoenix_kit_user_oauth_providers,
[:provider, :provider_uid],
prefix: prefix,
name: :phoenix_kit_oauth_providers_provider_uid_idx
)
# Performance index for searching by provider email
create_if_not_exists index(:phoenix_kit_user_oauth_providers, [:provider_email],
prefix: prefix
)
# Performance index for searching by provider
create_if_not_exists index(:phoenix_kit_user_oauth_providers, [:provider], prefix: prefix)
# Performance index for searching by user_id
create_if_not_exists index(:phoenix_kit_user_oauth_providers, [:user_id], prefix: prefix)
# Set version comment on phoenix_kit table for version tracking
execute "COMMENT ON TABLE #{prefix_table_name("phoenix_kit", prefix)} IS '16'"
end
@doc """
Rollback the V16 migration.
"""
def down(%{prefix: prefix} = _opts) do
# Drop OAuth indexes first
drop_if_exists index(:phoenix_kit_user_oauth_providers, [:user_id], prefix: prefix)
drop_if_exists index(:phoenix_kit_user_oauth_providers, [:provider], prefix: prefix)
drop_if_exists index(:phoenix_kit_user_oauth_providers, [:provider_email], prefix: prefix)
drop_if_exists unique_index(
:phoenix_kit_user_oauth_providers,
[:provider, :provider_uid],
prefix: prefix,
name: :phoenix_kit_oauth_providers_provider_uid_idx
)
drop_if_exists unique_index(:phoenix_kit_user_oauth_providers, [:user_id, :provider],
prefix: prefix,
name: :phoenix_kit_oauth_providers_user_provider_idx
)
# Drop OAuth table
drop_if_exists table(:phoenix_kit_user_oauth_providers, prefix: prefix)
# Revert tokens table changes
drop_user_id_check_constraint(prefix)
alter_tokens_table_user_id_not_null(prefix)
# Update version comment on phoenix_kit table to previous version
execute "COMMENT ON TABLE #{prefix_table_name("phoenix_kit", prefix)} IS '15'"
end
# Private helper functions
defp alter_tokens_table_user_id_nullable(prefix) do
execute(
"""
ALTER TABLE #{prefix_table_name("phoenix_kit_users_tokens", prefix)}
ALTER COLUMN user_id DROP NOT NULL
""",
# Rollback: Make user_id NOT NULL again (down migration)
"""
ALTER TABLE #{prefix_table_name("phoenix_kit_users_tokens", prefix)}
ALTER COLUMN user_id SET NOT NULL
"""
)
end
defp alter_tokens_table_user_id_not_null(prefix) do
table_name = prefix_table_name("phoenix_kit_users_tokens", prefix)
# First, delete any tokens with NULL user_id (magic link registration tokens)
execute(
"""
DELETE FROM #{table_name}
WHERE user_id IS NULL AND context = 'magic_link_registration'
""",
# Rollback: No need to restore deleted tokens
"SELECT 1"
)
# Then set NOT NULL constraint
execute(
"""
ALTER TABLE #{table_name}
ALTER COLUMN user_id SET NOT NULL
""",
# Rollback: Allow NULL again (up migration)
"""
ALTER TABLE #{table_name}
ALTER COLUMN user_id DROP NOT NULL
"""
)
end
defp add_user_id_check_constraint(prefix) do
constraint_name = "user_id_required_for_non_registration_tokens"
table_name = prefix_table_name("phoenix_kit_users_tokens", prefix)
execute(
"""
ALTER TABLE #{table_name}
ADD CONSTRAINT #{constraint_name}
CHECK (
CASE
WHEN context = 'magic_link_registration' THEN true
ELSE user_id IS NOT NULL
END
)
""",
# Rollback: Drop the constraint
"""
ALTER TABLE #{table_name}
DROP CONSTRAINT IF EXISTS #{constraint_name}
"""
)
end
defp drop_user_id_check_constraint(prefix) do
constraint_name = "user_id_required_for_non_registration_tokens"
table_name = prefix_table_name("phoenix_kit_users_tokens", prefix)
execute(
"""
ALTER TABLE #{table_name}
DROP CONSTRAINT IF EXISTS #{constraint_name}
""",
# Rollback: Recreate the constraint (up migration)
"""
ALTER TABLE #{table_name}
ADD CONSTRAINT #{constraint_name}
CHECK (
CASE
WHEN context = 'magic_link_registration' THEN true
ELSE user_id IS NOT NULL
END
)
"""
)
end
# Helper function to build table name with prefix
defp prefix_table_name(table_name, nil), do: table_name
defp prefix_table_name(table_name, prefix), do: "#{prefix}.#{table_name}"
end