UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

97 lines (96 loc) 3.68 kB
import { deleteSessionCookie } from "../../cookies/index.mjs"; import { createAuthEndpoint } from "@better-auth/core/api"; import * as z from "zod"; //#region src/api/routes/sign-out.ts const signOutBodySchema = z.object({ callbackURL: z.string().meta({ description: "The URL to redirect to after provider logout" }).optional(), disableRedirect: z.boolean().meta({ description: "Return the provider logout URL without redirecting" }).optional(), state: z.string().meta({ description: "State to pass to the provider logout endpoint" }).optional() }).optional(); const signOutResponse = (providerLogout) => ({ success: true, url: providerLogout?.url, redirect: providerLogout?.redirect }); const signOut = createAuthEndpoint("/sign-out", { method: "POST", body: signOutBodySchema, operationId: "signOut", requireHeaders: true, metadata: { openapi: { operationId: "signOut", description: "Sign out the current user", responses: { "200": { description: "Success", content: { "application/json": { schema: { type: "object", properties: { success: { type: "boolean" }, url: { type: "string", description: "Provider logout URL when RP-initiated logout is available" }, redirect: { type: "boolean", description: "Whether the client should redirect to the provider logout URL" } } } } } } } } } }, async (ctx) => { const sessionCookieToken = await ctx.getSignedCookie(ctx.context.authCookies.sessionToken.name, ctx.context.secret); let currentSession = null; if (sessionCookieToken) try { currentSession = await ctx.context.internalAdapter.findSession(sessionCookieToken); } catch (e) { ctx.context.logger.error("Failed to read session from database", e); } if (sessionCookieToken) try { await ctx.context.internalAdapter.deleteSession(sessionCookieToken); } catch (e) { ctx.context.logger.error("Failed to delete session from database", e); } deleteSessionCookie(ctx); const providerLogoutResult = await (async () => { try { if (!currentSession) return null; const accounts = await ctx.context.internalAdapter.findAccounts(currentSession.user.id); const providersById = new Map(ctx.context.socialProviders.map((provider) => [provider.id, provider])); const logoutAccounts = accounts.filter((account) => Boolean(providersById.get(account.providerId)?.createEndSessionURL)).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()); if (logoutAccounts.length === 0) return null; const seenProviderIds = /* @__PURE__ */ new Set(); const postLogoutRedirectURI = ctx.body?.callbackURL ? new URL(ctx.body.callbackURL, ctx.context.baseURL).toString() : void 0; for (const account of logoutAccounts) { if (seenProviderIds.has(account.providerId)) continue; seenProviderIds.add(account.providerId); const provider = providersById.get(account.providerId); try { const url = await provider?.createEndSessionURL?.({ idToken: account.idToken, postLogoutRedirectURI, state: ctx.body?.state }); if (url) return url.toString(); } catch (e) { ctx.context.logger.error(`Failed to create logout URL for provider "${account.providerId}"`, e); } } return null; } catch (e) { ctx.context.logger.error("Failed to create provider logout URL", e); return null; } })(); if (providerLogoutResult) { const shouldRedirect = !ctx.body?.disableRedirect; if (shouldRedirect) ctx.setHeader("Location", providerLogoutResult); return ctx.json(signOutResponse({ url: providerLogoutResult, redirect: shouldRedirect })); } return ctx.json(signOutResponse()); }); //#endregion export { signOut };