Drop auth.server.ts re-exports + rename to .server.ts convention (task 5.2)

Two cleanups in one pass:

1. Update import paths app-wide from `~/lib/auth.server` to
   `~/lib/auth/session.server` for the four session helpers
   (sessionStorage, createSession, getSessionUser, destroySession).
   ~40 files: 33 simple path swaps where the file imported only session
   symbols, 5 splits where it also imported per-method auth functions
   (auth.verify.tsx, api.settings.email.ts, activities.\$id.tsx,
   routes.\$id.tsx, auth.accept-terms.tsx) — those keep one import
   from auth.server (for verifyMagicToken, canView, recordTermsAcceptance,
   etc.) and gain a second import from auth/session.server.
   Two more files used relative paths and were missed by the first
   grep pass (lib/oauth.server.ts and routes/oauth.authorize.tsx) —
   migrated too.
   The @deprecated re-exports block in auth.server.ts is gone.

2. Rename the new auth files to follow the project's `.server.ts`
   convention so Vite/React Router treat them as server-only (they
   read process.env.SESSION_SECRET, hit the DB, etc. — must NOT enter
   the client bundle):
   - auth/session.ts → auth/session.server.ts
   - auth/completion.ts → auth/completion.server.ts
   - auth/completion.test.ts → auth/completion.server.test.ts
   Done with `git mv` so blame is preserved.

Verified: typecheck + lint green; 126 unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ullrich Schäfer 2026-05-08 03:01:30 +02:00
parent fc2f938cf5
commit b9aac2859a
No known key found for this signature in database
GPG key ID: A32FF691A0F752D9
49 changed files with 57 additions and 62 deletions

View file

@ -396,20 +396,10 @@ export async function verifyEmailChange(token: string, userId: string): Promise<
return newEmail;
}
// --- Sessions ---
//
// Cookie session storage moved to `./auth/session.ts` so the post-verify
// chokepoint (`./auth/completion.ts`, see ADR-0004) can compose it. The
// re-exports below preserve the legacy `~/lib/auth.server` import path —
// new code should import from `~/lib/auth/session` directly.
/** @deprecated Import from `~/lib/auth/session` instead. */
export {
sessionStorage,
createSession,
getSessionUser,
destroySession,
} from "./auth/session.ts";
// Cookie session storage lives at `./auth/session.ts` (see ADR-0004 + the
// `auth/completion.ts` chokepoint). Import session helpers directly from
// there; this file owns only per-method identity verification (passkey
// ceremony + magic-token lifecycle) and Terms recording.
/**
* A row that carries the minimum a visibility check needs.

View file

@ -2,7 +2,7 @@
// docs/adr/0005-no-authmethod-polymorphism.md for why this exists.
import { describe, it, expect } from "vitest";
import { completeAuth } from "./completion.ts";
import { completeAuth } from "./completion.server.ts";
function reqWith(headers: Record<string, string> = {}) {
return new Request("https://localhost/whatever", { headers });

View file

@ -19,7 +19,7 @@
// user-creation time, before any path can reach completeAuth.
import { redirect } from "react-router";
import { createSession } from "./session.ts";
import { createSession } from "./session.server.ts";
export type CompleteAuthMode = "redirect" | "json";

View file

@ -7,7 +7,7 @@ import {
oauthCodes,
oauthTokens,
} from "@trails-cool/db/schema/journal";
import { getSessionUser } from "./auth.server.ts";
import { getSessionUser } from "./auth/session.server.ts";
const CODE_EXPIRY_MS = 10 * 60 * 1000; // 10 minutes
const ACCESS_TOKEN_EXPIRY_MS = 60 * 60 * 1000; // 1 hour

View file

@ -5,7 +5,7 @@ import type { Route } from "./+types/root";
import * as Sentry from "@sentry/react";
import { useTranslation } from "react-i18next";
import { detectLocale } from "@trails-cool/i18n";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { LocaleProvider } from "~/components/LocaleContext";
import { AlphaBanner } from "~/components/AlphaBanner";
import { useUnreadNotifications } from "~/hooks/useUnreadNotifications";

View file

@ -1,7 +1,8 @@
import { data, redirect } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/activities.$id";
import { canView, getSessionUser } from "~/lib/auth.server";
import { canView } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getActivity, deleteActivity, linkActivityToRoute, createRouteFromActivity, updateActivityVisibility } from "~/lib/activities.server";
import { deleteImportByActivity } from "~/lib/sync/imports.server";
import { listRoutes } from "~/lib/routes.server";

View file

@ -1,7 +1,7 @@
import { data, redirect } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/activities._index";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { listActivities } from "~/lib/activities.server";
import { ClientDate } from "~/components/ClientDate";
import { ClientMap } from "~/components/ClientMap";

View file

@ -1,6 +1,6 @@
import { data, redirect } from "react-router";
import type { Route } from "./+types/activities.new";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { createActivity } from "~/lib/activities.server";
import { listRoutes } from "~/lib/routes.server";

View file

@ -1,7 +1,7 @@
import { data } from "react-router";
import type { Route } from "./+types/api.auth.login";
import { startAuthentication, finishAuthentication, createMagicToken, verifyLoginCode } from "~/lib/auth.server";
import { completeAuth } from "~/lib/auth/completion";
import { completeAuth } from "~/lib/auth/completion.server";
import { sendMagicLink } from "~/lib/email.server";
export async function action({ request }: Route.ActionArgs) {

View file

@ -1,7 +1,7 @@
import { data } from "react-router";
import type { Route } from "./+types/api.auth.register";
import { startRegistration, finishRegistration, addPasskeyStart, addPasskeyFinish, registerWithMagicLink } from "~/lib/auth.server";
import { completeAuth } from "~/lib/auth/completion";
import { completeAuth } from "~/lib/auth/completion.server";
import { sendWelcome, sendMagicLink } from "~/lib/email.server";
import { logger } from "~/lib/logger.server";

View file

@ -1,5 +1,5 @@
import type { Route } from "./+types/api.events";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { register } from "~/lib/events.server";
import { countUnread } from "~/lib/notifications.server";

View file

@ -1,6 +1,6 @@
import { data } from "react-router";
import type { Route } from "./+types/api.follows.$id.approve";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { approveFollowRequest } from "~/lib/follow.server";
export async function action({ request, params }: Route.ActionArgs) {

View file

@ -1,6 +1,6 @@
import { data } from "react-router";
import type { Route } from "./+types/api.follows.$id.reject";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { rejectFollowRequest } from "~/lib/follow.server";
export async function action({ request, params }: Route.ActionArgs) {

View file

@ -1,6 +1,6 @@
import { data } from "react-router";
import type { Route } from "./+types/api.notifications.$id.read";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { markRead } from "~/lib/notifications.server";
export async function action({ request, params }: Route.ActionArgs) {

View file

@ -1,6 +1,6 @@
import { data } from "react-router";
import type { Route } from "./+types/api.notifications.read-all";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { markAllRead } from "~/lib/notifications.server";
export async function action({ request }: Route.ActionArgs) {

View file

@ -1,6 +1,6 @@
import { data } from "react-router";
import type { Route } from "./+types/api.routes.$id.edit-in-planner";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getRouteWithVersions } from "~/lib/routes.server";
import { createRouteToken } from "~/lib/jwt.server";

View file

@ -1,7 +1,7 @@
import { redirect } from "react-router";
import { eq } from "drizzle-orm";
import type { Route } from "./+types/api.settings.delete-account";
import { getSessionUser, destroySession } from "~/lib/auth.server";
import { getSessionUser, destroySession } from "~/lib/auth/session.server";
import { getDb } from "~/lib/db";
import { users } from "@trails-cool/db/schema/journal";

View file

@ -1,6 +1,7 @@
import { data, redirect } from "react-router";
import type { Route } from "./+types/api.settings.email";
import { getSessionUser, initiateEmailChange } from "~/lib/auth.server";
import { initiateEmailChange } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { sendMagicLink } from "~/lib/email.server";
export async function action({ request }: Route.ActionArgs) {

View file

@ -1,7 +1,7 @@
import { data, redirect } from "react-router";
import { eq, and } from "drizzle-orm";
import type { Route } from "./+types/api.settings.passkey.delete";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getDb } from "~/lib/db";
import { credentials } from "@trails-cool/db/schema/journal";

View file

@ -1,7 +1,7 @@
import { data, redirect } from "react-router";
import { eq } from "drizzle-orm";
import type { Route } from "./+types/api.settings.profile";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getDb } from "~/lib/db";
import { users } from "@trails-cool/db/schema/journal";

View file

@ -1,6 +1,6 @@
import { redirect, data } from "react-router";
import type { Route } from "./+types/api.sync.callback.$provider";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getManifest, link } from "~/lib/connected-services";
import {
decodeOAuthState,

View file

@ -1,6 +1,6 @@
import { redirect, data } from "react-router";
import type { Route } from "./+types/api.sync.connect.$provider";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getManifest } from "~/lib/connected-services";
import { encodeOAuthState } from "~/lib/connected-services/oauth-state.server";

View file

@ -1,6 +1,6 @@
import { redirect, data } from "react-router";
import type { Route } from "./+types/api.sync.disconnect.$provider";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getManifest, unlinkByUserProvider } from "~/lib/connected-services";
export async function action({ params, request }: Route.ActionArgs) {

View file

@ -1,6 +1,6 @@
import { redirect, data } from "react-router";
import type { Route } from "./+types/api.sync.push.$provider.$routeId";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getManifest } from "~/lib/connected-services";
import { pushRouteToProvider } from "~/lib/connected-services/push-action.server";
import { encodeOAuthState } from "~/lib/connected-services/oauth-state.server";

View file

@ -1,6 +1,6 @@
import { data } from "react-router";
import type { Route } from "./+types/api.users.$username.follow";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { followUser, FollowError } from "~/lib/follow.server";
export async function action({ request, params }: Route.ActionArgs) {

View file

@ -1,6 +1,6 @@
import { data } from "react-router";
import type { Route } from "./+types/api.users.$username.unfollow";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { unfollowUser, FollowError } from "~/lib/follow.server";
export async function action({ request, params }: Route.ActionArgs) {

View file

@ -2,7 +2,8 @@ import { useState } from "react";
import { Form, data, redirect, useLoaderData, useSearchParams } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/auth.accept-terms";
import { getSessionUser, recordTermsAcceptance } from "~/lib/auth.server";
import { recordTermsAcceptance } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { TERMS_VERSION } from "~/lib/legal";
export function meta() {

View file

@ -1,6 +1,6 @@
import { redirect } from "react-router";
import type { Route } from "./+types/auth.logout";
import { destroySession } from "~/lib/auth.server";
import { destroySession } from "~/lib/auth/session.server";
export async function action({ request }: Route.ActionArgs) {
const cookie = await destroySession(request);

View file

@ -1,7 +1,8 @@
import { redirect, data } from "react-router";
import type { Route } from "./+types/auth.verify";
import { verifyMagicToken, verifyEmailChange, getSessionUser } from "~/lib/auth.server";
import { completeAuth } from "~/lib/auth/completion";
import { verifyMagicToken, verifyEmailChange } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { completeAuth } from "~/lib/auth/completion.server";
export async function loader({ request }: Route.LoaderArgs) {
const url = new URL(request.url);

View file

@ -2,7 +2,7 @@ import { data } from "react-router";
import { Link } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/explore";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import {
EXPLORE_DEFAULT_PAGE_SIZE,
countFollowersBatch,

View file

@ -2,7 +2,7 @@ import { data, redirect } from "react-router";
import { Link } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/feed";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { listSocialFeed, listRecentPublicActivities } from "~/lib/activities.server";
import { ClientDate } from "~/components/ClientDate";
import { ClientMap } from "~/components/ClientMap";

View file

@ -3,7 +3,7 @@ import { data } from "react-router";
import { useTranslation } from "react-i18next";
import { eq, count } from "drizzle-orm";
import type { Route } from "./+types/home";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getDb } from "~/lib/db";
import { credentials } from "@trails-cool/db/schema/journal";
import { listActivities, listRecentPublicActivities } from "~/lib/activities.server";

View file

@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import { inArray, eq, and } from "drizzle-orm";
import type { Route } from "./+types/notifications";
import { getDb } from "~/lib/db";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { listForUser } from "~/lib/notifications.server";
import { linkFor } from "~/lib/notifications/link-for";
import { readPayload } from "~/lib/notifications/payload";

View file

@ -1,6 +1,6 @@
import type { Route } from "./+types/oauth.authorize";
import { redirect } from "react-router";
import { getSessionUser } from "../lib/auth.server.ts";
import { getSessionUser } from "../lib/auth/session.server.ts";
import {
getOAuthClient,
validateRedirectUri,

View file

@ -1,7 +1,7 @@
import { data, redirect } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/routes.$id.edit";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getRoute, updateRoute } from "~/lib/routes.server";
import type { Visibility } from "@trails-cool/db/schema/journal";

View file

@ -3,7 +3,8 @@ import { data, redirect } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/routes.$id";
import { and, eq } from "drizzle-orm";
import { canView, getSessionUser } from "~/lib/auth.server";
import { canView } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getRoute, getRouteWithVersions, deleteRoute, updateRoute } from "~/lib/routes.server";
import { getDb } from "~/lib/db";
import { syncPushes } from "@trails-cool/db/schema/journal";

View file

@ -1,7 +1,7 @@
import { data, redirect } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/routes._index";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { listRoutes } from "~/lib/routes.server";
import { ClientDate } from "~/components/ClientDate";
import { ClientMap } from "~/components/ClientMap";

View file

@ -1,7 +1,7 @@
import { data, redirect } from "react-router";
import type { Route } from "./+types/routes.new";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { createRoute } from "~/lib/routes.server";
export async function loader({ request }: Route.LoaderArgs) {

View file

@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
import { data, redirect, useFetcher } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/settings.account";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
export function meta() {
return [{ title: "Account — Settings — trails.cool" }];

View file

@ -2,7 +2,7 @@ import { data, redirect, useSearchParams } from "react-router";
import { useTranslation } from "react-i18next";
import { eq } from "drizzle-orm";
import type { Route } from "./+types/settings.connections";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getDb } from "~/lib/db";
import { connectedServices } from "@trails-cool/db/schema/journal";
import { getAllManifests } from "~/lib/connected-services";

View file

@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
import { data, redirect, useFetcher } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/settings.profile";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
export function meta() {
return [{ title: "Profile — Settings — trails.cool" }];

View file

@ -3,7 +3,7 @@ import { data, redirect, useFetcher } from "react-router";
import { useTranslation } from "react-i18next";
import { eq } from "drizzle-orm";
import type { Route } from "./+types/settings.security";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { getDb } from "~/lib/db";
import { credentials } from "@trails-cool/db/schema/journal";
import { ClientDate } from "~/components/ClientDate";

View file

@ -2,7 +2,7 @@ import { Outlet, redirect } from "react-router";
import { Link, useLocation } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/settings";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
export function meta() {
return [{ title: "Settings — trails.cool" }];

View file

@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
import { data, redirect, useFetcher } from "react-router";
import { useTranslation } from "react-i18next";
import type { Route } from "./+types/sync.import.$provider";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import {
getManifest,
getService,

View file

@ -4,7 +4,7 @@ import type { Route } from "./+types/users.$username.followers";
import { getDb } from "~/lib/db";
import { users } from "@trails-cool/db/schema/journal";
import { listFollowers, countFollowers, getFollowState } from "~/lib/follow.server";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { CollectionPage } from "~/components/CollectionPage";
export async function loader({ params, request }: Route.LoaderArgs) {

View file

@ -4,7 +4,7 @@ import type { Route } from "./+types/users.$username.following";
import { getDb } from "~/lib/db";
import { users } from "@trails-cool/db/schema/journal";
import { listFollowing, countFollowing, getFollowState } from "~/lib/follow.server";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { CollectionPage } from "~/components/CollectionPage";
export async function loader({ params, request }: Route.LoaderArgs) {

View file

@ -4,7 +4,7 @@ import { useTranslation } from "react-i18next";
import { getDb } from "~/lib/db";
import { users } from "@trails-cool/db/schema/journal";
import { eq } from "drizzle-orm";
import { getSessionUser } from "~/lib/auth.server";
import { getSessionUser } from "~/lib/auth/session.server";
import { listPublicRoutesForOwner } from "~/lib/routes.server";
import { listPublicActivitiesForOwner } from "~/lib/activities.server";
import { loadPersona } from "~/lib/demo-bot.server";