Zum Inhalt springen
Ulifyi-KontoProfil, Passwort und Geräte verwalten
Ulifyi-Konto erstellen

Mit Ulifyi anmelden

Die Anleitung für Entwickler: App anlegen, Anmeldung einbauen, Berechtigungen wählen und die App veröffentlichen.

Zur Developer Console

Überblick

Mit „Mit Ulifyi anmelden“ melden sich Menschen mit ihrem Ulifyi-Konto in deiner App an. Ulifyi nutzt dafür OAuth 2.0 mit dem Authorization-Code-Flow und PKCE. Deine App bekommt nie das Passwort zu sehen, sondern nur einen Token für genau die Berechtigungen, denen die Person zugestimmt hat.

Alle Adressen liegen auf https://auth.uli.fyi:

ZweckMethode und Pfad
Anmeldung startenGET /auth/identify/authorize
Code gegen Tokens tauschenPOST /auth/identify/token
Tokens erneuernPOST /auth/identify/refresh
Wer ist angemeldet?GET /auth/identify/userinfo
Abmelden (Token widerrufen)POST /auth/identify/revoke
Alle Berechtigungen (Scopes)GET /api/auth/identify/scopes

Der Ablauf in Kurzform:

  1. Deine App schickt den Browser zu authorize, mit Client-ID, Redirect-URI, Scopes, state und PKCE-Challenge.
  2. Ulifyi meldet die Person an (falls nötig) und zeigt den Zustimmungsbildschirm.
  3. Nach „Erlauben“ kommt der Browser mit ?code=…&state=… zu deiner Redirect-URI zurück.
  4. Dein Server tauscht den Code gegen ein Access-Token und ein Refresh-Token.
  5. Mit dem Access-Token fragst du userinfo ab und, je nach Scope, die Ulifyi-Business-API.

App anlegen

Apps legst du in der Developer Console an. Du brauchst nur ein Ulifyi-Konto.

  1. Neue App: Name, Support-E-Mail und Herausgeber. Herausgeber bist du selbst oder eine Firma, die du gegründet hast oder in der deine Rolle Zugriff auf die Entwickler-Einstellungen hat. Der Herausgeber steht später auf dem Zustimmungsbildschirm.
  2. Client-ID und Client-Secret werden angezeigt. Das Secret siehst du nur dieses eine Mal. Ulifyi speichert nur einen Hash davon. Verlierst du es, erzeugst du in der Console ein neues. Das alte gilt dann sofort nicht mehr.
  3. Redirect-URIs eintragen (siehe Redirect-URIs).
  4. Berechtigungen wählen (siehe Berechtigungen).
  5. Logo hochladen und Website, Datenschutzerklärung und optional Nutzungsbedingungen eintragen.

Grenzen: höchstens 10 Apps pro Konto, 10 Redirect-URIs und 100 Testnutzer pro App. Namen mit „Ulifyi“ sind für Ulifyi reserviert.

Schnellstart

Ein vollständiges Beispiel mit Node.js 18 oder neuer, ohne Bibliothek. Der Code-Austausch läuft auf deinem Server, damit das Client-Secret nie in den Browser gelangt.

1. Anmeldung starten
import crypto from "node:crypto";

const AUTH = "https://auth.uli.fyi";
const CLIENT_ID = process.env.ULIFYI_CLIENT_ID;
const REDIRECT_URI = "https://deine-app.de/auth/ulifyi/callback";

const base64url = (buf) => buf.toString("base64url");

export function startSignIn(session) {
  const state = base64url(crypto.randomBytes(16));
  const verifier = base64url(crypto.randomBytes(32));
  const challenge = base64url(crypto.createHash("sha256").update(verifier).digest());
  session.ulifyi = { state, verifier };

  const url = new URL("/auth/identify/authorize", AUTH);
  url.search = new URLSearchParams({
    response_type: "code",
    client_id: CLIENT_ID,
    redirect_uri: REDIRECT_URI,
    scope: "user user:email",
    state,
    code_challenge: challenge,
    code_challenge_method: "S256",
  }).toString();
  return url.toString(); // dorthin weiterleiten
}
2. Rückkehr zur Redirect-URI
export async function finishSignIn(session, query) {
  const saved = session.ulifyi;
  delete session.ulifyi;
  if (query.error) throw new Error(`Abgebrochen: ${query.error}`);
  if (!saved || query.state !== saved.state) throw new Error("state passt nicht");

  const res = await fetch(`${AUTH}/auth/identify/token`, {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      response_type: "code",
      client_id: CLIENT_ID,
      code: query.code,
      redirect_uri: REDIRECT_URI,
      code_verifier: saved.verifier,
    }),
  });
  if (!res.ok) throw new Error((await res.json()).error);
  return res.json(); // { access_token, refresh_token, … }
}
3. Nutzerdaten abrufen
export async function whoIsIt(accessToken) {
  const res = await fetch(`${AUTH}/auth/identify/userinfo`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  if (!res.ok) throw new Error((await res.json()).error);
  const { user } = await res.json();
  return user; // { user_id, username, display_name, email, avatar }
}

Autorisierungsanfrage

Schicke den Browser (kein Hintergrund-Request) zu GET https://auth.uli.fyi/auth/identify/authorize:

ParameterPflichtBedeutung
response_typejaImmer code.
client_idjaDie Client-ID aus der Console.
redirect_urijaMuss Zeichen für Zeichen einer eingetragenen Redirect-URI entsprechen.
scopejaScopes mit Leerzeichen getrennt, z. B. user user:email.
statejaZufallswert, mindestens 8 Zeichen. Kommt unverändert zurück.
code_challengeempfohlenPKCE: Base64url des SHA-256 deines code_verifier. Pflicht für Apps ohne Secret (Mobil, Desktop, Single-Page).
code_challenge_methodneinNur S256 (Standard).
promptneinlogin oder select_account: Konto wechseln. consent: Zustimmung immer neu abfragen.

Hat die Person deiner App schon einmal genau diese Scopes erlaubt, leitet Ulifyi ohne Zustimmungsbildschirm direkt zurück. Ausnahmen: prompt=consent oder Firmen-Scopes (die Firma wird jedes Mal gewählt).

Zurück kommt der Browser so:

Erlaubt
https://deine-app.de/auth/ulifyi/callback?code=9b1c…4e&state=Zp81vQe0aB
Abgelehnt
https://deine-app.de/auth/ulifyi/callback?error=access_denied&state=Zp81vQe0aB

Prüfe immer zuerst, ob state zu dem passt, was du gespeichert hast. Sonst verwirf die Antwort.

Code einlösen

POST https://auth.uli.fyi/auth/identify/token mit application/x-www-form-urlencoded oder JSON:

FeldBedeutung
response_typecode
client_iddeine Client-ID
codeder Code aus der Rückleitung
redirect_uridieselbe Redirect-URI wie bei der Anfrage
code_verifierder PKCE-Verifier, oder
client_secretdein Client-Secret (nur vom Server)

Der Code gilt 5 Minuten und nur einmal, auch wenn der Tausch fehlschlägt.

Antwort
{
  "access_token": "eyJhbGciOiJSUzI1NiIs…",
  "refresh_token": "eyJhbGciOiJSUzI1NiIs…",
  "expires_in": 1791367200000,
  "access_token_expires_in": 1791367200000,
  "refresh_token_expires_in": 1793958300000
}

Das Access-Token ist ein JWT (RS256). Behandle es trotzdem als undurchsichtig und frage userinfo ab, statt es selbst auszuwerten.

Nutzerdaten abrufen

GET https://auth.uli.fyi/auth/identify/userinfo mit Authorization: Bearer <access_token>:

Antwort
{
  "user": {
    "user_id": "214266353199973343",
    "username": "max",
    "display_name": "Max Muster",
    "email": "[email protected]",
    "avatar": "https://cdn.uli.fyi/avatars/214266353199973343/a1b2c3.png"
  },
  "scopes": ["user", "user:email"],
  "organization": null
}
  • email ist nur mit dem Scope user:email gesetzt, sonst null.
  • organization ist gesetzt, wenn die Person bei der Anmeldung eine Firma gewählt hat (siehe Firmen-Berechtigungen).
  • Ändert die Person ihr Passwort, werden ältere Tokens ungültig (401 password_changed). Melde sie dann neu an.

Tokens erneuern

Bevor das Access-Token abläuft, holst du mit dem Refresh-Token ein neues Paar:

Anfrage
curl -X POST https://auth.uli.fyi/auth/identify/refresh \
  -H "Content-Type: application/json" \
  -d '{"refresh_token":"eyJhbGciOiJSUzI1NiIs…"}'

Die Antwort hat dieselbe Form wie beim Code-Tausch. Speichere das neue Refresh-Token und verwirf das alte. Ulifyi tauscht Refresh-Tokens bei jeder Nutzung aus:

  • Wird das alte Token innerhalb von 60 Sekunden noch einmal benutzt (paralleler Request, Wiederholung), kommt dieselbe Antwort noch einmal.
  • Wird es später noch einmal benutzt, wertet Ulifyi das als gestohlenes Token, beendet die ganze Anmeldung und antwortet 401 token_replayed.
  • 401 authorization_revoked heißt: Die Person hat deine App in ihrem Konto getrennt. Melde sie neu an.

Abmelden

Meldet sich jemand in deiner App ab, beende auch die Verbindung zu Ulifyi:

Anfrage
curl -X POST https://auth.uli.fyi/auth/identify/revoke \
  -H "Content-Type: application/json" \
  -d '{"token":"<refresh_token>","client_id":"<deine Client-ID>"}'

Die Antwort ist immer 200 {}, auch für unbekannte Tokens. Das Refresh-Token funktioniert danach nicht mehr. Bereits ausgegebene Access-Tokens laufen nach spätestens 15 Minuten ab.

Berechtigungen

Jede Berechtigung (Scope) hat eine Stufe. Sie bestimmt, ob Ulifyi deine App prüfen muss, bevor sie die Berechtigung für alle nutzen darf.

StufeBedeutung
BasisFunktioniert sofort, auch veröffentlicht ohne Prüfung.
SensibelPrivate Daten lesen. Im Testmodus sofort, veröffentlicht erst nach der Prüfung.
EingeschränktIm Namen der Person handeln oder Firmendaten ändern. Im Testmodus sofort, veröffentlicht erst nach der Prüfung.
ScopeStufeWas die Person erlaubt
userBasisName, Benutzername und Profilbild sehen
user:emailBasisE-Mail-Adresse sehen
user:postsSensibelPrivate Posts sehen
user:friendsSensibelFreundesliste sehen
user:posts:uploadEingeschränktNeue Posts erstellen
user:posts:deleteEingeschränktPosts löschen
user:posts:visibilityEingeschränktSichtbarkeit von Posts ändern
user:add:friendEingeschränktFreunde hinzufügen
user:remove:friendEingeschränktFreunde entfernen
orgBasisWissen, für welche Firma die App genutzt wird
org:view:membersSensibelMitglieder der Firma sehen
org:view:settingsSensibelEinstellungen der Firma sehen
org:view:formsSensibelFormulare und Einsendungen sehen
org:view:financeSensibelKassenbuch der Firma sehen
pay:planSensibelTarif und Funktionen der Firma über Ulifyi Pay sehen
org:manage:membersEingeschränktMitglieder einladen, entfernen, Rollen ändern
org:manage:settings:generalEingeschränktName, Beschreibung und Website ändern
org:manage:settings:brandingEingeschränktLogo und Icon ändern
org:manage:rolesEingeschränktRollen und Rechte verwalten
org:manage:formsEingeschränktEinsendungen bearbeiten
org:manage:financeEingeschränktEinnahmen, Ausgaben, Konten und Belege verwalten
pay:subscriptionsEingeschränktAbos und Laufzeiten über Ulifyi Pay sehen

Unter-Scopes brauchen ihren Basis-Scope: user:email braucht user, jeder Firmen-Scope braucht org. Die Console ergänzt das beim Speichern selbst. Deine Anfrage muss sie aber mitschicken. Fordere nur an, was deine App wirklich braucht: Die Person sieht jede Zeile auf dem Zustimmungsbildschirm.

Die aktuelle Liste mit Texten in Deutsch und Englisch liefert GET https://auth.uli.fyi/api/auth/identify/scopes als JSON.

Firmen-Berechtigungen

Mit org und den org:- oder pay:-Scopes arbeitet deine App für eine Firma in Ulifyi Business:

  • Auf dem Zustimmungsbildschirm wählt die Person eine ihrer Firmen. Angeboten werden nur Firmen, in denen ihre Rolle die nötigen Rechte hat, z. B. „Formulare ansehen“ für org:view:forms.
  • Das Access-Token gilt dann für genau diese Firma. userinfo liefert sie unter organization, mit oId, Name, Rolle und Rechten.
  • Die Rechte werden bei jedem Aufruf neu geprüft. Verliert die Person ihre Rolle oder verlässt sie die Firma, schlagen Aufrufe fehl und das Refresh-Token endet.

Mit dem Access-Token rufst du die Ulifyi-Business-API auf, z. B. die Formulare der Firma:

Formulare der Firma (org:view:forms)
curl https://pay.uli.fyi/v1/orgs/<oId>/forms \
  -H "Authorization: Bearer <access_token>"

Testmodus und Veröffentlichung

Jede neue App startet im Testmodus. So kommst du Schritt für Schritt zu einer App, die alle nutzen können:

StufeWer kann sich anmeldenWelche Scopes funktionieren
1. TestmodusNur du und deine TestnutzerAlle gewählten Scopes. Der Zustimmungsbildschirm zeigt „Diese App wird noch getestet“.
2. VeröffentlichtJedes Ulifyi-KontoNur Basis-Scopes. Der Zustimmungsbildschirm warnt: „Ulifyi hat diese App nicht geprüft“.
3. GeprüftJedes Ulifyi-KontoAlle von Ulifyi freigegebenen Scopes. Keine Warnung, ein Häkchen am App-Namen.

Testnutzer fügst du in der Console mit E-Mail-Adresse oder @Benutzername hinzu (bis zu 100). Wer nicht auf der Liste steht, sieht im Testmodus „Zugriff blockiert“ und die Support-E-Mail deiner App.

Veröffentlichen kannst du, sobald Support-E-Mail, Website, Datenschutzerklärung und mindestens eine Redirect-URI eingetragen sind. Fragt eine veröffentlichte App einen Scope an, den Ulifyi noch nicht freigegeben hat, wird die Anmeldung blockiert. Die Person sieht dann „Zugriff blockiert“ mit dem Hinweis, dass Ulifyi diesen Zugriff noch nicht geprüft hat.

Prüfung durch Ulifyi

Für sensible und eingeschränkte Scopes beantragst du in der Console eine Prüfung. Beschreibe in mindestens 30 Zeichen, wofür deine App die Daten braucht und wo die Person das in deiner App sieht.

Das Ulifyi-Team prüft:

  • ob Website, Name und Logo zum Herausgeber passen und nicht mit Ulifyi oder anderen Marken verwechselt werden können,
  • ob deine Datenschutzerklärung erklärt, was mit den Ulifyi-Daten passiert,
  • ob jeder angefragte Scope für den beschriebenen Zweck nötig ist.

Das Ergebnis siehst du in der Console. Ulifyi kann alle, einige oder keine der Scopes freigeben und schreibt dir dazu eine Begründung. Danach kannst du jederzeit eine neue Prüfung beantragen.

Ist deine App schon geprüft und du brauchst später mehr, behält sie bis zur Entscheidung alles, was schon freigegeben ist. Änderst du nach der Prüfung Name, Website oder Datenschutzerklärung, sieht Ulifyi sich die App noch einmal an.

Das sieht die Person, bevor sie deiner App etwas erlaubt:

  • Logo, Name und Herausgeber deiner App, bei geprüften Apps mit Häkchen.
  • Jede angefragte Berechtigung als eigene Zeile in ihrer Sprache (Deutsch oder Englisch).
  • Bei Firmen-Scopes die Auswahl der Firma.
  • Hinweise, wenn die App getestet wird oder nicht geprüft ist.
  • Links zu Website, Datenschutzerklärung und Nutzungsbedingungen deiner App.

Die Person kann eine Verbindung jederzeit in ihrem Ulifyi-Konto unter „Verbundene Apps“ trennen.

Logo und Branding

  • Formate: PNG, JPEG, WebP oder SVG, höchstens 5 MB.
  • Ulifyi macht daraus ein quadratisches PNG mit 512 × 512 Pixeln. Nimm ein quadratisches Motiv mit etwas Rand.
  • Das Logo erscheint auf dem Zustimmungsbildschirm und in „Verbundene Apps“.
  • Nutze keine Ulifyi-Logos, den Ulifyi-Bären oder Namen, die nach Ulifyi klingen. Dein App-Name darf „Ulifyi“ nicht enthalten.
  • Für einen Login-Button nutze „Mit Ulifyi anmelden“ (Englisch: „Sign in with Ulifyi“).

Redirect-URIs

Ulifyi leitet nur an Adressen zurück, die du in der Console eingetragen hast, und vergleicht sie Zeichen für Zeichen:

  • https:// für Websites, z. B. https://deine-app.de/auth/ulifyi/callback.
  • http:// nur für localhost, 127.0.0.1 und [::1] zum Entwickeln.
  • Eigene Schemata für Mobil- und Desktop-Apps mit Punkt im Namen, z. B. de.deineapp:/oauth.
  • Nicht erlaubt: Fragmente (#…), Zugangsdaten in der Adresse und Schemata wie javascript:, data: oder file:.

Fehler

WoWas du siehstBedeutung
AutorisierungFehlerseite 400Anfrage ungültig: Parameter fehlt, redirect_uri nicht eingetragen, Scope unbekannt oder Basis-Scope fehlt.
AutorisierungSeite „Zugriff blockiert“ 403Testmodus und die Person ist kein Testnutzer, oder ein nicht freigegebener Scope in einer veröffentlichten App.
Rückleitung?error=access_deniedDie Person hat auf „Abbrechen“ geklickt.
Token400 mit errorCode abgelaufen oder schon benutzt, redirect_uri anders, falscher Verifier oder falsches Secret.
Refresh401 token_replayedAltes Refresh-Token noch einmal benutzt: Anmeldung beendet.
Refresh401 authorization_revokedDie Person hat deine App getrennt.
userinfo401 invalid_tokenToken abgelaufen oder ungültig: erneuern oder neu anmelden.
alle429Zu viele Anfragen. Warte so viele Sekunden, wie Retry-After angibt.

Sicherheit

  • Immer state prüfen und immer PKCE nutzen, auch wenn deine App ein Secret hat.
  • Das Client-Secret gehört nur auf deinen Server, nie in eine Mobil-App, eine Single-Page-App oder ein Repository. Ist es bekannt geworden, erzeuge in der Console sofort ein neues.
  • Refresh-Tokens sicher speichern (verschlüsselt, nur serverseitig oder im sicheren Speicher des Geräts) und nach jedem Erneuern das neue speichern.
  • Nur nötige Scopes anfragen. Weniger Scopes heißt weniger Fragen auf dem Zustimmungsbildschirm und eine schnellere Prüfung.
  • Sicherheitslücken meldest du über eine Support-Anfrage mit dem Thema „Sicherheit“.

Etwas unklar oder fehlt etwas?

Anfrage an den Support