Skip to content

SSO- und OAuth-Authentifizierung

Anmeldung über Google, GitHub, HuggingFace und generisches OIDC in Potato konfigurieren. Zugriff nach E-Mail-Domain oder GitHub-Organisation beschränken und SSO neben der lokalen Passwortanmeldung betreiben.

Neu in v2.3.0

Standardmäßig verwendet Potato eine einfache Anmeldung per Benutzername, bei der Annotatoren einen beliebigen Benutzernamen eingeben. Das genügt für die lokale Entwicklung und kleine Projekte, aber Produktivumgebungen brauchen eine richtige Authentifizierung, um unbefugten Zugriff zu verhindern, die Identität der Annotatoren zu bestätigen und institutionelle Anforderungen zu erfüllen.

SSO wird an einer Stelle konfiguriert. Setzen Sie authentication.method auf oauth und listen Sie einen oder mehrere Anbieter unter authentication.providers auf. Jeder Anbieter erhält auf der Anmeldeseite eine eigene Schaltfläche.

Potato bringt eingebaute Endpunkt-Metadaten für drei Anbieter mit: google, github und huggingface. Jeder andere Anbietername wird als generisches OIDC behandelt und benötigt eine discovery_url.

Schnellstart

Eine minimale Google-Einrichtung:

yaml
authentication:
  method: "oauth"
  providers:
    google:
      client_id: ${GOOGLE_CLIENT_ID}
      client_secret: ${GOOGLE_CLIENT_SECRET}
  user_identity_field: "email"
 
secret_key: ${POTATO_SECRET_KEY}

Legen Sie die Geheimnisse in der Umgebung ab statt in der Datei:

bash
export GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
export GOOGLE_CLIENT_SECRET="your-client-secret"
export POTATO_SECRET_KEY="$(python3 -c 'import secrets; print(secrets.token_hex(32))')"

secret_key ist ein Schlüssel der obersten Ebene und gehört nicht zum Block authentication. Setzen Sie ihn oder setzen Sie POTATO_SECRET_KEY. Fehlt beides, erzeugt Potato einen zufälligen Schlüssel, sodass jeder Neustart alle Sitzungen ungültig macht, und protokolliert eine Warnung, wenn OAuth ohne ihn konfiguriert ist.

Potato stellt zwei OAuth-Routen bereit. /auth/login/<provider> startet den Ablauf und /auth/callback/<provider> nimmt die Antwort entgegen. Der Callback ist die URL, die Sie beim Anbieter registrieren, für Google also https://your-domain.com/auth/callback/google. Einen Konfigurationsschlüssel redirect_uri gibt es nicht; die Route wird aus dem Anbieternamen abgeleitet.

Google

Erstellen Sie in der Google Cloud Console OAuth-2.0-Anmeldedaten vom Typ "Web application" und fügen Sie https://your-domain.com/auth/callback/google als autorisierte Weiterleitungs-URI hinzu. Für die lokale Entwicklung wird http://localhost:8000/auth/callback/google akzeptiert.

yaml
authentication:
  method: "oauth"
  providers:
    google:
      client_id: ${GOOGLE_CLIENT_ID}
      client_secret: ${GOOGLE_CLIENT_SECRET}
      allowed_domain: "umich.edu"

allowed_domain nimmt eine einzelne Domain als String. Nur Konten, deren E-Mail-Adresse zu dieser Domain gehört, können sich anmelden.

GitHub

Registrieren Sie eine OAuth-App unter GitHub Settings > Developer settings > OAuth Apps und setzen Sie die Callback-URL auf https://your-domain.com/auth/callback/github.

yaml
authentication:
  method: "oauth"
  providers:
    github:
      client_id: ${GITHUB_CLIENT_ID}
      client_secret: ${GITHUB_CLIENT_SECRET}
      allowed_org: "my-research-lab"

allowed_org nimmt einen einzelnen Organisationsnamen. Ist er gesetzt, fordert Potato den Scope read:org automatisch an, sodass Sie die Scopes nicht selbst angeben müssen. Die Mitgliedschaft des Annotators in der Organisation muss öffentlich sein, damit die Prüfung sie sieht.

HuggingFace

Erstellen Sie eine OAuth-Anwendung unter huggingface.co/settings/applications.

yaml
authentication:
  method: "oauth"
  providers:
    huggingface:
      client_id: ${HF_CLIENT_ID}
      client_secret: ${HF_CLIENT_SECRET}
  user_identity_field: "username"

Der HuggingFace-Anbieter verbindet sich mit dem OIDC-Discovery-Endpunkt von HuggingFace und fordert standardmäßig openid profile email an.

Generisches OIDC

Jeder Anbietername außer google, github und huggingface wird als generisches OIDC behandelt und erfordert discovery_url. Geben Sie ihm einen display_name, damit die Anmeldeschaltfläche lesbar ist.

yaml
authentication:
  method: "oauth"
  providers:
    oidc:
      display_name: "University SSO"
      discovery_url: "https://sso.university.edu/.well-known/openid-configuration"
      client_id: ${OIDC_CLIENT_ID}
      client_secret: ${OIDC_CLIENT_SECRET}
      scopes:
        - "openid"
        - "email"
        - "profile"

Die Callback-URL, die Sie beim Anbieter registrieren, folgt dem Anbieterschlüssel. Ein Block namens oidc verwendet also https://your-domain.com/auth/callback/oidc.

Discovery-URLs gängiger Anbieter:

AnbieterDiscovery-URL
Oktahttps://your-org.okta.com/.well-known/openid-configuration
Azure ADhttps://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration
Auth0https://your-domain.auth0.com/.well-known/openid-configuration
Keycloakhttps://keycloak.example.com/realms/{realm}/.well-known/openid-configuration

scopes ist standardmäßig ["openid", "email", "profile"].

Mehrere Anbieter

providers nimmt so viele Einträge auf wie nötig, und jeder erzeugt eine eigene Anmeldeschaltfläche.

yaml
authentication:
  method: "oauth"
  providers:
    google:
      client_id: ${GOOGLE_CLIENT_ID}
      client_secret: ${GOOGLE_CLIENT_SECRET}
    github:
      client_id: ${GITHUB_CLIENT_ID}
      client_secret: ${GITHUB_CLIENT_SECRET}
    oidc:
      display_name: "University SSO"
      discovery_url: "https://sso.university.edu/.well-known/openid-configuration"
      client_id: ${OIDC_CLIENT_ID}
      client_secret: ${OIDC_CLIENT_SECRET}

Mit user_identity_field: "email" ist jemand, der sich über Google und später mit derselben Adresse über den Anbieter der Universität anmeldet, derselbe Potato-Benutzer und landet wieder bei denselben Zuweisungen.

Identität und automatische Registrierung

user_identity_field legt fest, welcher Claim des Anbieters zum Potato-Benutzernamen wird. Erlaubt sind email, username, sub oder name, Standard ist email. Der Wert ist wichtig, weil die Ausgabeverzeichnisse der Annotatoren nach dem Benutzernamen benannt sind. Wer ihn mitten in einer Studie ändert, trennt einen Annotator von seiner früheren Arbeit.

auto_register steuert, ob eine erste erfolgreiche Anmeldung das Potato-Konto anlegt. Standard ist true.

yaml
authentication:
  method: "oauth"
  providers:
    google:
      client_id: ${GOOGLE_CLIENT_ID}
      client_secret: ${GOOGLE_CLIENT_SECRET}
      allowed_domain: "umich.edu"
  user_identity_field: "email"
  auto_register: true

Die Zugriffskontrolle läuft über den Anbieter und nicht über eine Adressliste in der Konfiguration. Um einzuschränken, wer sich anmelden kann, verwenden Sie allowed_domain für Google, allowed_org für GitHub oder die Gruppen- und App-Zuweisungsfunktionen Ihres OIDC-Anbieters.

Gemischter Modus

allow_local_login fügt unter den SSO-Schaltflächen das Formular für Benutzername und Passwort hinzu. Standard ist false, SSO ersetzt also die lokale Anmeldung, sofern Sie diese nicht einschalten.

yaml
require_password: true
 
user_config:
  allow_all_users: true
 
authentication:
  method: "oauth"
  providers:
    google:
      client_id: ${GOOGLE_CLIENT_ID}
      client_secret: ${GOOGLE_CLIENT_SECRET}
  allow_local_login: true

Bei einer Migration laufen beide eine Zeit lang parallel. Stellen Sie die Annotatoren um und entfernen Sie allow_local_login, sobald sich alle über den Anbieter angemeldet haben. Der gemischte Modus deckt auch Studien ab, die Annotatoren der eigenen Institution mit externen Annotatoren kombinieren, die dort kein Konto haben.

Lokale Konten im gemischten Modus verwenden dasselbe gesalzene PBKDF2-Hashing wie jedes andere Passwortkonto. Zu den Abläufen zum Zurücksetzen siehe Passwortverwaltung.

Sitzungen

Die Sitzungsdauer ist ein Schlüssel der obersten Ebene, in Tagen:

yaml
session_lifetime_days: 7

Erst ein stabiler secret_key sorgt dafür, dass Sitzungen einen Neustart überstehen.

Admin-Zugriff

Admin-Endpunkte authentifizieren sich mit einem API-Schlüssel statt über den OAuth-Block:

yaml
admin_api_key: ${POTATO_ADMIN_API_KEY}

Potato liest admin_api_key zuerst aus der Konfiguration, dann aus der Umgebungsvariablen POTATO_ADMIN_API_KEY. Ist keines von beiden gesetzt, erzeugt es einen Schlüssel und schreibt ihn in admin_api_key.txt im Aufgabenverzeichnis. Aufrufer übergeben ihn im Header X-API-Key.

HTTPS

OAuth-Anbieter verlangen in der Produktion HTTPS-Weiterleitungs-URIs. localhost ist davon ausgenommen, sodass die Entwicklung über HTTP ohne Zertifikat funktioniert.

Hinter einem Reverse Proxy müssen Sie das ursprüngliche Schema weiterreichen, sonst verwendet die von Potato erzeugte Callback-URL http und der Anbieter lehnt sie ab:

nginx
location / {
    proxy_pass http://localhost:8000;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
}

Konfigurationsreferenz

SchlüsselTypStandardBeschreibung
authentication.methodstringin_memoryin_memory, database, clerk oder oauth
authentication.providersdicterforderlich für oauthEin Eintrag pro Anbieter, mit dem Anbieternamen als Schlüssel
authentication.user_identity_fieldstringemailemail, username, sub oder name
authentication.allow_local_loginboolfalseZeigt das Passwortformular neben den SSO-Schaltflächen
authentication.auto_registerbooltrueLegt das Potato-Konto bei der ersten Anmeldung an
secret_keystringzufälligOberste Ebene. Für dauerhafte Sitzungen ist ein stabiler Wert nötig
session_lifetime_daysint7Sitzungsdauer, oberste Ebene
admin_api_keystringgeneriertSchlüssel der obersten Ebene für Admin-Endpunkte

Schlüssel pro Anbieter:

SchlüsselTypErforderlichBeschreibung
client_idstringjaVom Anbieter
client_secretstringjaVom Anbieter
discovery_urlstringfür generisches OIDCOIDC-Discovery-Endpunkt
display_namestringneinText der Anmeldeschaltfläche
allowed_domainstringneinBeschränkt auf eine E-Mail-Domain
allowed_orgstringneinBeschränkt auf eine GitHub-Organisation
scopeslistneinStandard ist ["openid", "email", "profile"]

Vollständiges Beispiel

yaml
annotation_task_name: "Annotation Project"
task_dir: "."
 
authentication:
  method: "oauth"
  providers:
    google:
      client_id: ${GOOGLE_CLIENT_ID}
      client_secret: ${GOOGLE_CLIENT_SECRET}
      allowed_domain: "umich.edu"
  user_identity_field: "email"
  auto_register: true
 
secret_key: ${POTATO_SECRET_KEY}
session_lifetime_days: 7
admin_api_key: ${POTATO_ADMIN_API_KEY}
 
data_files:
  - "data/instances.jsonl"
 
item_properties:
  id_key: id
  text_key: text
 
annotation_schemes:
  - annotation_type: radio
    name: sentiment
    description: "What is the sentiment of this text?"
    labels: [Positive, Neutral, Negative]
 
output_annotation_dir: "output/"
export_annotation_format: "jsonl"

Fehlerbehebung

Der Anbieter lehnt die Weiterleitungs-URI ab. Die beim Anbieter registrierte URI muss exakt /auth/callback/<provider> entsprechen. Schema und ein etwaiger nachfolgender Pfad zählen zum Abgleich, daher erzeugt ein Proxy, der X-Forwarded-Proto nicht weiterreicht, einen http-Callback, den der Anbieter ablehnt.

Nach einem Neustart sind alle abgemeldet. secret_key ist nicht gesetzt, also hat Potato beim Start einen neuen erzeugt.

Eine GitHub-Organisationsbeschränkung weist ein Mitglied ab. Seine Mitgliedschaft in der Organisation ist privat. Entweder macht das Mitglied sie öffentlich, oder Sie entfernen allowed_org und steuern den Zugriff auf andere Weise.

Die Validierung schlägt mit "providers is required" fehl. method: "oauth" braucht mindestens einen Eintrag unter providers. Der Wert der Methode ist oauth, nicht der Name eines Anbieters.


Weiterführende Informationen

Implementierungsdetails finden Sie in der Quelldokumentation.