Skip to main content
Was this helpful?

Secrets Management

This page documents how PrimeCalendar manages secrets at deployment time, what the current state is, and what precautions operators must take.


Current Secrets Storage Model

PrimeCalendar currently stores all runtime secrets in backend-nestjs/.env. This is a plain-text file that must never be committed to the repository.

The .gitignore excludes .env files from git tracking. This is a repository-level control, not a secrets management system. There is no integration with Azure Key Vault, HashiCorp Vault, AWS Secrets Manager, or equivalent — this is a known gap documented in SECURITY.md (Section 7).


Required Secrets

The following secrets must be set in backend-nestjs/.env before starting the application. All of these have corresponding entries in .env.example.

JWT Authentication

JWT_SECRET=<minimum-32-character-random-value>
JWT_ISSUER=cal3-backend
JWT_AUDIENCE=cal3-users
JWT_ACCESS_TTL=900s
JWT_REFRESH_TTL=1209600s

JWT_SECRET must be cryptographically random and at least 32 characters. Generate it with:

openssl rand -base64 32

Rotating JWT_SECRET: changing this value immediately invalidates all existing access tokens and refresh tokens. All logged-in users will be signed out on their next request. There is no grace period. Plan JWT secret rotations as a maintenance event.

Database Credentials

DB_TYPE=postgres
DB_HOST=taseventeeen.tarhely.eu
DB_PORT=5432
DB_USERNAME=ijaszate_admin
DB_PASSWORD=<database-password>
DB_NAME=ijaszate_cseloteipeter_primecalendar
DB_SSL=false
DB_SYNCHRONIZE=false

SSL Note: DB_SSL=false because the hosted provider at taseventeeen.tarhely.eu does not support SSL connections. Do not set DB_SSL=true — it will cause connection failures. This means database traffic is not encrypted in transit on this provider.

DB_SYNCHRONIZE must remain false in all environments. If set to true, TypeORM will modify the database schema automatically without creating auditable migration files.

OAuth Secrets

GOOGLE_CLIENT_ID=<from-google-cloud-console>
GOOGLE_CLIENT_SECRET=<from-google-cloud-console>
MICROSOFT_CLIENT_ID=<from-azure-portal>
MICROSOFT_CLIENT_SECRET=<from-azure-portal>
MICROSOFT_TENANT_ID=<from-azure-portal>

These are only required if ENABLE_OAUTH=true. Obtain them from the respective provider consoles. Callback URLs must be registered in those consoles and must match GOOGLE_CALLBACK_URL / MICROSOFT_CALLBACK_URL in .env.

Stripe

STRIPE_SECRET_KEY=sk_live_<your-key>
STRIPE_WEBHOOK_SECRET=whsec_<your-webhook-secret>

The STRIPE_WEBHOOK_SECRET is used to verify that webhook events came from Stripe and have not been tampered with.

Field Encryption (Optional)

FIELD_ENCRYPTION_KEY=<64-hex-character-key>

If set, the application encrypts certain sensitive fields at rest using this key. Generate with:

openssl rand -hex 32

Files That Must Never Be Committed

From CLAUDE.md File Security Guidelines:

FileReason
backend-nestjs/.envContains DB credentials, JWT secret, OAuth secrets
*.local.json / settings.local.jsonLocal development overrides
Any file containing API keys or tokensSelf-explanatory

Verify nothing is tracked accidentally:

git status
git ls-files | grep -E "\.env$|\.env\."

If a secret has been committed to git history, it must be treated as compromised and rotated immediately. git rm --cached removes it from tracking but the value remains in git history and must be considered exposed.


CORS Configuration

The CORS allowed origins are built from these environment variables at startup (see security.config.ts):

SECURITY_ALLOWED_ORIGINS=https://yourfrontend.domain.com
FRONTEND_URL=https://yourfrontend.domain.com

In production, set SECURITY_ALLOWED_ORIGINS to only the exact origin where your frontend is served. Do not use wildcards in production. The backend logs a warning to stdout for every request blocked by CORS.


Rate Limiting and Brute Force Protection

These are configured via environment variables:

RATE_LIMIT_WINDOW_SEC=60
RATE_LIMIT_MAX_REQUESTS=120
LOGIN_MAX_ATTEMPTS=5
LOGIN_BLOCK_SECONDS=900

Tune these for your deployment. Lower RATE_LIMIT_MAX_REQUESTS or LOGIN_MAX_ATTEMPTS for higher security environments.


What Is Not Yet Implemented

The following items are planned but not yet implemented (tracked in SECURITY.md Section 7):

  • Validation of env vars at boot — the application does not fail closed if a required variable is missing or weak; it may start in a degraded state
  • Integration with a secrets manager (Azure Key Vault, Vault, AWS SSM) — secrets are read only from .env or process environment
  • Secret rotation automation — all rotations are manual

Until a secrets manager is integrated, treat backend-nestjs/.env with the same care as a private key file: restrict filesystem permissions, never share it over unencrypted channels, and back it up securely outside the repository.


  1. Generate a strong JWT_SECRET if you have not already: openssl rand -base64 32
  2. Confirm backend-nestjs/.env is in .gitignore and not tracked: git ls-files backend-nestjs/.env should return nothing
  3. Set NODE_ENV=production in production deployments
  4. Set SECURITY_ALLOWED_ORIGINS to only your production frontend URL
  5. Store a copy of production secrets in a password manager or team secrets vault outside the repository