Refinex CSV import and PDf export closes #299 and #433 #446

Merged
carla merged 16 commits from feat/299_plz into main 2026-02-24 16:32:32 +01:00
36 changed files with 250 additions and 187 deletions
Showing only changes of commit c8d7dd3e55 - Show all commits

View file

@ -22,8 +22,8 @@ ASSOCIATION_NAME="Sportsclub XYZ"
# These have defaults in docker-compose.prod.yml, only override if needed
# OIDC_CLIENT_ID=mv
# OIDC_BASE_URL=http://localhost:8080/auth/v1
# OIDC_REDIRECT_URI=http://localhost:4001/auth/user/rauthy/callback
# OIDC_CLIENT_SECRET=your-rauthy-client-secret
# OIDC_REDIRECT_URI=http://localhost:4001/auth/user/oidc/callback
# OIDC_CLIENT_SECRET=your-oidc-client-secret
# Optional: OIDC group → Admin role sync (e.g. Authentik groups from profile scope)
# If OIDC_ADMIN_GROUP_NAME is set, users in that group get Admin role on registration/sign-in.

View file

@ -983,9 +983,9 @@ defmodule Mv.Accounts.User do
hashed_password_field :hashed_password
end
oauth2 :rauthy do
oidc :oidc do
client_id fn _, _ ->
Application.fetch_env!(:mv, :rauthy)[:client_id]
Application.fetch_env!(:mv, :oidc)[:client_id]
end
# ... other config
end
@ -1866,7 +1866,7 @@ authentication do
hashed_password_field :hashed_password
end
oauth2 :rauthy do
oidc :oidc do
# OIDC configuration
end
end
@ -2093,7 +2093,7 @@ plug :protect_from_forgery
```elixir
# config/runtime.exs
config :mv, :rauthy,
config :mv, :oidc,
client_id: System.get_env("OIDC_CLIENT_ID") || "mv",
client_secret: System.get_env("OIDC_CLIENT_SECRET"),
base_url: System.get_env("OIDC_BASE_URL")

View file

@ -142,7 +142,7 @@ Mila uses OIDC for Single Sign-On. In development, a local **Rauthy** instance i
3. Login with "admin@localhost" and password from `BOOTSTRAP_ADMIN_PASSWORD_PLAIN` in docker-compose.yml
4. add client from the admin panel
- Client ID: mv
- redirect uris: http://localhost:4000/auth/user/rauthy/callback
- redirect uris: http://localhost:4000/auth/user/oidc/callback
- Authorization Flows: authorization_code
- allowed origins: http://localhost:4000
- access/id token algortihm: RS256 (EDDSA did not work for me, found just few infos in the ashauthentication docs)
@ -153,13 +153,13 @@ Now you can log in to Mila via OIDC!
### OIDC with other providers (Authentik, Keycloak, etc.)
Mila works with any OIDC-compliant provider. The internal strategy is named `:rauthy`, but this is just a name — it works with any provider.
Mila works with any OIDC-compliant provider. The internal strategy is named `:oidc` — it works with any OIDC-compliant provider.
**Important:** The redirect URI must always end with `/auth/user/rauthy/callback`.
**Important:** The redirect URI must always end with `/auth/user/oidc/callback`.
Example for Authentik:
1. Create an OAuth2/OpenID Provider in Authentik
2. Set the redirect URI to: `https://your-domain.com/auth/user/rauthy/callback`
2. Set the redirect URI to: `https://your-domain.com/auth/user/oidc/callback`
3. Configure environment variables:
```bash
DOMAIN=your-domain.com # or PHX_HOST=your-domain.com
@ -168,7 +168,7 @@ Example for Authentik:
OIDC_CLIENT_SECRET=your-client-secret # or use OIDC_CLIENT_SECRET_FILE
```
The `OIDC_REDIRECT_URI` is auto-generated as `https://{DOMAIN}/auth/user/rauthy/callback` if not explicitly set.
The `OIDC_REDIRECT_URI` is auto-generated as `https://{DOMAIN}/auth/user/oidc/callback` if not explicitly set.
## ⚙️ Configuration
@ -238,7 +238,7 @@ For testing the production Docker build locally:
# OIDC_CLIENT_ID=mv
# OIDC_BASE_URL=http://localhost:8080/auth/v1
# OIDC_CLIENT_SECRET=<from-your-oidc-provider>
# OIDC_REDIRECT_URI is auto-generated as https://{DOMAIN}/auth/user/rauthy/callback
# OIDC_REDIRECT_URI is auto-generated as https://{DOMAIN}/auth/user/oidc/callback
# Alternative: Use _FILE variables for Docker secrets (takes priority over regular vars):
# SECRET_KEY_BASE_FILE=/run/secrets/secret_key_base

View file

@ -357,4 +357,16 @@
}
}
/* ============================================
Collapsed Sidebar: User Menu Dropdown Richtung
============================================ */
/* Bei eingeklappter Sidebar liegt der Avatar-Button am linken Rand.
dropdown-end würde das Menü nach links öffnen (off-screen).
Stattdessen nach rechts öffnen (in den Content-Bereich). */
#app-layout[data-sidebar-expanded="false"] .dropdown.dropdown-top > ul.dropdown-content {
right: auto !important;
left: 0 !important;
}
/* This file is for your main application CSS */

View file

@ -87,6 +87,16 @@ Hooks.SidebarState = {
}
},
updated() {
// LiveView patches data-sidebar-expanded back to the template default ("true")
// on every DOM update. Re-apply the stored state from localStorage after each patch.
const expanded = localStorage.getItem('sidebar-expanded') !== 'false'
const current = this.el.dataset.sidebarExpanded === 'true'
if (current !== expanded) {
this.setSidebarState(expanded)
}
},
setSidebarState(expanded) {
// Convert boolean to string for consistency
const expandedStr = expanded ? 'true' : 'false'
@ -228,6 +238,13 @@ document.addEventListener("DOMContentLoaded", () => {
// Listen for changes to the drawer checkbox
drawerToggle.addEventListener("change", () => {
// On desktop (lg:drawer-open), the mobile drawer must never open.
// The hamburger label is lg:hidden, but guard here as a safety net
// against any accidental toggles (e.g. from overlapping elements or JS).
if (drawerToggle.checked && window.innerWidth >= 1024) {
drawerToggle.checked = false
return
}
const isOpen = drawerToggle.checked
updateAriaExpanded()
updateSidebarTabIndex(isOpen)

View file

@ -93,11 +93,11 @@ config :mv, :secret_key_base, "ryn7D6ssmIHQFWIks2sFiTGATgwwAR1+3bN8p7fy6qVtB8qnx
# Signing Secret for Authentication
config :mv, :token_signing_secret, "IwUwi65TrEeExwBXXFPGm2I7889NsL"
config :mv, :rauthy,
config :mv, :oidc,
client_id: "mv",
base_url: "http://localhost:8080/auth/v1",
client_secret: System.get_env("OIDC_CLIENT_SECRET"),
redirect_uri: "http://localhost:4000/auth/user/rauthy/callback"
redirect_uri: "http://localhost:4000/auth/user/oidc/callback"
# AshAuthentication development configuration
config :mv, :session_identifier, :jti

View file

@ -129,8 +129,7 @@ if config_env() == :prod do
config :mv, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
# OIDC configuration (works with any OIDC provider: Authentik, Rauthy, Keycloak, etc.)
# Note: The strategy is named :rauthy internally, but works with any OIDC provider.
# The redirect_uri callback path is always /auth/user/rauthy/callback regardless of provider.
# The redirect_uri callback path is /auth/user/oidc/callback.
#
# Supports OIDC_CLIENT_SECRET or OIDC_CLIENT_SECRET_FILE for Docker secrets.
# OIDC_CLIENT_SECRET is required only if OIDC is being used (indicated by explicit OIDC env vars).
@ -150,9 +149,9 @@ if config_env() == :prod do
# Build redirect_uri: use OIDC_REDIRECT_URI if set, otherwise build from host.
# Uses HTTPS since production runs behind TLS termination.
default_redirect_uri = "https://#{host}/auth/user/rauthy/callback"
default_redirect_uri = "https://#{host}/auth/user/oidc/callback"
config :mv, :rauthy,
config :mv, :oidc,
client_id: oidc_client_id || "mv",
base_url: oidc_base_url || "http://localhost:8080/auth/v1",
client_secret: client_secret,

View file

@ -18,11 +18,11 @@ services:
PHX_HOST: "${PHX_HOST:-localhost}"
PORT: "4001"
PHX_SERVER: "true"
# Rauthy OIDC config - use host.docker.internal to reach host services
# OIDC config - use host.docker.internal to reach host services
OIDC_CLIENT_ID: "mv"
OIDC_BASE_URL: "http://host.docker.internal:8080/auth/v1"
OIDC_CLIENT_SECRET_FILE: "/run/secrets/oidc_client_secret"
OIDC_REDIRECT_URI: "http://localhost:4001/auth/user/rauthy/callback"
OIDC_REDIRECT_URI: "http://localhost:4001/auth/user/oidc/callback"
secrets:
- db_password
- secret_key_base

View file

@ -39,8 +39,8 @@
### Where It Runs
1. Registration: register_with_rauthy after_action calls OidcRoleSync.
2. Sign-in: sign_in_with_rauthy prepare after_action calls OidcRoleSync for each user.
1. Registration: register_with_oidc after_action calls OidcRoleSync.
2. Sign-in: sign_in_with_oidc prepare after_action calls OidcRoleSync for each user.
### Internal Action

View file

@ -886,7 +886,7 @@ just regen-migrations <name>
**Checklist:**
1. ✅ Rauthy running: `docker compose ps`
2. ✅ Client created in Rauthy admin panel
3. ✅ Redirect URI matches exactly: `http://localhost:4000/auth/user/rauthy/callback`
3. ✅ Redirect URI matches exactly: `http://localhost:4000/auth/user/oidc/callback`
4. ✅ OIDC_CLIENT_SECRET in .env
5. ✅ App restarted after .env update

View file

@ -501,8 +501,8 @@ Since this is a **Phoenix LiveView** application with **Ash Framework**, we have
|--------|-------|---------|------|---------|----------|
| `GET` | `/auth/user/password/sign_in` | Show password login form | 🔓 | - | HTML form |
| `POST` | `/auth/user/password/sign_in` | Submit password login | 🔓 | `{email, password}` | Redirect + session cookie |
| `GET` | `/auth/user/rauthy` | Initiate OIDC flow | 🔓 | - | Redirect to Rauthy |
| `GET` | `/auth/user/rauthy/callback` | Handle OIDC callback | 🔓 | `{code, state}` | Redirect + session cookie |
| `GET` | `/auth/user/oidc` | Initiate OIDC flow | 🔓 | - | Redirect to Rauthy |
| `GET` | `/auth/user/oidc/callback` | Handle OIDC callback | 🔓 | `{code, state}` | Redirect + session cookie |
| `POST` | `/auth/user/sign_out` | Sign out user | 🔐 | - | Redirect to login |
| `GET` | `/auth/link-oidc-account` | OIDC account linking (password verification) | 🔓 | - | LiveView form | ✅ Implemented |
| `GET` | `/auth/user/password/reset` | Show password reset form | 🔓 | - | HTML form |
@ -515,9 +515,9 @@ Since this is a **Phoenix LiveView** application with **Ash Framework**, we have
| Resource | Action | Purpose | Auth | Input | Output |
|----------|--------|---------|------|-------|--------|
| `User` | `:sign_in_with_password` | Password authentication | 🔓 | `{email, password}` | `{:ok, user}` or `{:error, reason}` |
| `User` | `:sign_in_with_rauthy` | OIDC authentication | 🔓 | `{oidc_id, email, user_info}` | `{:ok, user}` or `{:error, reason}` |
| `User` | `:sign_in_with_oidc` | OIDC authentication | 🔓 | `{oidc_id, email, user_info}` | `{:ok, user}` or `{:error, reason}` |
| `User` | `:register_with_password` | Create user with password | 🔓 | `{email, password}` | `{:ok, user}` |
| `User` | `:register_with_rauthy` | Create user via OIDC | 🔓 | `{oidc_id, email}` | `{:ok, user}` |
| `User` | `:register_with_oidc` | Create user via OIDC | 🔓 | `{oidc_id, email}` | `{:ok, user}` |
| `User` | `:request_password_reset` | Generate reset token | 🔓 | `{email}` | `{:ok, token}` |
| `User` | `:reset_password` | Reset password with token | 🔓 | `{token, password}` | `{:ok, user}` |
| `Token` | `:revoke` | Revoke authentication token | 🔐 | `{jti}` | `{:ok, token}` |

View file

@ -10,10 +10,10 @@ This feature implements secure account linking between password-based accounts a
#### 1. Security Fix: `lib/accounts/user.ex`
**Change**: The `sign_in_with_rauthy` action now filters by `oidc_id` instead of `email`.
**Change**: The `sign_in_with_oidc` action now filters by `oidc_id` instead of `email`.
```elixir
read :sign_in_with_rauthy do
read :sign_in_with_oidc do
argument :user_info, :map, allow_nil?: false
argument :oauth_tokens, :map, allow_nil?: false
prepare AshAuthentication.Strategy.OAuth2.SignInPreparation

View file

@ -9,7 +9,7 @@ defmodule Mv.Accounts do
## Public API
The domain exposes these main actions:
- User CRUD: `create_user/1`, `list_users/0`, `update_user/2`, `destroy_user/1`
- Authentication: `create_register_with_rauthy/1`, `read_sign_in_with_rauthy/1`
- Authentication: `create_register_with_oidc/1`, `read_sign_in_with_oidc/1`
"""
use Ash.Domain,
extensions: [AshAdmin.Domain, AshPhoenix]
@ -24,8 +24,8 @@ defmodule Mv.Accounts do
define :list_users, action: :read
define :update_user, action: :update_user
define :destroy_user, action: :destroy
define :create_register_with_rauthy, action: :register_with_rauthy
define :read_sign_in_with_rauthy, action: :sign_in_with_rauthy
define :create_register_with_oidc, action: :register_with_oidc
define :read_sign_in_with_oidc, action: :sign_in_with_oidc
end
resource Mv.Accounts.Token

View file

@ -28,7 +28,7 @@ defmodule Mv.Accounts.User do
@doc """
AshAuthentication specific: Defines the strategies we want to use for authentication.
Currently password and SSO with Rauthy as OIDC provider
Currently password and SSO via OIDC (supports any provider: Authentik, Rauthy, Keycloak, etc.)
"""
authentication do
session_identifier Application.compile_env!(:mv, :session_identifier)
@ -52,7 +52,7 @@ defmodule Mv.Accounts.User do
end
strategies do
oidc :rauthy do
oidc :oidc do
client_id Mv.Secrets
base_url Mv.Secrets
redirect_uri Mv.Secrets
@ -88,7 +88,7 @@ defmodule Mv.Accounts.User do
# Always use one of these explicit create actions instead:
# - :create_user (for manual user creation with optional member link)
# - :register_with_password (for password-based registration)
# - :register_with_rauthy (for OIDC-based registration)
# - :register_with_oidc (for OIDC-based registration)
defaults [:read]
destroy :destroy do
@ -267,7 +267,7 @@ defmodule Mv.Accounts.User do
prepare AshAuthentication.Preparations.FilterBySubject
end
read :sign_in_with_rauthy do
read :sign_in_with_oidc do
# Single record expected; required for AshAuthentication OAuth2 strategy (returns list of 0 or 1).
get? true
argument :user_info, :map, allow_nil?: false
@ -302,7 +302,7 @@ defmodule Mv.Accounts.User do
end)
end
create :register_with_rauthy do
create :register_with_oidc do
argument :user_info, :map, allow_nil?: false
argument :oauth_tokens, :map, allow_nil?: false
upsert? true

View file

@ -52,7 +52,8 @@ defmodule Mv.Membership.CustomField do
use Ash.Resource,
domain: Mv.Membership,
data_layer: AshPostgres.DataLayer,
authorizers: [Ash.Policy.Authorizer]
authorizers: [Ash.Policy.Authorizer],
primary_read_warning?: false
postgres do
table "custom_fields"
@ -60,9 +61,13 @@ defmodule Mv.Membership.CustomField do
end
actions do
defaults [:read]
default_accept [:name, :value_type, :description, :required, :show_in_overview]
read :read do
primary? true
prepare build(sort: [name: :asc])
end
create :create do
accept [:name, :value_type, :description, :required, :show_in_overview]
change Mv.Membership.Changes.GenerateSlug

View file

@ -2,7 +2,7 @@ defmodule Mv.OidcRoleSync do
@moduledoc """
Syncs user role from OIDC user_info (e.g. groups claim Admin role).
Used after OIDC registration (register_with_rauthy) and on sign-in so that
Used after OIDC registration (register_with_oidc) and on sign-in so that
users in the configured admin group get the Admin role; others get Mitglied.
Configure via OIDC_ADMIN_GROUP_NAME and OIDC_GROUPS_CLAIM (see OidcRoleSyncConfig).

View file

@ -7,7 +7,7 @@ defmodule Mv.Secrets do
particularly for OIDC (Rauthy) authentication.
## Configuration Source
Secrets are read from the `:rauthy` key in the application configuration,
Secrets are read from the `:oidc` key in the application configuration,
which is typically set in `config/runtime.exs` from environment variables:
- `OIDC_CLIENT_ID`
- `OIDC_CLIENT_SECRET`
@ -21,7 +21,7 @@ defmodule Mv.Secrets do
use AshAuthentication.Secret
def secret_for(
[:authentication, :strategies, :rauthy, :client_id],
[:authentication, :strategies, :oidc, :client_id],
Mv.Accounts.User,
_opts,
_meth
@ -30,7 +30,7 @@ defmodule Mv.Secrets do
end
def secret_for(
[:authentication, :strategies, :rauthy, :redirect_uri],
[:authentication, :strategies, :oidc, :redirect_uri],
Mv.Accounts.User,
_opts,
_meth
@ -39,7 +39,7 @@ defmodule Mv.Secrets do
end
def secret_for(
[:authentication, :strategies, :rauthy, :client_secret],
[:authentication, :strategies, :oidc, :client_secret],
Mv.Accounts.User,
_opts,
_meth
@ -48,7 +48,7 @@ defmodule Mv.Secrets do
end
def secret_for(
[:authentication, :strategies, :rauthy, :base_url],
[:authentication, :strategies, :oidc, :base_url],
Mv.Accounts.User,
_opts,
_meth
@ -58,7 +58,7 @@ defmodule Mv.Secrets do
defp get_config(key) do
:mv
|> Application.fetch_env!(:rauthy)
|> Application.fetch_env!(:oidc)
|> Keyword.fetch!(key)
|> then(&{:ok, &1})
end

View file

@ -54,7 +54,7 @@ defmodule MvWeb.Layouts do
data-sidebar-expanded="true"
phx-hook="SidebarState"
>
<input id="mobile-drawer" type="checkbox" class="drawer-toggle" />
<input id="mobile-drawer" type="checkbox" class="drawer-toggle" phx-update="ignore" />
<div class="drawer-content flex flex-col relative z-0">
<!-- Mobile Header (only visible on mobile) -->

View file

@ -48,8 +48,8 @@ defmodule MvWeb.AuthController do
log_failure_safely(activity, reason)
case {activity, reason} do
{{:rauthy, _action}, reason} ->
handle_rauthy_failure(conn, reason)
{{:oidc, _action}, reason} ->
handle_oidc_failure(conn, reason)
{_, %AshAuthentication.Errors.AuthenticationFailed{caused_by: caused_by}} ->
handle_authentication_failed(conn, caused_by)
@ -61,8 +61,8 @@ defmodule MvWeb.AuthController do
end
end
# Log authentication failures safely, avoiding sensitive data for {:rauthy, _} activities
defp log_failure_safely({:rauthy, _action} = activity, reason) do
# Log authentication failures safely, avoiding sensitive data for {:oidc, _} activities
defp log_failure_safely({:oidc, _action} = activity, reason) do
# For Assent errors, use safe_assent_meta to avoid logging tokens/URLs with query params
case reason do
%Assent.ServerUnreachableError{} = err ->
@ -76,7 +76,7 @@ defmodule MvWeb.AuthController do
Logger.warning(message)
_ ->
# For other rauthy errors, log only error type, not full details
# For other OIDC errors, log only error type, not full details
error_type = get_error_type(reason)
Logger.warning(
@ -86,7 +86,7 @@ defmodule MvWeb.AuthController do
end
defp log_failure_safely(activity, reason) do
# For non-rauthy activities, safe to log full reason
# For non-OIDC activities, safe to log full reason
Logger.warning(
"Authentication failure - Activity: #{inspect(activity)}, Reason: #{inspect(reason)}"
)
@ -119,12 +119,12 @@ defmodule MvWeb.AuthController do
if Enum.empty?(parts), do: "", else: " - " <> Enum.join(parts, ", ")
end
# Handle all Rauthy (OIDC) authentication failures
defp handle_rauthy_failure(conn, %Ash.Error.Invalid{errors: errors}) do
# Handle all OIDC authentication failures
defp handle_oidc_failure(conn, %Ash.Error.Invalid{errors: errors}) do
handle_oidc_email_collision(conn, errors)
end
defp handle_rauthy_failure(conn, %AshAuthentication.Errors.AuthenticationFailed{
defp handle_oidc_failure(conn, %AshAuthentication.Errors.AuthenticationFailed{
caused_by: caused_by
}) do
case caused_by do
@ -139,7 +139,7 @@ defmodule MvWeb.AuthController do
end
# Handle Assent server unreachable errors (network/connectivity issues)
defp handle_rauthy_failure(conn, %Assent.ServerUnreachableError{} = _err) do
defp handle_oidc_failure(conn, %Assent.ServerUnreachableError{} = _err) do
# Logging already done safely in failure/3 via log_failure_safely/2
# No need to log again here to avoid duplicate logs
@ -152,7 +152,7 @@ defmodule MvWeb.AuthController do
end
# Handle Assent invalid response errors (configuration or malformed responses)
defp handle_rauthy_failure(conn, %Assent.InvalidResponseError{} = _err) do
defp handle_oidc_failure(conn, %Assent.InvalidResponseError{} = _err) do
# Logging already done safely in failure/3 via log_failure_safely/2
# No need to log again here to avoid duplicate logs
@ -165,7 +165,7 @@ defmodule MvWeb.AuthController do
end
# Catch-all clause for any other error types
defp handle_rauthy_failure(conn, _reason) do
defp handle_oidc_failure(conn, _reason) do
# Logging already done safely in failure/3 via log_failure_safely/2
# No need to log again here to avoid duplicate logs

View file

@ -84,7 +84,7 @@ defmodule MvWeb.LinkOidcAccountLive do
:info,
dgettext("auth", "Account activated! Redirecting to complete sign-in...")
)
|> Phoenix.LiveView.redirect(to: ~p"/auth/user/rauthy")
|> Phoenix.LiveView.redirect(to: ~p"/auth/user/oidc")
{:error, error} ->
Logger.warning(
@ -223,7 +223,7 @@ defmodule MvWeb.LinkOidcAccountLive do
"Your OIDC account has been successfully linked! Redirecting to complete sign-in..."
)
)
|> Phoenix.LiveView.redirect(to: ~p"/auth/user/rauthy")}
|> Phoenix.LiveView.redirect(to: ~p"/auth/user/oidc")}
{:error, error} ->
Logger.warning(

View file

@ -214,47 +214,49 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do
</:col>
<:action :let={cycle}>
<div class="flex gap-1">
<div class="flex gap-2">
<%= if @can_update_cycle do %>
<button
:if={cycle.status != :paid}
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="paid"
phx-target={@myself}
class="btn btn-sm btn-success"
title={gettext("Mark as paid")}
>
<.icon name="hero-check-circle" class="size-4" />
{gettext("Paid")}
</button>
<button
:if={cycle.status != :suspended}
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="suspended"
phx-target={@myself}
class="btn btn-sm btn-outline btn-warning"
title={gettext("Mark as suspended")}
>
<.icon name="hero-pause-circle" class="size-4" />
{gettext("Suspended")}
</button>
<button
:if={cycle.status != :unpaid}
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="unpaid"
phx-target={@myself}
class="btn btn-sm btn-error"
title={gettext("Mark as unpaid")}
>
<.icon name="hero-x-circle" class="size-4" />
{gettext("Unpaid")}
</button>
<div class="join">
<button
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="paid"
phx-target={@myself}
class={cycle_status_btn_class(cycle.status, :paid)}
aria-pressed={cycle.status == :paid}
title={gettext("Mark as paid")}
>
<.icon name="hero-check-circle" class="size-4" />
{gettext("Paid")}
</button>
<button
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="suspended"
phx-target={@myself}
class={cycle_status_btn_class(cycle.status, :suspended)}
aria-pressed={cycle.status == :suspended}
title={gettext("Mark as suspended")}
>
<.icon name="hero-pause-circle" class="size-4" />
{gettext("Suspended")}
</button>
<button
type="button"
phx-click="mark_cycle_status"
phx-value-cycle_id={cycle.id}
phx-value-status="unpaid"
phx-target={@myself}
class={cycle_status_btn_class(cycle.status, :unpaid)}
aria-pressed={cycle.status == :unpaid}
title={gettext("Mark as unpaid")}
>
<.icon name="hero-x-circle" class="size-4" />
{gettext("Unpaid")}
</button>
</div>
<% end %>
<%= if @can_destroy_cycle do %>
<button
@ -1219,6 +1221,20 @@ defmodule MvWeb.MemberLive.Show.MembershipFeesComponent do
defp translate_receipt_type("income"), do: gettext("Income")
defp translate_receipt_type(other), do: other
# Returns CSS classes for a cycle status button.
# Active (current) status is highlighted with color and non-interactive;
# inactive buttons are neutral gray. Matches the filter button pattern.
defp cycle_status_btn_class(current_status, btn_status) do
base = "join-item btn btn-sm"
case {current_status == btn_status, btn_status} do
{true, :paid} -> "#{base} btn-success btn-active pointer-events-none"
{true, :suspended} -> "#{base} btn-warning btn-active pointer-events-none"
{true, :unpaid} -> "#{base} btn-error btn-active pointer-events-none"
_ -> base
end
end
# Helper component for section box
attr :title, :string, required: true
slot :inner_block, required: true

View file

@ -59,6 +59,12 @@ msgstr ""
msgid "Sign in"
msgstr ""
## Dynamic string from ash_authentication_phoenix OAuth2 component (strategy_name = "Oidc").
## Not auto-extractable because the msgid is constructed at runtime via string interpolation.
## Generated by Phoenix.Naming.humanize(:oidc) = "Oidc"
msgid "Sign in with Oidc"
msgstr ""
msgid "Signing in ..."
msgstr ""

View file

@ -58,6 +58,9 @@ msgstr "Neues Passwort setzen"
msgid "Sign in"
msgstr "Anmelden"
msgid "Sign in with Oidc"
msgstr "Single Sign On"
msgid "Signing in ..."
msgstr "Anmelden..."

View file

@ -55,6 +55,9 @@ msgstr ""
msgid "Sign in"
msgstr ""
msgid "Sign in with Oidc"
msgstr "Single Sign On"
msgid "Signing in ..."
msgstr ""

View file

@ -328,6 +328,7 @@ member_attrs_list = [
city: "Berlin",
street: "Kastanienallee",
house_number: "8",
postal_code: "10435",
membership_fee_type_id: Enum.at(all_fee_types, 2).id,
cycle_status: :mixed
},
@ -338,7 +339,8 @@ member_attrs_list = [
join_date: ~D[2022-11-10],
city: "Berlin",
street: "Kastanienallee",
house_number: "8"
house_number: "8",
postal_code: "10435"
# No membership_fee_type_id - member without fee type
}
]

View file

@ -103,13 +103,13 @@ defmodule Mv.Accounts.UserAuthenticationTest do
"preferred_username" => "oidc.user@example.com"
}
# Use sign_in_with_rauthy to find user by oidc_id
# Use sign_in_with_oidc to find user by oidc_id
# Note: This test will FAIL until we implement the security fix
# that changes the filter from email to oidc_id
system_actor = Mv.Helpers.SystemActor.get_system_actor()
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -145,11 +145,11 @@ defmodule Mv.Accounts.UserAuthenticationTest do
"preferred_username" => "newuser@example.com"
}
# Should create via register_with_rauthy
# Should create via register_with_oidc
system_actor = Mv.Helpers.SystemActor.get_system_actor()
{:ok, new_user} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -196,8 +196,8 @@ defmodule Mv.Accounts.UserAuthenticationTest do
describe "Mixed authentication scenarios" do
@tag :test_proposal
test "user with oidc_id cannot be found by email-only query in sign_in_with_rauthy" do
# This test verifies the security fix: sign_in_with_rauthy should NOT
test "user with oidc_id cannot be found by email-only query in sign_in_with_oidc" do
# This test verifies the security fix: sign_in_with_oidc should NOT
# match users by email, only by oidc_id
_user =
@ -218,7 +218,7 @@ defmodule Mv.Accounts.UserAuthenticationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -238,12 +238,12 @@ defmodule Mv.Accounts.UserAuthenticationTest do
:ok
other ->
flunk("sign_in_with_rauthy should not match by email alone, got: #{inspect(other)}")
flunk("sign_in_with_oidc should not match by email alone, got: #{inspect(other)}")
end
end
@tag :test_proposal
test "password user (oidc_id=nil) is not found by sign_in_with_rauthy" do
test "password user (oidc_id=nil) is not found by sign_in_with_oidc" do
# Create a password-only user
_user =
create_test_user(%{
@ -262,7 +262,7 @@ defmodule Mv.Accounts.UserAuthenticationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -283,7 +283,7 @@ defmodule Mv.Accounts.UserAuthenticationTest do
other ->
flunk(
"Password-only user should not be found by sign_in_with_rauthy, got: #{inspect(other)}"
"Password-only user should not be found by sign_in_with_oidc, got: #{inspect(other)}"
)
end
end

View file

@ -206,7 +206,7 @@ defmodule Mv.Accounts.UserEmailSyncTest do
# Simulate OIDC registration
{:ok, user} =
Mv.Accounts.User
|> Ash.Changeset.for_create(:register_with_rauthy, %{
|> Ash.Changeset.for_create(:register_with_oidc, %{
user_info: user_info,
oauth_tokens: oauth_tokens
})

View file

@ -283,7 +283,7 @@ defmodule Mv.Accounts.UserPoliciesTest do
assert user_with_role.role.name == "Mitglied"
end
test "register_with_rauthy works without actor via AshAuthentication bypass" do
test "register_with_oidc works without actor via AshAuthentication bypass" do
# Test that AshAuthentication bypass allows OIDC registration without actor
user_info = %{
"sub" => "oidc_sub_#{System.unique_integer([:positive])}",
@ -294,7 +294,7 @@ defmodule Mv.Accounts.UserPoliciesTest do
changeset =
Accounts.User
|> Ash.Changeset.for_create(:register_with_rauthy, %{
|> Ash.Changeset.for_create(:register_with_oidc, %{
user_info: user_info,
oauth_tokens: oauth_tokens
})
@ -306,7 +306,7 @@ defmodule Mv.Accounts.UserPoliciesTest do
assert user.oidc_id == user_info["sub"]
end
test "sign_in_with_rauthy works without actor via AshAuthentication bypass" do
test "sign_in_with_oidc works without actor via AshAuthentication bypass" do
# First create a user with OIDC ID (using system_actor for setup)
system_actor = Mv.Helpers.SystemActor.get_system_actor()
@ -319,16 +319,16 @@ defmodule Mv.Accounts.UserPoliciesTest do
{:ok, user} =
Accounts.User
|> Ash.Changeset.for_create(:register_with_rauthy, %{
|> Ash.Changeset.for_create(:register_with_oidc, %{
user_info: user_info_create,
oauth_tokens: oauth_tokens
})
|> Ash.create(actor: system_actor)
# Now test sign_in_with_rauthy without actor (should work via AshAuthentication bypass)
# Now test sign_in_with_oidc without actor (should work via AshAuthentication bypass)
query =
Accounts.User
|> Ash.Query.for_read(:sign_in_with_rauthy, %{
|> Ash.Query.for_read(:sign_in_with_oidc, %{
user_info: user_info_create,
oauth_tokens: oauth_tokens
})

View file

@ -104,8 +104,8 @@ defmodule Mv.OidcRoleSyncTest do
end
end
# B3: Role sync after registration is implemented via after_action in register_with_rauthy.
# Full integration tests (create_register_with_rauthy + assert role) are skipped: when the
# B3: Role sync after registration is implemented via after_action in register_with_oidc.
# Full integration tests (create_register_with_oidc + assert role) are skipped: when the
# nested Ash.update! runs inside the create's after_action, authorization may evaluate in
# the create context so set_role_from_oidc_sync bypass does not apply. Sync logic is covered
# by the apply_admin_role_from_user_info tests above. B4 sign-in sync will also use that.

View file

@ -254,7 +254,7 @@ defmodule MvWeb.AuthControllerTest do
end
# OIDC/Rauthy error handling tests
describe "handle_rauthy_failure/2" do
describe "handle_oidc_failure/2" do
test "Assent.ServerUnreachableError redirects to sign-in with error flash", %{
conn: authenticated_conn
} do
@ -266,7 +266,7 @@ defmodule MvWeb.AuthControllerTest do
reason: %Mint.TransportError{reason: :econnrefused}
}
conn = MvWeb.AuthController.failure(conn, {:rauthy, :callback}, error)
conn = MvWeb.AuthController.failure(conn, {:oidc, :callback}, error)
assert redirected_to(conn) == ~p"/sign-in"
@ -288,7 +288,7 @@ defmodule MvWeb.AuthControllerTest do
}
}
conn = MvWeb.AuthController.failure(conn, {:rauthy, :callback}, error)
conn = MvWeb.AuthController.failure(conn, {:oidc, :callback}, error)
assert redirected_to(conn) == ~p"/sign-in"
@ -302,7 +302,7 @@ defmodule MvWeb.AuthControllerTest do
conn = build_unauthenticated_conn(authenticated_conn)
unknown_reason = :oops
conn = MvWeb.AuthController.failure(conn, {:rauthy, :callback}, unknown_reason)
conn = MvWeb.AuthController.failure(conn, {:oidc, :callback}, unknown_reason)
assert redirected_to(conn) == ~p"/sign-in"
@ -326,7 +326,7 @@ defmodule MvWeb.AuthControllerTest do
log =
capture_log(fn ->
MvWeb.AuthController.failure(conn, {:rauthy, :callback}, error)
MvWeb.AuthController.failure(conn, {:oidc, :callback}, error)
end)
# Should log redacted URL (only scheme and host)
@ -352,17 +352,17 @@ defmodule MvWeb.AuthControllerTest do
log =
capture_log(fn ->
MvWeb.AuthController.failure(conn, {:rauthy, :callback}, error)
MvWeb.AuthController.failure(conn, {:oidc, :callback}, error)
end)
# Should log error type but not full error details
assert log =~ "Authentication failure"
assert log =~ "rauthy"
assert log =~ "oidc"
# Should not log full error struct with inspect
refute log =~ "Assent.InvalidResponseError"
end
test "does not log full reason for unknown rauthy errors", %{
test "does not log full reason for unknown OIDC errors", %{
conn: authenticated_conn
} do
conn = build_unauthenticated_conn(authenticated_conn)
@ -375,19 +375,19 @@ defmodule MvWeb.AuthControllerTest do
log =
capture_log(fn ->
MvWeb.AuthController.failure(conn, {:rauthy, :callback}, error_with_sensitive_data)
MvWeb.AuthController.failure(conn, {:oidc, :callback}, error_with_sensitive_data)
end)
# Should log error type but not full error details
assert log =~ "Authentication failure"
assert log =~ "rauthy"
assert log =~ "oidc"
# Should NOT log sensitive data
refute log =~ "secret_token_123"
refute log =~ "access_token=abc123"
refute log =~ "callback?access_token"
end
test "logs full reason for non-rauthy activities (password auth)", %{
test "logs full reason for non-OIDC activities (password auth)", %{
conn: authenticated_conn
} do
conn = build_unauthenticated_conn(authenticated_conn)
@ -401,7 +401,7 @@ defmodule MvWeb.AuthControllerTest do
MvWeb.AuthController.failure(conn, {:password, :sign_in}, reason)
end)
# For non-rauthy activities, full reason is safe to log
# For non-OIDC activities, full reason is safe to log
assert log =~ "Authentication failure"
assert log =~ "password"
assert log =~ "AuthenticationFailed"

View file

@ -23,7 +23,7 @@ defmodule MvWeb.OidcE2EFlowTest do
# Call register action
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -38,7 +38,7 @@ defmodule MvWeb.OidcE2EFlowTest do
# Verify user can be found by oidc_id
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -74,7 +74,7 @@ defmodule MvWeb.OidcE2EFlowTest do
# Register (upsert) with new email
{:ok, updated_user} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: updated_user_info,
oauth_tokens: %{}
@ -107,7 +107,7 @@ defmodule MvWeb.OidcE2EFlowTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -146,7 +146,7 @@ defmodule MvWeb.OidcE2EFlowTest do
}
{:error, %Ash.Error.Invalid{errors: errors}} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -185,7 +185,7 @@ defmodule MvWeb.OidcE2EFlowTest do
# Step 5: User can now sign in via OIDC
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -222,7 +222,7 @@ defmodule MvWeb.OidcE2EFlowTest do
# Collision detected
{:error, %Ash.Error.Invalid{}} =
Mv.Accounts.create_register_with_rauthy(%{
Mv.Accounts.create_register_with_oidc(%{
user_info: user_info,
oauth_tokens: %{}
})
@ -279,7 +279,7 @@ defmodule MvWeb.OidcE2EFlowTest do
# Collision detected
{:error, %Ash.Error.Invalid{}} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -333,7 +333,7 @@ defmodule MvWeb.OidcE2EFlowTest do
# Sign-in should fail (no matching oidc_id)
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -357,7 +357,7 @@ defmodule MvWeb.OidcE2EFlowTest do
# Registration should trigger password requirement
{:error, %Ash.Error.Invalid{errors: errors}} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -389,7 +389,7 @@ defmodule MvWeb.OidcE2EFlowTest do
# Should trigger hard error (not PasswordVerificationRequired)
{:error, %Ash.Error.Invalid{errors: errors}} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -431,7 +431,7 @@ defmodule MvWeb.OidcE2EFlowTest do
}
{:error, %Ash.Error.Invalid{errors: errors}} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -453,7 +453,7 @@ defmodule MvWeb.OidcE2EFlowTest do
}
{:error, %Ash.Error.Invalid{errors: errors}} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -472,7 +472,7 @@ defmodule MvWeb.OidcE2EFlowTest do
}
{:error, %Ash.Error.Invalid{errors: errors}} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}

View file

@ -28,7 +28,7 @@ defmodule MvWeb.OidcEmailUpdateTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
@ -70,7 +70,7 @@ defmodule MvWeb.OidcEmailUpdateTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
@ -135,7 +135,7 @@ defmodule MvWeb.OidcEmailUpdateTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
@ -190,7 +190,7 @@ defmodule MvWeb.OidcEmailUpdateTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
@ -234,7 +234,7 @@ defmodule MvWeb.OidcEmailUpdateTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
@ -271,7 +271,7 @@ defmodule MvWeb.OidcEmailUpdateTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}

View file

@ -24,11 +24,11 @@ defmodule MvWeb.OidcIntegrationTest do
"preferred_username" => "existing@example.com"
}
# Test sign_in_with_rauthy action directly
# Test sign_in_with_oidc action directly
system_actor = Mv.Helpers.SystemActor.get_system_actor()
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -48,17 +48,17 @@ defmodule MvWeb.OidcIntegrationTest do
assert found_user.oidc_id == "existing_oidc_123"
end
test "new OIDC user gets created via register_with_rauthy" do
test "new OIDC user gets created via register_with_oidc" do
# Simulate OIDC callback for completely new user
user_info = %{
"sub" => "brand_new_oidc_456",
"preferred_username" => "newuser@example.com"
}
# Test register_with_rauthy action
# Test register_with_oidc action
system_actor = Mv.Helpers.SystemActor.get_system_actor()
case Mv.Accounts.create_register_with_rauthy(
case Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -78,7 +78,7 @@ defmodule MvWeb.OidcIntegrationTest do
describe "OIDC sign-in security tests" do
@tag :test_proposal
test "sign_in_with_rauthy does NOT match user with only email (no oidc_id)" do
test "sign_in_with_oidc does NOT match user with only email (no oidc_id)" do
# SECURITY TEST: Ensure password-only users cannot be accessed via OIDC
# Create a password-only user (no oidc_id)
_password_user =
@ -98,7 +98,7 @@ defmodule MvWeb.OidcIntegrationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -123,7 +123,7 @@ defmodule MvWeb.OidcIntegrationTest do
end
@tag :test_proposal
test "sign_in_with_rauthy only matches when oidc_id matches" do
test "sign_in_with_oidc only matches when oidc_id matches" do
# Create user with specific OIDC ID
user =
create_test_user(%{
@ -140,7 +140,7 @@ defmodule MvWeb.OidcIntegrationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: correct_user_info,
oauth_tokens: %{}
@ -164,7 +164,7 @@ defmodule MvWeb.OidcIntegrationTest do
}
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: wrong_user_info,
oauth_tokens: %{}
@ -189,7 +189,7 @@ defmodule MvWeb.OidcIntegrationTest do
end
@tag :test_proposal
test "sign_in_with_rauthy does not match user with empty string oidc_id" do
test "sign_in_with_oidc does not match user with empty string oidc_id" do
# Edge case: empty string should be treated like nil
_user =
create_test_user(%{
@ -205,7 +205,7 @@ defmodule MvWeb.OidcIntegrationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
result =
Mv.Accounts.read_sign_in_with_rauthy(
Mv.Accounts.read_sign_in_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -248,7 +248,7 @@ defmodule MvWeb.OidcIntegrationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -284,7 +284,7 @@ defmodule MvWeb.OidcIntegrationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -308,7 +308,7 @@ defmodule MvWeb.OidcIntegrationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -338,7 +338,7 @@ defmodule MvWeb.OidcIntegrationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
{:ok, user} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -360,7 +360,7 @@ defmodule MvWeb.OidcIntegrationTest do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
{:ok, user} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}

View file

@ -32,7 +32,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -70,7 +70,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
{:error, %Ash.Error.Invalid{errors: errors}} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -167,7 +167,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -201,7 +201,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
# This should work via upsert
{:ok, updated_user} =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -308,7 +308,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{}
@ -380,7 +380,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
Mv.Accounts.create_register_with_oidc(%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
})
@ -421,7 +421,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
@ -459,7 +459,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
@ -507,7 +507,7 @@ defmodule MvWeb.OidcPasswordLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(
Mv.Accounts.create_register_with_oidc(
%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}

View file

@ -45,7 +45,7 @@ defmodule MvWeb.OidcPasswordlessLinkingTest do
# Now OIDC sign-in should work
result =
Mv.Accounts.User
|> Ash.Query.for_read(:sign_in_with_rauthy, %{
|> Ash.Query.for_read(:sign_in_with_oidc, %{
user_info: %{
"sub" => "auto_link_oidc_123",
"preferred_username" => "invited@example.com"
@ -79,7 +79,7 @@ defmodule MvWeb.OidcPasswordlessLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
Mv.Accounts.create_register_with_oidc(%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
})
@ -119,7 +119,7 @@ defmodule MvWeb.OidcPasswordlessLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
Mv.Accounts.create_register_with_oidc(%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
})
@ -165,7 +165,7 @@ defmodule MvWeb.OidcPasswordlessLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
Mv.Accounts.create_register_with_oidc(%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
})
@ -200,7 +200,7 @@ defmodule MvWeb.OidcPasswordlessLinkingTest do
}
result =
Mv.Accounts.create_register_with_rauthy(%{
Mv.Accounts.create_register_with_oidc(%{
user_info: user_info,
oauth_tokens: %{"access_token" => "test_token"}
})

View file

@ -89,7 +89,7 @@ defmodule MvWeb.ProfileNavigationTest do
user =
Mv.Accounts.User
|> Ash.Changeset.for_create(:register_with_rauthy, %{
|> Ash.Changeset.for_create(:register_with_oidc, %{
user_info: user_info,
oauth_tokens: oauth_tokens
})
@ -140,7 +140,7 @@ defmodule MvWeb.ProfileNavigationTest do
oidc_user =
Mv.Accounts.User
|> Ash.Changeset.for_create(:register_with_rauthy, %{
|> Ash.Changeset.for_create(:register_with_oidc, %{
user_info: user_info,
oauth_tokens: oauth_tokens
})