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
@@ -0,0 +1,52 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { notFound } from 'next/navigation'
|
||||
import { autorisierungsAdresse } from '@/server/kalender/oauth'
|
||||
import {
|
||||
alsAnbieter,
|
||||
faehigkeitFuer,
|
||||
istAnbieter,
|
||||
zustandErzeugen,
|
||||
} from '@/server/kalender/verbinden'
|
||||
import { angemeldet } from '@/server/berechtigung'
|
||||
import { erzwingeFaehigkeit } from '@/server/instanz'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
/**
|
||||
* Startet das Verbinden eines Kalenderkontos und leitet zum Anbieter weiter.
|
||||
*
|
||||
* Der Riegel sitzt hier serverseitig: Google gibt es nur daheim. In der
|
||||
* Firmeninstanz liefert diese Route 404 — nicht etwa einen Hinweis, dass es
|
||||
* die Möglichkeit anderswo gäbe.
|
||||
*/
|
||||
export async function GET(
|
||||
_anfrage: Request,
|
||||
{ params }: { params: Promise<{ anbieter: string }> },
|
||||
) {
|
||||
const { anbieter: roh } = await params
|
||||
if (!istAnbieter(roh)) notFound()
|
||||
|
||||
const anbieter = alsAnbieter(roh)
|
||||
erzwingeFaehigkeit(faehigkeitFuer(anbieter))
|
||||
|
||||
const benutzer = await angemeldet()
|
||||
if (!benutzer) {
|
||||
return NextResponse.redirect(
|
||||
new URL('/anmelden?weiter=%2Fkalenderkonten', process.env.APP_URL ?? ''),
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const zustand = zustandErzeugen(anbieter, benutzer.id)
|
||||
return NextResponse.redirect(autorisierungsAdresse(anbieter, zustand))
|
||||
} catch (fehler) {
|
||||
const meldung =
|
||||
fehler instanceof Error ? fehler.message : 'Der Anbieter ist nicht eingerichtet.'
|
||||
return NextResponse.redirect(
|
||||
new URL(
|
||||
`/kalenderkonten?fehler=${encodeURIComponent(meldung)}`,
|
||||
process.env.APP_URL ?? '',
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user