defmodule Mv.Accounts.User do @moduledoc """ The ressource for keeping user-specific data related to the login process. It is used by AshAuthentication to handle the Authentication strategies like SSO. """ use Ash.Resource, domain: Mv.Accounts, data_layer: AshPostgres.DataLayer, extensions: [AshAuthentication] # authorizers: [Ash.Policy.Authorizer] postgres do table "users" repo Mv.Repo end @doc """ AshAuthentication specific: Defines the strategies we want to use for authentication. Currently password and SSO with Rauthy as OIDC provider """ authentication do session_identifier Application.get_env(:mv, :session_identifier) tokens do enabled? true token_resource Mv.Accounts.Token require_token_presence_for_authentication? Application.get_env( :mv, :require_token_presence_for_authentication ) store_all_tokens? true # signing_algorithm "EdDSA" -> https://git.local-it.org/local-it/mitgliederverwaltung/issues/87 signing_secret fn _, _ -> {:ok, Application.get_env(:mv, :token_signing_secret)} end end strategies do oidc :rauthy do client_id Mv.Secrets base_url Mv.Secrets redirect_uri Mv.Secrets client_secret Mv.Secrets auth_method :client_secret_jwt code_verifier true # id_token_signed_response_alg "EdDSA" #-> https://git.local-it.org/local-it/mitgliederverwaltung/issues/87 end password :password do identity_field :email hash_provider AshAuthentication.BcryptProvider confirmation_required? false end end end actions do 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_rauthy 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 create :register_with_rauthy do argument :user_info, :map, allow_nil?: false argument :oauth_tokens, :map, allow_nil?: false upsert? true upsert_identity :unique_email change AshAuthentication.GenerateTokenChange change fn changeset, _ctx -> user_info = Ash.Changeset.get_argument(changeset, :user_info) changeset |> Ash.Changeset.change_attribute(:email, user_info["preferred_username"]) |> Ash.Changeset.change_attribute(:oidc_id, user_info["id"]) end 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 :oidc_id, :string, allow_nil?: true end relationships do belongs_to :member, Mv.Membership.Member end identities do identity :unique_email, [:email] identity :unique_oidc_id, [:oidc_id] end # 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