Wat is OAuth 2 eigenlijk?!
In deze blogpost, wil ik je graag een overzichtelijk inkijkje geven in de OAuth 2-specificatie. Toen ik me hier mee bezig ging houden, verdwaalde ik al snel in alle verschillende aspecten die erbij komen kijken. Om er zeker van te zijn dat jij niet hetzelfde hoeft door te maken, zal ik OAuth 2 uitleggen alsof je helemaal geen technische achtergrond hebt. Aangezien er veel te behandelen valt, ga ik maar meteen van start!
De kernbegrippen van veiligheid
Als het gaat om het beveiligen van een applicatie, zijn er 2 kernbegrippen om in gedachten te houden: authenticatie en autorisatie.
Authenticatie
Met authenticatie probeer je de vraag te beantwoorden "Wie is iemand?" of "Wie is
deze gebruiker?" Je moet de kwestie bekijken vanuit het perspectief van je applicatie of je server. Door beiden wordt iets vreemds in principe altijd als een gevaar gezien. Ze weten niet wie je bent en er is geen manier waarop ze dat kunnen achterhalen, tenzij jij je kunt identificeren. Met andere woorden, authenticatie is het
proces waarmee je aan de applicatie bewijst dat je bent wie je zegt te zijn.
In een echt voorbeeld zou dit het tonen van je ID-kaart of paspoort aan de poliOpen ID Connect, die zich met dit onderwerp bezig houdt.
Autorisatie
Autorisatie is de keerzijde van authenticatie. Zodra een gebruiker heeft bewezen wie hij of zij is, moet de applicatie uitzoeken wat die gebruiker mag doen. Dat is in wezen
wat het autorisatieproces doet. Dit werkt zo'n beetje als volgt. Als je leraar bent op een school, kun je informatie opvragen over de leerlingen in je klas. Maar als directeur van de school heb je waarschijnlijk toegang tot de gegevens van alle leerlingen op
school. Je hebt meer toegang vanwege je functie.
OAuth 2 rollen
Om OAuth 2 volledig te begrijpen, moet je je bewust zijn van de volgende 4 actoren die deel uitmaken van de specificatie:
- Resource-eigenaar
- Resourceserver
- Autorisatieserver
- Client / applicatie
Net zoals hiervoor zal ik met een heel eenvoudig voorbeeld uitleggen hoe het eigenlijk werkt.
Laten we zeggen dat je een jas hebt. Aangezien je de eigenaar bent van die jas, ben je de Resource-eigenaar en de jas is de Resource die je wilt beschermen. Je wilt de jas voor de veiligheid opbergen in een locker. De locker fungeert als de Resourceserver. Jij bezit de Resourceserver niet, maar het bewaart je spullen voor jou. Je wilt de jas veilig opbergen,
zodat hij niet door iemand anders wordt gestolen. Daarom moet je een slot op de locker doen. Dat slot is de Autorisatieserver. Het regelt alles op gebied van veiligheid en zorgt ervoor dat alleen jij toegang kan krijgen tot de jas, of eventueel iemand anders die jij daar toestemming voor hebt gegeven. Als je wilt dat je vriend jouw jas uit het kastje haalt, kan die vriend gezien worden als de Client of Application actor in de OAuth-stroom. De Client handelt altijd namens de gebruiker.
Tokens
Het volgende concept waar je veel over gaat horen zijn tokens. Er zijn verschillende soorten
tokens, maar ze zijn allemaal heel eenvoudig te begrijpen. De 2 soorten tokens die je het meest tegenkomt zijn toegangs- en vernieuwingstokens.
Als het om toegangstokens gaat, heb je misschien wel eens gehoord van JWT-tokens, bearer tokens of ondoorzichtige tokens. Dat zijn eigenlijk allemaal enkel details voor de implementatie die ik niet in dit artikel ga behandelen.
In essentie is een toegangstoken iets dat je aan de resourceserver geeft om toegang te krijgen tot de voorwerpen die het voor je bewaart. Je zou toegangstokens kunnen zien als een papieren kaartje dat je op de kermis koopt. Wanneer je in een attractie wilt, toon je jouw ticket aan de persoon in de cabine, en daardoor word je doorgelaten. Je geniet van je ritje, en na afloop vervalt je ticket. Het is belangrijk om op te merken dat wie het token heeft, er de eigenaar van is. Wees er dus heel voorzichtig mee. Als iemand anders je token in handen krijgt, kan hij of zij in jouw naam toegang krijgen tot je spullen!
Vernieuwingstokens lijken sterk op toegangstokens. In wezen gebruik je ze om meer
toegangstokens te krijgen. Toegangstokens gaan meestal niet lang mee, vernieuwingstokens daarentegen hebben doorgaans een langere houdbaarheidsdatum. In ons eerdere voorbeeld van de kermis, zou een vernieuwingstoken bijvoorbeeld de creditcard van je ouders zijn, waarmee je meer kaartjes kunt kopen voor attracties.
Scope
Het volgende concept dat we moeten behandelen is scope. Een scope is in feite een beschrijving van de dingen die een persoon kan doen in een applicatie. Je kunt het zien als een functie in het echte leven (bijv. directeur of leraar op een middelbare school). Bepaalde scopes geven je meer rechten dan anderen.
Ik weet dat ik heb gezegd dat ik niet te technisch zou worden, maar als je bekend bent met Spring Security, dan kun je scopes vergelijken met wat Spring Security rollen noemt. Een scope komt één-op-één overeen met het concept van een rol. De OAuth-specificatie schrijft niet voor hoe een scope er precies uit moet zien, maar vaak zijn het door punten gescheiden Strings zoals blog.write. Google daarentegen gebruikt URL's als scope. Bijvoorbeeld:
om 'alleen lezen' toegang te geven tot iemands agenda, geven ze de scope mee in de URL: https://www.googleapis.com/auth/calendar.readonly.
Toekenningstypes
De toekenningstypes is meestal waar het voor vele mensen erg verwarrend wordt. Laten we beginnen met een overzicht van de meest gebruikte soorten toekenningstypes:
- Klantreferenties
- Autorisatiecode
- Apparaatcode
- Vernieuwen
- Wachtwoord & Impliciet
Klantreferenties is een toekenningstype dat heel vaak wordt gebruikt wanneer 2 back-endservices op een veilige manier met elkaar moeten communiceren.
De volgende is het toekenningstype autorisatiecode, wat waarschijnlijk het moeilijkste
toekenningstype is om helemaal in de vingers te krijgen. Je gebruikt dit toekenningstype als je wilt dat gebruikers inloggen via een inlogformulier dat op een browser wordt ingevuld. Als je ooit de knop 'Log in met Facebook' of 'Log in met Google' op een website hebt gebruikt, dan heb je al ervaring met een Authorization Code stroom zonder dat je er erg in had!
Het volgende type is de Apparaatcode, een vrij nieuw type in de wereld van OAuth 2.
Het wordt meestal gebruikt op toestellen met beperkte invoermogelijkheden, zoals een TV.
Bijvoorbeeld: als je wilt inloggen op Netflix krijg je, in plaats van je gebruikersnaam en wachtwoord op te geven, een pop-up met een link die een code weergeeft. Deze code moet je invoeren op de app voor mobiel.
Het toekenningstype Vernieuwen gaat meestal hand in hand met de Autorisatiecodestroom.
Aangezien toegangstokens van korte duur zijn, wil je niet dat je gebruikers eindeloos maar
moeten blijven inloggen, elke keer als hun toegangstoken verloopt. De vernieuwingsstroom gebruikt die vernieuwingstokens om nieuwe toegangstokens te verwerven wanneer de oude tokens op het punt staan te verlopen.
De laatste 2 toekenningstypes zijn Wachtwoord en Impliciet. Deze soorten toekenningstype zijn minder veilige opties en niet aan te bevelen voor het bouwen van nieuwe applicaties. We zullen deze onderwerpen kort aandoen in het volgende gedeelte, waarin de bovengenoemde toekenningstypes nader worden uitgelegd.
Autorisatiestromen
Een autorisatiestroom bevat een of meer stappen die moeten worden uitgevoerd om een
gebruiker door het systeem te laten autoriseren.
We bespreken hier 4 autorisatiestromen:
- Klantreferentiestroom
- Wachtwoordstroom
- Autorisatiecodestroom
- Impliciete stroom
Klantreferentiestroom
De Klantreferentiestroom is de stroom die het eenvoudigst is om te implementeren. Het werkt ongeveer hetzelfde als een traditionele gebruikersnaam/wachtwoord login. Gebruik deze stroom alleen als je kunt vertrouwen op de client/applicatie, aangezien de klantreferenties in de applicatie worden opgeslagen. Gebruik dit niet voor single page apps (SPA's) of mobiele apps, want kwaadwillende gebruikers kunnen de app dan deconstrueren om de referenties te bemachtigen en toegang te krijgen tot beveiligde resources.
In de meeste use-cases wordt deze stroom gebruikt om veilig te communiceren tussen 2
backend-systemen.
Dus, hoe werkt de Klantreferentiestroom? Elke applicatie heeft een klant-ID en een geheim
die op de autorisatieserver zijn geregistreerd. Deze referentie worden aangeboden aan de autorisatieserver om zo een toegangstoken te krijgen en ze worden gebruikt om de beveiligde resource te krijgen van de resourceserver. Als op een bepaald moment het toegangstoken verloopt, herhaalt het proces zich om een
nieuw token te krijgen.
Wachtwoordstroom
De Wachtwoordstroom om lijkt veel op de Klantreferentiestroom, maar is erg onveiligomdat er een derde actor bij betrokken is: de eigenlijke eindgebruiker. In plaats van een beveiligde client, waarbij we ervan uit kunnen gaan dat er een ID en geheim aan de autorisatieprovider worden aangeboden,hebben we nu te maken met een gebruiker die 'praat' met een client. In een Wachtwoordstroom geeft de gebruiker zijn of haar persoonlijke referenties op
aan de client. De client gebruikt deze gegevens vervolgens om toegangstokens te krijgen van de autorisatieserver. Dit is waarom een wachtwoordstroom niet veilig is, want we moeten er absoluut op kunnen vertrouwen dat de client de referenties niet gebruikt voor kwaadwillende doeleinden.
Uitzonderingen op het gebruik van deze stroom zijn opdrachtregelapplicaties of bedrijfswebsites waar de eindgebruiker moet kunnen vertrouwen op de client-apps die dagelijks worden gebruikt. Maar
afgezien daarvan is het niet aan te bevelen deze stroom te implementeren.
Autorisatiecodestroom
Dit is de stroom die je zeker wilt begrijpen, omdat het de stroom is die het meest wordt gebruikt bij het beveiligen van applicaties met OAuth 2. Deze stroom is iets gecompliceerder dan de eerder besproken stromen. Het is belangrijk om te begrijpen dat deze stroom vertrouwelijk en veilig is en draait om een browser.
Deze stroom maakt allerlei HTTP-omleidingen aan, en dat is waarom de browser een belangrijke actor is in deze stroom. Er is ook een back-channelverzoek (dit wordt zo genoemd omdat de gebruiker niet betrokken is bij dit deel van de stroom) waarin de client of applicatie rechtstreeks praat met de autorisatieserver. In deze stroom moet de gebruiker gewoonlijk de scopes of machtigingen goedkeuren die aan de applicatie worden verleend. Een voorbeeld hiervan is een applicatie van een derde partij die vraagt of het toegang mag hebben tot je Facebook profielfoto, nadat je hebt ingelogd met de knop 'Log in met Facebook'. Laten we de Authorisatiecodestroom eens toepassen op ons voorbeeld van de 'jas in de locker' om het beter te kunnen begrijpen.
Onze jas ligt in de locker en we willen hem uitlenen aan een vriend. Onze vriend gaat naar de (hightech) locker. De locker belt ons, want wij zijn de Resource-eigenaar. Deze oproep is een van die omleidingen waar we het eerder over hadden. Op dit moment maken we een beveiligde verbinding met de locker, die fungeert als een autorisatieserver. We kunnen nu veilig onze referenties afgeven om toestemming te verlenen het slot te openen. De autorisatieserver verstrekt dan een tijdelijke code genaamd OAuth aan onze vriend. De vriend gebruikt dan die OAuth-code om een toegangscode te krijgen om de locker te openen en mijn jas te pakken.
Impliciete stroom
De Impliciete stroom is in principe hetzelfde als de Autorisatiecodestroom, maar dan zonder de tijdelijke OAuth-code. Na het inloggen zal de autorisatieserver dus onmiddellijk
een toegangstoken terugsturen zonder dat er een back-channelverzoek nodig is. Dit is minder veilig, omdat het token kan worden onderschept via een man-in-the-middle-aanval.
Conclusie
OAuth 2 kan in eerste instantie afschrikken vanwege alle verschillende actoren die erbij betrokken zijn. Hopelijk begrijp je nu beter hoe ze op elkaar inwerken. Met deze
kennis in het achterhoofd is het veel makkelijker om de technische details te begrijpen als je in deze materie duikt.