Flexible und sichere Internetdienste mit OAuth 2.0

Seite 2: OAuth-Browser-Flow

Inhaltsverzeichnis

Konzeptionell trennt OAuth 2.0 das Erlangen eines Access Token und dessen Verwendung strikt. Konsequenterweise sind beide Aspekte in unterschiedlichen Spezifikationen, der Kern-Spezifikation und dem neuen HTTP-Autorisierungsschema Bearer, definiert. Die erste definiert diverse Wege zum Erlangen von Token, die sogenannten Grant Types. Jeder Grant Type ist auf bestimmte Anwendungsfälle optimiert. Die Interaktion des Clients mit der API erfolgt unabhängig hiervon immer auf die gleiche Weise und wird in der zweiten Spezifikation festgelegt.

Grant Type Beschreibung Anwendungen
Authorization Code Browserbasierter, flexibler Ablauf Web, Apps
Implicit vereinfachte Variante des Browser-Ablaufs für Clients, die direkt im Browser ausgeführt werden JavaScript u.Ä.
Resource Owner Password Credentials Benutzername und Passwort des Benutzers werden direkt in ein Access Token umgetauscht Apps
Client Credentials Nutzung von id und secret eines Clients für das Erlangen eines Access Token Web
Refresh Token Langlebiges Tokens, das zum Erlangen eines neuen Access Tokens verwendet wird Web, Apps

Der vielseitigste Grant Type ist "Authorization Code", an dem sich der Ablauf des Protokolls illustrieren lässt. Beim Client handelt es sich um eine Webanwendung, die auf das Adressbuch des Benutzers zugreifen möchte. Der Client initiiert hierzu den OAuth-Browserflow, indem er eine HTTP-Weiterleitung zum Autorisierungs-Endpunkt des Authorization Server sendet:

https://accounts.service.com/oauth2/auth
?client_id=5365365163AF67BCD244534567
&redirect_uri=https://photo-site.com/oauthcb
&response_type=code
&scope=contacts
&state=4546454545

Diese Anfrage enthält einige interessante Informationen. Zum einen kann der Authorization Server den Client mit client_id identifizieren.

Der Parameter redirect_uri teilt ihm mit, zu welcher URL der Browser nach Abschluss des Autorisierungsprozesses weitergeleitet werden soll. Dabei handelt es sich in der Regel um eine spezielle (Callback-)URL für die Behandlung derartiger Vorgänge. response_type steuert den Protokollablauf, code selektiert den Grant Type „Authorization Code“. Im Parameter state übergibt der Client einen Wert an den Autorisierungsserver, den dieser dann unverändert nach dem Abschluss des Vorgangs an den Client zurücksendet. Damit kann der Client den Vorgang wiedererkennen und insbesondere CSRF-Angriffe (Cross-Site Request Forgery) erkennen und verhindern. Zu guter Letzt teilt der Client über den Parameter scope mit, für welche Ressourcen er welche Art von Zugriff anfordert. Der Wert von scope ist nicht durch den OAuth-Standard spezifiziert, ihn definieren derzeit jeder einzelne Autorisierungsserver sowie einzelne APIs selbst. Im Beispiel signalisiert der Client, dass er auf die Kontakte des Benutzers zugreifen möchte.

Im nächsten Schritt authentifiziert der Authorization Server den Benutzer. Da er hierbei in Interaktion mit dem Benutzer tritt, kann er hierfür jede gewünschte Methode einsetzen; eine Spezifikation dieses Schritts aus OAuth-Sicht ist nicht erforderlich. Dadurch ist der "Authorization Code" als flexibelster aller Grant Types anzusehen, denn die Schnittstelle zwischen Client und Authorization Server reduziert sich auf eine HTTP-Weiterleitung. Nach erfolgreicher Authentifizierung sollte der Authorization Server dem Benutzer den Authorization Request des Clients in geeigneter Weise anzeigen und um dessen Zustimmung bitten. In der Regel werden der Name und die URL des Clients zusammen mit den gewünschten Zugriffen angezeigt.

Vorsicht ist geboten, wenn sich die Daten hinsichtlich der Identität des Clients nicht prüfen lassen. Das ist bei sogenannten "Public Clients" der Fall. Sie besitzen nur eine client_id, aber kein korrespondierendes Geheimnis, das client_secret, und lassen sich daher nicht authentifizieren. In einem solchen Fall sind Angaben in Bezug auf den Client nicht durch den Authorization Server prüfbar. Dieser muss den Benutzer darauf hinweisen, die Entscheidung hinsichtlich der Authentizität des Clients muss dann der Benutzer treffen (siehe Abb. 4).

Gibt der Benutzer letztlich die Anfrage frei, sendet der Authorization Server eine Weiterleitung an die vom Client eingangs mitgeteilte redirect_uri, etwa: https://photo-site.com/oauthcb?code=4/5A-gmOdlpb4W-21LXQJKDTdhkDwU&state=4546454545. Als Parameter werden der Authorization Code und der State übertragen. Der Code repräsentiert die Autorisierung durch den Benutzer und wird im nächsten Schritt unmittelbar gegen ein Access Token getauscht. Dazu sendet der Client auf einer direkten HTTPS-Verbindung eine POST-Anfrage an den Authorization Server:

POST https://accounts.service.com/oauth2/tokens
?client_id=5365365163AF67BCD244534567
&client_secret=E6x...FVu3
&grant_type=authorization_code
&redirect_uri=https%3A%2F%2Fphoto-site.com%2Foauthcb
&code=4/5A-gmOdlpb4W-21LXQJKDTdhkDwU

In der Anfrage ist dieselbe client_id wie beim initialen Authorization Request zu verwenden, in dem Fall ergänzt um das korrespondierende client_secret. Erst danach lässt sich hiermit der Client authentifizieren. Das client_secret wird erst in diesem Schritt übertragen, weil es hier im Gegensatz zum Redirect gegen Ausspähen gesichert ist. Mit dem Parameter grant_type selektiert der Client in diesem Fall den Grant Type Authorization Code. Als Konsequenz erwartet der Authorization Server zwei weitere Parameter, nämlich die initial verwendete redirect_uri und den Code. Nach erfolgreicher Prüfung aller Parameter sendet der Server folgendes JSON-Objekt in der HTTP-Response:

{
"access_token":
"ya29.AHE...5CLRaw",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "1/wMTx...mmyI"
}

Neben dem Access Token enthält das Objekt Informationen zum Typ des Access Token und dessen Laufzeit (in Sekunden). Des Weiteren stellt der Authorization Server in diesem Fall zusätzlich ein Refresh Token aus. Nun kann der Client das erste Mal auf das Adressbuch des Benutzers zugreifen. Hierzu verwendet er den Access Token und sendet diesen im Authorization Header mit jeder Anfrage mit. Der Authorization Header nutzt das oben erwähnte neue HTTP-Autorisierungsschema Bearer:

GET https://mail.service.com/contacts/people/@me/@all

Authorization: BEARER ya29.AHE...5CLRaw

{
"startIndex": 0,
"totalResults": 3,
"entry": [
{
"profileUrl":
"https://mail.service.com/contacts/profiles/user1ID", ...

Der Grant Type „Authorization Code“ lässt sich verwenden, um Access Tokens für mobile und Desktop-Anwendungen zu erlangen. Da diese Anwendungen in der Regel nicht in einem Browser ausgeführt werden, muss man hier einen "Umweg" in Kauf nehmen. Im Wesentlichen gibt es zwei Muster für die Integration dieser Anwendungen mit OAuth. Die Anwendung kann die Ausführung des OAuth-Flows an den auf dem Gerät installierten Browser übergeben. Das hat den Vorteil, dass sich Sitzungen mit dem Dienstanbieter wiederverwenden lassen.

Der Rücksprung in die Anwendung gestaltet sich aber etwas schwieriger. Hierzu wird ein sogenanntes "Custom URL Scheme" für die redirect_uri verwendet. Es kommt anstelle eines Standardschemas wie https ein speziell für die Anwendung definiertes Schema wie myapp zum Einsatz. redirect_uri könnte dann wie folgt aussehen: myapp://redirect/oauthcb. Im Browserkontext muss das Schema mit der Anwendung als Handler assoziiert sein. Dann aktiviert der Browser beim Rücksprung aus dem Autorisierungsprozess die Anwendung und übergibt ihr die Rücksprungs-URL, zum Beispiel:

myapp://redirect/oauthcb?code=4/5A-gmOdlpb4W-21LXQJKDTdhkDwU&state=4546454545

Diese Technik ist weitverbreitet, aber mit folgenden Herausforderungen behaftet: Die Aktivierung eines Handlers wird von vielen modernen Browsern wie jeder andere Aufruf eines ausführbaren Programms behandelt und erfordert die Zustimmung des Benutzers. Da die Ausführung von Programmen im Kontext von Webseiten im Allgemeinen mit Schadsoftware assoziiert wird, werden viele Benutzer wahrscheinlich an der Stelle den Prozess abbrechen. Des Weiteren könnte eine Schadsoftware auf dem Gerät einen eigenen Handler für das Custom Scheme registrieren, den Rückruf abfangen und selbst das Token abfragen und verwenden. Zu guter Letzt unterstützen nicht alle Plattformen Custom Schemes.

Die Alternative stellt die Einbettung eines Browsers in die Anwendung dar. Bei der Technik führt die Anwendung den Autorisierungsprozess im eingebetteten Browser durch und beobachtet den Nachrichtenfluss. Als Ziel der redirect_uri wird in dem Fall eine lokale oder Serverressource verwendet. Die Anwendung extrahiert dann den Authorization Code direkt aus der URL.

Die beschriebenen Muster sind eindeutig aufwendiger zu implementieren als die Integration einer Webanwendung. Warum sollte man diesen Aufwand treiben und sendet nicht einfach Benutzername sowie Passwort an den Autorisierungsserver? Das kann man auch tun. Hierzu gibt es den Grant Type "Resource Owner Password Credentials". Aber wieder ist das Passwort-Anti-Pattern zu beachten.

Wenn App und Resource Server zum selben Dienstanbieter gehören und eine sorgsame Behandlung der Passwörter sichergestellt ist, ist dagegen nichts einzuwenden.

Handelt es sich aber um die Anwendung einer dritten Partei, ist das kritisch zu sehen. Hinzu kommt, dass bei der direkten Verwendung von Benutzernamen und Passwort die direkte Interaktion zwischen Benutzer und Authorization Server entfällt. Der Authorization Server kann also die Freigabe des Zugriffs nicht direkt beim Benutzer nachfragen. Damit tritt das Prinzip der Benutzerkontrolle in den Hintergrund. Deshalb bieten viele Internet-Dienstanbieter den Grant Type "Resource Owner Password Credentials" gar nicht erst an.