Skip to content

RFC|Feature // Login|Authentifizierung

  • App Spricht mit jedem Service selber
  • Loginstate sollte überall über Hook abgerufen werden.

Konzept OAuth / OpenID Connect:

Die App soll OAuth 2.1 und OpenID Connect umsetzen, um den Login in den Servicen umzusetzen. Dabei müssen verschiedene Aspekte Spezifiziert werden:

  • Kommunikation zwischen Teilnehmer + Erstellen/Prüfen/Validieren/Zusenden aller benötigten Token und Codes
  • UI-Worklow
  • Sicherheitskonzept

Die App wird keine Registrierung unterstützen, es wir eine ein Einloggen vorhandener Benutzer ermöglicht.

Um einen Überblick zu geben, indem jeder Aspekt angesprochen wird, wird im Folgenden ein Workflow beschrieben. Einzelne Aspekte werden genauer in den entsprechenden Spezifikationsbereichen beschrieben:

Von OAuth wird der Authorization-Code Flow verwendet.

  1. Mit der Installation der App wird ein Deep-Link(Android) bzw. Universal-Link(iOS) in dem Handy registriert. Jede Universitätsapp hat seinen eigenen Link, damit Apps parallel installiert werden können.
  2. Nachdem der Nutzer die App gestartet hat, möchte dieser sich in der App einloggen. Dazu klickt der Nutzer auf den Login-Button, wodurch die App die Login-URL des Authorization-Servers/OAuth-Server in eine Webview oder ein Browser öffnet. Die Login-URL des Authorization-Server wurde vorher in der App hinterlegt. Dabei wird der Deep-Link/Universal-Link in dem 'redirect_uri'-Querry-Paramter dem Authorization-Server mitgegeben. Dadurch heißt diese URI "Redirect-URI"
  3. Dem Nutzer wird die Login-Webseite des Authorization-Server in einer Webview/ein Browser angezeigt. Hier muss der Nutzer seine Login-Daten eingeben und bestätigen.
  4. Anschließend loggt der Authorization-Server den Nutzer ein und leitet den Browser auf die im Schritt 1 am Handy registrierten und in Schritt 2 mitgesendeten Deep-Link/Universal-Link bzw. Redirect-URI um. Dafür wird ein HTTP-Status-Code 302 verwendet und die Redirect-URI im Location-Header an dem Browser übergeben, wodurch der Browser über die auf den Deep-Link/Universal-Link Daten an die App übergibt. Die Redirect-URI wird mit einem Query-Paramwter 'code' erweitert, der Authorization-Code genannt wird.
  5. Die App löst den Authorization-Code beim Authorization-Server ein, um ein Access-Token und ein ID-Token zu bekommen. Diese Token können nun verwendet werden, um Zugriff auf Benutzerdaten zu bekommen. Zusätzlich wird der App ein Refresh-Token mitgesendet.
  6. Die App ruft nun den Datenservice mit dem Access-Token ab, um an die Nutzerdaten zu kommen. Dafür wird die Web-API des Datenservices normal verwendet, wobei das Access-Token vorzugsweise als Bearer-Token an den Requests angehängt wird. Beispielsweise, könnte der Studenplanserver eine URL www.uni-timetable.de/timetable anbieten von der aus man den persönlichen Stundenplan abrufen kann. Die App sendet einen GET-Request auf die URL, welcher den Access-Token als Bearer-Token enthält. Der Studenplanserver nimmt die Anfrage entgegen und prüft den Bearer-Token, welcher den Access-Token enthält.

Anforderungen an App

UI Workflow

Wenn der Nutzer sich einloggen möchte, soll eine Webview oder ein Browser aufgehen indem eine Login-Webseite aufgerufen wird. In diesem Fenster soll der Nutzer seine Login-Daten eingeben.

Kommunikation zwischen Teilnehmer + Erstellen/Prüfen/Validieren/Zusenden aller benötigten Token und Codes

App erhält Token

Initial ist der Nutzer nicht in der App eingeloggt, wodurch die App nicht auf nutzerspezifische Informationen zugreifen darf. Deshalb muss der Nutzer sich über OpenID-Connect bzw OAuth einloggen. Damit der Login und damit der Erhalt des Tokens funktioniert, muss die App einen Deeplink im Handy-System registrieren.

Im folgenden ist ein Diagramm und eine Beschreibung der einzelnen Schritte zu finden, welche den Login-Prozess und eine Nachfolgende Beispielabfrage erklärt:

sequenceDiagram
    autonumber

    actor Nutzer

    box Handy
    participant App
    participant Browser
    end

    box Uni-Server
    participant oAuth-Server
    participant Stundenplan-Server
    end

    par Nutzer-Login
    activate Nutzer
    Nutzer->>App: Startet Applogin
    activate App
    Note right of App: App generiert code_verifier/code_challenge
    App->>+Browser: App öffnet Login-URL in Browser/Webview
    deactivate App
    Note over App,Browser: Login-URL enthält Redirect-URI, scopes und code_challenge
    Browser->>oAuth-Server: GET: Browser öffnet Login-URL von oAuth-Server
    oAuth-Server->>Browser: 200: Sendet Login-Webpage
    Browser->>Nutzer: Zeigt Nutzer Login des oAuth
    Nutzer->>Browser: Trägt Logindaten ein und bestätigt
    deactivate Nutzer
    Browser->>oAuth-Server: POST: Sendet Logindaten
    oAuth-Server->>Browser: X : Sendet Webseite mit angeforderten Scopes(Berechtigungen) an
    Browser->>Nutzer: Zeigt Nutzer von der App angeforderte Berechtigungen
    Nutzer->>Browser: Nutzer akzeptiert Berechtigungen
    Browser->>oAuth-Server: POST: Sendet Nutzerbestätigung
    oAuth-Server->>Browser: 302: Umleiten auf Redirect-URL(Location-Header)
    Note over oAuth-Server,Browser: Redirect-URL enthält Authorization-Code
    Browser->>-App: Leitet per Deep-Link auf Redirect-URI um
    end

    par App holt Token
    App->>oAuth-Server: GET: Holt Access-token und/oder ID-Token
    Note over App,oAuth-Server: Request enthält Authorization-Code und code_verifier
    Note right of oAuth-Server: oAuth-Server verifiziert code_verifier und code_challenge
    oAuth-Server->>App: 200: Antwort mit access_token und id_token
    end
    par App verwendet token
    App->>+Stundenplan-Server: GET: Holt Stundenplan des Nutzers über Access-Token
    Stundenplan-Server->>oAuth-Server: Stellt Anfrage ob Token valide ist
    oAuth-Server->>Stundenplan-Server: Teilt dem Stundenplan-Server valididät des Token mit
    Stundenplan-Server->>-App: 200: Sendet App den Stundenplan zu 
    end
  1. Der Nutzer erkennt, dass er nicht eingeloggt ist und startet den Login.
  2. Die App öffnet für den Nutzer eine Webview/ einen Browser und ruft die Login-URL des Authorization-Servers/OAuth-Server auf. Authorization-Servers zeigt nun einen Login-Dialog an indem der Nutzer seinen Nutzernamen und sein Passwort eingibt. Die Login-URL kann fest in der App hinterlegt werden oder per OpenID-Connect Discovery von der App abgerufen werden.

App refresched Token

sequenceDiagram
    autonumber

    participant App

    box Uni-Server
    participant oAuth-Server
    participant Stundenplan-Server
    end

    par Festellen, dass Token abgelaufen ist
    App->>+Stundenplan-Server: GET: Holt Stundenplan des Nutzers über Access-Token
    Stundenplan-Server->>+oAuth-Server: Stellt Anfrage ob Token valide ist
    oAuth-Server->>-Stundenplan-Server: Teilt dem Stundenplan-Server mit, dass Token abgelaufen
    Stundenplan-Server->>-App: 401: Verweigert Stundenplan-Daten
    end
    par Acess-Token erneuern
    App->>+oAuth-Server: GET: Sendet Refresh-Token
    oAuth-Server->>-App: 200: Bekommt neues Access-Token und Refresh-Token
    end

Ist die Zeit der Gleichzeitigkeit des Access-Token abgelaufen oder ein Server antwortet mit einem HTTP-Statuscode 401(Unauthorized), muss das Access-Token über das Refresh-Token erneuert werden. Dafür ist keine Interaktion mit dem Nutzer nötig.

1.-4. App stellt invalidität des Access-Tokens fest.
5. App sendet Refresh-Token an Authorization-Server an Token-Refresh-URL.
6. Authorization-Server Antwortet mit nuen Access-Token und Refresh Token, dabei verfällt der alte Refresh-Token.

Alter Entwurf:

https://gitlab.hrz.tu-chemnitz.de/OpenASiST/openasist/-/wikis/Konzeption-Authentifizierung

Geräteverlust

Über den Authorization-Server können Access-Token(zugriff auf Servicen) und Refresh-Token(Neue Access-Token austellen) als invalide markieren, wodurch diese nicht mehr benutzt werden können. Da die Server bei jeder App-Anfrage die Access-Token vom Authorization-Server prüfen lassen, sind diese sofort ausgeschaltet. Natürlich bedingt dies eine Oberfläche(nicht in der App) um Token invalide zu schalten.

Integrationsmöglichkeiten

TUC

Die TU Chemnitz wird die Daten nicht mehr über einen Kollektor zusammenführen, sondern die Datenquellen unter Verwendung der Access-Token einzeln abfragen, dabei prüft jede Datenquelle die Berechtigungen des Tokens selber. Datenquellen müssen bei jeder App-Abfrage das mitgesendete Access-Token vom Authorization-Server prüfen bzw. validieren lassen. Das Konzept dafür wird im Ticket #444 behandelt.

flowchart LR;
  APP[OpenASiST-App]
  SPS[Stundenplan-Server]
  OAS[Authorization-Server]
  HIS[HisInOne]

  APP -- 1. Login --> OAS
  OAS -- 2. Sendet App Access-Token und Refresh-Token --> APP

  APP -- 3. Fragt Nutzerstundenplan mit Access-Token ab  --> SPS
  SPS -- 4. Lässt validität des Access-Token prüfen --> OAS
  OAS -- 5. Access-Token ist valide --> SPS
  SPS -- 6. Sendet Nutzerstundenplan --> APP

  APP -- 3. Fragt Nutzernoten mit Access-Token ab --> HIS
  HIS -- 4. Lässt validität des Access-Token prüfen --> OAS
  OAS -- 5. Access-Token ist valide --> HIS
  HIS -- 6. Sendet Nutzernoten --> APP

HTWK

Die HTWK Leipzig wird als zentralen Punkt den Kollektor behalten und alle Datenquellen bei diesen zusammenführen. Hier gibt es 2 Möglichkeiten den Umgang mit den Token zu gestalten:

  1. Der Kollektor gibt den Access-Token an die Datenquelle weiter durch Hier gibt der Kollektor den Access-Token an den Datenquellen weiter, wodurch die Datenquelle den Access-Token selber prüft bzw. vom Authorization Server validieren läst. Der Kollektor kann hier nur auf der Berechtigungsebene des Access-Tokens agieren.
flowchart LR;
  APP[OpenASiST-App]
  HSK[Kollektor];
  OAS[Authorization-Server]
  SPS[Stundenplan-Server]
  APP -- 1. Login --> OAS
  OAS -- 2. Sendet App Access-Token und Refresh-Token --> APP

  APP -- 3. Ruft Nutzerstundenplan mit Access-Token ab --> HSK
  HSK -- 4. Lässt validität des Access-Token prüfen --> OAS
  OAS -- 5. Access-Token ist valide --> HSK
  HSK -- 6. Ruft Nutzerstundenplan ab --> SPS
  SPS -- 7. Sendet Nutzerstundenplan --> HSK
  HSK -- 8. Sendet Nutzerstundenplan --> APP
  1. Der Kollektor prüft Access-Token Hier prüft der Kollektor die Berechtigungen des Access-Token und lässt diesen vom Authorization-Server validieren. Anschließend fragt der Kollektor die Datenquellen über die Berechtigungen auf Kollektorebene ab.
flowchart LR;
  APP[OpenASiST-App]
  HSK[Kollektor];
  OAS[Authorization-Server]
  SPS[Stundenplan-Server]
  APP -- 1. Login --> OAS
  OAS -- 2. Sendet App Access-Token und Refresh-Token --> APP

  APP -- 3. Ruft Nutzerstundenplan mit Access-Token ab --> HSK
  HSK -- 4. Ruft Nutzerstundenplan mit Access-Token ab --> SPS
  SPS -- 5. Lässt validität des Access-Token prüfen --> OAS
  OAS -- 6. Access-Token ist valide --> SPS
  SPS -- 7. Sendet Nutzerstundenplan --> HSK
  HSK -- 8. Sendet Nutzerstundenplan --> APP

Gemeinsamkeiten:

  • App nutzt Kollektor als zentrale Abfragestelle
  • Kollektor vereinheitlicht API
  • Token muss vom OAuth-Server validiert werden

Unterscheide:

  • Kollektor lässt Access-Token validieren <-> Dienste lassen Access-Token validieren
  • Kollektor bekommt über Access-Token des Nutzers Zugriff auf Datenquelle <-> Kollektor besitzt eigene Zugriffsberechtigung auf Datenquelle

OpenID-Connect Userinfo

Allgemeine Informationen zu dem Nutzer werden mit Hilfe des Access-Token von der userinfo-API-Ressource abgerufen. Dabei werden Standartfelder zur Verfügung gestellt. Es müssen zusätzliche Felder für die App mitgesendet werden:

Feld Type(mögliche Werte) Beschreibung Beispiel
organizational_units Strucktur-Liste Liste von Strucktur-JSON-Objekten [{"name": "Fakultät für Informatik", "short_name": "IF", "number": "134400"}]
member_types String-Liste ( student, employee ) Wie gehört der Nutzer der Universität an? ["student", "employee"]

JSON-Objekte
Strucktur:

  • name: Voller Name der Struktur,
  • short_name: Kurzbezeichnung der Struktur,
  • number: Nummer/Id der Struktur,

Die zusätzlichen Felder können über die gleichnamigen Claims freigeschaltet werden. Zusätzlich sind diese Felder über den profile-Claim freigeschaltet.

Alternativ können diese Daten über den Kollektor ausgespielt werden, dafür müsste eine weitere API angeboten werden.

Fragen/Erweiterungen

OpenID Connect Discovery

Bevor die App mit dem Login beginnt, kann sie sich über OpenID Connect Discovery alle wichtigen Metadaten vorher abrufen. Dann muss die Login-URL des Authorization-Server nicht in der App hinterlegt werden. Stattdessen wird die Discovery-URL hinterlegt.

Ja, wir werden OpenID Connect Discovery voraussetzen. Erst wenn eine Universität OpenID Connect Discovery nicht zur Verfügung stellen kann, wird in der App beim Custemizing eine fest hinterlegte OpenID Connect-Konfiguration hinterlegt.

Token-API bekannt geben

Nachdem der Auhtorization-Code ausgestellt wurde, muss sich über einen API-Endpunkt ein Access-Token geholt werden. Dieser API-Endpunkt kann in der App hinterlegt werden oder während des zusenden des Auhtorization-Code den Token-API-Endpunkt über einen Parameter mitsenden. Welche Variante (vlt. auch beides) wollen wir unterstützen?

Begriffsübersicht

Begriff Erklärung Verweise
Access-Token Token welcher die App vom Authorization-Server bekommt, um Zugriff auf Datenquellen zu bekommen. Überlick
Login-URL URL des Authorization-Server die dem Nutzer eine Login-Webseite zur Verfügung stellt Kommunikation

Prototypische Implementierung

Die Implementierung wurde prototypisch im Ticket #535 (closed) umgesetzt.

Dabei sind folgende Probleme im Prototyp:

  • Der Browser wird nach dem Login nicht geschlossen
  • Es können sich andere Apps auf den Deeplink setzen
  • Access-Tokens/ID-Tokens werden nicht zuverlässig erneuert
  • State wird nicht geprüft

Todos

  • Auf jeden Fall OAuth 2.1. Die Spezifikation ist etwas genauer und lässt somit weniger Spielraum(aka Fehler) auf Entwicklerseite zu. zB Erhalten des Authorization Codes mit PKCE
  • PKCE - Flow sollten wir mit beschrieben
    • PKCE ist nötig da die App kein "Confidential Client" sonder einen "Public-Client" darstellt und sich deswegen nicht über ein hinterlegtes Client-Secret identifizeren kann. Sonst könnte Anwendung Authentication-Codes/Access-Token requesten.
  • Im Flow fehlt auch der User-Consent nach/beim Login (zB wichtig um sicherzustellen, dass ein realer User am Client sitzt)
  • Claims für uniinteressen erweitern
  • Prüfen ob uns die selbe Lücke trifft:
    https://www.heise.de/hintergrund/eID-und-AusweisApp-kritische-Sicherheitsluecke-aber-auch-gefaehrlich-9632374.html
    https://ctrlalt.medium.com/space-attack-spoofing-eids-password-authenticated-connection-establishment-11561e5657b1
    • Lösungen:
      • Login in App-interner Webview machen
      • Sicheren Rücksprung vom Browser zurück zur App implementieren:
        • iOS: Universal Links
        • Android: ???
      • Vlt. schon mit PKCE gelöst, weil beim Token-Request der code_verifier gesendet wird unf mit dem vorher gesendeten code_challenge geprüft wird.
  • Klärung der Scopes/Claim-Strucktur:
    • Möglchkeiten:
      • Große Scopes, welche für ein Modul verwendet werden (lesen/schreiben)
      • Mittelgroße Scopes, welche eine Ressource erlauben (lesen/schreib)
      • Kleine Scopes, welche eine Ressource in Ihre Operationen unterteilen
Edited by Toni Beier
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information