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.
- 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.
- 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"
- 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.
- 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.
- 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.
- 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
- App muss für jede Uni einen Deep-Link(Android) bzw. Universal-Link(iOS) zur Verfügung stellen
- Im besten Fall unterschiedlich, damit beide Uni-Apps auf einem Handy betrieben werden können.
- App muss OpenID-Connect beherschen und damit auch OAuth 2.X
- Mögliche Bibliotheken für App:
- OIDC React : https://github.com/bjerkio/oidc-react
- Expo: https://docs.expo.dev/versions/latest/sdk/auth-session/
- authTS: https://github.com/authts/react-oidc-context
- Eigene Umsetzung?
- Mögliche Bibliotheken für App:
- TUC:
- App muss einzelne Dienste ansprechen/verstehen können.
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
- Der Nutzer erkennt, dass er nicht eingeloggt ist und startet den Login.
- 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:
- 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
- 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.
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.
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.
- Lösungen: