Sign in through the admin app instead of this one

The frontend half of the calendar auth cutover (step 4 of
docs/calendar-auth-migration.md in the API repo). This app now shares one
identity with the tickets, feedback and admin apps.

Every call carries the session cookie via withCredentials rather than
appending sessionId/sessionKey to the URL, so there is no credential left in
api.service.ts at all - that was DEFERRED_SECURITY.md item 1.

The login and registration forms are gone. Accounts exist only by invitation
from the admin app, so both were one redirect; sign-out ends the session for
all four apps and returns here, so doing it by accident costs one click.

401 and 403 are deliberately not collapsed. Only 401 goes to the login page:
redirecting on 403 produces a loop where signing in succeeds and lands
straight back on the refusal, and doing it for an unreachable API produces the
same loop with no way out. Both of those now render a message instead.

src/app/models/session.ts and the unrouted LoginComponent are dead but left in
place; removing files is a separate decision.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-06 22:23:45 +02:00
parent ded89d54f3
commit 7ce42324da
9 changed files with 232 additions and 242 deletions
+39 -6
View File
@@ -17,9 +17,35 @@ No linter is configured in this project.
Angular 18 single-page app for managing calendar events for the "Nachklang" organization. Uses Angular Material for UI, RxJS for async data, and reactive forms. No NgRx — state lives in component local variables.
**Environments:**
- Dev: `http://localhost:3000` (expects backend running locally)
- Prod: `https://api.nachklang.art`
**Environments** (`src/environments/`): `apiUrl` and `adminAppUrl`.
- Dev: `http://localhost:3000` (expects backend running locally) / `http://localhost:3002`
- Prod: `https://api.nachklang.art` / `https://admin.nachklang.art`
## Authentication
**This app has no login form and no accounts of its own.** Since the auth cutover
(`docs/calendar-auth-migration.md` in the API repo) it shares one identity with the tickets,
feedback and admin apps: accounts live in the admin app, and the session is an httpOnly
cookie on `.nachklang.art` that the *API's* host sets. Consequences that cannot be designed
around:
- `withCredentials: true` is mandatory on every call (`api.service.ts` sets it once). Without
it the browser sends no cookie and the API answers 401.
- This app can never read or verify the session. It calls `GET /admin/me` and believes the
answer; the API's `requireAppAccess('calendar')` is the actual gate.
- 401 and 403 mean different things and must not be collapsed. 401 means "nobody is signed
in" and is the only one worth redirecting to the login page - redirecting on 403 produces a
loop where signing in succeeds and lands straight back on the refusal. See `failure` in
`admin.component.ts`.
- Sign-out ends the session for *all four* apps; there is only one.
The dev server must be reachable at a port the API trusts. `ng serve` defaults to 4200, which
is in better-auth's dev `localhostOrigins` and in the admin app's
`NEXT_PUBLIC_ALLOWED_REDIRECT_ORIGINS`; another port fails sign-out and the return redirect,
not the sign-in.
**The public calendar stays anonymous.** `GET /calendar/events/public/json` needs no session
at all, because nachklang.art reads it to show the next upcoming event.
**Routing** (`app.routing.ts`):
- `/``LandingpageComponent`
@@ -27,8 +53,9 @@ Angular 18 single-page app for managing calendar events for the "Nachklang" orga
- `**``NotfoundComponent`
**Service layer** (`src/app/services/`):
- `api.service.ts` — all HTTP calls to the backend REST API; session credentials are passed as query params (`sessionId`, `sessionKey`)
- `utils.service.ts`localStorage helpers for persisting session and user data
- `api.service.ts` — all HTTP calls to the backend REST API; carries the session cookie via `withCredentials`, holds no credential itself
- `admin-auth.service.ts`where signing in happens: the admin app's login URL (with a `?redirect=` back here) and sign-out
- `utils.service.ts` — caches the signed-in user's display name for unsaved draft rows. Cosmetic only; the server takes the author from the session
**Data models** (`src/app/models/`): `Event`, `User`, `Session`
@@ -36,4 +63,10 @@ Angular 18 single-page app for managing calendar events for the "Nachklang" orga
**Calendar-specific behavior:** Birthday calendar auto-sets recurrence to YEARLY. Events have a `status` field (`DRAFT` / `DELETED`).
**Session lifecycle:** `checkSession()` is called on `AdminComponent` init; on failure it redirects to `/`.
**Session lifecycle:** `AdminComponent` calls `me()` on init. 401 redirects to the admin app's
login carrying this URL as the return target; 403 (or a signed-in account without the
`calendar` permission) shows a refusal with Reload/Sign out; anything else shows "cannot be
reached". None of those three redirect, on purpose.
**Dead since the cutover, kept only because deleting files needs a decision:**
`src/app/models/session.ts` and the unrouted `LoginComponent`.