Ga naar inhoud

Authenticatie (Backend)

Technisch Ontwerp

De backend gebruikt JWT-tokens (SimpleJWT) met tenant-claims om strikte scheiding tussen apotheken (tenants) af te dwingen. Een gebruiker kan lid zijn van meerdere apotheken: het lidmaatschap staat in UserTenantMembership en het token is altijd gescoped op precies een apotheek.

De login is single-factor met een verplichte, vooraf gekoppelde tweede factor. POST /api/auth/login/password/ valideert e-mail en wachtwoord en geeft alleen tokens uit als de gebruiker een bevestigde TOTP-device heeft. Er is geen losse login/totp/-stap meer: de TOTP-koppeling gebeurt eenmalig tijdens de onboarding (uitnodigingsflow), niet bij elke login. Naast wachtwoord-login bestaan er flows voor vertrouwde apparaten (HMAC challenge-response), web-inlog via QR, en OIDC (ZORG-ID, Google, Apple).

Gevoelige acties zijn niet afhankelijk van een tweede login-stap maar van een aparte step-up: een verse her-authenticatie via TOTP of biometrisch apparaatbewijs, die server-side een 30-minuten-venster opent.

  • Tenant enforcement: common/authentication.py bevat TenantEnforcedJWTAuthentication. De klasse hergebruikt het token dat al door TenantFromJWTSchemaMiddleware is gevalideerd, controleert dat tenant_id/tenant_schema op het token horen bij een actief UserTenantMembership van de gebruiker, en weigert geblacklistete tokens. Een support_mode-claim laat superusers toe om tijdelijk in een andere tenant te werken.
  • Twee-factor (TOTP): loopt via django_otp.plugins.otp_totp. De device wordt tijdens onboarding bevestigd. Wachtwoord-login en switch-tenant/ weigeren fail-closed wanneer er geen bevestigde TOTP-device is, met een uniforme foutmelding tegen user-enumeration.
  • Step-up (auth/views/step_up.py, common/stepup/): RequiresFreshStepUp is de DRF-permissie die patientdata, beheeracties, OIDC-koppelen/ontkoppelen, e-mailwijziging en erasure afschermt. Step-up gebeurt met TOTP of met een biometrische challenge-response op een step-up-bevoegd apparaat; de elevatie staat per (user, device_label) 30 minuten in Redis.
  • Brute-force-bescherming: een Redis-counter per e-mailadres (auth/services/password_lockout.py) blokkeert na het ingestelde aantal mislukte pogingen (default 5) voor PASSWORD_LOGIN_LOCKOUT_SECONDS (default 15 minuten). Er wordt altijd een dummy-hash gecheckt om timing-side-channels op user-enumeration te voorkomen.
  • Vertrouwde apparaten: een geregistreerd toestel krijgt eenmalig een HMAC-sleutel (auth/services/device_crypto.py) die versleuteld wordt opgeslagen. Het toestel logt in via challenge-response (POST /api/auth/device-login/challenge/ gevolgd door POST /api/auth/device-login/). Alleen een toestel met can_step_up=True (gekoppeld na TOTP-bevestiging) levert een biometrische factor die step-up kan openen; een toestel dat via gewone login is gebootstrapt kan dat niet.
  • Web-inlog via QR: een WebLoginIntent met challenge en korte TTL wordt door de desktop gestart; de mobiele client bevestigt via sessie of via apparaat-HMAC; de desktop wisselt een eenmalige exchange_code om voor JWT-tokens. Hetzelfde mechanisme bedient een step-up-intent waarmee de telefoon een ingelogde websessie verhoogt.
  • OIDC: ZORG-ID, Google en Apple worden vastgelegd als provider-agnostische UserOidcIdentity. Koppelen gebeurt step-up-gated; inloggen met een nog niet gekoppelde identiteit vraagt eenmalig e-mail plus wachtwoord om de identiteit aan een account te binden.
  • Sessies: SimpleJWT's OutstandingToken/BlacklistedToken tonen en trekken actieve refresh-sessies in. Bij wachtwoordwijziging of 2FA-reset worden alle refresh-tokens geblacklist en alle vertrouwde apparaten ingetrokken.
flowchart TD
    A[Client: POST /api/auth/login/password/] --> B{password_lockout.is_locked?}
    B -- ja --> B1[401 uniforme fout]
    B -- nee --> C[authenticate email + password]
    C -- fail --> C1[dummy-hash + record_failure + 401]
    C -- ok --> D{bevestigde TOTP-device?}
    D -- nee --> D1[401 uniforme fout, fail-closed]
    D -- ja --> E[services.complete_login]
    E --> F{aantal actieve lidmaatschappen?}
    F -- 1 --> G[issue_tokens_for_user + set_auth_cookies]
    F -- meerdere --> H[200 requires_tenant_choice + login_state]
    H --> I[Client: POST /api/auth/switch-tenant/ met tenant_id]
    I --> G
    G --> J[ip_tracking.check_and_record + _rid cookie]
    J --> K[200 access + refresh]

Datamodel (ERD)

Het User-model leeft in het publieke schema. Er is geen directe tenant-FK op User: de koppeling apotheek-gebruiker loopt via UserTenantMembership. TOTP-devices komen uit django_otp en sessies (OutstandingToken, BlacklistedToken) uit SimpleJWT; beide staan niet in het ERD. Het model is gesplitst in drie diagrammen: kernidentiteit, login-apparaten en onboarding/AVG.

Kernidentiteit, OIDC en lidmaatschap

erDiagram
    Apotheek ||--o{ UserTenantMembership : "lidmaatschap"
    User ||--o{ UserTenantMembership : "lid van"
    Role ||--o{ UserTenantMembership : "pending_role"
    User ||--o{ UserOidcIdentity : "gekoppelde identiteiten"

    Apotheek {
        int id PK
        string name
        string schema_name
    }
    User {
        int id PK
        string email "unique-partial, live rows"
        string first_name
        string last_name
        string phone_number "encrypted"
        date birth_date "encrypted, nullable"
        boolean is_active
        boolean is_staff
        boolean is_superuser
        string preferred_model
        boolean has_completed_global_walkthrough
        boolean has_completed_patient_walkthrough
        boolean has_completed_review_walkthrough
        datetime date_joined
        datetime deleted_at "nullable, soft-delete"
    }
    UserOidcIdentity {
        int id PK
        int user_id FK
        string provider "zorgid|google|apple|dezi"
        string subject "encrypted"
        string subject_hash "sha256, indexed, unique per provider"
        datetime created_at
    }
    UserTenantMembership {
        int id PK
        int user_id FK
        int tenant_id FK
        string status "invited|active"
        int pending_role_id FK "nullable"
        string accept_token_hash "nullable"
        datetime accept_token_expires_at "nullable"
        datetime created_at
    }

Login-apparaten en web-login

erDiagram
    User ||--o{ TrustedDevice : "bezit"
    TrustedDevice ||--o{ DeviceKnownIP : "ip-historie"
    User ||--o{ UserKnownIP : "logt in vanaf"
    User ||--o{ UserKnownBrowser : "browser-cookies"
    User ||--o{ WebLoginIntent : "approved_by / target_user"

    User {
        int id PK
        string email
        boolean is_active
    }
    TrustedDevice {
        uuid id PK
        int user_id FK
        string name
        string platform
        string device_info
        string install_id_hash "nullable, unique per user"
        text hmac_key_enc "encrypted"
        boolean can_step_up
        int failed_attempts
        datetime locked_until "nullable"
        datetime created_at
        datetime last_seen_at
        datetime revoked_at "nullable"
    }
    DeviceKnownIP {
        uuid id PK
        uuid device_id FK
        string ip_address
        datetime first_seen_at
        datetime last_seen_at
    }
    UserKnownIP {
        uuid id PK
        int user_id FK
        string ip_address
        datetime first_seen_at
        datetime last_seen_at
    }
    UserKnownBrowser {
        uuid id PK
        int user_id FK
        uuid browser_id
        datetime first_seen_at
        datetime last_seen_at
    }
    WebLoginIntent {
        uuid id PK
        string challenge
        string status "pending|approved|denied|expired|consumed"
        string purpose "login|step_up"
        int target_user_id FK "nullable"
        string target_device_label "nullable"
        int approved_by_id FK "nullable"
        string approved_via "biometric|session|password"
        datetime approved_at "nullable"
        string exchange_code_hash "nullable"
        datetime exchange_code_expires_at "nullable"
        text user_agent
        string ip_hash
        datetime created_at
        datetime expires_at
        datetime consumed_at "nullable"
    }

Onboarding, reset en AVG

erDiagram
    Apotheek ||--o{ UserInvitation : "verstuurt"
    Apotheek ||--o{ PasswordResetToken : "scope"
    Apotheek ||--o{ TotpAdminResetToken : "scope"
    User ||--o{ PasswordResetToken : "reset"
    User ||--o{ TotpAdminResetToken : "2fa-reset"
    User ||--|| ErasureRequest : "erasure"
    User ||--o{ DSARDownloadToken : "data-export"

    UserInvitation {
        uuid id PK
        int tenant_id FK
        string email
        string first_name
        string last_name
        string phone_number "encrypted, admin-vast"
        datetime sms_confirmed_at "nullable"
        date birth_date "encrypted, nullable"
        string invited_given_names
        string invited_initials
        string invited_tussenvoegsel
        string invited_surname
        int role_id FK
        string token_hash "sha256, unique"
        datetime created_at
        datetime expires_at
        datetime used_at "nullable"
        datetime cancelled_at "nullable"
        int invited_by_id FK
    }
    SmsOtpChallenge {
        int id PK
        string subject "unique"
        string code_hash "pbkdf2"
        smallint attempts
        datetime created_at
        datetime expires_at
    }
    PasswordResetToken {
        uuid id PK
        int tenant_id FK
        int user_id FK
        string token_hash "sha256"
        datetime created_at
        datetime expires_at
        datetime used_at "nullable"
    }
    TotpAdminResetToken {
        uuid id PK
        int tenant_id FK
        int user_id FK
        int reset_by_id FK
        string token_hash "sha256"
        datetime created_at
        datetime expires_at
        datetime used_at "nullable"
    }
    ErasureRequest {
        uuid id PK
        int user_id FK "one-to-one"
        int requested_by_id FK
        string status "pending|cancelled|executed"
        string reason
        datetime requested_at
        datetime scheduled_at
        datetime executed_at "nullable"
        datetime cancelled_at "nullable"
    }
    DSARDownloadToken {
        uuid token PK
        int user_id FK
        string s3_key
        string filename
        datetime created_at
        datetime expires_at
        datetime downloaded_at "nullable"
    }

API & Communicatie

De endpoints staan onder /api/auth/. JWT-tokens worden als JSON-payload en via secure cookies (access / refresh) verstuurd; het refresh-cookie is scoped op de refresh-route.

  • Login en step-up:

    • POST /api/auth/login/password/: valideert e-mail en wachtwoord. Vereist een bevestigde TOTP-device (anders uniforme 401). Bij een enkel lidmaatschap volgt { access, refresh, is_superuser? }; bij meerdere lidmaatschappen { requires_tenant_choice: true, login_state, memberships }.
    • POST /api/auth/switch-tenant/: kies een apotheek na login (met login_state) of wissel als ingelogde gebruiker van apotheek. Geeft een nieuw tenant-gescoped tokenpaar terug.
    • POST /api/auth/step-up/: verse her-authenticatie met code (TOTP) of met device_id+challenge+signature (biometrisch). Opent een 30-minuten elevatie-venster.
    • POST /api/auth/token/refresh/: vernieuwt het access token (en roteert het refresh token). Leest het refresh-cookie als er geen body is.
    • POST /api/auth/logout/: blacklist refresh- en access-token, wist de elevatie en de auth-cookies.
  • Web-inlog via QR:

    • POST /api/auth/weblogin/start/: start een login-intent; geeft intent_id + challenge (TTL 120s).
    • POST /api/auth/weblogin/step-up/start/: start een step-up-intent voor de eigen ingelogde websessie.
    • GET /api/auth/weblogin/status/{intent_id}/: poll-fallback voor de WebSocket-status.
    • POST /api/auth/weblogin/confirm/: goedkeuren/weigeren vanuit een ingelogde mobiele sessie (approved_via=session).
    • POST /api/auth/weblogin/confirm-device/: goedkeuren via vertrouwd apparaat (HMAC); approved_via=biometric alleen bij can_step_up.
    • POST /api/auth/weblogin/fetch-code/: desktop claimt na approval de eenmalige exchange_code.
    • POST /api/auth/weblogin/exchange/: desktop wisselt exchange_code om voor tokens.
  • Vertrouwde apparaten:

    • GET /api/auth/devices/: eigen toestellen (of alle tenant-toestellen met settings.manage_users).
    • POST /api/auth/devices/register/: koppel een toestel (step-up-gated); idempotent op install_id. Retourneert eenmalig { device_id, device_secret } en zet can_step_up=True.
    • POST /api/auth/devices/revoke/: trek een toestel in.
    • POST /api/auth/device-login/challenge/: start een HMAC-challenge.
    • POST /api/auth/device-login/: device-login met device_id, challenge, signature.
  • OIDC (ZORG-ID, Google, Apple):

    • GET /api/auth/zorgid/authorize/ en GET|POST /api/auth/zorgid/callback/: de bij VZVZ geregistreerde ZORG-ID-route.
    • GET /api/auth/oauth/{provider}/authorize/ en GET|POST /api/auth/oauth/{provider}/callback/: generieke provider-route.
    • POST /api/auth/oauth/link/start/: start het koppelen van een provider aan het ingelogde account (step-up-gated).
    • GET /api/auth/oauth/identities/ en DELETE /api/auth/oauth/identities/{provider}/: gekoppelde providers tonen en ontkoppelen (ontkoppelen step-up-gated).
    • POST /api/auth/oauth/exchange/: wissel de eenmalige callback-code om voor tokens.
    • POST /api/auth/oauth/link-login/: bind een nog niet gekoppelde identiteit aan een account via e-mail + wachtwoord.
    • POST /api/auth/oauth/apple/native/ en POST /api/auth/oauth/apple/native/link/: native Apple-login en -koppeling op iOS.
  • Profiel en preferences:

    • GET /api/auth/me/: profiel, actieve tenant, rollen, permissies, enabled_modules, walkthrough-state en support-vlaggen.
    • POST /api/auth/me/email-change/confirm/: bevestig een e-mailwijziging (step-up-gated).
    • GET /api/auth/memberships/: actieve lidmaatschappen van de gebruiker.
    • PATCH /api/auth/preferences/: wijzig preferred_model (pro of fast).
    • POST /api/auth/walkthrough/{global|patient|review}/{complete|reset}/: walkthrough-status.
  • Sessies en security:

    • GET /api/auth/sessions/ en POST /api/auth/sessions/revoke/: actieve refresh-sessies tonen en intrekken.
    • POST /api/auth/security/password/change/: wachtwoordwijziging; revoket alle refresh-tokens en vertrouwde apparaten.
    • POST /api/auth/security/2fa/reset/: zelf-reset van 2FA.
    • POST /api/auth/security/csp-report/: ontvangt CSP-violation reports (geen persistentie).
  • Onboarding, uitnodigingen en reset (geen auth):

    • GET /api/auth/invite/{token}/validate/: valideer uitnodiging en geef de stap terug.
    • POST /api/auth/invite/{token}/sms/start/ en .../sms/verify/: sms-bevestiging eerst; verify mint een onboarding-grant.
    • POST /api/auth/invite/{token}/accept/: stel het wachtwoord in (vereist live grant); maakt de gebruiker + onbevestigde TOTP-device aan.
    • POST /api/auth/invite/{token}/complete/: bevestig TOTP, ken het lidmaatschap + rol toe en geef tokens uit.
    • GET /api/auth/invite-membership/{token}/validate/, .../accept/, .../decline/: tweede-apotheek-uitnodiging voor een bestaande gebruiker.
    • POST /api/auth/password-reset/request/, GET .../{token}/validate/, POST .../{token}/confirm/: wachtwoord-reset (antwoord altijd 200 tegen enumeration).
    • GET /api/auth/totp-admin-reset/{token}/validate/, POST .../confirm/: door een beheerder geinitieerde 2FA-reset.
    • POST /api/auth/totp-help-request/: een gebruiker op het TOTP-scherm vraagt de beheerders om een 2FA-reset. Identificatie via login_state (bewijst dat het wachtwoord klopte); enumeration-veilig (antwoordt altijd met een neutrale bevestiging).
  • Beheer (settings.manage_users, step-up-gated):

    • GET /api/auth/admin/users/ en GET /api/auth/admin/users/{user_id}/: lijst en detail.
    • POST /api/auth/admin/users/{user_id}/totp-reset/: stuur een 2FA-resetlink.
    • POST /api/auth/admin/users/{user_id}/erasure/ en GET /api/auth/admin/erasure-requests/: erasure plannen en openstaande verzoeken tonen.
    • GET /api/auth/admin/pending-invitations/, POST /api/auth/admin/invitations/{id}/resend/, DELETE /api/auth/admin/invitations/{id}/: openstaande uitnodigingen beheren.
    • GET /api/auth/admin/audit-log/: audit-log-viewer.
  • AVG (zelfservice):

    • POST /api/auth/me/data-export/ en GET /api/auth/dsar/download/{token}/: data-export (DSAR, art. 15).
    • POST /api/auth/me/erasure-request/start/ en GET|POST /api/auth/me/erasure-request/: eigen erasure aanvragen of annuleren binnen de respijtperiode (art. 17).
  • Support (alleen superuser):

    • GET /api/auth/support/tenants/ en POST /api/auth/support/enter-tenant/: tijdelijke support-toegang met een support_mode-token.

WebSocket: het kanaal weblogin_<intent_id> (Channels) duwt de approve/deny-status naar de wachtende desktop.

Foutafhandeling & Statuscodes

  • 400 Bad Request: ontbrekende of ongeldige velden, ongeldige TOTP-code, verlopen of ongeldige login_state, mismatchende wachtwoordbevestiging.
  • 401 Unauthorized: ongeldige inloggegevens, ontbrekende bevestigde TOTP-device (fail-closed, uniform), ongeldig/verlopen/geblacklist JWT, mislukte HMAC-validatie bij device-login of weblogin/confirm-device/, ingetrokken trusted device, lockout na te veel pogingen.
  • 403 Forbidden: ontbrekende permissie, resource buiten de eigen tenant, of ontbrekende step-up-elevatie op een door RequiresFreshStepUp afgeschermde actie (patientdata, beheer, OIDC-koppelen, erasure).
  • 404 Not Found: onbekende user, intent, sessie, uitnodiging of token.
  • 410 Gone: uitnodiging, membership-uitnodiging of resetlink is gebruikt, ingetrokken of verlopen.
  • 429 Too Many Requests: device-lockout (DEVICE_LOGIN_MAX_FAILURES / DEVICE_LOGIN_LOCKOUT_SECONDS), sms-throttle of een overschreden ScopedRateThrottle. Wachtwoord-lockout wordt als uniforme 401 teruggegeven om enumeration te voorkomen.

Specifieke error_code-velden: device_revoked (toestel ingetrokken, biometrie opnieuw koppelen) en tenant_mismatch / no_membership (tenant-claim hoort niet bij een actief lidmaatschap).

Autorisatie & Beveiliging

  • JWT-claims: tokens dragen tenant_id, tenant_schema, device_label en een auth_origin. TenantFromJWTSchemaMiddleware zet het django-tenants-schema; TenantEnforcedJWTAuthentication valideert dat de gebruiker een actief UserTenantMembership voor die tenant heeft.
  • Lidmaatschap-invariant: elke rol-toekenning vereist een actief lidmaatschap voor (user, tenant). auth/services/membership.py houdt rollen en lidmaatschappen synchroon; het intrekken van een lidmaatschap trekt de rollen in die tenant mee in.
  • Token-lifetimes: ACCESS_TOKEN_LIFETIME = 30 minuten, REFRESH_TOKEN_LIFETIME = 12 uur (sliding), ROTATE_REFRESH_TOKENS = True, BLACKLIST_AFTER_ROTATION = True. Een absolute bovengrens (REFRESH_ABSOLUTE_MAX_SECONDS, 7 dagen) dwingt na een doorlopende sessie van maximaal 7 dagen een nieuwe login af. Patientdata is los hiervan afgeschermd door step-up, dus een werkdaglange sessie is aanvaardbaar.
  • Step-up: RequiresFreshStepUp (common/stepup/permissions.py) controleert de Redis-elevatie per (user, device_label). Step-up gebeurt met TOTP of met een biometrische challenge-response op een can_step_up-toestel. Een toestel krijgt can_step_up alleen na TOTP-bevestiging (onboarding) of na een verse step-up, nooit vanuit een gewone wachtwoord-login.
  • Wachtwoordbeleid: minimaal 10 tekens, minstens 1 cijfer en 1 speciaal teken, geen veelgebruikt of numeriek-only wachtwoord, niet lijkend op user-attributen, en gecontroleerd tegen HaveIBeenPwned via k-anonymity (common/validators/password.py, fail-open bij netwerkfouten). Hashing volgt de Django-default (PBKDF2-SHA256).
  • Cookies: access- en refresh-cookie zijn Secure, HttpOnly en SameSite; het refresh-cookie is path-scoped op de refresh-endpoint.
  • Encryptie: phone_number, birth_date (op User en UserInvitation), UserOidcIdentity.subject en TrustedDevice.hmac_key_enc worden met Fernet versleuteld. Tokens (token_hash, subject_hash, accept_token_hash) worden gehasht opgeslagen; de OIDC-subject wordt opgezocht via subject_hash, nooit via de plaintext.
  • IP- en device-tracking: auth/services/ip_tracking.py legt nieuwe IP's per gebruiker en per device vast en triggert bij een nieuw-apparaat-en-nieuw-IP-combinatie een audit-event plus waarschuwingsmail.
  • OIDC-binding: ZORG-ID, Google en Apple zijn optionele identiteitskoppelingen in UserOidcIdentity. Koppelen en ontkoppelen zijn step-up-gated; inloggen met een nog niet gekoppelde identiteit vereist eenmalig e-mail + wachtwoord. Een uniek subject_hash voorkomt dat dezelfde provider-identiteit aan meerdere accounts hangt. Geen Vektis.
  • AVG: dsar.py bouwt een cross-tenant export (profiel, audit, agenda, chat, review-metadata, sessies); erasure.py doet soft-delete plus pseudonimisering (sentinel-e-mail op @invalid.local, naam/telefoon/geboortedatum geleegd) na een respijtperiode en trekt apparaten, tokens en OIDC-identiteiten in.
  • Audit logging: alle gevoelige events (login, logout, step-up, device-register/revoke/lock, weblogin-approve/deny, OIDC link/unlink, tenant-switch, erasure, DSAR) gaan via common/audit/service.py met expliciete event-namen uit common/audit/events.py.

Bestandsstructuur & Verantwoordelijkheden

  • backend/auth/models.py: User, UserOidcIdentity, UserTenantMembership, TrustedDevice, DeviceKnownIP, UserKnownIP, UserKnownBrowser, WebLoginIntent, UserInvitation, SmsOtpChallenge, TotpAdminResetToken, PasswordResetToken, ErasureRequest, DSARDownloadToken.
  • backend/auth/managers.py: UserManager (filtert soft-deleted users) en AllObjectsManager.
  • backend/auth/views/auth.py: PasswordLoginStartView, SwitchTenantView, RefreshView, LogoutView.
  • backend/auth/views/step_up.py: StepUpView voor verse her-authenticatie.
  • backend/auth/views/weblogin.py: alle web-inlog endpoints (start, step-up start, status, confirm, confirm-device, fetch-code, exchange).
  • backend/auth/views/devices.py en device_login.py / device_challenge.py: apparaatbeheer en HMAC-login.
  • backend/auth/views/zorgid.py: OidcAuthorizeView, OidcCallbackView (ZORG-ID en generieke providers).
  • backend/auth/views/oauth.py: link/exchange/link-login/identities en native Apple-login.
  • backend/auth/views/me.py: MeView, PreferencesView, EmailChangeConfirmView, membership-views.
  • backend/auth/views/sessions.py, security.py, password_reset.py, invite_accept.py, invite_membership.py, totp_admin_reset.py, admin_users.py, erasure.py, dsar.py, audit_log.py, support.py, walkthrough.py.
  • backend/auth/services/jwt_tokens.py: tenant-gescoped tokens uitgeven, refresh-tokens revoken, device-label resolven.
  • backend/auth/services/membership.py: lidmaatschappen en de rol-invariant.
  • backend/auth/services/weblogin.py: lifecycle van WebLoginIntent.
  • backend/auth/services/oauth_identity.py en oauth_bridge.py: OIDC-identiteiten resolven/koppelen en eenmalige code-/ticket-uitwisseling.
  • backend/auth/services/password_lockout.py, device_crypto.py, ip_tracking.py, login_state.py, onboarding.py, onboarding_grant.py, dsar.py, erasure.py, email.py.
  • backend/auth/consumers.py: WebSocket-consumer voor weblogin_<intent_id>.
  • backend/common/authentication.py: TenantEnforcedJWTAuthentication.
  • backend/common/stepup/: step-up-permissies en het Redis-elevatie-venster.
  • backend/common/auth_cookies.py: secure access- en refresh-cookies.
  • backend/common/validators/password.py: wachtwoordvalidators incl. HIBP.
  • backend/rbac/: rollen, permissies en HasPermissionCode (apart gedocumenteerd).

Belangrijke bestanden

  • backend/auth/views/auth.py
  • backend/auth/views/step_up.py
  • backend/auth/views/weblogin.py
  • backend/auth/views/zorgid.py
  • backend/auth/services/jwt_tokens.py
  • backend/auth/services/membership.py
  • backend/common/authentication.py
  • backend/common/stepup/permissions.py
  • backend/auth/models.py

API & Communicatie (Swagger)