24 changed files with 682 additions and 25 deletions
|
|
@ -1,7 +1,9 @@
|
|||
[
|
||||
import_deps: [
|
||||
:ash_authentication_phoenix,
|
||||
:ash_admin,
|
||||
:ash_postgres,
|
||||
:ash_authentication,
|
||||
:ash_phoenix,
|
||||
:ash,
|
||||
:reactor,
|
||||
|
|
|
|||
10
.igniter.exs
Normal file
10
.igniter.exs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# This is a configuration file for igniter.
|
||||
# For option documentation, see https://hexdocs.pm/igniter/Igniter.Project.IgniterConfig.html
|
||||
# To keep it up to date, use `mix igniter.setup`
|
||||
[
|
||||
module_location: :outside_matching_folder,
|
||||
extensions: [{Igniter.Extensions.Phoenix, []}],
|
||||
deps_location: :last_list_literal,
|
||||
source_folders: ["lib", "test/support"],
|
||||
dont_move_files: [~r"lib/mix"]
|
||||
]
|
||||
|
|
@ -7,6 +7,7 @@ const path = require("path")
|
|||
|
||||
module.exports = {
|
||||
content: [
|
||||
"../deps/ash_authentication_phoenix/**/*.*ex",
|
||||
"./js/**/*.js",
|
||||
"../lib/mv_web.ex",
|
||||
"../lib/mv_web/**/*.*ex"
|
||||
|
|
|
|||
|
|
@ -84,3 +84,8 @@ config :phoenix_live_view,
|
|||
|
||||
# Disable swoosh api client as it is only required for production adapters.
|
||||
config :swoosh, :api_client, false
|
||||
|
||||
config :mv, :secret_key_base, "ryn7D6ssmIHQFWIks2sFiTGATgwwAR1+3bN8p7fy6qVtB8qnxOuk1uyAwHz1Q8WB"
|
||||
|
||||
# Signing Secret for Authentication
|
||||
config :mv, :token_signing_secret, "IwUwi65TrEeExwBXXFPGm2I7889NsL"
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@ defmodule Mv.Accounts do
|
|||
|
||||
resources do
|
||||
resource Mv.Accounts.User do
|
||||
define(:create_user, action: :create)
|
||||
define(:list_users, action: :read)
|
||||
define(:update_user, action: :update)
|
||||
define(:destroy_user, action: :destroy)
|
||||
define :create_user, action: :create
|
||||
define :list_users, action: :read
|
||||
define :update_user, action: :update
|
||||
define :destroy_user, action: :destroy
|
||||
end
|
||||
|
||||
resource Mv.Accounts.Token
|
||||
end
|
||||
end
|
||||
|
|
|
|||
11
lib/accounts/token.ex
Normal file
11
lib/accounts/token.ex
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
defmodule Mv.Accounts.Token do
|
||||
use Ash.Resource,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
extensions: [AshAuthentication.TokenResource],
|
||||
domain: Mv.Accounts
|
||||
|
||||
postgres do
|
||||
table "tokens"
|
||||
repo Mv.Repo
|
||||
end
|
||||
end
|
||||
|
|
@ -1,26 +1,78 @@
|
|||
defmodule Mv.Accounts.User do
|
||||
use Ash.Resource,
|
||||
domain: Mv.Accounts,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
extensions: [AshAuthentication]
|
||||
|
||||
# authorizers: [Ash.Policy.Authorizer]
|
||||
|
||||
postgres do
|
||||
table("users")
|
||||
repo(Mv.Repo)
|
||||
table "users"
|
||||
repo Mv.Repo
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
authentication do
|
||||
tokens do
|
||||
enabled? true
|
||||
token_resource Mv.Accounts.Token
|
||||
signing_secret fn _, _ ->
|
||||
{:ok, Application.get_env(:mv, :token_signing_secret)}
|
||||
end
|
||||
end
|
||||
|
||||
attribute(:email, :string, allow_nil?: true, public?: true)
|
||||
attribute(:password_hash, :string, sensitive?: true)
|
||||
attribute(:oicd_id, :string)
|
||||
strategies do
|
||||
password :password do
|
||||
identity_field :email
|
||||
hash_provider AshAuthentication.BcryptProvider
|
||||
confirmation_required? false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
actions do
|
||||
defaults([:read, :destroy, :create, :update])
|
||||
defaults [:read, :create, :destroy, :update]
|
||||
|
||||
read :get_by_subject do
|
||||
description "Get a user by the subject claim in a JWT"
|
||||
argument :subject, :string, allow_nil?: false
|
||||
get? true
|
||||
prepare AshAuthentication.Preparations.FilterBySubject
|
||||
end
|
||||
|
||||
# read :sign_in_with_example do
|
||||
# argument :user_info, :map, allow_nil?: false
|
||||
# argument :oauth_tokens, :map, allow_nil?: false
|
||||
# prepare AshAuthentication.Strategy.OAuth2.SignInPreparation
|
||||
|
||||
# filter expr(email == get_path(^arg(:user_info), [:email]))
|
||||
# end
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
|
||||
attribute :email, :ci_string, allow_nil?: false, public?: true
|
||||
attribute :hashed_password, :string, sensitive?: true, allow_nil?: true
|
||||
attribute :oicd_id, :string, allow_nil?: true
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to(:member, Mv.Membership.Member)
|
||||
belongs_to :member, Mv.Membership.Member
|
||||
end
|
||||
|
||||
identities do
|
||||
identity :unique_email, [:email]
|
||||
end
|
||||
|
simon marked this conversation as resolved
Outdated
|
||||
|
||||
# You can customize this if you wish, but this is a safe default that
|
||||
# only allows user data to be interacted with via AshAuthentication.
|
||||
# policies do
|
||||
# bypass AshAuthentication.Checks.AshAuthenticationInteraction do
|
||||
# authorize_if(always())
|
||||
# end
|
||||
|
||||
# policy always() do
|
||||
# forbid_if(always())
|
||||
# end
|
||||
# end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
defmodule Mv.Accounts.User.Senders.SendNewUserConfirmationEmail do
|
||||
@moduledoc """
|
||||
Sends an email for a new user to confirm their email address.
|
||||
"""
|
||||
|
||||
use AshAuthentication.Sender
|
||||
use MvWeb, :verified_routes
|
||||
|
||||
import Swoosh.Email
|
||||
|
||||
alias Mv.Mailer
|
||||
|
||||
@impl true
|
||||
def send(user, token, _) do
|
||||
new()
|
||||
# TODO: Replace with your email
|
||||
|> from({"noreply", "noreply@example.com"})
|
||||
|> to(to_string(user.email))
|
||||
|> subject("Confirm your email address")
|
||||
|> html_body(body(token: token))
|
||||
|> Mailer.deliver!()
|
||||
end
|
||||
|
||||
defp body(params) do
|
||||
url = url(~p"/confirm_new_user/#{params[:token]}")
|
||||
|
||||
"""
|
||||
<p>Click this link to confirm your email:</p>
|
||||
<p><a href="#{url}">#{url}</a></p>
|
||||
"""
|
||||
end
|
||||
end
|
||||
32
lib/mv/accounts/user/senders/send_password_reset_email.ex
Normal file
32
lib/mv/accounts/user/senders/send_password_reset_email.ex
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
defmodule Mv.Accounts.User.Senders.SendPasswordResetEmail do
|
||||
@moduledoc """
|
||||
Sends a password reset email
|
||||
"""
|
||||
|
||||
use AshAuthentication.Sender
|
||||
use MvWeb, :verified_routes
|
||||
|
||||
import Swoosh.Email
|
||||
|
||||
alias Mv.Mailer
|
||||
|
||||
@impl true
|
||||
def send(user, token, _) do
|
||||
new()
|
||||
# TODO: Replace with your email
|
||||
|> from({"noreply", "noreply@example.com"})
|
||||
|> to(to_string(user.email))
|
||||
|> subject("Reset your password")
|
||||
|> html_body(body(token: token))
|
||||
|> Mailer.deliver!()
|
||||
end
|
||||
|
||||
defp body(params) do
|
||||
url = url(~p"/password-reset/#{params[:token]}")
|
||||
|
||||
"""
|
||||
<p>Click this link to reset your password:</p>
|
||||
<p><a href="#{url}">#{url}</a></p>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
@ -14,6 +14,7 @@ defmodule Mv.Application do
|
|||
{Phoenix.PubSub, name: Mv.PubSub},
|
||||
# Start the Finch HTTP client for sending emails
|
||||
{Finch, name: Mv.Finch},
|
||||
{AshAuthentication.Supervisor, otp_app: :my},
|
||||
# Start a worker by calling: Mv.Worker.start_link(arg)
|
||||
# {Mv.Worker, arg},
|
||||
# Start to serve requests, typically the last entry
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ defmodule Mv.Repo do
|
|||
@impl true
|
||||
def installed_extensions do
|
||||
# Add extensions here, and the migration generator will install them.
|
||||
["ash-functions"]
|
||||
["ash-functions", "citext"]
|
||||
end
|
||||
|
||||
# Don't open unnecessary transactions
|
||||
|
|
|
|||
20
lib/mv_web/auth_overrides.ex
Normal file
20
lib/mv_web/auth_overrides.ex
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
defmodule MvWeb.AuthOverrides do
|
||||
use AshAuthentication.Phoenix.Overrides
|
||||
|
||||
# configure your UI overrides here
|
||||
|
||||
# First argument to `override` is the component name you are overriding.
|
||||
# The body contains any number of configurations you wish to override
|
||||
# Below are some examples
|
||||
|
||||
# For a complete reference, see https://hexdocs.pm/ash_authentication_phoenix/ui-overrides.html
|
||||
|
||||
# override AshAuthentication.Phoenix.Components.Banner do
|
||||
# set :image_url, "https://media.giphy.com/media/g7GKcSzwQfugw/giphy.gif"
|
||||
# set :text_class, "bg-red-500"
|
||||
# end
|
||||
|
||||
# override AshAuthentication.Phoenix.Components.SignIn do
|
||||
# set :show_banner, false
|
||||
# end
|
||||
end
|
||||
55
lib/mv_web/controllers/auth_controller.ex
Normal file
55
lib/mv_web/controllers/auth_controller.ex
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
defmodule MvWeb.AuthController do
|
||||
use MvWeb, :controller
|
||||
use AshAuthentication.Phoenix.Controller
|
||||
|
||||
def success(conn, activity, user, _token) do
|
||||
return_to = get_session(conn, :return_to) || ~p"/"
|
||||
|
||||
message =
|
||||
case activity do
|
||||
{:confirm_new_user, :confirm} -> "Your email address has now been confirmed"
|
||||
{:password, :reset} -> "Your password has successfully been reset"
|
||||
_ -> "You are now signed in"
|
||||
|
simon marked this conversation as resolved
Outdated
simon
commented
Die ganzen Messages müssten mit der Internationalisierung jetzt auch mit gettext aufgerufen werden, oder? Die ganzen Messages müssten mit der Internationalisierung jetzt auch mit gettext aufgerufen werden, oder?
carla
commented
Yess, ist ja erst mit dem letzten merge des PRs dazu gekommen, aber kann ich jetzt nch machen. Yess, ist ja erst mit dem letzten merge des PRs dazu gekommen, aber kann ich jetzt nch machen.
Ich würde aber trotzdem die Übersetzungen jetzt erstmal leer machen und ehrlich gesagt dann in einem Rutsch machen.
|
||||
end
|
||||
|
||||
conn
|
||||
|> delete_session(:return_to)
|
||||
|> store_in_session(user)
|
||||
# If your resource has a different name, update the assign name here (i.e :current_admin)
|
||||
|> assign(:current_user, user)
|
||||
|> put_flash(:info, message)
|
||||
|> redirect(to: return_to)
|
||||
end
|
||||
|
||||
def failure(conn, activity, reason) do
|
||||
message =
|
||||
case {activity, reason} do
|
||||
{_,
|
||||
%AshAuthentication.Errors.AuthenticationFailed{
|
||||
caused_by: %Ash.Error.Forbidden{
|
||||
errors: [%AshAuthentication.Errors.CannotConfirmUnconfirmedUser{}]
|
||||
}
|
||||
}} ->
|
||||
"""
|
||||
You have already signed in another way, but have not confirmed your account.
|
||||
You can confirm your account using the link we sent to you, or by resetting your password.
|
||||
"""
|
||||
|
||||
_ ->
|
||||
"Incorrect email or password"
|
||||
|
rafael
commented
I think this case will match if there's any other error, e.g. when there's a problem with the database. To help with debugging, you could add a I think this case will match if there's any other error, e.g. when there's a problem with the database. To help with debugging, you could add a `Logger.error` call before returning this message so we can see what went wrong in the terminal. If you do this, don't forget to `require Logger` at the top of the file :)
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_flash(:error, message)
|
||||
|> redirect(to: ~p"/sign-in")
|
||||
end
|
||||
|
||||
def sign_out(conn, _params) do
|
||||
return_to = get_session(conn, :return_to) || ~p"/"
|
||||
|
||||
conn
|
||||
|> clear_session()
|
||||
|> put_flash(:info, "You are now signed out")
|
||||
|> redirect(to: return_to)
|
||||
end
|
||||
end
|
||||
44
lib/mv_web/live_user_auth.ex
Normal file
44
lib/mv_web/live_user_auth.ex
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
defmodule MvWeb.LiveUserAuth do
|
||||
@moduledoc """
|
||||
Helpers for authenticating users in LiveViews.
|
||||
"""
|
||||
|
||||
import Phoenix.Component
|
||||
use MvWeb, :verified_routes
|
||||
|
||||
# This is used for nested liveviews to fetch the current user.
|
||||
# To use, place the following at the top of that liveview:
|
||||
# on_mount {MvWeb.LiveUserAuth, :current_user}
|
||||
def on_mount(:current_user, _params, session, socket) do
|
||||
return_to = session[:return_to]
|
||||
socket =
|
||||
socket
|
||||
|> assign(:return_to, return_to)
|
||||
|> AshAuthentication.Phoenix.LiveSession.assign_new_resources(session)
|
||||
{:cont, session, socket}
|
||||
end
|
||||
|
||||
def on_mount(:live_user_optional, _params, _session, socket) do
|
||||
if socket.assigns[:current_user] do
|
||||
{:cont, socket}
|
||||
else
|
||||
{:cont, assign(socket, :current_user, nil)}
|
||||
end
|
||||
end
|
||||
|
||||
def on_mount(:live_user_required, _params, _session, socket) do
|
||||
if socket.assigns[:current_user] do
|
||||
{:cont, socket}
|
||||
else
|
||||
{:halt, Phoenix.LiveView.redirect(socket, to: ~p"/sign-in")}
|
||||
end
|
||||
end
|
||||
|
||||
def on_mount(:live_no_user, _params, _session, socket) do
|
||||
if socket.assigns[:current_user] do
|
||||
{:halt, Phoenix.LiveView.redirect(socket, to: ~p"/")}
|
||||
else
|
||||
{:cont, assign(socket, :current_user, nil)}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
defmodule MvWeb.MemberLive.Index do
|
||||
use MvWeb, :live_view
|
||||
|
||||
on_mount {MvWeb.LiveUserAuth, :live_user_required}
|
||||
|
||||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
defmodule MvWeb.Router do
|
||||
use MvWeb, :router
|
||||
|
||||
use AshAuthentication.Phoenix.Router
|
||||
|
||||
import AshAuthentication.Plug.Helpers
|
||||
|
||||
pipeline :browser do
|
||||
plug :accepts, ["html"]
|
||||
plug :fetch_session
|
||||
|
|
@ -8,22 +12,46 @@ defmodule MvWeb.Router do
|
|||
plug :put_root_layout, html: {MvWeb.Layouts, :root}
|
||||
plug :protect_from_forgery
|
||||
plug :put_secure_browser_headers
|
||||
plug :load_from_session
|
||||
plug :set_locale
|
||||
end
|
||||
|
||||
pipeline :api do
|
||||
plug :accepts, ["json"]
|
||||
plug :load_from_bearer
|
||||
plug :set_actor, :user
|
||||
end
|
||||
|
||||
scope "/", MvWeb do
|
||||
pipe_through :browser
|
||||
|
||||
ash_authentication_live_session :authenticated_routes do
|
||||
# in each liveview, add one of the following at the top of the module:
|
||||
#
|
||||
# If an authenticated user must be present:
|
||||
# on_mount {MvWeb.LiveUserAuth, :live_user_required}
|
||||
#
|
||||
# If an authenticated user *may* be present:
|
||||
# on_mount {MvWeb.LiveUserAuth, :live_user_optional}
|
||||
#
|
||||
# If an authenticated user must *not* be present:
|
||||
# on_mount {MvWeb.LiveUserAuth, :live_no_user}
|
||||
end
|
||||
end
|
||||
|
||||
scope "/", MvWeb do
|
||||
pipe_through :browser
|
||||
|
||||
get "/", PageController, :home
|
||||
live "/members", MemberLive.Index, :index
|
||||
live "/members/new", MemberLive.Index, :new
|
||||
live "/members/:id/edit", MemberLive.Index, :edit
|
||||
live "/members/:id", MemberLive.Show, :show
|
||||
live "/members/:id/show/edit", MemberLive.Show, :edit
|
||||
|
||||
ash_authentication_live_session :session_name do
|
||||
live "/members", MemberLive.Index, :index
|
||||
live "/members/new", MemberLive.Index, :new
|
||||
live "/members/:id/edit", MemberLive.Index, :edit
|
||||
live "/members/:id", MemberLive.Show, :show
|
||||
live "/members/:id/show/edit", MemberLive.Show, :edit
|
||||
end
|
||||
|
||||
|
||||
live "/property_types", PropertyTypeLive.Index, :index
|
||||
live "/property_types/new", PropertyTypeLive.Index, :new
|
||||
|
|
@ -38,6 +66,30 @@ defmodule MvWeb.Router do
|
|||
live "/properties/:id/show/edit", PropertyLive.Show, :edit
|
||||
|
||||
post "/set_locale", LocaleController, :set_locale
|
||||
auth_routes AuthController, Mv.Accounts.User, path: "/auth"
|
||||
sign_out_route AuthController
|
||||
|
||||
# Remove these if you'd like to use your own authentication views
|
||||
sign_in_route register_path: "/register",
|
||||
reset_path: "/reset",
|
||||
auth_routes_prefix: "/auth",
|
||||
on_mount: [{MvWeb.LiveUserAuth, :live_no_user}],
|
||||
overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default]
|
||||
|
||||
# Remove this if you do not want to use the reset password feature
|
||||
reset_route auth_routes_prefix: "/auth",
|
||||
overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default]
|
||||
|
||||
# Remove this if you do not use the confirmation strategy
|
||||
confirm_route Mv.Accounts.User, :confirm_new_user,
|
||||
auth_routes_prefix: "/auth",
|
||||
overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default]
|
||||
|
||||
# Remove this if you do not use the magic link strategy.
|
||||
# magic_sign_in_route(Mv.Accounts.User, :magic_link,
|
||||
# auth_routes_prefix: "/auth",
|
||||
# overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default]
|
||||
# )
|
||||
end
|
||||
|
||||
# Other scopes may use custom stacks.
|
||||
|
|
|
|||
3
mix.exs
3
mix.exs
|
|
@ -92,7 +92,8 @@ defmodule Mv.MixProject do
|
|||
"tailwind mv --minify",
|
||||
"esbuild mv --minify",
|
||||
"phx.digest"
|
||||
]
|
||||
],
|
||||
"phx.routes": ["phx.routes", "ash_authentication.phoenix.routes"]
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
10
mix.lock
10
mix.lock
|
|
@ -15,7 +15,8 @@
|
|||
"ecto": {:hex, :ecto, "3.12.6", "8bf762dc5b87d85b7aca7ad5fe31ef8142a84cea473a3381eb933bd925751300", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4c0cba01795463eebbcd9e4b5ef53c1ee8e68b9c482baef2a80de5a61e7a57fe"},
|
||||
"ecto_commons": {:hex, :ecto_commons, "0.3.6", "7b1d9e59396cf8c8cbe5a26d50d03f9b6d0fe6c640210dd503622f276f1e59bb", [:mix], [{:burnex, "~> 3.0", [hex: :burnex, repo: "hexpm", optional: true]}, {:ecto, "~> 3.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:ex_phone_number, "~> 0.2", [hex: :ex_phone_number, repo: "hexpm", optional: false]}, {:luhn, "~> 0.3.0", [hex: :luhn, repo: "hexpm", optional: false]}], "hexpm", "3f12981a1e398f206c5d2014e7b732b7ec91b110b9cb84875cb5b28fc75d7a0a"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"},
|
||||
"esbuild": {:hex, :esbuild, "0.10.0", "b0aa3388a1c23e727c5a3e7427c932d89ee791746b0081bbe56103e9ef3d291f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "468489cda427b974a7cc9f03ace55368a83e1a7be12fba7e30969af78e5f8c70"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
|
||||
"esbuild": {:hex, :esbuild, "0.9.0", "f043eeaca4932ca8e16e5429aebd90f7766f31ac160a25cbd9befe84f2bc068f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b415027f71d5ab57ef2be844b2a10d0c1b5a492d431727f43937adce22ba45ae"},
|
||||
"ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"},
|
||||
"ex_phone_number": {:hex, :ex_phone_number, "0.4.5", "2065cc48c3e9d1ed9821f50877c32f2f6898362cb990f44147ca217c5d1374ed", [:mix], [{:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: false]}], "hexpm", "67163f8706f8cbfef1b1f4b9230c461f19786d0d79fd0b22cbeeefc6f0b99d4a"},
|
||||
"expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"},
|
||||
|
|
@ -24,12 +25,14 @@
|
|||
"floki": {:hex, :floki, "0.37.1", "d7aaee758c8a5b4a7495799a4260754fec5530d95b9c383c03b27359dea117cf", [:mix], [], "hexpm", "673d040cb594d31318d514590246b6dd587ed341d3b67e17c1c0eb8ce7ca6f04"},
|
||||
"gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"},
|
||||
"glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"},
|
||||
"heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "0435d4ca364a608cc75e2f8683d374e55abbae26", [tag: "v2.2.0", sparse: "optimized", depth: 1]},
|
||||
"heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized", depth: 1]},
|
||||
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
|
||||
"igniter": {:hex, :igniter, "0.6.7", "4e183afc59d89289e223c4282fd3e9bb39b82e28d0aa6d3369f70fbd3e21a243", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "43b0a584dc84fd1320772c87047355b604ed2bcdd25392b17f7da8bdd09b61ac"},
|
||||
"inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"},
|
||||
"iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"},
|
||||
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
||||
"joken": {:hex, :joken, "2.6.2", "5daaf82259ca603af4f0b065475099ada1b2b849ff140ccd37f4b6828ca6892a", [:mix], [{:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5134b5b0a6e37494e46dbf9e4dad53808e5e787904b7c73972651b51cce3d72b"},
|
||||
"jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"},
|
||||
"libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"},
|
||||
"live_debugger": {:hex, :live_debugger, "0.2.4", "2e0b02874ca562ba2d8cebb9e024c25c0ae9c1f4ee499135a70814e1dea6183e", [:mix], [{:igniter, ">= 0.5.40 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20.4 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "bfd0db143be54ccf2872f15bfd2209fbec1083d0b06b81b4cedeecb2fa9ac208"},
|
||||
"luhn": {:hex, :luhn, "0.3.3", "5aa0c6a32c2db4b9db9f9b883ba8301c1ae169d57199b9e6cb1ba2707bc51d96", [:mix], [], "hexpm", "3e823a913a25aab51352c727f135278d22954874d5f0835be81ed4fec3daf78d"},
|
||||
|
|
@ -54,7 +57,8 @@
|
|||
"reactor": {:hex, :reactor, "0.15.4", "ef0c56a901c132529a14ab59fed0ccb4fcecb24308fb189a94c908255d4fdafc", [:mix], [{:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}, {:ymlr, "~> 5.0", [hex: :ymlr, repo: "hexpm", optional: false]}], "hexpm", "783bf62fd0c72ded033afabdb8b6190b7048769771a2a97256e6f0bf4fb0a891"},
|
||||
"req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"},
|
||||
"rewrite": {:hex, :rewrite, "1.1.2", "f5a5d10f5fed1491a6ff48e078d4585882695962ccc9e6c779bae025d1f92eda", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "7f8b94b1e3528d0a47b3e8b7bfeca559d2948a65fa7418a9ad7d7712703d39d4"},
|
||||
"sobelow": {:hex, :sobelow, "0.14.0", "dd82aae8f72503f924fe9dd97ffe4ca694d2f17ec463dcfd365987c9752af6ee", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "7ecf91e298acfd9b24f5d761f19e8f6e6ac585b9387fb6301023f1f2cd5eed5f"},
|
||||
"slugify": {:hex, :slugify, "1.3.1", "0d3b8b7e5c1eeaa960e44dce94382bee34a39b3ea239293e457a9c5b47cc6fd3", [:mix], [], "hexpm", "cb090bbeb056b312da3125e681d98933a360a70d327820e4b7f91645c4d8be76"},
|
||||
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
|
||||
"sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"},
|
||||
"spark": {:hex, :spark, "2.2.65", "4c10d109c108417ce394158f330be09ef184878bde45de6462397fbda68cec29", [:mix], [{:igniter, ">= 0.3.64 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: true]}], "hexpm", "d66d5070a77f4c69cb4f007e941ac17d5d751ce71190fcd6e6e5fb42ba86f101"},
|
||||
"spitfire": {:hex, :spitfire, "0.2.1", "29e154873f05444669c7453d3d931820822cbca5170e88f0f8faa1de74a79b47", [:mix], [], "hexpm", "6eeed75054a38341b2e1814d41bb0a250564092358de2669fdb57ff88141d91b"},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
defmodule Mv.Repo.Migrations.AddAccountsDomainExtensions1 do
|
||||
@moduledoc """
|
||||
Installs any extensions that are mentioned in the repo's `installed_extensions/0` callback
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
execute("CREATE EXTENSION IF NOT EXISTS \"citext\"")
|
||||
end
|
||||
|
||||
def down do
|
||||
# Uncomment this if you actually want to uninstall the extensions
|
||||
# when this migration is rolled back:
|
||||
# execute("DROP EXTENSION IF EXISTS \"citext\"")
|
||||
end
|
||||
end
|
||||
31
priv/repo/migrations/20250602071122_add_accounts_domain.exs
Normal file
31
priv/repo/migrations/20250602071122_add_accounts_domain.exs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
defmodule Mv.Repo.Migrations.AddAccountsDomain do
|
||||
@moduledoc """
|
||||
Updates resources based on their most recent snapshots.
|
||||
|
||||
This file was autogenerated with `mix ash_postgres.generate_migrations`
|
||||
"""
|
||||
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
rename table(:users), :password_hash, to: :hashed_password
|
||||
|
||||
alter table(:users) do
|
||||
modify :email, :citext, null: false
|
||||
modify :id, :uuid, default: fragment("gen_random_uuid()")
|
||||
end
|
||||
|
||||
create unique_index(:users, [:email], name: "users_unique_email_index")
|
||||
end
|
||||
|
||||
def down do
|
||||
drop_if_exists unique_index(:users, [:email], name: "users_unique_email_index")
|
||||
|
||||
alter table(:users) do
|
||||
modify :id, :uuid, default: fragment("uuid_generate_v7()")
|
||||
modify :email, :text, null: true
|
||||
end
|
||||
|
||||
rename table(:users), :hashed_password, to: :password_hash
|
||||
end
|
||||
end
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"ash_functions_version": 5,
|
||||
"installed": [
|
||||
"ash-functions"
|
||||
"ash-functions",
|
||||
"citext"
|
||||
]
|
||||
}
|
||||
89
priv/resource_snapshots/repo/tokens/20250602064357.json
Normal file
89
priv/resource_snapshots/repo/tokens/20250602064357.json
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "updated_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"(now() AT TIME ZONE 'utc')\")",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "created_at",
|
||||
"type": "utc_datetime_usec"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "extra_data",
|
||||
"type": "map"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "purpose",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "expires_at",
|
||||
"type": "utc_datetime"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "subject",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "jti",
|
||||
"type": "text"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "FEFA652DA83D7A45390F6667C92DB6E8E8D5CDF709B37834CC4C8AD38E52CFFC",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.Mv.Repo",
|
||||
"schema": null,
|
||||
"table": "tokens"
|
||||
}
|
||||
88
priv/resource_snapshots/repo/users/20250602064357.json
Normal file
88
priv/resource_snapshots/repo/users/20250602064357.json
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"uuid_generate_v7()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "email",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "password_hash",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "oicd_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "users_member_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "members"
|
||||
},
|
||||
"size": null,
|
||||
"source": "member_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "48E05AF26A1C25A2D34E5BB3AB26654456D7BD4A73B9C92FC439835D9E453861",
|
||||
"identities": [],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.Mv.Repo",
|
||||
"schema": null,
|
||||
"table": "users"
|
||||
}
|
||||
103
priv/resource_snapshots/repo/users/20250602071122.json
Normal file
103
priv/resource_snapshots/repo/users/20250602071122.json
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
{
|
||||
"attributes": [
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "fragment(\"gen_random_uuid()\")",
|
||||
"generated?": false,
|
||||
"primary_key?": true,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "id",
|
||||
"type": "uuid"
|
||||
},
|
||||
{
|
||||
"allow_nil?": false,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "email",
|
||||
"type": "citext"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "hashed_password",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": null,
|
||||
"size": null,
|
||||
"source": "oicd_id",
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"allow_nil?": true,
|
||||
"default": "nil",
|
||||
"generated?": false,
|
||||
"primary_key?": false,
|
||||
"references": {
|
||||
"deferrable": false,
|
||||
"destination_attribute": "id",
|
||||
"destination_attribute_default": null,
|
||||
"destination_attribute_generated": null,
|
||||
"index?": false,
|
||||
"match_type": null,
|
||||
"match_with": null,
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"name": "users_member_id_fkey",
|
||||
"on_delete": null,
|
||||
"on_update": null,
|
||||
"primary_key?": true,
|
||||
"schema": "public",
|
||||
"table": "members"
|
||||
},
|
||||
"size": null,
|
||||
"source": "member_id",
|
||||
"type": "uuid"
|
||||
}
|
||||
],
|
||||
"base_filter": null,
|
||||
"check_constraints": [],
|
||||
"custom_indexes": [],
|
||||
"custom_statements": [],
|
||||
"has_create_action": true,
|
||||
"hash": "C8B3A4BCBE42E1FFECED346B254902C69E402ADC2528CF4941D342A6E08164FC",
|
||||
"identities": [
|
||||
{
|
||||
"all_tenants?": false,
|
||||
"base_filter": null,
|
||||
"index_name": "users_unique_email_index",
|
||||
"keys": [
|
||||
{
|
||||
"type": "atom",
|
||||
"value": "email"
|
||||
}
|
||||
],
|
||||
"name": "unique_email",
|
||||
"nils_distinct?": true,
|
||||
"where": null
|
||||
}
|
||||
],
|
||||
"multitenancy": {
|
||||
"attribute": null,
|
||||
"global": null,
|
||||
"strategy": null
|
||||
},
|
||||
"repo": "Elixir.Mv.Repo",
|
||||
"schema": null,
|
||||
"table": "users"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue
rauthy ist ja nur einer von beliebig tauschbaren OIDC-providern, oder?
Nach meinem Verständnis müsste der code sich sprachlich dann doch allgemein auf oidc provider beziehen und nicht rauthy speziell.
Wenn ich nicht irgendwas übersehe oder noch nicht verstehe
Nein da hast du Recht, vielleicht ist da auch sso passend, es wird dann auch in der UI im Button automatisch angezeigt. Ich habs jetzt einfach so gemacht um es klarer zu halten. Also für mich macht es erstmal so Sinn, aber wenn du dich mit was anderem besser fühlst kann ichs gern ändern :)