End-to-End IAM, del 3: Hur reconciliation och JML fungerar
Det här är del 3, och sista delen, i serien om end-to-end-iam. Del 1 beskrev varför Elytra och Ascendra hålls helt separata. Del 2 beskrev hur Ascendra faktiskt provisionerar konton i Entra ID via Microsoft Graph. Den här delen handlar om vad som händer när något går fel längs vägen, och varför en enda mekanism inte räcker för att hålla HR och Entra ID i synk över tid.
JML i praktiken: mer än en teoretisk modell
Joiner-Mover-Leaver (JML) beskriver de tre livscykelhändelser varje anställning går igenom: någon börjar (Joiner), byter roll (Mover), eller slutar (Leaver). Det låter enkelt i teorin, men i praktiken är det precis det problem alla organisationer med fler än en handfull anställda måste lösa säkert:
- Joiner: rätt konton måste skapas automatiskt, med rätt åtkomst från dag ett, varken för mycket eller för lite.
- Mover: när någon byter roll eller avdelning måste både ny åtkomst läggas till och gammal åtkomst som inte längre är motiverad tas bort. Det senare glöms ofta bort, vilket över tid gör att användare gradvis samlar på sig fler och fler behörigheter de en gång behövde men inte längre använder.
- Leaver: när någon slutar måste åtkomsten försvinna snabbt och säkert. Det här är den mest säkerhetskritiska av de tre. En kvarlämnad, aktiv identitet efter en avslutad anställning är ett direkt intrångsspår.
end-to-end-iam-projektet modellerar hela det här flödet, från händelsen i Elytra till ett faktiskt skapat, uppdaterat eller inaktiverat konto i Entra ID, via den händelsestyrda provisionering som del 2 beskriver.
Varför händelser inte räcker på egen hand
Den händelsestyrda modellen (Elytra publicerar, Ascendra läser och agerar) fungerar bra i normalfallet. Problemet är att den bygger på antagandet att varje händelse faktiskt når fram och bearbetas korrekt, exakt en gång. I verkligheten kan flera saker gå fel:
- Ett Graph-anrop kan misslyckas på grund av ett tillfälligt nätverksfel eller att Entra ID:s gränssnitt är hårt belastat just då.
- Ascendra kan vara nere precis när en händelse borde ha bearbetats.
- Ett fel i koden kan göra att en händelse bearbetas felaktigt utan att själva anropet tekniskt sett misslyckas, vilket är precis vad som hände i fallet nedan.
Två mekanismer i Ascendra hanterar de här riskerna på olika sätt: Retry och reconciliation.
Retry är det enkla fallet: om ett Graph-anrop misslyckas görs ett nytt automatiskt försök, i stället för att felet bara tystnar. Det fångar upp tillfälliga, övergående problem.
Reconciliation är det grövre skyddsnätet. Var 15:e minut jämför Ascendra sin egen bild av verkligheten (byggd från händelserna i utkorgen) med vad Entra ID faktiskt visar via Graph, och rättar till eventuella skillnader. Det spelar ingen roll om skillnaden beror på en tappad händelse, ett driftstopp i Ascendra eller ett fel i koden. Reconciliation bryr sig bara om slutresultatet: stämmer Entra ID överens med det HR-systemet säger just nu, eller inte.
Det är den kombinationen, händelsestyrd provisionering i realtid plus periodisk reconciliation som skyddsnät, som gör att systemet klarar sig även när enskilda steg fallerar.
Resync-felet: när skyddsnätet fick ett hål
Utöver den automatiska reconciliationen finns en manuell knapp i Ascendra, Resync, som tvingar fram en ny synk av en specifik identitet mot Entra ID, till exempel om en tidigare synk misslyckats. Resync läser inte något nytt från HR-systemet. Den tar det Ascendra redan tror sig veta om identiteten och skickar det till Entra igen via Graph.
Det var precis den knappen som orsakade det allvarligaste felet i hela projektet. Koden bakom Resync skickade alltid accountEnabled = true till Graph, oavsett om personen faktiskt fortfarande var anställd. Konkret gick det till så här:
- En anställd sades upp. Kontot inaktiverades korrekt i Entra ID, precis som det ska.
- Jag klickade på Resync för samma person igen, bara för att testa funktionen.
- Kontot återaktiverades i Entra ID, trots att HR-systemet Elytra fortfarande visade personen som uppsagd.
Grundorsaken var att koden bakom Resync hade hårdkodat antagandet att en manuell synk alltid innebär “sätt kontot till aktivt”, i stället för att fråga HR-systemet om personens faktiska aktuella status. Det är en klassisk variant av att en genväg som fungerar i det vanliga fallet (någon klickar Resync för att felsöka en aktiv anställd) tyst blir farlig i undantagsfallet (någon klickar Resync för en redan avslutad anställd).
Lösningen var att ta bort antagandet helt: accountEnabled härleds alltid från HR-systemets aktuella anställningsstatus, oavsett vilken del av koden som faktiskt gör Graph-anropet, vare sig det är den vanliga händelsestyrda provisioneringen, en Retry, en reconciliation-körning eller en manuell Resync. Ett och samma ställe avgör sanningen om kontot ska vara aktivt eller inte, och alla vägar in går via det stället.
Varför den här typen av fel är farlig
Det som gör felet i Resync värt att dröja vid är inte koden i sig, utan vad den visar om hur säkerhetsbrister uppstår i IAM-system. Felet krävde ingen extern attack, inget intrång, inget avancerat. Det krävde bara att någon (i det här fallet jag själv, i testsyfte) klickade en helt legitim knapp i fel ordning. En tidigare anställds åtkomst hade kunnat återställas med en enda extra knapptryckning, utan att någon annan del av systemet flaggade det som fel, eftersom Graph-anropet i sig lyckades perfekt. Det var bara innehållet i anropet som var fel.
Det är också precis den typen av fel som reconciliation är designad att fånga upp, även om det ursprungliga felet i koden bakom Resync aldrig hade rättats till: nästa reconciliation hade jämfört Entra ID mot HR-systemet, sett att kontot var aktivt trots att personen var uppsagd i Elytra, och korrigerat det inom 15 minuter. Reconciliation fungerar alltså både som ett skydd mot tappade händelser och som ett skyddsnät mot fel i annan kod, så länge reconciliation-logiken själv har rätt källa till sanning.
Sammanfattning av serien
Genom de här tre delarna har vi gått från arkitekturbeslutet att hålla HR och IGA helt separata (del 1), via hur den separationen faktiskt provisionerar riktiga konton i Entra ID genom Microsoft Graph (del 2), till hur systemet fångar upp sina egna fel genom periodisk reconciliation och en tydlig, enhetlig regel för vad som är sant om en identitet (del 3).
Den röda tråden är densamma hela vägen: så länge en integration är simulerad är det lätt att tro att allt fungerar. Det är först när den kopplas mot ett riktigt system, och man aktivt letar efter var den kan gå sönder, som de verkliga problemen dyker upp, och det är då ett IAM-system börjar likna ett riktigt sådant, inte bara en demo.