Säkra molnbaserade applikationer med OAuth 2.0 och OpenID Connect
En komplett guide för att säkra dina molnapplikationer med OAuth 2.0 och OpenID Connect och hur man erbjuder en fantastisk användarupplevelse med autentisering och auktorisering.
Introduktion
Molnbaserade applikationer är trenden nuförtiden. Även om applikationstypen varierar (webb, mobil, desktop, etc.), har de alla en molnbakgrund som tillhandahåller tjänster som lagring, beräkning och databaser. Oftast behöver dessa applikationer autentisera användare och auktorisera dem för att få tillgång till vissa resurser.
Även om egentillverkade autentiserings- och auktoriseringsmekanismer är möjliga, har säkerhet blivit en av de främsta bekymren vid utveckling av molnapplikationer. Lyckligtvis har vår bransch stridsprövade standarder som OAuth 2.0 och OpenID Connect för att hjälpa oss att implementera säker autentisering och auktorisering.
Detta inlägg har följande antaganden:
- Du har en grundläggande förståelse av applikationsutveckling (webb, mobil eller någon annan typ).
- Du har hört talas om begreppen autentisering och auktorisering.
- Du har hört talas om OAuth 2.0 och OpenID Connect.
Ja, "hört" är tillräckligt för både 2 och 3. Detta inlägg kommer att använda exempel från verkligheten för att förklara begrepp och illustrera processen med diagram. Låt oss börja!
OAuth 2.0 vs. OpenID Connect
Om du är bekant med OAuth 2.0 och OpenID Connect kan du också fortsätta läsa eftersom vi kommer att täcka några exempel från verkligheten i detta avsnitt; om du är ny till dessa standarder är det också säkert att fortsätta eftersom vi kommer introducera dem på ett enkelt sätt.
OAuth 2.0
OAuth 2.0 är ett auktoriseringsramverk som tillåter en applikation att få begränsad tillgång till skyddade resurser på en annan applikation å en användares eller applikationens vägnar. De flesta populära tjänster som Google, Facebook och GitHub använder OAuth 2.0 för social inloggning (t.ex. "Logga in med Google").
Till exempel, du har en webbapplikation MyApp som vill ha tillgång till användarens Google Drive. Istället för att be användaren om deras Google Drive-referenser kan MyApp använda OAuth 2.0 för att begära tillgång till Google Drive å användarens vägnar. Här är ett förenklat flöde:
I detta flöde ser MyApp aldrig användarens Google Drive-referenser. Istället får den en åtkomsttoken från Google som tillåter den att få tillgång till Google Drive å användarens vägnar.
I OAuth 2.0-termer är MyApp kunden, Google är både auktoriseringsservern och resursservern för enkelhetens skull. I verkligheten har vi ofta separata auktoriserings- och resursservrar för att erbjuda en enkel inloggning (SSO) upplevelse. Till exempel, Google är auktoriseringsservern och det kan ha flera resursservrar som Google Drive, Gmail och YouTube.
Observera att det faktiska auktoriseringsflödet är mer komplext än detta. OAuth 2.0 har olika bidragstyper, omfattningar och andra begrepp som du bör vara medveten om. Vi lämnar det åt sidan för nu och går vidare till OpenID Connect.
OpenID Connect (OIDC)
OAuth 2.0 är bra för auktorisering, men du kanske märker att det inte har ett sätt att identifiera användaren (dvs. autentisering). OpenID Connect är ett identitetslager överst på OAuth 2.0 som lägger till autentiseringsfunktioner.
I exemplet ovan behöver MyApp veta vem användaren är innan den initierar auktoriseringsflödet. Observera att det finns två användare inblandade här: användaren av MyApp och användaren av Google Drive. I detta fall behöver MyApp veta användaren av sin egen applikation.
Låt oss se ett enkelt exempel, förutsatt att användare kan logga in på MyApp med användarnamn och lösenord:
Eftersom vi autentiserar användaren av vår egen applikation är det vanligtvis inte nödvändigt att be om tillstånd som Google gjorde i OAuth 2.0-flödet. Under tiden behöver vi något som kan identifiera användaren. OpenID Connect introducerar begrepp som ID-token och användarinfoändpunkt för att hjälpa oss med det.
Du kan märka att identitetsleverantören (IdP) är en ny fristående deltagare i flödet. Det är detsamma som auktoriseringsservern i OAuth 2.0, men för bättre tydlighet använder vi termen IdP för att visa att det är ansvaret för användarautentisering och identitetshantering.
När ditt företag växer kan du ha flera applikationer som delar samma användardatabas. Precis som OAuth 2.0 tillåter OpenID Connect dig att ha en enda auktoriseringsserver som kan autentisera användare för flera applikationer. Om användaren redan är inloggad i en applikation behöver de inte ange sina referenser igen när en annan applikation dirigerar dem till IdP. Flödet kan ske automatiskt utan användarinteraktion. Detta kallas enkel inloggning (SSO).
Återigen är detta ett högst förenklat flöde och det finns fler detaljer under ytan. För nu går vi vidare till nästa avsnitt för att undvika informationsöverbelastning.
Applikationstyper
I föregående avsnitt använde vi webbapplikationer som exempel medan världen är mer diversifierad än så. För en identitetsleverantör spelar det exakta programspråket, ramverket eller plattformen du använder egentligen ingen roll. I praktiken är en märkbar skillnad om applikationen är en offentlig klient eller en privat (betrodd) klient:
- Offentlig klient: En klient som inte kan hålla sina referenser konfidentiella, vilket innebär att resursägaren (användaren) kan komma åt dem. Till exempel, en webbapplikation som körs i en webbläsare (t.ex. en enkel-sidig applikation).
- Privat klient: En klient som har förmågan att hålla sina referenser konfidentiella utan att exponera dem för (resursägare) användare. Till exempel, en webbapplikation som körs på en server (t.ex. server-sidig webbapplikation) eller en API-tjänst.
Med detta i åtanke, låt oss se hur OAuth 2.0 och OpenID Connect kan användas i olika applikationstyper.
"Applikation" och "klient" kan användas omväxlande i denna posts sammanhang.
Webbapplikationer som körs på en server
Applikationen körs på en server och betjänar HTML-sidor till användare. Många populära webbramverk som Express.js, Django och Ruby on Rails faller in i denna kategori; och backend-för-front-ramverk (BFF) som Next.js och Nuxt.js är också inkluderade. Dessa applikationer har följande egenskaper:
- Eftersom en server enbart tillåter privat åtkomst (det finns inget sätt för allmänna användare att se serverns kod eller referenser) anses den vara en privat klient.
- Det övergripande användarautentiseringsflödet är detsamma som det vi diskuterade i avsnittet "OpenID Connect".
- Applikationen kan använda ID-token som utfärdats av identitetsleverantören (dvs. OpenID Connect-leverantören) för att identifiera användaren och visa användarspecifikt innehåll.
- För att hålla applikationen säker använder applikationen vanligtvis auktoriseringskodflödet för användarautentisering och för att erhålla token.
Under tiden kan applikationen behöva få tillgång till andra interna API-tjänster i en mikrotjänstarkitektur; eller det är en monolitisk applikation som behöver åtkomstkontroll för olika delar av applikationen. Vi kommer att diskutera detta i avsnittet "Skydda din API".
Enkel-sidiga applikationer (SPAs)
Applikationen körs i en användares webbläsare och kommunicerar med servern via API:er. React, Angular och Vue.js är populära ramverk för att bygga SPAs. Dessa applikationer har följande egenskaper:
- Eftersom applikationens kod är synlig för allmänheten anses den vara en offentlig klient.
- Det övergripande användarautentiseringsflödet är detsamma som det vi diskuterade i avsnittet "OpenID Connect".
- Applikationen kan använda ID-token som utfärdats av identitetsleverantören (dvs. OpenID Connect-leverantören) för att identifiera användaren och visa användarspecifikt innehåll.
- För att hålla applikationen säker använder applikationen vanligtvis auktoriseringskodflödet med PKCE (Proof Key for Code Exchange) för användarautentisering och för att erhålla token.
Vanligtvis behöver SPAs få tillgång till andra API-tjänster för datainsamling och uppdatering. Vi kommer att diskutera detta i avsnittet "Skydda din API".
Mobila applikationer
Applikationen körs på en mobil enhet (iOS, Android, etc.) och kommunicerar med servern via API:er. I de flesta fall har dessa applikationer samma egenskaper som SPAs.
Maskin-till-maskin (M2M) applikationer
Maskin-till-maskin applikationer är klienter som körs på en server (maskin) och kommunicerar med andra servrar. Dessa applikationer har följande egenskaper:
- Liksom webbapplikationer som körs på en server är M2M-applikationer privata klienter.
- Applikationen kanske inte behöver identifiera användaren; istället behöver den autentisera sig själv för att få tillgång till andra tjänster.
- Applikationen kan använda åtkomsttoken utfärdad av identitetsleverantören (dvs. OAuth 2.0-leverantören) för att få tillgång till andra tjänster.
- För att hålla applikationen säker använder applikationen vanligtvis klientemanda-flödet för att erhålla åtkomsttoken.
När man får tillgång till andra tjänster kan applikationen behöva tillhandahålla åtkomsttoket i begäransrubriken. Vi kommer att diskutera detta i avsnittet "Skydda din API".
Applikationer som körs på IoT-enheter
Applikationen körs på en IoT-enhet (t.ex. smarta hemanordningar, wearables, etc.) och kommunicerar med servern via API:er. Dessa applikationer har följande egenskaper:
- Beroende på enhetens kapacitet kan det vara en offentlig eller privat klient.
- Det totala autentiseringsflödet kan vara annorlunda än det vi diskuterade i avsnittet "OpenID Connect" beroende enhetens kapacitet. Till exempel kan vissa enheter sakna en skärm för användare att ange sina referenser på.
- Om enheten inte behöver identifiera användaren kanske den inte behöver använda ID-tokens eller användarinfoändpunkter; istället kan den behandlas som en maskin-till-maskin (M2M) applikation.
- För att hålla applikationen säker kan applikationen använda auktoriseringskodflödet med PKCE (Proof Key for Code Exchange) för användarautentisering och erhållande av token eller klientemanda-flödet för att erhålla åtkomsttoken.
När man kommunicerar med servern kan enheten behöva tillhandahålla åtkomsttoken i begäransrubriken. Vi kommer att diskutera detta i avsnittet "Skydda din API".
Skydda din API
Med OpenID Connect är det möjligt att identifiera användaren och hämta användarspecifik data via ID-tokens eller användarinfoändpunkter. Denna process kallas autentisering. Men du kanske inte vill exponera alla dina resurser till alla autentiserade användare, till exempel, endast administratörer kan få tillgång till användarhanteringssidan.
Detta är där auktorisering spelar en roll. Kom ihåg att OAuth 2.0 är ett auktoriseringsramverk, och OpenID Connect är ett identitetslager överst på OAuth 2.0; vilket innebär att du också kan använda OAuth 2.0 när OpenID Connect redan är på plats.
Låt oss minnas exemplet vi använde i avsnittet "OAuth 2.0": MyApp vill få tillgång till användarens Google Drive. Det är inte praktiskt att låta MyApp få tillgång till alla användarens filer i Google Drive. Istället bör MyApp explicit påstå vad den vill ha tillgång till (t.ex. skrivskyddad åtkomst till filer i en specifik mapp). I OAuth 2.0-termer kallas detta en omfattning.
Du kan se termen "tillstånd" användas omväxlande med "omfattning" i sammanhanget med OAuth 2.0 eftersom ibland "omfattning" är tvetydig för icke-tekniska användare.
När användaren ger tillgång till MyApp utfärdar auktoriseringsservern en åtkomsttoken med den begärda omfattningen. Åtkomsttoken är sedan skickad till resursservern (Google Drive) för att få tillgång till användarens filer.
Naturligtvis kan vi ersätta Google Drive med våra egna API-tjänster. Till exempel, MyApp behöver få tillgång till OrderService för att hämta användarens orderhistorik. Denna gång, eftersom användarautentisering redan är gjord av identitetsleverantören och både MyApp och OrderService är under vår kontroll, kan vi hoppa över att be användaren att ge tillgång; MyApp kan direkt skicka begäran till OrderService med åtkomsttoken utfärdad av identitetsleverantören.
Åtkomsttoken kan innehålla en read:order
omfattning för att indikera att användaren kan läsa sin orderhistorik.
Nu, låt oss säga att användaren av misstag anger en administrativ sidas URL i webbläsaren. Eftersom användaren inte är en administratör finns det ingen admin
-omfattning i åtkomsttoken. OrderService kommer att avvisa begäran och returnera ett felmeddelande.
I det här fallet kan OrderService returnera en 403 Forbidden statuskod för att indikera att användaren inte är auktoriserad att få tillgång till den administrativa sidan.
För maskin-till-maskin (M2M) applikationer är ingen användare inblandad i processen. Applikationer kan direkt begära åtkomsttoken från identitetsleverantören och använda dem för att få tillgång till andra tjänster. Samma koncept gäller: åtkomsttoken innehåller de nödvändiga omfattningarna för att få tillgång till resurserna.
Auktoriseringsdesign
Vi kan se två viktiga saker att överväga när vi designar auktorisering för att skydda dina API-tjänster:
- Omfattningar: Definiera vad klienten kan få tillgång till. Omfattningar kan vara finfördelade (t.ex.
read:order
,write:order
) eller mer allmänna (t.ex.order
) beroende på dina krav. - Åtkomstkontroll: Definiera vem som kan ha specifika omfattningar. Till exempel kan bara administratörer ha
admin
omfattningen.
När det gäller åtkomstkontroll är några populära tillvägagångssätt:
- Rollbaserad åtkomstkontroll (RBAC): Tilldela roller till användare och definiera vilka roller som kan få tillgång till vilka resurser. Till exempel, en administratörsroll kan få tillgång till admin sidan.
- Attributhanterad åtkomstkontroll (ABAC): Definiera policyer baserat på attribut (t.ex. användarens avdelning, plats, etc.) och fatta åtkomstkontrollbeslut baserat på dessa attribut. Till exempel, en användare från "Engineering" avdelningen kan få tillgång till ingenjörssidan.
Det är värt att nämna att för båda tillvägagångssätten är det standardmässigt att verifiera åtkomstkontroll genom att kontrollera åtkomsttokenets omfattningar, istället för roller eller attribut. Roller och attribut kan vara mycket dynamiska och omfattningar är mer statiska vilket gör dem mycket enklare att hantera.
För detaljerad information om åtkomstkontroll kan du hänvisa till RBAC och ABAC: Åtkomstkontrollmodellerna du bör känna till.
Åtkomsttoken
Även om vi har nämnt termen "åtkomsttoken" många gånger har vi inte diskuterat hur man får en. I OAuth 2.0 utfärdas en åtkomsttoken av auktoriseringsservern (identitetsleverantören) efter ett lyckat auktoriseringsflöde.
Låt oss titta närmare på Google Drive-exemplet och anta att vi använder auktoriseringskodflödet:
Det finns några viktiga steg i flödet för utfärdande av åtkomsttoken:
- Steg 2 (Omdirigera till Google): MyApp omdirigerar användaren till Google med en auktoriseringsbegäran. Vanligtvis inkluderar denna begäran följande information:
- Vilken klient (MyApp) som initierar begäran
- Vilka omfattningar MyApp begär
- Vart Google ska omdirigera användaren efter att auktoriseringen är klar
- Steg 5 (Be om tillstånd att få tillgång till Google Drive): Google ber användaren om att ge tillgång till MyApp. Användaren kan välja att ge eller neka tillgång. Detta steg kallas samtycke.
- Steg 7 (Omdirigera till MyApp med auktoriseringsdata): Detta steg är nyligen introducerat i diagrammet. Istället för att returnera åtkomsttoken direkt returnerar Google en engångs auktorisationskod till MyApp för ett säkrare utbyte. Denna kod används för att erhålla åtkomsttoken.
- Steg 8 (Använd autorisationskod för att byta mot åtkomsttoken): Detta är också ett nytt steg. MyApp skickar autorisationskoden till Google för att byta mot en åtkomsttoken. Som en identitetsleverantör kommer Google att sätta samman sammanhanget för begäran och besluta om att utfärda en åtkomsttoken:
- Klienten (MyApp) är den den påstår sig vara
- Användaren har gett tillgång till klienten
- Användaren är den hen påstår sig vara
- Användaren har de nödvändiga omfattningarna
- Autorisationskoden är giltig och inte utgått
Det ovanstående exemplet antar att auktoriseringsservern (identitetsleverantören) och resursservern är densamma (Google). Om de är separerade, tar vi exemplet med MyApp och OrderService, flödet kommer att se ut så här:
I detta flöde utfärdar auktoriseringsservern (IdP) både en ID-token och en åtkomsttoken till MyApp (steg 8). ID-token används för att identifiera användaren, och åtkomsttoken används för att få tillgång till andra tjänster som OrderService. Eftersom både MyApp och OrderService är förstapartstjänster brukar de inte be användaren om att ge tillgång; istället litar de på åtkomstkontrollen i identitetsleverantören för att avgöra om användaren kan få tillgång till resurserna (dvs. om åtkomsttoken innehåller de nödvändiga omfattningarna).
Slutligen, låt oss se hur åtkomsttoken används i maskin-till-maskin (M2M) applikationer. Eftersom ingen användare är inblandad i processen och applikationen är betrodd kan den direkt begära en åtkomsttoken från identitetsleverantören:
Åtkomstkontroll kan fortfarande tillämpas här. För OrderService spelar det ingen roll vem användaren är eller vilken applikation som begär datan; den bryr sig bara om åtkomsttokenet och de omfattningar det innehåller.
Åtkomsttoken är vanligtvis kodade som JSON Web Tokens (JWT). För att lära dig mer om JWT kan du hänvisa till Vad är JSON Web Token (JWT)?.
Resursindikatorer
OAuth 2.0 introducerar konceptet omfattningar för åtkomstkontroll. Men du kan snabbt inse att omfattningar inte räcker:
- OpenID Connect definierar en uppsättning standardomfattningar som
openid
,offline_access
ochprofile
. Det kan vara förvirrande att blanda dessa standardomfattningar med dina anpassade omfattningar. - Du kan ha flera API-tjänster som delar samma omfattningsnamn men har olika betydelser.
En vanlig lösning är att lägga till suffix (eller prefix) till omfattningsnamnen för att indikera resursen (API-tjänsten). Till exempel, read:order
och read:product
är tydligare än read
och read
. En OAuth 2.0-förlängning RFC 8707 introducerar en ny parameter resource
för att indikera resursservern som klienten vill få tillgång till.
I verkligheten är API-tjänster vanligtvis definierade av URL:er, så det är mer naturligt att använda URL:er som resursindikatorer. Till exempel kan OrderService API representeras som:
Som du kan se, ger parametern bekvämligheten att använda de faktiska resurs-URL:erna i auktoriseringsförfrågningar och åtkomsttoken. Det är värt att nämna att RFC 8707 kanske inte implementeras av alla identitetsleverantörer. Du bör kontrollera dokumentationen för din identitetsleverantör för att se om den stöder resource
-parametern (Logto stöder den).
I korthet kan resource
-parametern användas i auktoriseringsförfrågningar och åtkomsttoken för att indikera resursen som klienten vill få tillgång till.
Det finns ingen begränsning på resursindikatorernas tillgänglighet, dvs. resursindikatorn behöver inte vara en verklig URL som pekar på en API-tjänst. Därför namnet "resursindikator" speglar väl dess roll i auktoriseringsprocessen. Du kan använda virtuella URL:er för att representera resurser du vill skydda. Till exempel kan du definiera en virtuell URL
https://api.example.com/admin
i din monolitiska applikation för att representera resursen som endast administratörer kan få tillgång till.
Få ihop allt
Vid det här laget har vi täckt grunderna för OAuth 2.0 och OpenID Connect, och hur man använder dem i olika applikationstyper och scenarier. Även om vi har diskuterat dem separat kan du kombinera dem enligt dina affärskrav. Den övergripande arkitekturen kan vara så enkel som detta:
Eller lite mer komplicerad:
När din applikation växer kommer du att se att identitetsleverantören (IdP) spelar en kritisk roll i arkitekturen; men det relaterar inte direkt till dina affärsmål. Medan det är en bra idé att lägga ut den på en pålitlig leverantör, behöver vi välja identitetsleverantören klokt. En bra identitetsleverantör kan avsevärt förenkla processen, minska utvecklingsinsatsen och spara dig från potentiella säkerhetsfallgropar.
Avslutande anteckningar
För moderna molnapplikationer är identitetsleverantören (eller auktoriseringsservern) den centrala platsen för användar autentisering, identitetshantering, och åtkomstkontroll. Medan vi diskuterade många begrepp i detta inlägg finns det fortfarande många nyanser att överväga när man implementerar ett sådant system. Om du är intresserad av att lära dig mer kan du bläddra i vår blogg för mer djupgående artiklar.