OpenID Connect: Autorisierung interner Anwendungen von Eigenbau bis zum Standard

Vor einigen Monaten habe ich einen OpenID Connect-Server implementiert, um den Zugriff auf Hunderte unserer internen Anwendungen zu steuern. Von eigenen Lösungen, die für kleinere Umfänge geeignet waren, sind wir zu einem allgemein anerkannten Standard übergegangen. Der Zugang über einen zentralen Dienst vereinfacht monotone Vorgänge erheblich, senkt die Kosten für die Implementierung von Authentifizierungen und ermöglicht es, zahlreiche fertige Lösungen zu finden, anstatt sich bei der Entwicklung neuer Gedanken zu machen. In diesem Artikel werde ich über diesen Übergang berichten und von den Erfahrungen erzählen, die wir gemacht haben.

OpenID Connect: Autorisierung interner Anwendungen von Eigenbau bis zum Standard

Es war einmal... Wie alles begann

Vor einigen Jahren, als die internen Anwendungen zu zahlreich wurden, um sie manuell zu verwalten, entwickelten wir eine Anwendung zur Zugangskontrolle innerhalb des Unternehmens. Es handelte sich um eine einfache Rails-Anwendung, die sich mit einer Datenbank verband, die Informationen über die Mitarbeiter enthielt, wobei der Zugriff auf verschiedene Funktionen konfiguriert wurde. In diesem Zusammenhang richteten wir das erste SSO ein, das auf der Überprüfung von Tokens zwischen dem Client und dem Authentifizierungsserver basierte. Das Token wurde verschlüsselt mit mehreren Parametern übermittelt und auf dem Authentifizierungsserver überprüft. Dies war jedoch nicht die praktischste Lösung, da für jede interne Anwendung ein erheblicher Teil der Logik beschrieben werden musste und die Mitarbeiterdatenbanken nicht synchron mit dem Authentifizierungsserver waren.

Nach einiger Zeit haben wir uns entschieden, die zentrale Authentifizierung zu vereinfachen. Das SSO wurde auf den Load Balancer übertragen. Mithilfe von OpenResty auf Lua haben wir ein Template hinzugefügt, das die Tokens überprüfte, wusste, auf welche Anwendung die Anfrage ging und prüfen konnte, ob der Zugriff gewährt wurde. Dieser Ansatz hat die Verwaltung des Zugriffs auf interne Anwendungen erheblich vereinfacht – in jedem Anwendungs-Code musste keine zusätzliche Logik mehr beschrieben werden. Letztendlich haben wir den externen Verkehr gesperrt, während die Anwendung selbst von der Authentifizierung nichts wusste.

Eine der Probleme blieb jedoch ungelöst. Wie soll man mit Anwendungen umgehen, die Informationen über Mitarbeiter benötigen? Man hätte ein API für den Authentifizierungsservice schreiben können, aber dann müsste für jede dieser Anwendungen zusätzliche Logik hinzugefügt werden. Zudem wollten wir die Abhängigkeit von einer unserer selbstgebauten Anwendungen, die wir zukünftig in OpenSource übersetzen möchten, von unserem internen Authentifizierungsserver loswerden. Darüber werden wir ein anderes Mal berichten. Die Lösung für beide Probleme war OAuth.

Zu den allgemein anerkannten Standards

OAuth ist ein verständlicher, allgemein anerkannter Standard für die Autorisierung, doch da dessen Funktionalität allein nicht ausreicht, begann man, auch OpenID Connect (OIDC) zu betrachten. OIDC selbst ist die dritte Implementierung eines offenen Authentifizierungsstandards, der als Erweiterung des OAuth 2.0-Protokolls (einem offenen Autorisierungsprotokoll) dient. Diese Lösung behebt das Problem, dass keine Daten über den Endbenutzer vorhanden sind, und ermöglicht zudem den Wechsel des Autorisierungsanbieters.

Wir haben uns jedoch nicht für einen bestimmten Anbieter entschieden und beschlossen, die Integration mit OIDC für unseren bestehenden Autorisierungsserver hinzuzufügen. Ein Vorteil dieser Entscheidung ist die hohe Flexibilität von OIDC in Bezug auf die Autorisierung des Endbenutzers. So entstand die Möglichkeit, die OIDC-Unterstützung auf unserem aktuellen Autorisierungsserver zu implementieren.

OpenID Connect: Autorisierung interner Anwendungen von Eigenbau bis zum Standard

Unser Weg zur Implementierung eines eigenen OIDC-Servers

1) Daten in die erforderliche Form bringen

Für die Integration von OIDC müssen die aktuellen Benutzerdaten in ein Format gebracht werden, das dem Standard entspricht. In OIDC werden diese als Claims bezeichnet. Claims sind im Grunde genommen die endgültigen Felder in der Benutzerdatenbank (name, email, phone usw.). Es gibt eine стандартный список клеймов, und alles, was nicht in dieser Liste enthalten ist, wird als benutzerdefiniert betrachtet. Daher ist der erste Punkt, auf den Sie achten sollten, wenn Sie einen vorhandenen OIDC-Anbieter auswählen möchten – die Möglichkeit zur einfachen Anpassung neuer Claims.

Die Gruppe von Claims wird in die folgende Teilmenge zusammengefasst – Scope. Bei der Autorisierung erfolgt die Anfrage nicht an spezifische Claims, sondern an die Scopes, selbst wenn einige Claims aus dem Scope nicht benötigt werden.

2) Die benötigten Grants implementiert

Der nächste Teil der OIDC-Integration ist die Auswahl und Implementierung der Authentifizierungstypen, der sogenannten Grants. Das gewählte Grant beeinflusst das weitere Interaktionsszenario der ausgewählten Anwendung mit dem Authentifizierungsserver. Ein schematischer Überblick zur Auswahl des benötigten Grants ist im nachstehenden Bild dargestellt.

OpenID Connect: Autorisierung interner Anwendungen von Eigenbau bis zum Standard

Für unsere erste Anwendung haben wir den am weitesten verbreiteten Grant – den Authorization Code – verwendet. Das Besondere an ihm ist, dass er dreistufig ist, d.h. eine zusätzliche Überprüfung erfolgt. Zuerst stellt der Benutzer eine Anfrage zur Authorisierung, erhält ein Token – den Authorization Code, und verwendet dieses Token dann wie eine Fahrkarte, um ein Zugangstoken anzufordern. Die gesamte Interaktion in diesem Authorisierungsszenario basiert auf Weiterleitungen zwischen der Anwendung und dem Autorisierungsserver. Weitere Informationen zu diesem Grant finden Sie hier. hier.

OAuth folgt dem Konzept, dass die erhaltenen Zugriffstoken nach der Autorisierung temporär sein sollten und idealerweise alle 10 Minuten wechseln. Der Authorization Code Grant ist eine dreistufige Prüfung über Weiterleitungen, und ehrlich gesagt, alle 10 Minuten einen Schritt durchzuführen, ist nicht die angenehmste Aufgabe. Um dieses Problem zu lösen, gibt es einen weiteren Grant – den Refresh Token, den wir ebenfalls implementiert haben. Hier ist es einfacher: Während der Prüfung mit einem anderen Grant wird neben dem Hauptzugriffstoken auch ein Refresh Token ausgegeben, das nur einmal verwendet werden kann und dessen Lebensdauer in der Regel wesentlich länger ist. Mit diesem Refresh Token wird, wenn die TTL (Time to Live) des Hauptzugriffstokens abläuft, eine Anfrage für ein neues Zugriffstoken an den Endpoint des anderen Grants gesendet. Der verwendete Refresh Token wird sofort zurückgesetzt. Diese Prüfung ist zweistufig und kann im Hintergrund, unbemerkt für den Benutzer, durchgeführt werden.

3) Formate für die Ausgabe benutzerdefinierter Daten konfiguriert

Nachdem die ausgewählten Grants implementiert sind und die Authentifizierung funktioniert, sollte die Beschaffung von Informationen über den Endbenutzer erwähnt werden. In OIDC gibt es hierfür einen speziellen Endpoint, über den man mit dem aktuellen Zugriffstoken und sofern dieses gültig ist, Nutzerinformationen anfordern kann. Wenn die Benutzerdaten nicht so häufig geändert werden und man häufig die aktuellen Informationen abrufen muss, könnte eine Lösung wie JWT-Tokens in Betracht gezogen werden. Diese Tokens werden ebenfalls durch den Standard unterstützt. Ein JWT-Token besteht aus drei Teilen: Header (Token-Informationen), Payload (alle benötigten Daten) und Signature (die Signatur, die vom Server erstellt wird und mit der der Ursprung der Signatur verifiziert werden kann).

In der Implementierung von OIDC wird das JWT-Token als id_token bezeichnet. Es kann zusammen mit dem regulären Zugriffstoken angefordert werden und alles, was bleibt, ist die Überprüfung der Signatur. Für diesen Zweck gibt es auf dem Autorisierungsserver einen speziellen Endpoint mit einem Set von öffentlichen Schlüsseln im Format JWK. In diesem Zusammenhang sollte auch erwähnt werden, dass es einen weiteren Endpoint gibt, der auf dem Standard RFC5785 spiegelt die aktuelle Konfiguration des OIDC-Servers wider. Es enthält alle Endpunktadressen (einschließlich der Adresse für die Verknüpfung von öffentlichen Schlüsseln, die für die Signatur verwendet werden), unterstützte Ansprüche und Scopes, verwendete Verschlüsselungsalgorithmen, unterstützte Grants usw.

Beispielsweise in Google:

{
 "issuer": "https://accounts.google.com",
 "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
 "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code",
 "token_endpoint": "https://oauth2.googleapis.com/token",
 "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
 "revocation_endpoint": "https://oauth2.googleapis.com/revoke",
 "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
 "response_types_supported": [
  "code",
  "token",
  "id_token",
  "code token",
  "code id_token",
  "token id_token",
  "code token id_token",
  "none"
 ],
 "subject_types_supported": [
  "public"
 ],
 "id_token_signing_alg_values_supported": [
  "RS256"
 ],
 "scopes_supported": [
  "openid",
  "email",
  "profile"
 ],
 "token_endpoint_auth_methods_supported": [
  "client_secret_post",
  "client_secret_basic"
 ],
 "claims_supported": [
  "aud",
  "email",
  "email_verified",
  "exp",
  "family_name",
  "given_name",
  "iat",
  "iss",
  "locale",
  "name",
  "picture",
  "sub"
 ],
 "code_challenge_methods_supported": [
  "plain",
  "S256"
 ],
 "grant_types_supported": [
  "authorization_code",
  "refresh_token",
  "urn:ietf:params:oauth:grant-type:device_code",
  "urn:ietf:params:oauth:grant-type:jwt-bearer"
 ]
}

Mit dem id_token können alle erforderlichen Claims im Payload des Tokens übermittelt werden, ohne dass jedes Mal der Autorisierungsserver kontaktiert werden muss, um Benutzerdaten abzurufen. Ein Nachteil dieses Ansatzes ist, dass Änderungen der Benutzerdaten nicht sofort vom Server übermittelt werden, sondern mit dem neuen Zugriffstoken.

Ergebnisse der Implementierung

Nach der Implementierung eines eigenen OIDC-Servers und der Konfiguration der Verbindungen auf der Anwendungsseite haben wir das Problem der Übermittlung von Benutzerinformationen gelöst.
Da OIDC ein offener Standard ist, hatten wir die Möglichkeit, einen bestehenden Anbieter auszuwählen oder einen eigenen Server zu implementieren. Wir haben Keycloak getestet, das sich als sehr benutzerfreundlich in der Konfiguration erwiesen hat. Nach der Einrichtung und Anpassung der Verbindungsparameter auf der Anwendungsseite ist es betriebsbereit. Die Anwendungen müssen lediglich die Verbindungsparameter anpassen.

Über bestehende Lösungen sprechen

Im Rahmen unserer Organisation haben wir unsere eigene Implementierung als ersten OIDC-Server erstellt, die bei Bedarf erweitert wurde. Nach einer gründlichen Prüfung anderer fertiger Lösungen lässt sich sagen, dass dies ein umstrittenes Thema ist. Die Entscheidung, einen eigenen Server zu implementieren, wurde durch Bedenken seitens der Anbieter hinsichtlich fehlender benötigter Funktionen sowie durch das Vorhandensein eines alten Systems motiviert, das verschiedene benutzerdefinierte Authentifizierungen für einige Dienste beinhaltete und bereits viele Daten über Mitarbeiter gespeichert hatte. Allerdings bieten die fertigen Lösungen Vorteile bei der Integration. Zum Beispiel hat Keycloak ein eigenes Benutzermanagementsystem und die Daten werden direkt dort gespeichert. Es ist daher nicht schwierig, unsere Benutzer dorthin zu migrieren. Dazu steht in Keycloak eine API zur Verfügung, die alle erforderlichen Schritte für den Transfer vollständig ermöglicht.

Ein weiteres Beispiel für eine zertifizierte, interessante Implementierung ist Ory Hydra. Interessant ist sie, weil sie aus verschiedenen Komponenten besteht. Für die Integration müssen Sie Ihren Benutzerverwaltungsdienst mit ihrem Autorisierungsdienst verbinden und bei Bedarf erweitern.

Keycloak und Ory Hydra sind nicht die einzigen verfügbaren Lösungen. Am besten wählt man eine zertifizierte Implementierung der OpenID Foundation. Solche Lösungen tragen normalerweise das OpenID-Zertifizierungslogo.

OpenID Connect: Autorisierung interner Anwendungen von Eigenbau bis zum Standard

Vergessen Sie auch nicht die bestehenden kostenpflichtigen Anbieter, wenn Sie nicht Ihren eigenen OIDC-Server betreiben möchten. Derzeit gibt es viele gute Optionen.

Was kommt als Nächstes

In naher Zukunft planen wir, den Datenverkehr zu internen Diensten auf eine andere Weise zu sichern. Wir beabsichtigen, unser aktuelles SSO über einen Load Balancer mit OpenResty durch einen Proxy, der auf OAuth basiert, zu migrieren. Auch hierfür gibt es bereits viele Lösungen, zum Beispiel:
github.com/bitly/oauth2_proxy
github.com/ory/oathkeeper
github.com/keycloak/keycloak-gatekeeper

Zusätzliches Material

jwt.io – ein guter Dienst zur Überprüfung von JWT-Token
openid.net/developers/certified — eine Liste zertifizierter OIDC-Implementierungen

Quelle: habr.com

Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server 🔥 Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server | ProHoster