The highest-risk areas in this repository are the public auth mutation flows, the refresh-token rotation boundary, and the integrity of server-side session state. The current design already includes strong controls for token typing, replay detection, validation, and session revocation, but risk remains concentrated around credential abuse, refresh-token theft, rate-limit degradation under infrastructure failure, and operational exposure if production configuration or logging are mishandled.
In scope:
src/prisma/schema.prisma.github/workflows/ci.yml.github/workflows/deploy.ymlrailway.json
Out of scope:
- future identity-platform features not implemented in this repo
- external provider internals for Railway, PostgreSQL, Redis, and GitHub Actions
- local development-only Compose networking beyond its effect on production posture
Assumptions:
- the service is deployed as a public internet-facing API, with the target production flow documented for Railway in
docs/deployment/railway.md - this is a single application boundary for one product/team, not a multi-tenant auth SaaS
- sensitive data is limited to credentials, password hashes, JWTs, refresh-token state, session metadata, and audit logs
- there is no privileged admin API beyond the user session management endpoints already exposed in
src/routes/authRoutes.ts - user registration is public and self-service
Open questions that would materially change ranking:
- will the production demo stay single-instance or scale horizontally behind Railway
- will production request logs be retained centrally and for how long
- will any PII beyond name and email be added in later iterations
User clarification status:
- no extra service metadata was provided during this documentation pass
- conclusions below therefore use the assumptions above and should be recalibrated if the deployment or data sensitivity changes
- Express 5 API server with JSON input parsing, request correlation, request logging, route validation, and error handling in
src/app.ts - Auth service layer implementing registration, session creation, refresh rotation, and revocation in
src/services/authService.ts - JWT issuance and verification layer with separate access and refresh secrets in
src/services/tokenService.ts - PostgreSQL persistence through Prisma for users, sessions, and refresh-token state in
prisma/schema.prisma - Optional Redis rate-limit backend with in-memory fail-soft fallback in
src/config/redis.tsandsrc/middlewares/rateLimiter.ts - GitHub Actions CI and deployment workflows in
.github/workflows/ci.ymland.github/workflows/deploy.yml
-
Internet client -> Express API
- Data: JSON credentials, refresh tokens, bearer access tokens, headers, correlation IDs
- Channel: HTTPS in production, HTTP locally
- Security guarantees: request validation on auth inputs, IP-based rate limiting on mutation routes, bearer parsing, strict JWT verification, request-size limit
1mb - Validation/normalization: route schemas via
validate(...), email normalization, correlation ID generation insrc/middlewares/requestId.ts
-
Express API -> PostgreSQL via Prisma
- Data: user records, password hashes, session state, refresh-token metadata and hashes
- Channel: database connection string from
DATABASE_URL - Security guarantees: structured ORM access, session-state checks before protected actions
- Validation/normalization: typed Prisma schema, explicit status transitions in repositories/services
-
Express API -> Redis
- Data: rate-limit counters keyed by bucket and client identity
- Channel: Redis connection string from
REDIS_URL - Security guarantees: bounded retry behavior, disabled offline queue, fallback on failure
- Validation/normalization: key names scoped as
rl:<bucket>:<identity>
-
Express API -> log sink
- Data: correlation IDs, route metadata, auth audit events, fallback warnings
- Channel: application logs via Pino in
src/logger.tsand request logger middleware - Security guarantees: structured logging and correlation, but no explicit log redaction policy is defined in-repo
- Validation/normalization: log payloads are constructed from selected fields in middleware/services
-
GitHub Actions -> Railway deployment boundary
- Data: repository contents, Railway token, project/service/environment identifiers, smoke-test URL
- Channel: GitHub-hosted runners and Railway CLI in
.github/workflows/deploy.yml - Security guarantees: repository secrets, release/manual trigger separation, post-deploy smoke checks
- Validation/normalization: workflow verifies required secrets before deploy
flowchart LR
A["Internet Client"] --> B["Express API"]
B["Express API"] --> C["Validation and Auth"]
C["Validation and Auth"] --> D["PostgreSQL"]
C["Validation and Auth"] --> E["Redis"]
C["Validation and Auth"] --> F["Structured Logs"]
G["GitHub Actions"] --> H["Railway Deploy"]
H["Railway Deploy"] --> B["Express API"]
| Asset | Why it matters | Security objective |
|---|---|---|
| Password hashes | Credential compromise leads to account takeover and password cracking risk | C / I |
| Access tokens | Bearer possession enables protected route access until expiry | C / I |
| Refresh tokens and token lineage | Theft or replay can extend attacker access and undermine session integrity | C / I |
| Session state | Revocation and compromise decisions depend on session integrity | I / A |
| User identity records | User names and emails are still personal account data | C / I |
| Rate-limit counters | Abuse resistance depends on correct counting under load or attack | I / A |
| GitHub deployment secrets | Leakage enables unauthorized deploy activity or environment manipulation | C / I |
| Audit and request logs | Needed for detection, incident reconstruction, and operational safety | I / A |
- unauthenticated remote attacker can reach public auth endpoints and health/docs endpoints
- attacker can automate high-volume credential attempts or refresh abuse from one or more source IPs
- attacker may steal a bearer token or refresh token from a compromised client/device
- attacker can manipulate request headers and JSON payloads within route schemas
- attacker may observe behavioral differences between valid, invalid, revoked, expired, and reused token paths
- attacker is not assumed to have direct database, Redis, Railway, or GitHub Actions access
- attacker is not assumed to bypass TLS or compromise the Node runtime itself
- attacker is not assumed to control repository code or CI configuration without a separate supply-chain compromise
| Surface | How reached | Trust boundary | Notes | Evidence (repo path / symbol) |
|---|---|---|---|---|
POST /v1/auth/register |
public HTTP | Internet -> API | public account creation with password input | src/routes/authRoutes.ts, registerInputSchema |
POST /v1/auth/sessions |
public HTTP | Internet -> API | credential verification and session creation | src/routes/authRoutes.ts, createSession |
POST /v1/auth/tokens/refresh |
public HTTP | Internet -> API | refresh rotation and replay handling | src/routes/authRoutes.ts, refreshSession |
POST /v1/auth/sessions/current/revoke |
public HTTP | Internet -> API | refresh-token-presenting revoke path | src/routes/authRoutes.ts, revokeCurrentSession |
GET /v1/auth/me and session deletion routes |
bearer-protected HTTP | Internet -> API | depends on access token and active server-side session | src/routes/authRoutes.ts, authMiddleware |
GET /docs and GET /docs.json |
public HTTP when enabled | Internet -> API | reveals public contract and operational surface area | src/routes/docsRoutes.ts |
GET /health and GET /ready |
public HTTP | Internet -> API | readiness leaks dependency state by design | src/controllers/healthController.ts |
| GitHub release/manual deploy | GitHub Actions | CI -> deploy boundary | secrets-backed production deploy path | .github/workflows/deploy.yml |
- Attacker sends repeated login attempts to
POST /v1/auth/sessions, rotates source IPs, and attempts to brute force credentials until an account is compromised. - Attacker steals a refresh token from a client, refreshes before the legitimate client does, and attempts to maintain session access through token rotation.
- Attacker replays an already-used refresh token to force session compromise and create a denial-of-service condition against the victim account.
- Attacker steals a still-valid access token and uses it to access protected endpoints until expiry or until the backing session is revoked.
- Attacker exploits Redis unavailability to push the system onto weaker in-memory rate limiting and increase the effectiveness of distributed auth abuse.
- Attacker studies
/docs.json,/ready, and auth error behavior to map the service, detect dependency degradation, and optimize credential or replay attacks. - Attacker with leaked GitHub repository secrets triggers unauthorized deployments or redirects smoke checks to attacker-controlled infrastructure.
| Threat ID | Threat source | Prerequisites | Threat action | Impact | Impacted assets | Existing controls (evidence) | Gaps | Recommended mitigations | Detection ideas | Likelihood | Impact severity | Priority |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| TM-001 | Remote unauthenticated attacker | Public reachability to auth mutation routes and a target account set | Automate credential stuffing or brute-force login attempts against session creation | Account takeover and auth service pressure | Passwords, user accounts, rate-limit capacity | IP-based auth mutation rate limiter in src/routes/authRoutes.ts; Redis-backed counters plus fallback in src/middlewares/rateLimiter.ts; invalid credential handling in src/services/authService.ts |
No account-centric throttling, no lockout/backoff, and fallback is weaker in horizontally scaled deployments | Add account/email-aware throttling, per-route metrics, and alerting on login failure spikes; consider risk-based lockout/backoff for future iterations | Alert on auth.login.failed spikes, 429 increases, and Redis fallback warnings |
medium | high | high |
| TM-002 | Remote attacker with stolen refresh token | Refresh token theft from a client/device | Use a valid refresh token to mint fresh access tokens before the legitimate client rotates or revokes it | Extended unauthorized session use | Refresh tokens, access tokens, session state | Separate refresh secret and token typing in src/services/tokenService.ts; stored hash comparison and server-side lookup in src/services/authService.ts; session-state checks in src/repositories/sessionRepository.ts |
A single stolen active refresh token remains usable until rotated or revoked; no device binding or step-up auth exists | Add operational alerts for refresh anomalies; consider device-binding or stronger re-auth controls in future non-core scope | Track auth.refresh.succeeded volume per session and detect unusual IP/user-agent changes |
medium | high | high |
| TM-003 | Remote attacker or malicious client with reused refresh token | Possession of a previously used or tampered refresh token | Replay or alter a refresh token to trigger session compromise | Victim session denial of service and incident noise | Session availability, refresh-token chain integrity | Replay detection marks sessions compromised in src/services/authService.ts; token status transitions in src/repositories/refreshTokenRepository.ts; active-session enforcement in src/middlewares/authMiddleware.ts |
Intentional replay can still force a valid user session into COMPROMISED; no user-facing recovery flow exists in this repo |
Add user-visible incident messaging in future product scope; add replay metrics/alerts; document operator recovery steps in runbooks | Alert on auth.refresh.reuse_detected and session compromise counts |
medium | medium | medium |
| TM-004 | Remote attacker with stolen access token | Access token theft before expiry | Call protected endpoints while the token is still valid | Temporary unauthorized read or session-management actions | Access tokens, session data | Access tokens have short TTL, separate secret, explicit typ, iss, and aud checks in src/services/tokenService.ts; active-session check in src/middlewares/authMiddleware.ts |
Access tokens remain bearer tokens without sender-constraining; exposure window lasts until expiry or session revocation | Keep access token TTL short; add metrics for protected-route 401/200 ratios; consider proof-of-possession only if the product scope expands significantly | Monitor session revocations followed by protected route attempts; track unusual request patterns by session ID | medium | medium | medium |
| TM-005 | Remote attacker exploiting degraded infrastructure | Redis unavailable or unstable during an attack wave | Force the service onto in-memory fallback and distribute attempts across instances or restarts | Reduced abuse resistance and degraded availability posture | Rate-limit controls, auth endpoint availability | Redis fallback warning path in src/middlewares/rateLimiter.ts; bounded Redis retry behavior in src/config/redis.ts; readiness exposes dependency state in src/controllers/healthController.ts |
In-memory fallback is not globally coordinated and may be much weaker under horizontal scale | Add observability for fallback frequency; prefer alerts when Redis is down; document scale-related residual risk explicitly | Alert on rate_limiter_fallback, Redis readiness failures, and auth traffic spikes |
medium | medium | medium |
| TM-006 | Attacker with leaked CI/deploy secret | Compromise of GitHub repository secrets or maintainer environment | Trigger unauthorized deployments or point smoke validation at attacker-chosen targets | Production integrity compromise | Deployment pipeline, production environment, public trust | Deploy workflow validates required secrets and uses GitHub Actions environments in .github/workflows/deploy.yml; branch protection requires quality and integration checks |
No in-repo evidence of OIDC-based deploy auth, environment approval rules, or secret rotation policy | Use least-privilege Railway tokens, enable environment protection rules, rotate secrets regularly, and log deployment initiators | Alert on unexpected release/manual deploy events and review deployment history regularly | low | high | medium |
For this repository and its current portfolio/demo context:
- Critical means unauthenticated remote compromise of arbitrary accounts or infrastructure without meaningful barriers.
- Example: auth bypass across all protected endpoints
- Example: production deploy takeover through weak secret handling with broad blast radius
- High means realistic compromise of user accounts or durable integrity loss in the auth lifecycle.
- Example: effective credential stuffing because rate controls are bypassed
- Example: active refresh-token theft leading to sustained unauthorized session use
- Medium means meaningful security or availability degradation with constrained scope or partial controls already in place.
- Example: replay forcing targeted session compromise
- Example: Redis failure weakening coordinated rate limiting
- Example: short-lived access token theft with server-side session revocation still available
- Low means limited information exposure or hard-to-exploit issues with low user harm in the current design.
- Example: public OpenAPI discovery of already-documented routes
- Example: noisy health/readiness enumeration without further compromise path
| Path | Why it matters | Related Threat IDs |
|---|---|---|
src/services/authService.ts |
Core logic for login, refresh rotation, replay handling, and session revocation | TM-001, TM-002, TM-003 |
src/services/tokenService.ts |
JWT claim issuance and verification, token hashing, and identity extraction | TM-002, TM-004 |
src/middlewares/authMiddleware.ts |
Protected-route enforcement of active session state | TM-004 |
src/middlewares/rateLimiter.ts |
Main abuse-control boundary and Redis fail-soft behavior | TM-001, TM-005 |
src/config/redis.ts |
Redis reliability posture and fallback trigger conditions | TM-005 |
src/routes/authRoutes.ts |
Public attack surface for auth mutation and protected session routes | TM-001, TM-002, TM-003, TM-004 |
prisma/schema.prisma |
Integrity-critical persistence model for sessions and refresh-token lineage | TM-002, TM-003 |
.github/workflows/deploy.yml |
Production deploy path and repository-secret usage | TM-006 |
railway.json |
Versioned deploy behavior, healthcheck, and migration execution | TM-006 |
- All discovered runtime entry points are represented in the entry-point table.
- Each main trust boundary appears in either the abuse paths or the threat table.
- Runtime behavior is separated from CI/deploy concerns.
- No additional user clarification was available, so assumptions are explicit.
- Open questions that change priority are listed in scope and assumptions.