For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt, and this page is available as Markdown at /adr/0005-aai-waldur-user-sync.md.

Themelio compatibility reference. This page describes the retained Themelio implementation or its historical design. Check the parallel-operation guide before applying it to the current platform.

ADR-0005: AAI → Waldur User Synchronization

Status: Accepted

Date: 2026-05-13

Deciders: TBD

Relates to: ADR-0003 (Waldur Proposal Workflow) — users must exist in Waldur before they can be added to projects and orders.

Context

Users register and authenticate via the Pharos AAI service (Keycloak-based OIDC provider at staging.aai.pharos.grnet.gr). Waldur (dev.waldur.pharos.grnet.gr) is used for resource allocation — managing projects, orders, and memberships.

Currently, Waldur has no awareness of AAI-registered users until they interact with Waldur directly. This means a Waldur admin cannot add a user to a project or order unless that user already exists in Waldur. We need a mechanism to propagate AAI user registrations into Waldur so that users are available for membership assignment immediately.

Requirements

  1. When a new user registers on the AAI service, they should be created in Waldur automatically.
  2. Waldur admins must be able to add these users as members to projects and orders.
  3. User attributes (name, email, organization, affiliations) should be kept in sync.
  4. The mechanism should be idempotent — re-syncing an existing user must not cause errors or data loss.

Decision Drivers

  • Operational simplicity — minimize new moving parts.
  • Latency — how quickly after AAI registration does the user appear in Waldur?
  • Reliability — what happens if Waldur is temporarily unavailable?
  • Maintainability — leverage existing infrastructure where possible.

Considered Options

Option A: Waldur Identity Bridge API (push-based)

Waldur exposes POST /api/auth/identity-bridge/ — a dedicated endpoint for external identity sources to push user records. It accepts username, email, name, organization, and other attributes, and performs idempotent create-or-update. The caller needs staff or "identity manager" role with the source registered in managed_isds.

A sync service would:

  1. Query the AAI Keycloak Admin API for users (periodically or via event listener).
  2. Push each user to Waldur via the Identity Bridge endpoint.

Pros:

  • Purpose-built Waldur API for this exact use case.
  • Idempotent — safe to re-run.
  • Attribute source tracking built in (attribute_sources per field).
  • Users appear in Waldur without needing to log into Waldur.

Cons:

  • Requires Keycloak Admin API access or an event listener on the AAI side.
  • Requires an identity manager account in Waldur with the source in managed_isds.
  • We must build and operate the sync service.

Option B: Waldur OIDC provider integration (login-triggered)

Configure Waldur to use the AAI as an OIDC provider directly. Waldur's waldur_auth_social module auto-creates users on first OIDC login via create_or_update_oauth_user().

Pros:

  • No custom sync service needed.
  • Users are created with full OIDC attribute mapping.

Cons:

  • Users only exist in Waldur after they log into Waldur — does not solve the core problem.
  • Waldur admin cannot pre-assign users to projects before their first login.

Option C: Remote eduTEAMS-style pull (periodic)

Waldur has built-in background tasks (pull_remote_eduteams_users) that periodically fetch user data from a remote userinfo API. This could potentially be pointed at the AAI.

Pros:

  • Built into Waldur — no external service to build.
  • Periodic sync with configurable interval.

Cons:

  • Designed specifically for eduTEAMS API shape — may not match the AAI's Keycloak API.
  • Requires the AAI to expose a compatible userinfo endpoint per user.
  • Less control over sync logic and error handling.

Themelio is already in the authentication loop — the NextAuth signIn callback fires on every AAI login. This is the natural trigger for Waldur sync, requiring no separate sync service.

Flow:

  1. A project lead submits a project request in Themelio (project name, resources, member emails).
  2. Themelio emails the Waldur admin with the project name and member list.
  3. The Waldur admin creates the project in Waldur and adds a pending invitation for each member email.
  4. Waldur sends a customised invitation email to each member linking to Themelio (not Waldur's UI).
  5. The member clicks the link, authenticates via AAI (Keycloak), and lands on Themelio.
  6. Inside the NextAuth signIn callback, Themelio performs three steps server-side using a Waldur staff token:
    • POST /api/auth/identity-bridge/ — creates or updates the user in Waldur (idempotent).
    • GET /api/invitations/?email={email}&state=pending — retrieves matching pending invitations.
    • POST /api/invitations/{uuid}/accept/ — accepts each invitation on the user's behalf.
  7. The user is now a Waldur project member. They never see or log into Waldur.

Pros:

  • No separate sync service or Kubernetes CronJob to build and operate.
  • Real-time — membership is assigned at first login, not on a polling interval.
  • Themelio already owns user lifecycle (GitLab provisioning); Waldur sync fits the same pattern.
  • Identity Bridge call is idempotent — safe to re-run on every login.
  • Users never interact with Waldur directly.

Cons:

  • Waldur admin must still manually create the project and invitations (accepted v1 trade-off).
  • Membership is only assigned when the user first logs into Themelio — not at invitation time.
  • Requires a Waldur staff token stored server-side in Themelio; in Ansible this token is generated by waldur-offering and stored in Secret/themelio-waldur-credentials.
  • Step 2 notification trigger depends on an upstream proposal/approval flow that is not yet designed (see Open Questions).

The sync mechanism described above covers users who have already registered on the AAI. A related problem is: how does an admin invite someone who hasn't registered anywhere yet to a Waldur project or order?

The chicken-and-egg problem

The identity chain is: AAI (Keycloak) → Themelio web → GitLab → Waldur. AAI is the source of truth for identity. If someone is invited via Waldur but hasn't registered on AAI, they can't actually log into anything — they'd exist in Waldur but nowhere else, which is a broken state.

Proposed invitation flow

Waldur admin invites user by email
  → User receives email with link to Themelio (not Waldur)
    → Themelio redirects to AAI for registration / login
      → User registers on AAI (Keycloak)
        → Redirected back to Themelio
          → User accepts Terms & Conditions on Themelio
            → Account provisioned (GitLab + Waldur via AAI→Waldur sync)
              → Waldur matches pending invitation by email
                → Membership auto-assigned

Where does invitation intent live?

The invite carries intent: "this person should become a member of project X when they show up." Two options for where that intent is stored:

Invitation Option 1: Waldur holds the pending invite. Admin creates an email-based invitation in Waldur (InvitationViewSet). After the user registers via AAI and gets synced to Waldur, Waldur matches the pending invitation by email and auto-assigns membership. Waldur already has this invitation infrastructure.

  • Pro: No invitation tracking to build — reuse Waldur's existing system.
  • Pro: Waldur is already the system that manages project membership.
  • Con: The invitation email must link to Themelio (not Waldur's default), which may require Waldur configuration or a redirect layer.
  • Unknown: Does Waldur's invitation system auto-resolve pending invites when a user is created via the Identity Bridge API, or only when the user logs in via OIDC?

Invitation Option 2: Themelio holds the pending invite. We build invitation tracking in Themelio. After registration + ToC acceptance, Themelio calls the Waldur API to assign membership.

  • Pro: Full control over the invitation UX and flow.
  • Pro: Invitation email naturally links to Themelio.
  • Con: Duplicates invitation logic that Waldur already has.
  • Con: More surface area to build and maintain.

Key constraint

AAI must be the first registration step regardless of which option is chosen. The ToC acceptance should happen on Themelio since that's the user-facing product. The invitation mechanism (Waldur or Themelio) determines where the pending membership intent is stored and resolved.

Open Questions (to be answered by engineers)

These are dark areas that must be resolved before a decision can be made.

Q1: AAI Keycloak Admin API access

Can we obtain service-account credentials for the Keycloak Admin API at staging.aai.pharos.grnet.gr? Specifically, can we call GET /admin/realms/{realm}/users to list registered users and their attributes? What scopes/roles are required, and who provisions them?

Q2: Keycloak event listeners / webhooks

Does the AAI Keycloak instance support registering custom event listeners (SPI) or webhook-based notifications for user registration events? If so, what is the delivery mechanism (HTTP callback, Kafka, etc.) and who manages the listener configuration?

Q3: Waldur Identity Manager setup

The Identity Bridge API requires a Waldur account with staff or "identity manager" role and the source registered in managed_isds. Has this been configured on dev.waldur.pharos.grnet.gr? What source identifier should we use (e.g., isd:pharos-aai)?

Q4: User attribute mapping

Which AAI (Keycloak) user attributes should be propagated to Waldur? At minimum: username, email, first/last name. Are there additional attributes (organization, affiliations, eduperson_assurance, schacPersonalUniqueID) that Waldur needs for project/order membership decisions?

Q5: Sync hosting and operational ownership

Where should the sync process run?

  • Extend gitlab-root-actor (already does Waldur ↔ GitLab sync)
  • New standalone service / Kubernetes CronJob
  • Waldur-side configuration (if Option C is viable)

Who owns and monitors the sync process in production?

Q6: Failure and retry semantics

If Waldur is temporarily unavailable during a sync run, what is the expected behavior? Should we queue failed pushes and retry, or is it acceptable to catch up on the next periodic run? Is there an SLA for how quickly a new AAI user must be visible in Waldur?

Q7: User deactivation / deletion

If a user is deactivated or deleted in the AAI, should that be propagated to Waldur? What is the expected behavior — soft-disable, hard-delete, or no action?

Q8: Waldur invitation auto-resolution via Identity Bridge

Resolved: Waldur does not auto-resolve pending invitations on Identity Bridge user creation. Invitations are only accepted when the invitee logs in to Waldur via OIDC. There is no post-save signal handler that triggers acceptance on user creation.

Consequence: Themelio must accept invitations programmatically using a Waldur staff token (POST /api/invitations/{uuid}/accept/) inside the NextAuth signIn callback. In Ansible this token is generated by waldur-offering and stored in Secret/themelio-waldur-credentials. This is confirmed viable — a staff account can accept invitations on the invitee's behalf. See Option D.

Q9: Waldur invitation email customization

Can Waldur's invitation emails be configured to link to Themelio (e.g., https://themelio.example.com/invite?token=...) instead of Waldur's own UI? If not, do we need a redirect layer or a custom invitation email template?

Q10: ToC acceptance flow

Where should Terms & Conditions acceptance be recorded — AAI (Keycloak), Themelio, or both? Is there an existing ToC acceptance mechanism in the AAI registration flow, or does this need to be built into Themelio's onboarding?

Q11: Step 2 notification trigger

What upstream event notifies the Waldur admin to create the project and invitations? Is there an existing proposal/approval flow (see ADR-0003), or does the notification fire immediately on project form submission? Who approves resource allocation before the admin is notified?

Q12: Waldur invitation expiry

What is the default expiry window for Waldur pending invitations? If a user takes longer than that to log into Themelio, the invitation will have expired and step 6 of Option D will find nothing to accept. Who is responsible for re-sending expired invitations, and should Themelio surface a warning to the user in this case?

Decision

Option D (sync-on-login via Themelio auth callback) is the recommended approach for v1. It requires no new services, leverages the existing NextAuth signIn callback, and keeps Waldur invisible to end users. The Waldur admin creation step (step 3) is an accepted manual trade-off for v1. Final decision pending resolution of Q1, Q3, Q11, and Q12.

Consequences

TBD — will be filled in once a decision is made.

Page source

Source snapshot · Revision f928834c8.