Merge branch 'main' into feature/220_ui_issues_2
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
commit
89b02aeacf
3 changed files with 52 additions and 13 deletions
31
README.md
31
README.md
|
|
@ -45,7 +45,7 @@ Our philosophy: **software should help people spend less time on administration
|
||||||
- 🚧 Sorting & filtering
|
- 🚧 Sorting & filtering
|
||||||
- 🚧 Roles & permissions (e.g. board, treasurer)
|
- 🚧 Roles & permissions (e.g. board, treasurer)
|
||||||
- ✅ Custom fields (flexible per club needs)
|
- ✅ Custom fields (flexible per club needs)
|
||||||
- ✅ SSO via OIDC (tested with Rauthy)
|
- ✅ SSO via OIDC (works with Authentik, Rauthy, Keycloak, etc.)
|
||||||
- 🚧 Self-service & online application
|
- 🚧 Self-service & online application
|
||||||
- 🚧 Accessibility, GDPR, usability improvements
|
- 🚧 Accessibility, GDPR, usability improvements
|
||||||
- 🚧 Email sending
|
- 🚧 Email sending
|
||||||
|
|
@ -147,7 +147,26 @@ Mila uses OIDC for Single Sign-On. In development, a local **Rauthy** instance i
|
||||||
5. copy client secret to `.env` file
|
5. copy client secret to `.env` file
|
||||||
6. abort and run `just run` again
|
6. abort and run `just run` again
|
||||||
|
|
||||||
Now you can log in to Mila via OIDC!
|
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.
|
||||||
|
|
||||||
|
**Important:** The redirect URI must always end with `/auth/user/rauthy/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`
|
||||||
|
3. Configure environment variables:
|
||||||
|
```bash
|
||||||
|
DOMAIN=your-domain.com # or PHX_HOST=your-domain.com
|
||||||
|
OIDC_CLIENT_ID=your-client-id
|
||||||
|
OIDC_BASE_URL=https://auth.example.com/application/o/your-app
|
||||||
|
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.
|
||||||
|
|
||||||
## ⚙️ Configuration
|
## ⚙️ Configuration
|
||||||
|
|
||||||
|
|
@ -210,13 +229,13 @@ For testing the production Docker build locally:
|
||||||
# Required variables:
|
# Required variables:
|
||||||
SECRET_KEY_BASE=<your-generated-secret>
|
SECRET_KEY_BASE=<your-generated-secret>
|
||||||
TOKEN_SIGNING_SECRET=<your-generated-secret>
|
TOKEN_SIGNING_SECRET=<your-generated-secret>
|
||||||
PHX_HOST=localhost
|
DOMAIN=localhost # or PHX_HOST=localhost
|
||||||
|
|
||||||
# Optional (have defaults in docker-compose.prod.yml):
|
# Optional OIDC configuration:
|
||||||
# OIDC_CLIENT_ID=mv
|
# OIDC_CLIENT_ID=mv
|
||||||
# OIDC_BASE_URL=http://localhost:8080/auth/v1
|
# OIDC_BASE_URL=http://localhost:8080/auth/v1
|
||||||
# OIDC_REDIRECT_URI=http://localhost:4001/auth/user/rauthy/callback
|
# OIDC_CLIENT_SECRET=<from-your-oidc-provider>
|
||||||
# OIDC_CLIENT_SECRET=<from-rauthy-client>
|
# OIDC_REDIRECT_URI is auto-generated as https://{DOMAIN}/auth/user/rauthy/callback
|
||||||
|
|
||||||
# Alternative: Use _FILE variables for Docker secrets (takes priority over regular vars):
|
# Alternative: Use _FILE variables for Docker secrets (takes priority over regular vars):
|
||||||
# SECRET_KEY_BASE_FILE=/run/secrets/secret_key_base
|
# SECRET_KEY_BASE_FILE=/run/secrets/secret_key_base
|
||||||
|
|
|
||||||
|
|
@ -112,12 +112,21 @@ if config_env() == :prod do
|
||||||
You can generate one by calling: mix phx.gen.secret
|
You can generate one by calling: mix phx.gen.secret
|
||||||
""")
|
""")
|
||||||
|
|
||||||
host = System.get_env("PHX_HOST") || raise "Please define the PHX_HOST environment variable."
|
# PHX_HOST or DOMAIN can be used to set the host for the application.
|
||||||
|
# DOMAIN is commonly used in deployment environments (e.g., Portainer templates).
|
||||||
|
host =
|
||||||
|
System.get_env("PHX_HOST") ||
|
||||||
|
System.get_env("DOMAIN") ||
|
||||||
|
raise "Please define the PHX_HOST or DOMAIN environment variable."
|
||||||
|
|
||||||
port = String.to_integer(System.get_env("PORT") || "4000")
|
port = String.to_integer(System.get_env("PORT") || "4000")
|
||||||
|
|
||||||
config :mv, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
|
config :mv, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
|
||||||
|
|
||||||
# Rauthy OIDC configuration
|
# 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.
|
||||||
|
#
|
||||||
# Supports OIDC_CLIENT_SECRET or OIDC_CLIENT_SECRET_FILE for Docker secrets.
|
# 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).
|
# OIDC_CLIENT_SECRET is required only if OIDC is being used (indicated by explicit OIDC env vars).
|
||||||
oidc_base_url = System.get_env("OIDC_BASE_URL")
|
oidc_base_url = System.get_env("OIDC_BASE_URL")
|
||||||
|
|
@ -134,12 +143,15 @@ if config_env() == :prod do
|
||||||
get_env_or_file.("OIDC_CLIENT_SECRET", nil)
|
get_env_or_file.("OIDC_CLIENT_SECRET", nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
|
||||||
config :mv, :rauthy,
|
config :mv, :rauthy,
|
||||||
client_id: oidc_client_id || "mv",
|
client_id: oidc_client_id || "mv",
|
||||||
base_url: oidc_base_url || "http://localhost:8080/auth/v1",
|
base_url: oidc_base_url || "http://localhost:8080/auth/v1",
|
||||||
client_secret: client_secret,
|
client_secret: client_secret,
|
||||||
redirect_uri:
|
redirect_uri: System.get_env("OIDC_REDIRECT_URI") || default_redirect_uri
|
||||||
System.get_env("OIDC_REDIRECT_URI") || "http://#{host}:#{port}/auth/user/rauthy/callback"
|
|
||||||
|
|
||||||
# Token signing secret from environment variable
|
# Token signing secret from environment variable
|
||||||
# This overrides the placeholder value set in prod.exs
|
# This overrides the placeholder value set in prod.exs
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ defmodule Mv.Accounts.User do
|
||||||
auth_method :client_secret_jwt
|
auth_method :client_secret_jwt
|
||||||
code_verifier true
|
code_verifier true
|
||||||
|
|
||||||
|
# Request email and profile scopes from OIDC provider (required for Authentik, Keycloak, etc.)
|
||||||
|
authorization_params scope: "openid email profile"
|
||||||
|
|
||||||
# id_token_signed_response_alg "EdDSA" #-> https://git.local-it.org/local-it/mitgliederverwaltung/issues/87
|
# id_token_signed_response_alg "EdDSA" #-> https://git.local-it.org/local-it/mitgliederverwaltung/issues/87
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -69,7 +72,7 @@ defmodule Mv.Accounts.User do
|
||||||
# Default actions for framework/tooling integration:
|
# Default actions for framework/tooling integration:
|
||||||
# - :read -> Standard read used across the app and by admin tooling.
|
# - :read -> Standard read used across the app and by admin tooling.
|
||||||
# - :destroy-> Standard delete used by admin tooling and maintenance tasks.
|
# - :destroy-> Standard delete used by admin tooling and maintenance tasks.
|
||||||
#
|
#
|
||||||
# NOTE: :create is INTENTIONALLY excluded from defaults!
|
# NOTE: :create is INTENTIONALLY excluded from defaults!
|
||||||
# Using a default :create would bypass email-synchronization logic.
|
# Using a default :create would bypass email-synchronization logic.
|
||||||
# Always use one of these explicit create actions instead:
|
# Always use one of these explicit create actions instead:
|
||||||
|
|
@ -185,7 +188,9 @@ defmodule Mv.Accounts.User do
|
||||||
oidc_user_info = Ash.Changeset.get_argument(changeset, :oidc_user_info)
|
oidc_user_info = Ash.Changeset.get_argument(changeset, :oidc_user_info)
|
||||||
|
|
||||||
# Get the new email from OIDC user_info
|
# Get the new email from OIDC user_info
|
||||||
new_email = Map.get(oidc_user_info, "preferred_username")
|
# Support both "email" (standard OIDC) and "preferred_username" (Rauthy)
|
||||||
|
new_email =
|
||||||
|
Map.get(oidc_user_info, "email") || Map.get(oidc_user_info, "preferred_username")
|
||||||
|
|
||||||
changeset
|
changeset
|
||||||
|> Ash.Changeset.change_attribute(:oidc_id, oidc_id)
|
|> Ash.Changeset.change_attribute(:oidc_id, oidc_id)
|
||||||
|
|
@ -239,8 +244,11 @@ defmodule Mv.Accounts.User do
|
||||||
change fn changeset, _ctx ->
|
change fn changeset, _ctx ->
|
||||||
user_info = Ash.Changeset.get_argument(changeset, :user_info)
|
user_info = Ash.Changeset.get_argument(changeset, :user_info)
|
||||||
|
|
||||||
|
# Support both "email" (standard OIDC like Authentik, Keycloak) and "preferred_username" (Rauthy)
|
||||||
|
email = user_info["email"] || user_info["preferred_username"]
|
||||||
|
|
||||||
changeset
|
changeset
|
||||||
|> Ash.Changeset.change_attribute(:email, user_info["preferred_username"])
|
|> Ash.Changeset.change_attribute(:email, email)
|
||||||
|> Ash.Changeset.change_attribute(:oidc_id, user_info["sub"] || user_info["id"])
|
|> Ash.Changeset.change_attribute(:oidc_id, user_info["sub"] || user_info["id"])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue