Packages
phoenix_kit
1.7.199
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
Current section
Files
lib/phoenix_kit_web/live/settings/authorization.html.heex
<PhoenixKitWeb.Components.LayoutWrapper.app_layout
socket={@socket}
flash={@flash}
phoenix_kit_current_scope={assigns[:phoenix_kit_current_scope]}
page_title={gettext("Authorization Settings")}
page_subtitle={gettext("Manage login page branding and authentication methods")}
current_path={@url_path}
project_title={@project_title}
current_locale={@current_locale}
>
<div class="container flex flex-col mx-auto px-4 py-6">
<%!-- Settings Form --%>
<div class="card bg-base-100 shadow-xl">
<div class="card-body">
<h2 class="card-title text-xl mb-6">{gettext("Authorization Configuration")}</h2>
<.form
:let={_form}
for={@changeset}
id="authorization_settings_form"
phx-submit="save_settings"
phx-change="validate_settings"
class="space-y-6"
>
<%!-- Hidden fields for required settings from other pages --%>
<%!-- General Settings --%>
<input
type="hidden"
name="settings[project_title]"
value={@saved_settings["project_title"]}
/>
<input
type="hidden"
name="settings[site_url]"
value={@saved_settings["site_url"]}
/>
<input
type="hidden"
name="settings[week_start_day]"
value={@saved_settings["week_start_day"]}
/>
<input
type="hidden"
name="settings[time_zone]"
value={@saved_settings["time_zone"]}
/>
<input
type="hidden"
name="settings[date_format]"
value={@saved_settings["date_format"]}
/>
<input
type="hidden"
name="settings[time_format]"
value={@saved_settings["time_format"]}
/>
<%!-- Users Settings --%>
<input
type="hidden"
name="settings[allow_registration]"
value={@saved_settings["allow_registration"]}
/>
<input
type="hidden"
name="settings[new_user_default_role]"
value={@saved_settings["new_user_default_role"]}
/>
<input
type="hidden"
name="settings[new_user_default_status]"
value={@saved_settings["new_user_default_status"]}
/>
<input
type="hidden"
name="settings[track_registration_geolocation]"
value={@saved_settings["track_registration_geolocation"]}
/>
<.section_header icon="hero-paint-brush" title={gettext("Login Page Branding")} />
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">
{gettext("Background Image (Desktop)")}
</span>
</label>
<input
type="hidden"
name="settings[auth_background_image_file_uuid]"
value={@settings["auth_background_image_file_uuid"]}
/>
<%= if (@settings["auth_background_image_file_uuid"] || "") != "" do %>
<div class="flex items-center gap-4">
<img
src={
signed_preview_url(@settings["auth_background_image_file_uuid"], "medium")
}
alt={gettext("Background preview")}
class="h-16 w-28 object-cover rounded-lg ring-1 ring-base-300 shadow-sm"
/>
<div class="flex flex-col gap-2">
<button
type="button"
class="btn btn-outline btn-sm"
phx-click="open_media_selector"
phx-value-target="background"
>
{gettext("Change image")}
</button>
<button
type="button"
class="btn btn-ghost btn-sm text-error"
phx-click="clear_branding_image"
phx-value-target="background"
>
{gettext("Remove")}
</button>
</div>
</div>
<% else %>
<button
type="button"
class="h-16 w-28 rounded-lg border-2 border-dashed border-base-300 hover:border-primary hover:text-primary text-base-content/40 flex items-center justify-center transition-colors"
title={gettext("Select Image")}
phx-click="open_media_selector"
phx-value-target="background"
>
<.icon name="hero-photo" class="w-6 h-6" />
</button>
<% end %>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
{gettext(
"Background image for auth pages on desktop screens (wider than 768px)."
)}
</span>
</div>
<.unsaved_hint dirty={
@settings["auth_background_image_file_uuid"] !=
@saved_settings["auth_background_image_file_uuid"]
} />
</div>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">
{gettext("Background Image (Mobile)")}
</span>
</label>
<input
type="hidden"
name="settings[auth_background_image_mobile_file_uuid]"
value={@settings["auth_background_image_mobile_file_uuid"]}
/>
<%= if (@settings["auth_background_image_mobile_file_uuid"] || "") != "" do %>
<div class="flex items-center gap-4">
<img
src={
signed_preview_url(
@settings["auth_background_image_mobile_file_uuid"],
"medium"
)
}
alt={gettext("Mobile background preview")}
class="h-16 w-28 object-cover rounded-lg ring-1 ring-base-300 shadow-sm"
/>
<div class="flex flex-col gap-2">
<button
type="button"
class="btn btn-outline btn-sm"
phx-click="open_media_selector"
phx-value-target="background_mobile"
>
{gettext("Change image")}
</button>
<button
type="button"
class="btn btn-ghost btn-sm text-error"
phx-click="clear_branding_image"
phx-value-target="background_mobile"
>
{gettext("Remove")}
</button>
</div>
</div>
<% else %>
<button
type="button"
class="h-16 w-28 rounded-lg border-2 border-dashed border-base-300 hover:border-primary hover:text-primary text-base-content/40 flex items-center justify-center transition-colors"
title={gettext("Select Image")}
phx-click="open_media_selector"
phx-value-target="background_mobile"
>
<.icon name="hero-photo" class="w-6 h-6" />
</button>
<% end %>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
{gettext(
"Background image for auth pages on mobile screens (768px and below). Falls back to desktop image if not set."
)}
</span>
</div>
<.unsaved_hint dirty={
@settings["auth_background_image_mobile_file_uuid"] !=
@saved_settings["auth_background_image_mobile_file_uuid"]
} />
</div>
</div>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">
{gettext("Background Color")}
</span>
</label>
<input
name="settings[auth_background_color]"
type="text"
value={@settings["auth_background_color"]}
class="input input-bordered w-full focus:input-primary"
placeholder={gettext("#1a1a2e or linear-gradient(135deg, #667eea, #764ba2)")}
phx-debounce="500"
/>
<div class="label">
<span class="label-text-alt text-xs text-base-content/50">
{gettext(
"CSS color or gradient for auth page background. Ignored if a background image is set."
)}
</span>
</div>
<.unsaved_hint
dirty={
@settings["auth_background_color"] != @saved_settings["auth_background_color"]
}
saved={@saved_settings["auth_background_color"]}
/>
</div>
<.section_header icon="hero-key" title={gettext("Authentication Methods")} />
<%!-- Authentication Methods Setting --%>
<div class="form-control w-full">
<%!-- Magic Link Methods --%>
<div class="mb-3">
<div class="text-sm font-semibold text-base-content/70 mb-2">
{gettext("Magic Link")}
</div>
<.checkbox
name="settings[magic_link_login_enabled]"
checked={@settings["magic_link_login_enabled"] == "true"}
label={gettext("Enable Magic Link for login")}
wrapper_class="mb-2 ml-4"
/>
<.checkbox
name="settings[magic_link_registration_enabled]"
checked={
@settings["magic_link_registration_enabled"] == "true" and
@settings["allow_registration"] == "true"
}
disabled={@settings["allow_registration"] != "true"}
wrapper_class="ml-4"
>
{gettext("Enable Magic Link for registration")}
<%= if @settings["allow_registration"] != "true" do %>
<span class="text-xs italic">{gettext("(registration disabled)")}</span>
<% end %>
</.checkbox>
</div>
<%!-- QR Code Sign-in --%>
<div class="mb-3">
<div class="text-sm font-semibold text-base-content/70 mb-2">
{gettext("QR Code")}
</div>
<div class="flex items-center gap-3 ml-4">
<input name="settings[qr_login_enabled]" type="hidden" value="false" />
<input
name="settings[qr_login_enabled]"
type="checkbox"
value="true"
checked={@settings["qr_login_enabled"] == "true"}
class="checkbox checkbox-primary"
/>
<span class="label-text">
{gettext("Enable QR code sign-in (scan from a signed-in phone)")}
</span>
</div>
</div>
<%!-- New Login Alerts --%>
<div class="mb-3">
<div class="text-sm font-semibold text-base-content/70 mb-2">
{gettext("Login Notifications")}
</div>
<.checkbox
name="settings[new_login_alert_enabled]"
checked={@settings["new_login_alert_enabled"] == "true"}
wrapper_class="ml-4"
>
{gettext("Email users when their account is signed in from a new device")}
</.checkbox>
</div>
<%!-- OAuth Methods --%>
<div>
<div class="text-sm font-semibold text-base-content/70 mb-2">
{gettext("OAuth Providers")}
</div>
<.checkbox
name="settings[oauth_enabled]"
checked={@settings["oauth_enabled"] == "true"}
wrapper_class="mb-2 ml-4"
>
<span class="font-semibold">{gettext("Enable OAuth (Master Switch)")}</span>
</.checkbox>
<%!-- Individual OAuth Providers (locked, not disabled, if master is off —
their real stored value must keep submitting so it isn't lost while
the master switch is toggled off and back on). --%>
<div class={[
"ml-8 space-y-2",
if(@settings["oauth_enabled"] != "true", do: "opacity-50", else: "")
]}>
<% oauth_locked? = @settings["oauth_enabled"] != "true" %>
<.checkbox
name="settings[oauth_google_enabled]"
checked={@settings["oauth_google_enabled"] == "true"}
label={gettext("Google Sign-In")}
wrapper_class={oauth_locked? && "pointer-events-none"}
/>
<.checkbox
name="settings[oauth_github_enabled]"
checked={@settings["oauth_github_enabled"] == "true"}
label={gettext("GitHub Sign-In")}
wrapper_class={oauth_locked? && "pointer-events-none"}
/>
<.checkbox
name="settings[oauth_facebook_enabled]"
checked={@settings["oauth_facebook_enabled"] == "true"}
label={gettext("Facebook Sign-In")}
wrapper_class={oauth_locked? && "pointer-events-none"}
/>
</div>
</div>
<%!-- OAuth Provider Credential Forms --%>
<%= if @settings["oauth_enabled"] == "true" do %>
<%!-- Google OAuth Credentials --%>
<div class={[
if(@settings["oauth_google_enabled"] != "true", do: "hidden", else: "")
]}>
<div class="card bg-base-200 p-4 mt-3">
<h3 class="font-semibold mb-3 flex items-center gap-2">
<PhoenixKitWeb.Components.Core.Icons.icon_google class="h-5 w-5" />
{gettext("Google OAuth Credentials")}
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("Client ID")}</span>
</label>
<input
type="text"
name="settings[oauth_google_client_id]"
value={@settings["oauth_google_client_id"] || ""}
placeholder={gettext("Your Google OAuth Client ID")}
class="input input-bordered"
/>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("Client Secret")}</span>
</label>
<input
type="password"
name="settings[oauth_google_client_secret]"
value={@settings["oauth_google_client_secret"] || ""}
placeholder={gettext("Your Google OAuth Client Secret")}
class="input input-bordered"
/>
</div>
</div>
<button
type="button"
phx-click="test_oauth"
phx-value-provider="google"
class="btn btn-sm btn-outline mt-3 w-fit"
>
{gettext("Test Credentials")}
</button>
<.oauth_setup_instructions
callback_url={get_oauth_callback_url(@settings, "google")}
copy_hint={gettext("Copy this URL to Google Cloud Console")}
>
<:steps>
<li>
{gettext("Visit")}
<a
href="https://console.cloud.google.com/apis/credentials"
target="_blank"
class="link link-primary"
>
Google Cloud Console
</a>
</li>
<li>{gettext("Create project or select existing one")}</li>
<li>
{gettext("Go to")} <strong>Credentials</strong>
→ <strong>Create Credentials</strong>
→ <strong>OAuth client ID</strong>
</li>
<li>
{gettext("Select")}
<strong>Web application</strong> {gettext("type")}
</li>
<li>
{gettext("Add callback URL above to")}
<strong>Authorized redirect URIs</strong>
</li>
<li>{gettext("Copy Client ID and Secret to fields above")}</li>
<li>
{gettext("Click")}
<strong>Test Credentials</strong> {gettext("to verify setup")}
</li>
</:steps>
</.oauth_setup_instructions>
</div>
</div>
<%!-- GitHub OAuth Credentials --%>
<div class={[
"card bg-base-200 p-4 mt-3",
if(@settings["oauth_github_enabled"] != "true", do: "hidden", else: "")
]}>
<h3 class="font-semibold mb-3 flex items-center gap-2">
<PhoenixKitWeb.Components.Core.Icons.icon_github class="h-5 w-5" />
{gettext("GitHub OAuth Credentials")}
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("Client ID")}</span>
</label>
<input
type="text"
name="settings[oauth_github_client_id]"
value={@settings["oauth_github_client_id"] || ""}
placeholder={gettext("Your GitHub OAuth Client ID")}
class="input input-bordered"
/>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("Client Secret")}</span>
</label>
<input
type="password"
name="settings[oauth_github_client_secret]"
value={@settings["oauth_github_client_secret"] || ""}
placeholder={gettext("Your GitHub OAuth Client Secret")}
class="input input-bordered"
/>
</div>
</div>
<button
type="button"
phx-click="test_oauth"
phx-value-provider="github"
class="btn btn-sm btn-outline mt-3 w-fit"
>
{gettext("Test Credentials")}
</button>
<.oauth_setup_instructions
callback_url={get_oauth_callback_url(@settings, "github")}
copy_hint={gettext("Copy this URL to GitHub OAuth Apps")}
>
<:steps>
<li>
{gettext("Visit")}
<a
href="https://github.com/settings/developers"
target="_blank"
class="link link-primary"
>
GitHub Developer Settings
</a>
</li>
<li>
{gettext("Go to")} <strong>OAuth Apps</strong>
→ <strong>New OAuth App</strong>
</li>
<li>
{gettext("Enter")} <strong>Application name</strong>
{gettext("and")} <strong>Homepage URL</strong>
</li>
<li>
{gettext("Add callback URL above to")}
<strong>Authorization callback URL</strong>
</li>
<li>
{gettext("Click")} <strong>Register application</strong>
</li>
<li>
{gettext("Copy")} <strong>Client ID</strong>
{gettext("and generate/copy")} <strong>Client Secret</strong>
</li>
<li>
{gettext("Click")}
<strong>Test Credentials</strong> {gettext("to verify setup")}
</li>
</:steps>
</.oauth_setup_instructions>
</div>
<%!-- Facebook OAuth Credentials --%>
<div class={[
"card bg-base-200 p-4 mt-3",
if(@settings["oauth_facebook_enabled"] != "true", do: "hidden", else: "")
]}>
<h3 class="font-semibold mb-3 flex items-center gap-2">
<PhoenixKitWeb.Components.Core.Icons.icon_facebook class="h-5 w-5" />
{gettext("Facebook OAuth Credentials")}
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("App ID")}</span>
</label>
<input
type="text"
name="settings[oauth_facebook_app_id]"
value={@settings["oauth_facebook_app_id"] || ""}
placeholder={gettext("Your Facebook App ID")}
class="input input-bordered"
/>
</div>
<div class="form-control">
<label class="label">
<span class="label-text">{gettext("App Secret")}</span>
</label>
<input
type="password"
name="settings[oauth_facebook_app_secret]"
value={@settings["oauth_facebook_app_secret"] || ""}
placeholder={gettext("Your Facebook App Secret")}
class="input input-bordered"
/>
</div>
</div>
<button
type="button"
phx-click="test_oauth"
phx-value-provider="facebook"
class="btn btn-sm btn-outline mt-3 w-fit"
>
{gettext("Test Credentials")}
</button>
<.oauth_setup_instructions
callback_url={get_oauth_callback_url(@settings, "facebook")}
copy_hint={gettext("Copy this URL to Facebook App Settings")}
>
<:steps>
<li>
{gettext("Visit")}
<a
href="https://developers.facebook.com/apps"
target="_blank"
class="link link-primary"
>
Facebook Developers Console
</a>
</li>
<li>
{gettext("Create a new app or select existing one")}
</li>
<li>
{gettext("Add")} <strong>Facebook Login</strong> {gettext("product")}
</li>
<li>
{gettext("Go to")} <strong>Facebook Login</strong>
→ <strong>Settings</strong>
</li>
<li>
{gettext("Add callback URL above to")}
<strong>Valid OAuth Redirect URIs</strong>
</li>
<li>
{gettext("Go to")} <strong>Settings</strong> → <strong>Basic</strong>
</li>
<li>
{gettext("Copy")} <strong>App ID</strong>
{gettext("and")} <strong>App Secret</strong>
{gettext("to fields above")}
</li>
<li>
{gettext("Switch app from")} <strong>Development</strong>
{gettext("to")} <strong>Live</strong>
{gettext("mode")}
</li>
<li>
{gettext("Click")}
<strong>Test Credentials</strong> {gettext("to verify setup")}
</li>
</:steps>
</.oauth_setup_instructions>
</div>
<%!-- OAuth Configuration Notice --%>
<div class="alert alert-info text-sm mt-3 max-w-prose">
<.icon name="hero-information-circle" class="stroke-current shrink-0 h-5 w-5" />
<div>
<div class="font-semibold">{gettext("OAuth Setup Instructions")}</div>
<div class="text-xs">
<p>{gettext("1. Enable the provider above and save settings")}</p>
<p>{gettext("2. Enter your OAuth credentials in the form that appears")}</p>
<p>{gettext("3. Save settings again to store credentials")}</p>
<p>{gettext("4. Click \"Test Credentials\" to verify configuration")}</p>
<p class="mt-1">
{gettext(
"Note: OAuth providers require ueberauth dependencies to be installed."
)}
</p>
</div>
</div>
</div>
<%!-- Reload OAuth Configuration Button --%>
<button
type="button"
phx-click="reload_oauth_config"
class="btn btn-sm btn-outline btn-secondary mt-3"
>
<PhoenixKitWeb.Components.Core.Icons.icon_refresh class="h-4 w-4" />
{gettext("Reload OAuth Configuration")}
</button>
<% end %>
</div>
<.section_header icon="hero-user-group" title={gettext("Sessions")} />
<%!-- Multiple Sessions (account switcher) --%>
<div class="form-control w-full">
<label class="label">
<span class="label-text text-base font-medium">
{gettext("Multiple Sessions")}
</span>
<span class="label-text-alt text-sm text-base-content/60">
{gettext("Account switcher for signing in to several accounts at once")}
</span>
</label>
<.checkbox
name="settings[multi_session_enabled]"
checked={@settings["multi_session_enabled"] == "true"}
label={gettext("Enable the multi-account switcher in the header")}
>
<:description>
{gettext(
"When on, any signed-in user can add other accounts (with their password) and switch between them from the user menu."
)}
</:description>
</.checkbox>
</div>
<%!-- Action Buttons --%>
<div class="flex gap-3 pt-4">
<button
type="submit"
class={[
"btn btn-primary flex-1",
if(@saving, do: "loading", else: "")
]}
disabled={@saving}
>
<%= if @saving do %>
{gettext("Saving...")}
<% else %>
{gettext("Save Authorization Settings")}
<% end %>
</button>
</div>
</.form>
</div>
</div>
</div>
<.live_component
:if={@show_media_selector}
module={PhoenixKitWeb.Live.Components.MediaSelectorModal}
id="authorization-media-selector"
show={@show_media_selector}
mode={:single}
selected_uuids={[]}
phoenix_kit_current_user={assigns[:phoenix_kit_current_user]}
/>
</PhoenixKitWeb.Components.LayoutWrapper.app_layout>