Identiteit, authenticatie en rolcontext
4.1 Doel van dit hoofdstuk
Dit hoofdstuk beschrijft hoe OefenHub technisch omgaat met identiteit, externe authenticatie, interne accountrecords, provisioning, sessiebeheer en rolcontext. Het hoofdstuk vult de architectuurkeuzes uit 02 Architectuuroverzicht en solution-opbouw en 03 Applicatielagen, projectstructuur en dependency-richting concreet in voor het identity-domein.
OefenHub verwerkt geen eigen wachtwoorden en valideert geen credentials. De externe identity provider is verantwoordelijk voor aanmelden, registreren, wachtwoordverwerking, resetflows en primaire authenticatiesessies. OefenHub vertaalt een geslaagde externe authenticatie pas daarna naar een interne applicatiecontext.
4.2 Belangrijke inputbronnen
Belangrijke inputbronnen zijn:
- Functioneel Ontwerp: rollen, context en autorisatie
- Functioneel Ontwerp: account, profiel en voorkeuren
- Software Requirements Specification: functionele requirements
- Database-informatie: identiteit en autorisatie
- Technisch Ontwerp: architectuuroverzicht en solution-opbouw
- Technisch Ontwerp: applicatielagen, projectstructuur en dependency-richting
4.3 Ontwerpuitgangspunten
| Onderwerp | Technische keuze |
|---|---|
| Authenticatiebron | Externe identity provider, in de eerste baseline lokale Keycloak of gelijkwaardige OIDC-provider. |
| Applicatieaccount | Intern Users-record binnen OefenHub, gekoppeld via ExternalId. |
| Wachtwoorden | Niet opgeslagen, gewijzigd of gevalideerd door OefenHub. |
| E-mailadres | Niet rechtstreeks in OefenHub gewijzigd; wijziging verloopt via identity-providerflow of gekoppelde accountomgeving. |
| Rollen | Applicatierollen worden intern bepaald, niet afgeleid uit clientstate of routeparameters. |
| Rolcontext | Server-side bepaald via Authorization, op basis van interne account- en roldata. |
| Sessietoegang | Geslaagde externe login is onvoldoende; intern account moet bestaan, actief zijn en een geldige OefenHub-context opleveren. |
| Clientstate | Browserstate, oude routeparameters of zichtbare navigatie mogen geen rolcontext afdwingen. |
| Provisioningfout | Geen reguliere sessie; gebruiker krijgt een veilige accountfout/contactroute. |
| Account lifecycle | Activeren, blokkeren, anonimiseren en interne accountverwijdering blijven OefenHub-domeinlogica. |
4.4 Betrokken projecten
| Project | Verantwoordelijkheid |
|---|---|
OefenHub.Web | OIDC-callback, cookieconfiguratie, login/logout-routes, veilige foutweergave en UI-doorverwijzing. |
OefenHub.Identity | Interne accountrecords, profielbasis, accountstatus, provisioning, account lifecycle en identity-gerelateerde publieke contracts. |
OefenHub.Authorization | Rollen, roltoekenningen, actieve rolcontext, contextprioriteit, policies en objecttoegangscontrole. |
OefenHub.Relationships | Pending relatie-uitnodigingen koppelen aan nieuw intern account wanneer Identity dit via contracts initieert. |
OefenHub.Communication | Systeemberichten aanmaken wanneer provisioning, relatie-uitnodigingen of accountflows dat functioneel vereisen. |
OefenHub.Admin | Beheerflows voor accountstatus, roltoekenningen, correcties en anonimisering. |
OefenHub.Infrastructure | Technische OIDC-, cookie-, options-, clock- en providerintegratie waar die niet domeinspecifiek is. |
OefenHub.Scheduling | Eventuele retrybare naverwerking rond accountlifecycle of uitnodigingskoppeling, uitsluitend via publieke domeincontracten. |
Er komt geen apart OefenHub.Security project voor deze baseline. Securitygerelateerde identity- en sessieregels worden verdeeld over Web, Infrastructure, Identity, Authorization en de logging-/securityhoofdstukken 19 Logging, audit, securitylogging en technische foutafhandeling en 20 Security, infrastructuur, secrets en omgevingen.
4.5 Dataverantwoordelijkheid
| Project | DbContext | Schema | Primaire data-eigendom |
|---|---|---|---|
OefenHub.Identity | IdentityDbContext | identity | Interne accounts, profielbasis, accountstatus, identity-koppeling en account lifecycle. |
OefenHub.Authorization | AuthorizationDbContext | authorization | Rollen, roltoekenningen, rolcontextregels, publieke/niet-publieke rollen en contextafleiding. |
Schema’s zijn eigendomsgrenzen, geen zelfstandige securityboundary. Tabellen en kolommen gebruiken PascalCase; schema’s gebruiken kleine letters, conform 07 Databaseontwerp, migraties, seeddata en constraints.
4.5.1 Cross-module verwijzingen
Verwijzingen vanuit andere modules naar gebruikers zijn standaard soft links naar het interne Users.Id, eventueel aangevuld met snapshotvelden. Andere modules krijgen geen directe toegang tot IdentityDbContext, Users-entities of identity-interne tabellen.
| Voorbeeld | Aanpak |
|---|---|
practice.ExerciseRuns.UserId | Soft link naar identity.Users.Id + snapshots voor historische context. |
support.Tickets.CreatedByUserId | Soft link + snapshot van naam/rolcontext op meldmoment waar nodig. |
communication.SystemMessages.RecipientUserId | Soft link naar gebruiker; autorisatie en leescontext via Communication-contracts. |
authorization.UserRoles.UserId | Cross-schema relatie binnen interne account-/autorisatiekern; technische afdwinging expliciet beoordelen in database-informatie en Technisch Ontwerp. |
Binnen Identity zelf zijn harde FK’s toegestaan. Over domeingrenzen heen zijn soft links en snapshots de standaard, tenzij een harde FK expliciet is gemotiveerd.
4.5.2 Profielbasis, UserSettings en ProfileAvatars
OefenHub.Identity is eigenaar van de profielbasis die nodig is om een interne gebruiker veilig te initialiseren, weer te geven en te personaliseren. Dit omvat Users, UserSettings en ProfileAvatars.
| Onderdeel | Technische afspraak |
|---|---|
Users | Bevat de interne accountkoppeling, profielbasis, accountstatus en ProfileAvatarId. |
UserSettings | Bevat gebruikersspecifieke voorkeuren, toegankelijkheidswaarden en actieve niveaucontext. Het record hoort bij Identity en is één-op-één gekoppeld aan Users. |
UserSettings.SelectedTeacherLevelId | Soft link naar het catalogus-/niveauobject. De geldigheid wordt server-side gecontroleerd via catalogus-/autorisatiecontracts, niet via een harde cross-module FK. |
ProfileAvatars | Vooraf gedefinieerde set profielavatars met actieve/selecteerbare status. Vrije upload, externe URL's en vrije bestandsnamen zijn geen onderdeel van de eerste baseline. |
Users.ProfileAvatarId | Verwijzing naar een actieve/selecteerbare ProfileAvatars-waarde binnen het identity-domein; bij ontbrekende of inactieve avatar gebruikt Web een veilige fallbackweergave. |
Bij provisioning wordt een UserSettings-record aangemaakt of idempotent gegarandeerd voordat reguliere gebruikersflows afhankelijk worden van instellingen. Profielavatarwijzigingen mogen uitsluitend de eigen Users.ProfileAvatarId aanpassen nadat de server-side validatie heeft bevestigd dat de gekozen avatar bestaat, actief is en selecteerbaar is.
4.6 Identity versus Authorization
Identity en Authorization blijven gescheiden.
| Onderdeel | Identity | Authorization |
|---|---|---|
| Intern account | Eigenaar | Gebruikt alleen via publieke identity-contracts. |
| ExternalId-koppeling | Eigenaar | Geen eigenaar. |
Accountstatus IsActive | Eigenaar | Gebruikt als voorwaarde voor autorisatiecontext. |
| Profielbasis | Eigenaar | Geen eigenaar. |
| Applicatierollen | Geen eigenaar | Eigenaar. |
| Niet-publieke rollen | Geen eigenaar | Eigenaar. |
| Actieve rolcontext | Levert accountbasis | Bepaalt rolcontext server-side. |
| Policies/objecttoegang | Geen eigenaar | Eigenaar. |
Een geslaagde identity-providerlogin levert dus eerst een geauthenticeerde externe identiteit op. Daarna bepaalt OefenHub via Identity of er een bruikbaar intern account bestaat. Vervolgens bepaalt Authorization welke applicatiecontext beschikbaar is.
4.7 Authenticatieflow
4.7.1 Normale loginflow
1. Gebruiker kiest inloggen in OefenHub.Web.
2. Web start de OIDC-flow richting Keycloak/externe identity provider.
3. De identity provider valideert credentials en voert eventuele MFA/providerstappen uit.
4. De identity provider stuurt de gebruiker terug naar de OefenHub callback.
5. Web valideert de OIDC-response volgens de geconfigureerde middleware.
6. Web haalt de externe subjectidentifier op.
7. Identity zoekt het interne account via Users.ExternalId.
8. Identity controleert accountstatus en provisioningstatus.
9. Authorization bepaalt server-side de beschikbare rolcontext.
10. Web maakt pas daarna een bruikbare OefenHub-sessie/context aan.
11. De gebruiker wordt doorgestuurd naar de passende veilige startcontext.
De oorspronkelijke retourroute na login mag alleen worden gebruikt wanneer na interne sessie- en autorisatiecontrole toegang tot die route bestaat. Bij ontbrekende toegang kiest OefenHub een veilige fallbackcontext.
4.7.2 Sequence-overzicht
4.8 Interne accountprovisioning
Provisioning is het aanmaken of initialiseren van een intern OefenHub-account nadat de externe identity provider succesvol heeft geauthenticeerd, maar nog geen volledig bruikbaar intern account bestaat.
4.8.1 Provisioningdoelen
| Doel | Regel |
|---|---|
| Intern account maken | Gebeurt op basis van betrouwbare externe identiteit en genormaliseerde providergegevens. |
ExternalId vastleggen | Stabiele koppeling tussen externe identiteit en intern Users-record. |
| Defaults initialiseren | Profielbasis, instellingen en technische defaults worden veilig aangemaakt wanneer vereist. |
| Rollen bepalen | Alleen publieke zelfregistratierollen of vooraf bestaande beheer-/uitnodigingscontext mogen worden toegepast. |
| Niet-publieke rollen | Nooit via zelfregistratie of clientinput activeren. |
| Pending uitnodigingen | Alleen claimen/koppelen wanneer e-mailadres geverifieerd is en status, geldigheid en context server-side kloppen; nooit automatisch accepteren. |
| Sessie pas na succes | Geen reguliere OefenHub-sessie zolang provisioning niet betrouwbaar is afgerond. |
4.8.2 Provisioningtransactie
De kern van provisioning is kritisch. Als het interne account niet consistent kan worden aangemaakt of gecontroleerd, krijgt de gebruiker geen reguliere toegang.
Kritiek binnen provisioning:
- Users-record aanmaken of ophalen;
- ExternalId uniek koppelen;
- accountstatus bepalen;
- verplichte basisinstellingen initialiseren;
- minimale profiel-/contextvoorwaarde vastleggen;
- technische logging met correlation-id.
Als één van deze kernstappen faalt, wordt de provisioning teruggedraaid of blijft het account in een expliciete onvolledige/geblokkeerde toestand die geen reguliere sessie toestaat.
4.8.3 Pending externe uitnodigingen bij provisioning
Pending relatie-uitnodigingen naar een nog onbekend e-mailadres kunnen na provisioning aan het nieuwe interne account worden geclaimd. Dit is een aparte reconciliation-stap direct na succesvolle provisioning. De stap draait alleen wanneer het e-mailadres door de identity provider als geverifieerd bekend is.
| Situatie | Gedrag |
|---|---|
| Uitnodiging is geldig, pending, nog extern en het e-mailadres is geverifieerd | Identity initieert claimen via een Relationships-contract. |
| Meerdere pending externe uitnodigingen voor hetzelfde e-mailadres | Alle geldige uitnodigingen worden geclaimd; de mail-link is niet de enige technische claimdrager. |
| Claimen lukt | ToUserId of gelijkwaardige ontvangerkoppeling wordt gevuld en InvitationClaimed wordt vastgelegd. |
| Claimen lukt niet vóór mutatie | Uitnodiging blijft ongewijzigd pending op e-mailadres; accountprovisioning kan los daarvan slagen. |
| Claimen faalt ná gedeeltelijke mutatie | Mutaties binnen die workflow worden teruggedraaid of in beheerbare foutstatus gezet; geen onbereikbare uitnodiging. |
| Uitnodiging is verlopen, ingetrokken, geweigerd, geaccepteerd of al gekoppeld | Niet opnieuw activeren en niet claimen. |
Claimen accepteert de relatie nooit automatisch en maakt nog geen systeembericht als acceptatie-ingang aan. De eerste relatiebeslissing gebeurt in de centrale onboardingflow wanneer de gebruiker nog niet volledig is onboarded, of later via de reguliere mailbox-/relatieflow wanneer onboarding al afgerond is.
Een nieuwe gebruiker mag niet worden geblokkeerd puur omdat een niet-kritieke uitnodigingsreconciliatie tijdelijk faalt. Maar wanneer OefenHub een uitnodigingsstatus wijzigt, moeten die stappen atomair of beheerbaar consistent worden uitgevoerd.
4.9 Accountstatus en toegang
| Accounttoestand | Technisch gedrag |
|---|---|
| Actief account met geldige context | Reguliere OefenHub-toegang na autorisatiecontrole. |
IsActive = false | Geen reguliere toegang; historie blijft bestaan. |
| Onvolledige provisioning | Geen reguliere sessie; veilige accountfout/contactroute. |
| Geen rolcontext | Beperkte frontpagecontext zonder rol of profiel-/contextflow, afhankelijk van situatie. |
| Ontbrekende verplichte profiel-/niveaucontext | Doorverwijzing naar profiel-/niveauflow zonder autorisatie te verruimen. |
| Geanonimiseerd account | Geen reguliere toegang; historische verwijzingen tonen geanonimiseerde waarden. |
Accountstatus wordt server-side gelezen. Clientstate, cookies buiten de beveiligde sessie, routeparameters en zichtbare navigatie mogen accountstatus nooit overrulen.
4.10 Rolcontextbepaling
Rolcontext wordt niet door Identity maar door Authorization bepaald. Identity levert de interne accountbasis, waarna Authorization server-side bepaalt welke context bruikbaar is.
4.10.1 Rolcontextregels
| Regel | Technische toepassing |
|---|---|
| Leerlingrol is exclusief | Leerling mag niet gecombineerd worden met ouder/voogd, docent, beheerder of TestDocent. |
| Ouder/voogd, docent en beheerder mogen gecombineerd worden | Authorization bepaalt context en beschikbare navigatie server-side. |
| Niet-publieke rollen zijn beheerbaar | Beheerder en TestDocent worden alleen via beheer toegekend of ingetrokken. |
| Frontpageprioriteit bij combinatierollen | Beheerder, daarna Docent, daarna Ouder/voogd. |
| Route is geen contextbewijs | Routeparameters en menu-items mogen rolcontext niet afdwingen. |
| Context wordt per actie hergecontroleerd | Mutaties en detailinzage voeren opnieuw server-side autorisatie uit. |
4.10.2 Contextresolver
Authorization levert een publieke contextresolver, bijvoorbeeld:
public interface IRoleContextResolver
{
Task<RoleContextResult> ResolveAsync(
Guid userId,
RoleContextRequest request,
CancellationToken cancellationToken);
}
RoleContextResult bevat geen gevoelige identity-providerdata en geen client-vertrouwde autorisatiewaarden. Het resultaat is een server-side applicatiecontext die door Web gebruikt wordt om routes, layouts en navigatie samen te stellen.
4.10.3 Permissionopbouw na contextbepaling
Na account- en rolcontextbepaling vraagt Web/Authorization de effectieve permissions van de gebruiker op. Deze permissions worden afgeleid uit actieve UserRoles, actieve Roles, actieve RolePermissions en actieve Permissions.
Regels:
- login pre-warmt de permissioncache waar mogelijk;
- de cachewaarde bevat geen identity-providerclaims, maar alleen interne permission-codes;
- de cache wordt niet in browser storage opgeslagen;
- een cache miss tijdens een check laadt permissions opnieuw uit de database;
- rolwijzigingen, publieke rolkeuze en accountstatuswijzigingen legen de permissioncache voor de betrokken gebruiker.
De sessie mag dus een interne user-id bevatten als ingang voor server-side lookup, maar niet de actuele rechten als niet-hercontroleerde waarheid.
4.11 Sessieopbouw
Na succesvolle interne contextbepaling bouwt OefenHub.Web een applicatiesessie op.
| Sessieonderdeel | Regel |
|---|---|
| Authenticatiecookie | Bevat alleen noodzakelijke sessie-/claiminformatie. |
| Interne user-id | Mag als claim/sessionwaarde aanwezig zijn, maar autorisatie wordt opnieuw server-side gevalideerd. |
| Rollen/permissions in cookie | Niet gebruiken als bron voor actuele rechten; permissions worden server-side via Authorization en permissioncache bepaald. |
| Externe tokens | Niet onnodig persistent opslaan. |
| Browser storage | Geen rolcontext, autorisatiegegevens, tokens of persoonsgegevens opslaan. |
| Toegankelijkheidscookie | Alleen technische voorkeuren, geen identiteit/rollen/autorisatie. |
Web mag op basis van de sessie UI samenstellen, maar backend-acties blijven afhankelijk van server-side permissionchecks in Authorization en domeinspecifieke objectcontroles in de eigenaarmodule.
4.12 Eerste login en terugkerende login
De applicatieschil toont na login een begroeting. De bepaling of het om de eerste bruikbare OefenHub-login gaat, mag niet uitsluitend uit browserstate komen.
| Gegeven | Technische bron |
|---|---|
| Eerste bruikbare login | Server-side account-/loginmetadata binnen Identity. |
| Laatste loginmoment | Server-side update na succesvolle interne sessieverwerking. |
| Welkomsttekst | UI-afleiding in Web op basis van veilige account- en auth-sessiecontext. |
Een externe identity-providerlogin telt pas als bruikbare OefenHub-login wanneer de interne sessieopbouw is gelukt. Bij het aanmaken van de OefenHub-auth-sessie wordt vóór het bijwerken van LastSeenAtUtc bepaald of dit de eerste bruikbare OefenHub-bezoeksessie is. Alleen wanneer LastSeenAtUtc dan nog leeg is, krijgt de auth-sessie een tijdelijke first-visit-context, bijvoorbeeld oefenhub:first_visit_session = true en oefenhub:first_visit_session_date = yyyy-MM-dd. De applicatieschil toont Welkom <voornaam> alleen zolang die vlag aanwezig is én de datum gelijk is aan de huidige serverdatum; daarna wordt Welkom terug, <voornaam> getoond. Deze context is UX-state in de beveiligde sessie en geen nieuw databaseveld.
4.13 Logout en sessiebeëindiging
Logout beëindigt minimaal de lokale OefenHub-applicatiesessie. Afhankelijk van de identity-providerconfiguratie kan daarnaast een federated logout richting de identity provider worden gestart.
| Stap | Regel |
|---|---|
| Lokale sessie beëindigen | Authenticatiecookie en beveiligde frontendcontext verwijderen. |
| Externe logout | Alleen via ondersteunde identity-providerflow. |
| Browserwaarden | Toegankelijkheidswaarden mogen blijven als zij geen persoonsgegevens of autorisatiedata bevatten. |
| Realtime verbindingen | SignalR-verbindingen worden beëindigd of verliezen autorisatiecontext. |
| Actieve oefenrun | Logout rondt een oefening niet automatisch af; Practice blijft bron van voortgang. |
| Live meekijken | LiveMonitoring beëindigt sessies veilig of markeert reconnect/ended-state volgens eigen regels. |
4.14 Account lifecycle
4.14.1 Deactiveren
Wanneer een account wordt gedeactiveerd, blokkeert Identity reguliere toegang. Andere modules behouden historische data volgens hun eigen domeinregels. Deactiveren verwijdert geen oefenruns, tickets, berichten, relatiehistorie of audit/history.
4.14.2 Heractiveren
Heractiveren is een expliciete beheerhandeling. Na heractiveren worden rollen, relaties en contexten opnieuw server-side beoordeeld. Oude clientstate of oude routecontext mag geen toegang herstellen.
4.14.3 Accountverwijdering en anonimisering
OefenHub verwijdert niet rechtstreeks het identity-provideraccount. Interne accountverwijdering leidt tot een interne anonimiseer- en opruimflow.
| Onderdeel | Gedrag |
|---|---|
| Intern account | Blokkeren/deactiveren en persoonsgegevens vervangen volgens vaste anonimiseringswaarden. |
| Rollen | Actieve roltoekenningen beëindigen of niet langer autoriserend maken. |
| Relaties | Actieve relaties administratief beëindigen met behoud van historie. |
| Oefenruns | Historisch behouden; persoonsgegevens in snapshots volgens privacyregels anonimiseren waar nodig. |
| Tickets/berichten | Historische context behouden waar toegestaan, actuele persoonsgegevens verwijderen of vervangen. |
| Identity provider | Niet rechtstreeks door OefenHub verwijderd, tenzij dit via een expliciet Technisch Ontwerp-besluit wordt ondersteund. |
4.15 Publieke contracts van Identity
Andere modules gebruiken uitsluitend publieke contracts van OefenHub.Identity. Zij gebruiken geen IdentityDbContext en geen Users-entity.
Voorbeelden van identity-contracts:
public interface ICurrentUserContextReader
{
Task<CurrentUserContext?> GetCurrentUserAsync(CancellationToken cancellationToken);
}
public interface IIdentityAccountReader
{
Task<AccountStatusResult> GetAccountStatusAsync(
Guid userId,
CancellationToken cancellationToken);
}
public interface IAccountProvisioningService
{
Task<ProvisioningResult> ResolveOrProvisionAsync(
ExternalIdentityContext externalIdentity,
CancellationToken cancellationToken);
}
public interface IAccountLifecycleService
{
Task<DeactivateAccountResult> DeactivateAsync(
DeactivateAccountCommand command,
CancellationToken cancellationToken);
}
Contractmodellen staan onder Contracts/Models en bevatten alleen gegevens die buiten het identity-domein nodig zijn. Interne profiel-, provider- of databasevelden worden niet als publieke DTO gelekt.
4.16 Interne projectstructuur
Voor OefenHub.Identity geldt de standaard moduleopbouw.
OefenHub.Identity/
Contracts/
Models/
Enums/
Data/
IdentityDbContext.cs
Entities/
Configurations/
Migrations/
Models/
Commands/
ReadModels/
Enums/
Services/
Interfaces/
Events/
Helpers/
Extensions/
Niet alle mappen hoeven direct aanwezig te zijn. Mappen worden toegevoegd wanneer er concrete inhoud is.
4.17 Relatie met Web
OefenHub.Web is verantwoordelijk voor de UI- en middlewarekant van login/logout, maar niet voor de identity-businessregels.
| Web-onderdeel | Regel |
|---|---|
| Loginpagina/knop | Start externe identity-providerflow. |
| Callbackroute | Verwerkt OIDC-callback en roept Identity/Authorization-contracts aan. |
| Profielmenu | Toont veilige accountcontext en navigatie. |
| Foutpagina | Toont generieke account-/initialisatiefout zonder technische details. |
| ViewModels | Samengesteld uit publieke contracts, niet uit entities. |
| Browserstate | Alleen UI-state en toegestane technische voorkeuren. |
Web mag geen IdentityDbContext, AuthorizationDbContext, Users-entity of roltoekenningstabellen gebruiken.
4.18 Relatie met Authorization
Authorization gebruikt identity-contracts om accountstatus en basale accountcontext op te halen. Identity gebruikt authorization-contracts alleen wanneer een accountflow expliciet rolcontext nodig heeft, bijvoorbeeld bij provisioning of beheerfeedback.
Circular dependencies worden vermeden. Indien nodig wordt een klein contract of requestmodel verplaatst naar het project dat functioneel eigenaar is van de betreffende beslissing.
4.19 Relatie met Admin
Beheerderacties rond accounts lopen via Admin-schermen, maar muteren identity- en authorizationdata via publieke contracts van de eigenaarmodules.
| Beheeractie | Eigenaar technische mutatie |
|---|---|
| Account deactiveren/heractiveren | Identity |
| Niet-publieke rol toekennen/intrekken | Authorization |
| Gebruikersinstelling corrigeren | Identity of gebruikersinstellingendomein, afhankelijk van uiteindelijke database-informatie-eigenaarschap. |
| Account anonimiseren | Identity orkestreert identitydeel; andere modules verwerken eigen persoonsgegevens via contracts/workflows. |
| Accountgeschiedenis tonen | Readmodels uit eigenaarmodules; geen centrale auditmodule. |
4.20 Logging en correlation
Identity- en loginflows zijn securitygevoelig. Logging moet herleidbaar zijn zonder credentials, tokens of persoonsgegevens onnodig vast te leggen.
Verplichte logvelden bij relevante identity-events:
| Veld | Doel |
|---|---|
CorrelationId | Koppelt HTTP-request, provisioning, role context en eventuele jobs. |
ExternalProvider | Geeft provider aan zonder tokens te loggen. |
ExternalSubjectHash | Alleen gehashte of gemaskeerde subjectwaarde indien nodig voor analyse. |
UserId | Interne user-id wanneer bekend. |
Action | Bijvoorbeeld LoginCallback, ProvisioningStarted, ProvisioningFailed, RoleContextResolved. |
Result | Succes, geweigerd, onvolledig, fout. |
ReasonCode | Functionele/technische reden zonder gevoelige details. |
Niet loggen:
- wachtwoorden;
- OIDC authorization codes;
- access tokens;
- refresh tokens;
- ID tokens;
- volledige raw claims;
- volledige cookies;
- persoonsgegevens zonder concrete noodzaak.
Verdachte toegangspogingen, ongeldige callbacks, state/nonce-fouten en herhaald geweigerde contextresoluties worden securitygericht gelogd conform 19 Logging, audit, securitylogging en technische foutafhandeling.
4.21 Foutafhandeling
| Foutsituatie | Gebruikersgedrag | Technisch gedrag |
|---|---|---|
| Identity-providerlogin faalt | Provider- of generieke loginfout. | Geen interne sessie. |
| Callback ongeldig | Generieke beveiligde foutpagina. | Securitylog met correlation-id. |
| Interne account niet gevonden en provisioning niet mogelijk | Accountfout/contactroute. | Geen reguliere sessie; provisioningfout loggen. |
| Account inactief | Toegang geweigerd/contactroute. | Geen reguliere sessie; reden server-side loggen. |
| Rolcontext ontbreekt | Beperkte context of profiel-/contextflow. | Geen automatische roltoekenning. |
| Niet-publieke rol uit clientinput | Negeren/weigeren. | Securitylog bij verdachte poging. |
| Provisioning gedeeltelijk mislukt | Geen reguliere toegang. | Rollback of expliciete onvolledige/geblokkeerde toestand. |
Foutmeldingen aan gebruikers mogen geen providerdetails, tokens, claims, interne identifiers, databasefouten of autorisatiedetails lekken.
4.22 Securitygrenzen
| Onderwerp | Regel |
|---|---|
| Credentials | Volledig buiten OefenHub. |
| Tokens | Niet onnodig opslaan; nooit loggen. |
| Claims | Alleen noodzakelijke claims verwerken en normaliseren. |
| Rollen | Interne OefenHub-rollen zijn leidend. |
| Clientstate | Geen autorisatiebron. |
| Routeparameters | Geen autorisatiebron. |
| Cookies | Secure, HttpOnly, SameSite volgens securityhoofdstuk. |
| Toegankelijkheidscookie | Geen persoons-, identiteit- of autorisatiedata. |
| Session fixation | Nieuwe veilige sessie na geslaagde login/contextopbouw. |
| Open redirects | Retourroutes server-side valideren. |
4.23 Teststrategie
| Testtype | Voorbeelden |
|---|---|
| Unit tests | Accountstatusregels, provisioningresultaten, statusovergangen, loginmetadata. |
| Contract tests | IAccountProvisioningService, IIdentityAccountReader, IRoleContextResolver-interactie. |
| Integration tests | OIDC-callbacksimulatie, provisioning met testdatabase, inactive account, ontbrekende context. |
| Authorization tests | Leerlingrol exclusief, combinatierollen, niet-publieke rollen, contextprioriteit. |
| Security tests | Geen tokenlogging, veilige callbackfouten, open redirect-blokkade, cookieconfiguratie. |
| Workflow tests | Pending invitation reconciliation, provisioning rollback/onvolledige toestand, logout-effecten. |
| Architecture tests | Web mag geen DbContext gebruiken; andere modules gebruiken alleen identity-contracts. |
4.24 Implementatiechecklist
- OIDC/Keycloak-configuratie via options met validatie bij startup.
-
IdentityDbContextgebruikt schemaidentity. -
AuthorizationDbContextgebruikt schemaauthorization. -
Users.ExternalIdis uniek per providercontext. - Geslaagde externe login geeft geen toegang zonder interne accountcontrole.
- Provisioning heeft expliciete transaction boundary.
- Provisioningfouten leveren geen reguliere sessie op.
- Accountstatus wordt server-side gecontroleerd bij sessieopbouw.
- Rolcontext wordt via
Authorizationbepaald. - Niet-publieke rollen kunnen niet via zelfregistratie of profielwijziging ontstaan.
- Browserstate bevat geen rolcontext of autorisatiedata.
- Logout beëindigt lokale sessie en relevante realtime contexten.
- Tokens, credentials en raw claims worden niet gelogd.
- Retourroutes worden server-side gevalideerd.
- Identity- en authorization-contracts zijn publiek; implementatieclasses en entities blijven internal.
4.25 Implementatieverificaties
| Punt | Te controleren / besluiten |
|---|---|
| OIDC-providerconfiguratie | Exacte Keycloak realm/client/scopes/claimmapping technisch vastleggen vóór implementatie. |
| ExternalId-vorm | Vastleggen of ExternalId providernaam + subject combineert of provider apart wordt opgeslagen. |
| Provisioning en pending invitations | Per uitnodigingsflow bepalen welke stappen kritisch atomair zijn. |
| Federated logout | Controleren welke Keycloak logoutflow wordt gebruikt en hoe fallback werkt. |
| Tokenopslag | Bevestigen dat access/refresh tokens niet persistent nodig zijn voor OefenHub-flows. |
| Securityevent-persistentie | Bepalen of verdachte pogingen alleen technisch gelogd worden of ook in een domeintabel komen. |
| MFA | OefenHub vertrouwt op provider; vastleggen of MFA verplicht wordt per omgeving of rol. |