Skip to main content

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:

4.3 Ontwerpuitgangspunten

OnderwerpTechnische keuze
AuthenticatiebronExterne identity provider, in de eerste baseline lokale Keycloak of gelijkwaardige OIDC-provider.
ApplicatieaccountIntern Users-record binnen OefenHub, gekoppeld via ExternalId.
WachtwoordenNiet opgeslagen, gewijzigd of gevalideerd door OefenHub.
E-mailadresNiet rechtstreeks in OefenHub gewijzigd; wijziging verloopt via identity-providerflow of gekoppelde accountomgeving.
RollenApplicatierollen worden intern bepaald, niet afgeleid uit clientstate of routeparameters.
RolcontextServer-side bepaald via Authorization, op basis van interne account- en roldata.
SessietoegangGeslaagde externe login is onvoldoende; intern account moet bestaan, actief zijn en een geldige OefenHub-context opleveren.
ClientstateBrowserstate, oude routeparameters of zichtbare navigatie mogen geen rolcontext afdwingen.
ProvisioningfoutGeen reguliere sessie; gebruiker krijgt een veilige accountfout/contactroute.
Account lifecycleActiveren, blokkeren, anonimiseren en interne accountverwijdering blijven OefenHub-domeinlogica.

4.4 Betrokken projecten

ProjectVerantwoordelijkheid
OefenHub.WebOIDC-callback, cookieconfiguratie, login/logout-routes, veilige foutweergave en UI-doorverwijzing.
OefenHub.IdentityInterne accountrecords, profielbasis, accountstatus, provisioning, account lifecycle en identity-gerelateerde publieke contracts.
OefenHub.AuthorizationRollen, roltoekenningen, actieve rolcontext, contextprioriteit, policies en objecttoegangscontrole.
OefenHub.RelationshipsPending relatie-uitnodigingen koppelen aan nieuw intern account wanneer Identity dit via contracts initieert.
OefenHub.CommunicationSysteemberichten aanmaken wanneer provisioning, relatie-uitnodigingen of accountflows dat functioneel vereisen.
OefenHub.AdminBeheerflows voor accountstatus, roltoekenningen, correcties en anonimisering.
OefenHub.InfrastructureTechnische OIDC-, cookie-, options-, clock- en providerintegratie waar die niet domeinspecifiek is.
OefenHub.SchedulingEventuele 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

ProjectDbContextSchemaPrimaire data-eigendom
OefenHub.IdentityIdentityDbContextidentityInterne accounts, profielbasis, accountstatus, identity-koppeling en account lifecycle.
OefenHub.AuthorizationAuthorizationDbContextauthorizationRollen, 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.

VoorbeeldAanpak
practice.ExerciseRuns.UserIdSoft link naar identity.Users.Id + snapshots voor historische context.
support.Tickets.CreatedByUserIdSoft link + snapshot van naam/rolcontext op meldmoment waar nodig.
communication.SystemMessages.RecipientUserIdSoft link naar gebruiker; autorisatie en leescontext via Communication-contracts.
authorization.UserRoles.UserIdCross-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.

OnderdeelTechnische afspraak
UsersBevat de interne accountkoppeling, profielbasis, accountstatus en ProfileAvatarId.
UserSettingsBevat gebruikersspecifieke voorkeuren, toegankelijkheidswaarden en actieve niveaucontext. Het record hoort bij Identity en is één-op-één gekoppeld aan Users.
UserSettings.SelectedTeacherLevelIdSoft link naar het catalogus-/niveauobject. De geldigheid wordt server-side gecontroleerd via catalogus-/autorisatiecontracts, niet via een harde cross-module FK.
ProfileAvatarsVooraf gedefinieerde set profielavatars met actieve/selecteerbare status. Vrije upload, externe URL's en vrije bestandsnamen zijn geen onderdeel van de eerste baseline.
Users.ProfileAvatarIdVerwijzing 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.

OnderdeelIdentityAuthorization
Intern accountEigenaarGebruikt alleen via publieke identity-contracts.
ExternalId-koppelingEigenaarGeen eigenaar.
Accountstatus IsActiveEigenaarGebruikt als voorwaarde voor autorisatiecontext.
ProfielbasisEigenaarGeen eigenaar.
ApplicatierollenGeen eigenaarEigenaar.
Niet-publieke rollenGeen eigenaarEigenaar.
Actieve rolcontextLevert accountbasisBepaalt rolcontext server-side.
Policies/objecttoegangGeen eigenaarEigenaar.

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

DoelRegel
Intern account makenGebeurt op basis van betrouwbare externe identiteit en genormaliseerde providergegevens.
ExternalId vastleggenStabiele koppeling tussen externe identiteit en intern Users-record.
Defaults initialiserenProfielbasis, instellingen en technische defaults worden veilig aangemaakt wanneer vereist.
Rollen bepalenAlleen publieke zelfregistratierollen of vooraf bestaande beheer-/uitnodigingscontext mogen worden toegepast.
Niet-publieke rollenNooit via zelfregistratie of clientinput activeren.
Pending uitnodigingenAlleen claimen/koppelen wanneer e-mailadres geverifieerd is en status, geldigheid en context server-side kloppen; nooit automatisch accepteren.
Sessie pas na succesGeen 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.

SituatieGedrag
Uitnodiging is geldig, pending, nog extern en het e-mailadres is geverifieerdIdentity initieert claimen via een Relationships-contract.
Meerdere pending externe uitnodigingen voor hetzelfde e-mailadresAlle geldige uitnodigingen worden geclaimd; de mail-link is niet de enige technische claimdrager.
Claimen luktToUserId of gelijkwaardige ontvangerkoppeling wordt gevuld en InvitationClaimed wordt vastgelegd.
Claimen lukt niet vóór mutatieUitnodiging blijft ongewijzigd pending op e-mailadres; accountprovisioning kan los daarvan slagen.
Claimen faalt ná gedeeltelijke mutatieMutaties binnen die workflow worden teruggedraaid of in beheerbare foutstatus gezet; geen onbereikbare uitnodiging.
Uitnodiging is verlopen, ingetrokken, geweigerd, geaccepteerd of al gekoppeldNiet 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

AccounttoestandTechnisch gedrag
Actief account met geldige contextReguliere OefenHub-toegang na autorisatiecontrole.
IsActive = falseGeen reguliere toegang; historie blijft bestaan.
Onvolledige provisioningGeen reguliere sessie; veilige accountfout/contactroute.
Geen rolcontextBeperkte frontpagecontext zonder rol of profiel-/contextflow, afhankelijk van situatie.
Ontbrekende verplichte profiel-/niveaucontextDoorverwijzing naar profiel-/niveauflow zonder autorisatie te verruimen.
Geanonimiseerd accountGeen 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

RegelTechnische toepassing
Leerlingrol is exclusiefLeerling mag niet gecombineerd worden met ouder/voogd, docent, beheerder of TestDocent.
Ouder/voogd, docent en beheerder mogen gecombineerd wordenAuthorization bepaalt context en beschikbare navigatie server-side.
Niet-publieke rollen zijn beheerbaarBeheerder en TestDocent worden alleen via beheer toegekend of ingetrokken.
Frontpageprioriteit bij combinatierollenBeheerder, daarna Docent, daarna Ouder/voogd.
Route is geen contextbewijsRouteparameters en menu-items mogen rolcontext niet afdwingen.
Context wordt per actie hergecontroleerdMutaties 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.

SessieonderdeelRegel
AuthenticatiecookieBevat alleen noodzakelijke sessie-/claiminformatie.
Interne user-idMag als claim/sessionwaarde aanwezig zijn, maar autorisatie wordt opnieuw server-side gevalideerd.
Rollen/permissions in cookieNiet gebruiken als bron voor actuele rechten; permissions worden server-side via Authorization en permissioncache bepaald.
Externe tokensNiet onnodig persistent opslaan.
Browser storageGeen rolcontext, autorisatiegegevens, tokens of persoonsgegevens opslaan.
ToegankelijkheidscookieAlleen 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.

GegevenTechnische bron
Eerste bruikbare loginServer-side account-/loginmetadata binnen Identity.
Laatste loginmomentServer-side update na succesvolle interne sessieverwerking.
WelkomsttekstUI-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.

StapRegel
Lokale sessie beëindigenAuthenticatiecookie en beveiligde frontendcontext verwijderen.
Externe logoutAlleen via ondersteunde identity-providerflow.
BrowserwaardenToegankelijkheidswaarden mogen blijven als zij geen persoonsgegevens of autorisatiedata bevatten.
Realtime verbindingenSignalR-verbindingen worden beëindigd of verliezen autorisatiecontext.
Actieve oefenrunLogout rondt een oefening niet automatisch af; Practice blijft bron van voortgang.
Live meekijkenLiveMonitoring 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.

OnderdeelGedrag
Intern accountBlokkeren/deactiveren en persoonsgegevens vervangen volgens vaste anonimiseringswaarden.
RollenActieve roltoekenningen beëindigen of niet langer autoriserend maken.
RelatiesActieve relaties administratief beëindigen met behoud van historie.
OefenrunsHistorisch behouden; persoonsgegevens in snapshots volgens privacyregels anonimiseren waar nodig.
Tickets/berichtenHistorische context behouden waar toegestaan, actuele persoonsgegevens verwijderen of vervangen.
Identity providerNiet 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-onderdeelRegel
Loginpagina/knopStart externe identity-providerflow.
CallbackrouteVerwerkt OIDC-callback en roept Identity/Authorization-contracts aan.
ProfielmenuToont veilige accountcontext en navigatie.
FoutpaginaToont generieke account-/initialisatiefout zonder technische details.
ViewModelsSamengesteld uit publieke contracts, niet uit entities.
BrowserstateAlleen 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.

BeheeractieEigenaar technische mutatie
Account deactiveren/heractiverenIdentity
Niet-publieke rol toekennen/intrekkenAuthorization
Gebruikersinstelling corrigerenIdentity of gebruikersinstellingendomein, afhankelijk van uiteindelijke database-informatie-eigenaarschap.
Account anonimiserenIdentity orkestreert identitydeel; andere modules verwerken eigen persoonsgegevens via contracts/workflows.
Accountgeschiedenis tonenReadmodels 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:

VeldDoel
CorrelationIdKoppelt HTTP-request, provisioning, role context en eventuele jobs.
ExternalProviderGeeft provider aan zonder tokens te loggen.
ExternalSubjectHashAlleen gehashte of gemaskeerde subjectwaarde indien nodig voor analyse.
UserIdInterne user-id wanneer bekend.
ActionBijvoorbeeld LoginCallback, ProvisioningStarted, ProvisioningFailed, RoleContextResolved.
ResultSucces, geweigerd, onvolledig, fout.
ReasonCodeFunctionele/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

FoutsituatieGebruikersgedragTechnisch gedrag
Identity-providerlogin faaltProvider- of generieke loginfout.Geen interne sessie.
Callback ongeldigGenerieke beveiligde foutpagina.Securitylog met correlation-id.
Interne account niet gevonden en provisioning niet mogelijkAccountfout/contactroute.Geen reguliere sessie; provisioningfout loggen.
Account inactiefToegang geweigerd/contactroute.Geen reguliere sessie; reden server-side loggen.
Rolcontext ontbreektBeperkte context of profiel-/contextflow.Geen automatische roltoekenning.
Niet-publieke rol uit clientinputNegeren/weigeren.Securitylog bij verdachte poging.
Provisioning gedeeltelijk misluktGeen 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

OnderwerpRegel
CredentialsVolledig buiten OefenHub.
TokensNiet onnodig opslaan; nooit loggen.
ClaimsAlleen noodzakelijke claims verwerken en normaliseren.
RollenInterne OefenHub-rollen zijn leidend.
ClientstateGeen autorisatiebron.
RouteparametersGeen autorisatiebron.
CookiesSecure, HttpOnly, SameSite volgens securityhoofdstuk.
ToegankelijkheidscookieGeen persoons-, identiteit- of autorisatiedata.
Session fixationNieuwe veilige sessie na geslaagde login/contextopbouw.
Open redirectsRetourroutes server-side valideren.

4.23 Teststrategie

TesttypeVoorbeelden
Unit testsAccountstatusregels, provisioningresultaten, statusovergangen, loginmetadata.
Contract testsIAccountProvisioningService, IIdentityAccountReader, IRoleContextResolver-interactie.
Integration testsOIDC-callbacksimulatie, provisioning met testdatabase, inactive account, ontbrekende context.
Authorization testsLeerlingrol exclusief, combinatierollen, niet-publieke rollen, contextprioriteit.
Security testsGeen tokenlogging, veilige callbackfouten, open redirect-blokkade, cookieconfiguratie.
Workflow testsPending invitation reconciliation, provisioning rollback/onvolledige toestand, logout-effecten.
Architecture testsWeb mag geen DbContext gebruiken; andere modules gebruiken alleen identity-contracts.

4.24 Implementatiechecklist

  • OIDC/Keycloak-configuratie via options met validatie bij startup.
  • IdentityDbContext gebruikt schema identity.
  • AuthorizationDbContext gebruikt schema authorization.
  • Users.ExternalId is 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 Authorization bepaald.
  • 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

PuntTe controleren / besluiten
OIDC-providerconfiguratieExacte Keycloak realm/client/scopes/claimmapping technisch vastleggen vóór implementatie.
ExternalId-vormVastleggen of ExternalId providernaam + subject combineert of provider apart wordt opgeslagen.
Provisioning en pending invitationsPer uitnodigingsflow bepalen welke stappen kritisch atomair zijn.
Federated logoutControleren welke Keycloak logoutflow wordt gebruikt en hoe fallback werkt.
TokenopslagBevestigen dat access/refresh tokens niet persistent nodig zijn voor OefenHub-flows.
Securityevent-persistentieBepalen of verdachte pogingen alleen technisch gelogd worden of ook in een domeintabel komen.
MFAOefenHub vertrouwt op provider; vastleggen of MFA verplicht wordt per omgeving of rol.