Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 63 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,40 @@ DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres
# ───────────────────────────────────────────────────────────────────────────────
# AUTHENTICATION (OIDC)
# ───────────────────────────────────────────────────────────────────────────────
# Supports any OpenID Connect provider (tested with ZITADEL)
# Supports any OpenID Connect provider (recommended: Logto — https://logto.io/)
#
# ┌─── Logto Setup Guide ───────────────────────────────────────────────────────┐
# │ │
# │ 1. Create a "Traditional Web" application in Logto Console │
# │ 2. Set the redirect URI to: <your-domain>/auth/login-callback │
# │ 3. Copy the App ID → PUBLIC_OIDC_CLIENT_ID │
# │ 4. Copy the App Secret → OIDC_CLIENT_SECRET │
# │ │
# │ Custom JWT Claims (required): │
# │ Go to Logto Console → JWT Customizer → User access token and add: │
# │ │
# │ const getCustomJwtClaims = async ({ │
# │ token, context, environmentVariables, api │
# │ }) => { │
# │ return { │
# │ roles: context.user.roles, │
# │ mfa: context.user.mfaVerificationFactors, │
# │ password: context.user.hasPassword, │
# │ sso_identities: context.user.ssoIdentities, │
# │ social_identities: │
# │ Object.keys(context.user.identities) ?? [] │
# │ }; │
# │ } │
# │ │
# │ This exposes user roles, MFA status, and identity provider info │
# │ in the access token so the application can use them for │
# │ authorization and UI display. │
# │ │
# └─────────────────────────────────────────────────────────────────────────────┘

# [REQUIRED] OIDC Discovery URL (usually ends with /.well-known/openid-configuration)
# Local mock: http://localhost:8080/default/.well-known/openid-configuration
# Production: https://your-oidc-provider.com
# Logto: https://<your-tenant>.logto.app/oidc/.well-known/openid-configuration
PUBLIC_OIDC_AUTHORITY=http://localhost:8080/default/.well-known/openid-configuration

# [REQUIRED] OAuth2 Client ID from your OIDC provider
Expand All @@ -46,12 +75,34 @@ PUBLIC_OIDC_CLIENT_ID=default
# OIDC_CLIENT_SECRET=your-client-secret

# [REQUIRED] OAuth2 scopes to request
# Must include "openid" and "offline_access" at minimum
OIDC_SCOPES="openid profile offline_access address email family_name gender given_name locale name phone preferred_username urn:zitadel:iam:org:projects:roles urn:zitadel:iam:user:metadata urn:zitadel:iam:org:project:id:zitadel:aud"

# [OPTIONAL] JWT claim path for user roles (ZITADEL-specific format shown)
# These scopes control ID token and userinfo claims, NOT the access token.
# Access token claims are set via the JWT Customizer script above.
#
# openid — required by OIDC spec
# profile — user name, picture, etc.
# offline_access — enables refresh tokens
# email — user email address
# phone — user phone number
# identity — Logto scope: linked social & SSO identities
# role — Logto scope: user roles for authorization
# custom_data — Logto scope: custom user data
OIDC_SCOPES="openid profile offline_access email phone identity role custom_data"

# [OPTIONAL] JWT claim path for user roles
# Used to determine team member permissions
OIDC_ROLE_CLAIM=urn:zitadel:iam:org:project:275671427955294244:roles
# With the custom JWT claims above, roles are available at the "roles" claim
OIDC_ROLE_CLAIM=roles

# [OPTIONAL] Machine-to-machine app credentials for Logto Management API
# Required for user impersonation feature (token exchange via subject tokens)
# Create an M2M app in Logto Console with access to the Management API resource
# OIDC_M2M_CLIENT_ID=your-m2m-app-id
# OIDC_M2M_CLIENT_SECRET=your-m2m-app-secret
# [OPTIONAL] Logto Management API resource indicator
# For Logto Cloud: https://<tenant-id>.logto.app/api
# For self-hosted: check your Logto API Resources settings (e.g. https://default.logto.app/api)
# If not set, derived from PUBLIC_OIDC_AUTHORITY by replacing /oidc with /api
# OIDC_M2M_RESOURCE=https://default.logto.app/api

# ───────────────────────────────────────────────────────────────────────────────
# APPLICATION FEATURES
Expand All @@ -64,6 +115,10 @@ OIDC_ROLE_CLAIM=urn:zitadel:iam:org:project:275671427955294244:roles
# [OPTIONAL] Enable global user notes visible to team members (default: false)
PUBLIC_GLOBAL_USER_NOTES_ACTIVE=true

# [OPTIONAL] Show OIDC migration notice before login (default: false)
# TEMPORARY: Enable during identity provider migration to warn users
# PUBLIC_OIDC_MIGRATION_NOTICE=false

# [OPTIONAL] Maximum character length for application motivation text (default: 1200)
PUBLIC_MAX_APPLICATION_TEXT_LENGTH=1200

Expand Down Expand Up @@ -195,4 +250,4 @@ SMTP_FROM_NAME=MUNIFY Delegator
# PUBLIC_VERSION=1.0.0

# [AUTO] Git commit SHA
# PUBLIC_SHA=abc123
# PUBLIC_SHA=abc123
2 changes: 1 addition & 1 deletion .github/actions/setup-bun/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ runs:
steps:
- uses: oven-sh/setup-bun@v2

- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: |
~/.bun/install/cache
Expand Down
20 changes: 10 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,38 @@ jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-bun
- run: bun run format:check

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-bun
- run: bunx svelte-kit sync
- run: bun run lint

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-bun
- run: bunx svelte-kit sync
- run: bun run test

i18n:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-bun
- run: bun run i18n:check
- run: bun run i18n:validate

security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-bun
- uses: aquasecurity/[email protected]
with:
Expand All @@ -67,7 +67,7 @@ jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-bun
- run: bunx svelte-kit sync
- run: bunx prisma generate
Expand All @@ -81,7 +81,7 @@ jobs:
env:
NODE_OPTIONS: '--max-old-space-size=8192'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: ./.github/actions/setup-bun
- run: bunx svelte-kit sync
- run: bunx prisma generate
Expand All @@ -101,7 +101,7 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_URL: ${{ secrets.SENTRY_URL }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- uses: docker/setup-buildx-action@v3

Expand Down Expand Up @@ -198,7 +198,7 @@ jobs:
needs: [format, lint, test, i18n, security, typecheck]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
fetch-depth: 2

Expand Down Expand Up @@ -285,7 +285,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5

- name: Extract version
id: version
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ MUNify DELEGATOR is a SvelteKit-based application for managing Model United Nati
- **Backend**: Node.js with SvelteKit server routes
- **Database**: PostgreSQL via Prisma ORM
- **GraphQL**: Pothos schema builder with graphql-yoga server, Houdini client
- **Auth**: OpenID Connect (OIDC) - tested with ZITADEL
- **Auth**: OpenID Connect (OIDC) - recommended provider: Logto
- **i18n**: Paraglide-JS for internationalization (default locale: German)
- **Runtime**: Bun (package manager and development runtime)
- **Observability**: OpenTelemetry tracing support
Expand Down Expand Up @@ -281,7 +281,7 @@ Required variables (see `.env.example`):
- `PUBLIC_OIDC_AUTHORITY` - OIDC provider URL
- `PUBLIC_OIDC_CLIENT_ID` - OAuth client ID
- `OIDC_SCOPES` - OAuth scopes (must include `openid`)
- `OIDC_ROLE_CLAIM` - JWT claim for roles (ZITADEL format)
- `OIDC_ROLE_CLAIM` - JWT claim for roles
- `CERTIFICATE_SECRET` - Secret for signing participation certificates
- OpenTelemetry vars (optional): `OTEL_ENDPOINT_URL`, `OTEL_SERVICE_NAME`

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ bunx lefthook install

## Deployment

The easiest way to deploy delegator on your own hardware is to use our provided [docker images](https://hub.docker.com/r/deutschemodelunitednations/delegator). You can find an example docker compose file in the [example](./example/) directoy. Please note that delegator relies on an [OIDC](https://auth0.com/intro-to-iam/what-is-openid-connect-oidc) issuer to be connected and properly configured. We recommend [ZITADEL](https://zitadel.com/) but any issuer of your choice will work. There are some additional instructions on this topic to be found in the example compose file.
The easiest way to deploy delegator on your own hardware is to use our provided [docker images](https://hub.docker.com/r/deutschemodelunitednations/delegator). You can find an example Docker Compose file in the [example](./example/) directory. Please note that delegator relies on an [OIDC](https://auth0.com/intro-to-iam/what-is-openid-connect-oidc) issuer to be connected and properly configured. We recommend [Logto](https://logto.io/) but any issuer of your choice will work. There are some additional instructions on this topic to be found in the example compose file.

## FAQ

Expand Down
4 changes: 2 additions & 2 deletions WARP.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ bun run machine-translate

#### Authentication & Authorization

- **OIDC Integration**: Uses OpenID Connect (designed for ZITADEL but supports any OIDC provider)
- **OIDC Integration**: Uses OpenID Connect (recommended: Logto, but supports any OIDC provider)
- **Context Building**: `src/api/context/context.ts` constructs request context with OIDC data
- **Permission System**: CASL ability-based authorization
- Definitions in `src/api/abilities/entities/`
Expand Down Expand Up @@ -221,5 +221,5 @@ Example: `feat(delegation): add nation preference selection`
Use provided Docker images: `deutschemodelunitednations/delegator`

- Example compose file in `example/` directory
- Requires external OIDC provider (ZITADEL recommended)
- Requires external OIDC provider (Logto recommended)
- Environment variables must be configured (see `.env.example`)
4 changes: 2 additions & 2 deletions dev.docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ services:
# "given_name": "Delegator Jr.",
# "preferred_username": "delegatoruser_123",
# "locale": "de",
# "urn:zitadel:iam:org:project:275671427955294244:roles": {"admin": {}}
# "roles": ["admin"]
# }
mockoidc:
image: ghcr.io/navikt/mock-oauth2-server:2.1.10
Expand Down Expand Up @@ -143,7 +143,7 @@ services:
- '1025:1025' # SMTP server
- '8025:8025' # Web UI
environment:
MP_SMTP_AUTH_ACCEPT_ANY: 1
MP_SMTP_AUTH: 'mailpit:mailpit'
MP_SMTP_AUTH_ALLOW_INSECURE: 1

# Bugsink - Self-hosted error tracking (Sentry-compatible)
Expand Down
27 changes: 27 additions & 0 deletions messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
"accessFlowSaved": "Zugangsausweis zugewiesen und Anwesenheit erfasst.",
"accountExists": "Konto vorhanden",
"accountHolder": "Kontoinhaber*in",
"accountUpdateSuccessBackupCodes": "Deine Backup-Codes wurden erfolgreich aktualisiert.",
"accountUpdateSuccessEmail": "Deine E-Mail-Adresse wurde erfolgreich aktualisiert.",
"accountUpdateSuccessMfa": "Deine Authenticator-App-Einstellungen wurden erfolgreich aktualisiert.",
"accountUpdateSuccessPasskey": "Deine Passkey-Einstellungen wurden erfolgreich aktualisiert.",
"accountUpdateSuccessPassword": "Dein Passwort wurde erfolgreich geändert.",
"accountUpdateSuccessUsername": "Dein Benutzername wurde erfolgreich aktualisiert.",
"actions": "Aktionen",
"activeConferences": "Aktive Konferenzen",
"activeMembers": "Aktive Mitglieder",
Expand Down Expand Up @@ -193,6 +199,7 @@
"authErrorTechnicalTitle": "Anmeldung fehlgeschlagen",
"authErrorTokenExchangeDescription": "Der Anmeldevorgang konnte nicht abgeschlossen werden. Das kann passieren, wenn die Anmeldung zu lange gedauert hat oder ein Netzwerkproblem aufgetreten ist.",
"authErrorTryAgain": "Erneut versuchen",
"authenticatorApp": "Authenticator-App",
"author": "Autor*in",
"authorization": "Berechtigung",
"averageAgeOnlyApplied": "Konferenz-Altersdurchschnitt (nur angemeldete)",
Expand All @@ -201,6 +208,7 @@
"backToConferencePapers": "Zurück zu Konferenz-Papieren",
"backToDashboard": "Zurück zum Dashboard",
"backToHome": "Zurück zur Homepage",
"backupCodes": "Backup-Codes",
"badgeDataDescription": "CSV-Exporte für den Druck von Namensschildern. Enthält Teilnehmernamen, Länder und Einwilligungsstatus.",
"badgeDataTitle": "Namensschild-Daten",
"bankName": "Name der Bank",
Expand Down Expand Up @@ -319,6 +327,9 @@
"certificateTemplate": "Basis-PDF für das Zertifikat",
"changeAnswer": "Antwort ändern",
"changeDelegationPreferences": "Wünsche anpassen",
"changeEmail": "Ändern",
"changePassword": "Ändern",
"changeUsername": "Ändern",
"changesSuccessful": "Die Änderungen wurden erfolgreich gespeichert.",
"chaseSeedData": "CHASE Seed-Daten",
"checkEmails": "E-Mails prüfen",
Expand Down Expand Up @@ -824,6 +835,7 @@
"male": "Männlich",
"malformedPlaceholders": "Textbaustein enthält fehlerhafte Platzhalter. Stelle sicher, dass alle \\{\\{ ein passendes \\}\\} haben.",
"manageConference": "Konferenzeinstellungen und Teilnehmende verwalten",
"managePasskeys": "Verwalten",
"managePreferences": "Einstellungen verwalten",
"manageSnippets": "Textbausteine verwalten",
"markAsProblem": "Als Problem markieren",
Expand All @@ -834,6 +846,19 @@
"mediaConsentStatus": "Fotostatus",
"members": "Mitglieder",
"membersPerDelegation": "Plätze pro Delegation",
"migrationNoticeContinue": "Weiter zum Login",
"migrationNoticeFaqEmailPasswordBody": "Melde dich einfach mit deiner bisherigen E-Mail-Adresse und deinem Passwort an. Alles sollte wie gewohnt funktionieren.",
"migrationNoticeFaqEmailPasswordTitle": "Ich habe mich mit E-Mail und Passwort angemeldet \u2014 was muss ich tun?",
"migrationNoticeFaqHelpBody": "Bei Problemen kontaktiere uns bitte unter <a href=\"mailto:{email}\" class=\"link\">{email}</a>.",
"migrationNoticeFaqHelpTitle": "Ich brauche Hilfe \u2014 wen kann ich kontaktieren?",
"migrationNoticeFaqIdentityProviderBody": "Wir haben unseren Identit\u00e4tsanbieter \u2014 das System, das deine Anmeldedaten verwaltet \u2014 von <a href=\"https://zitadel.com\" target=\"_blank\" class=\"link\">Zitadel</a> auf <a href=\"https://logto.io\" target=\"_blank\" class=\"link\">Logto</a> umgestellt. Das verbessert die Sicherheit und Zuverl\u00e4ssigkeit. Deine Kontodaten wurden \u00fcbertragen.",
"migrationNoticeFaqIdentityProviderTitle": "Was hat sich ge\u00e4ndert und warum?",
"migrationNoticeFaqNewUserBody": "Wenn du dich zum ersten Mal registrierst, betrifft dich diese \u00c4nderung nicht. Erstelle einfach auf der n\u00e4chsten Seite ein neues Konto.",
"migrationNoticeFaqNewUserTitle": "Ich bin neu und m\u00f6chte mich registrieren \u2014 was muss ich tun?",
"migrationNoticeFaqSocialBody": "Social-Login-Konten (z. B. Google) konnten nicht automatisch migriert werden. Bitte melde dich stattdessen mit deiner E-Mail-Adresse an. Falls du noch kein Passwort festgelegt hast, nutze die Option \u201ePasswort vergessen\u201c auf der Login-Seite, um eines zu erstellen.",
"migrationNoticeFaqSocialTitle": "Ich habe Google oder einen anderen Social-Login verwendet \u2014 was muss ich tun?",
"migrationNoticeSubtitle": "Wir haben unser Login-System aktualisiert. Bitte lies die folgenden Informationen, bevor du fortf\u00e4hrst.",
"migrationNoticeTitle": "Wichtig: Login-System aktualisiert",
"missingInformation": "Fehlende Informationen",
"motivation": "Motivation",
"myAccount": "Mein Konto",
Expand Down Expand Up @@ -1036,6 +1061,7 @@
"participation": "Teilnahme",
"participationCount": "Teilnahmen",
"participationType": "Teilnahmeart",
"passkeys": "Passkeys",
"password": "Passwort",
"pastConferences": "Vergangene Konferenzen",
"pathDoesNotExist": "Der Pfad {path} konnte nicht gefunden werden ({error})",
Expand Down Expand Up @@ -1382,6 +1408,7 @@
"snippets": "Textbausteine",
"specialRole": "Spezielle Rolle",
"specialWishes": "Spezielle Wünsche",
"ssoIdentities": "Verknüpfte Konten",
"start": "Start",
"startAssignment": "Assistent starten",
"startCamera": "Kamera starten",
Expand Down
Loading
Loading