From 4b004e0c04c3bddc49fe1b695a105fdcd3bdf400 Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Fri, 23 Jan 2026 17:29:30 -0500 Subject: [PATCH] add inngest workflow to cleanup expired oauth sessions --- app/api/inngest/client.ts | 3 + .../cleanup_expired_oauth_sessions.ts | 101 ++++++++++++++++++ app/api/inngest/route.tsx | 2 + 3 files changed, 106 insertions(+) create mode 100644 app/api/inngest/functions/cleanup_expired_oauth_sessions.ts diff --git a/app/api/inngest/client.ts b/app/api/inngest/client.ts index 77a2266a..caf5854f 100644 --- a/app/api/inngest/client.ts +++ b/app/api/inngest/client.ts @@ -26,6 +26,9 @@ export type Events = { did: string; }; }; + "user/cleanup-expired-oauth-sessions": { + data: {}; + }; }; // Create a client to send and receive events diff --git a/app/api/inngest/functions/cleanup_expired_oauth_sessions.ts b/app/api/inngest/functions/cleanup_expired_oauth_sessions.ts new file mode 100644 index 00000000..65cf814e --- /dev/null +++ b/app/api/inngest/functions/cleanup_expired_oauth_sessions.ts @@ -0,0 +1,101 @@ +import { supabaseServerClient } from "supabase/serverClient"; +import { inngest } from "../client"; +import { restoreOAuthSession } from "src/atproto-oauth"; + +export const cleanup_expired_oauth_sessions = inngest.createFunction( + { id: "cleanup_expired_oauth_sessions" }, + { event: "user/cleanup-expired-oauth-sessions" }, + async ({ step }) => { + const stats = { + totalIdentities: 0, + validSessions: 0, + expiredSessions: 0, + tokensDeleted: 0, + errors: [] as string[], + }; + + // Step 1: Get all identities with an atp_did (OAuth users) + const identities = await step.run("fetch-oauth-identities", async () => { + const { data, error } = await supabaseServerClient + .from("identities") + .select("id, atp_did") + .not("atp_did", "is", null); + + if (error) { + throw new Error(`Failed to fetch identities: ${error.message}`); + } + return data || []; + }); + + stats.totalIdentities = identities.length; + console.log(`Found ${identities.length} OAuth identities to check`); + + // Step 2: Check each identity's OAuth session and cleanup if expired + for (const identity of identities) { + if (!identity.atp_did) continue; + + const result = await step.run( + `check-session-${identity.id}`, + async () => { + console.log(`Checking OAuth session for DID: ${identity.atp_did}`); + + const sessionResult = await restoreOAuthSession(identity.atp_did!); + + if (sessionResult.ok) { + console.log(` Session valid for ${identity.atp_did}`); + return { valid: true, tokensDeleted: 0 }; + } + + // Session is expired/invalid - delete associated auth tokens + console.log( + ` Session expired for ${identity.atp_did}: ${sessionResult.error.message}`, + ); + + const { data: deletedTokens, error: deleteError } = + await supabaseServerClient + .from("email_auth_tokens") + .delete() + .eq("identity", identity.id) + .select("id"); + + if (deleteError) { + console.error( + ` Error deleting tokens for identity ${identity.id}: ${deleteError.message}`, + ); + return { + valid: false, + tokensDeleted: 0, + error: deleteError.message, + }; + } + + const deletedCount = deletedTokens?.length || 0; + console.log( + ` Deleted ${deletedCount} auth tokens for identity ${identity.id}`, + ); + + return { valid: false, tokensDeleted: deletedCount }; + }, + ); + + if (result.valid) { + stats.validSessions++; + } else { + stats.expiredSessions++; + stats.tokensDeleted += result.tokensDeleted; + if ("error" in result && result.error) { + stats.errors.push( + `Identity ${identity.id}: ${result.error}`, + ); + } + } + } + + console.log("Cleanup completed:", stats); + + return { + success: stats.errors.length === 0, + stats, + }; + }, +); diff --git a/app/api/inngest/route.tsx b/app/api/inngest/route.tsx index f8886099..37fc24c4 100644 --- a/app/api/inngest/route.tsx +++ b/app/api/inngest/route.tsx @@ -5,6 +5,7 @@ import { come_online } from "./functions/come_online"; import { batched_update_profiles } from "./functions/batched_update_profiles"; import { index_follows } from "./functions/index_follows"; import { migrate_user_to_standard } from "./functions/migrate_user_to_standard"; +import { cleanup_expired_oauth_sessions } from "./functions/cleanup_expired_oauth_sessions"; export const { GET, POST, PUT } = serve({ client: inngest, @@ -14,5 +15,6 @@ export const { GET, POST, PUT } = serve({ batched_update_profiles, index_follows, migrate_user_to_standard, + cleanup_expired_oauth_sessions, ], }); -- 2.51.2