feat(kalender): OAuth und Abgleich für Graph und Google
Verbinden, Token erneuern und seitenweise abrufen — mit blankem fetch, ohne SDK. Es geht um drei Aufrufe je Anbieter, und die Antworten werden ohnehin mit zod geprüft; ein SDK brächte vor allem Abhängigkeiten und blinde Flecken. Entscheidungen: - Der Zustandswert im OAuth-Ablauf ist mit AUTH_SECRET signiert und trägt die Benutzerkennung. Ohne das ließe sich ein fremdes Kalenderkonto an ein beliebiges Konto hängen. - Die Berechtigungen schließen das Schreiben ein, obwohl M3 nur liest. Sonst müsste ab M4 jedes Konto neu zustimmen — und die Zustimmung ist genau der Schritt, an dem Einrichtung scheitert. - Läuft der Abgleichverweis ab (beide Anbieter antworten mit 410), wird er verworfen und von vorn gelesen. Der Ablauf ist so gebaut, dass ein vollständiger Durchlauf jederzeit möglich ist. - Googles syncToken trägt die Zeitgrenzen nicht mit; Folgeabgleiche liefern also auch Änderungen außerhalb unseres Fensters. Was daneben liegt, wird verworfen — sonst wüchse der Spiegel unbegrenzt. Die Instanzregel greift serverseitig: Google gibt es nur daheim, in der Firmeninstanz liefert die Route 404 statt eines Hinweises. Fehlermeldungen nennen nie den Antwortkörper des Token-Endpunkts — der enthält das Client-Secret oft im Echo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6fda2a579e
commit
9946e7c308
@@ -30,6 +30,17 @@ const schema = z.object({
|
||||
WEBAUTHN_RP_NAME: z.string().min(1).default('Planer'),
|
||||
WEBAUTHN_ORIGIN: z.url(),
|
||||
|
||||
// --- Kalenderanbieter (M3) ---
|
||||
// Graph gibt es in beiden Instanzen, Google nur daheim. Welche Werte
|
||||
// tatsaechlich benutzt werden duerfen, entscheidet src/lib/instanz.ts.
|
||||
GRAPH_CLIENT_ID: z.string().trim().default(''),
|
||||
GRAPH_CLIENT_SECRET: z.string().trim().default(''),
|
||||
/// Tenant-ID, oder "organizations" fuer jeden Arbeitsplatz-Tenant.
|
||||
GRAPH_TENANT_ID: z.string().trim().default('organizations'),
|
||||
|
||||
GOOGLE_CLIENT_ID: z.string().trim().default(''),
|
||||
GOOGLE_CLIENT_SECRET: z.string().trim().default(''),
|
||||
|
||||
DEMO_MODUS: jaNein.default(false),
|
||||
})
|
||||
|
||||
@@ -37,6 +48,9 @@ export type Umgebung = z.infer<typeof schema> & {
|
||||
instanz: Instanz
|
||||
/** OIDC ist nur eingerichtet, wenn alle vier Werte gesetzt sind. */
|
||||
oidcAktiv: boolean
|
||||
/** Graph laesst sich verbinden, sobald Kennung und Geheimnis stehen. */
|
||||
graphAktiv: boolean
|
||||
googleAktiv: boolean
|
||||
}
|
||||
|
||||
let zwischengespeichert: Umgebung | null = null
|
||||
@@ -56,6 +70,8 @@ function lesen(): Umgebung {
|
||||
oidcAktiv: Boolean(
|
||||
werte.OIDC_ISSUER && werte.OIDC_CLIENT_ID && werte.OIDC_CLIENT_SECRET,
|
||||
),
|
||||
graphAktiv: Boolean(werte.GRAPH_CLIENT_ID && werte.GRAPH_CLIENT_SECRET),
|
||||
googleAktiv: Boolean(werte.GOOGLE_CLIENT_ID && werte.GOOGLE_CLIENT_SECRET),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user