Files
sase.tr/apps/api/drizzle/0005_past_elektra.sql
Semih Yesilyurt 2583b781ec
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
feat(internal-admin): readonly impersonation for Süper Panel
Lets the founder open a target user's session in a new tab from the
panel for debugging. Read-only enforced server-side — any non-GET
request from an impersonated session returns 403.

Schema
- sessions.impersonated_by (uuid, nullable) — founder Better Auth user id
- sessions.impersonation_readonly (bool, default false)
- index on impersonated_by

Service
- ImpersonationService.createReadonlySession({ targetUserId, founderId,
  ttlMinutes, reason, ipAddress, userAgent }):
  - Random sessionId + token (32 bytes hex each)
  - TTL clamped 1..60 min, default 15
  - Refuses to impersonate admin users
  - Inserts sessions row; signs cookie value with HMAC-SHA256(BETTER_AUTH_SECRET)
    matching better-call's signCookieValue format
  - Returns { cookieName, cookieValue, expiresAt, sessionId }

Guard
- ImpersonationReadonlyGuard runs after AuthGuard, before RolesGuard.
- GET/HEAD/OPTIONS pass through.
- For other methods: looks up sessions.impersonated_by + impersonation_readonly
  by request.session.id; throws ForbiddenException if both truthy.

Endpoints (InternalAdminModule)
- POST /internal/admin/users/:id/impersonate-readonly [InternalTokenGuard]
  body: { ttlMinutes, reason, founderId }
  returns: { redirectUrl, expiresAt, sessionIdPrefix }
  Hand-off is via signed consume URL (cross-origin Set-Cookie limitations).
- GET /admin/impersonate/consume?t=<signed> [@Public]
  Verifies HMAC-signed payload (<=60s validity), sets the Better Auth session
  cookie on sase.tr, redirects to /. One-shot.

Wiring
- InternalAdminModule imported in AppModule.
- ImpersonationReadonlyGuard registered as APP_GUARD between Auth and Roles.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 00:48:39 +03:00

3 lines
287 B
SQL

ALTER TABLE "sessions" ADD COLUMN "impersonated_by" uuid;--> statement-breakpoint
ALTER TABLE "sessions" ADD COLUMN "impersonation_readonly" boolean DEFAULT false NOT NULL;--> statement-breakpoint
CREATE INDEX "sessions_impersonated_by_idx" ON "sessions" USING btree ("impersonated_by");