Skip to content

Deployment

Container Deployment

Docker

Multi-stage build based on Node.js 20 Alpine:

  • Builder stage: Installs dependencies, compiles TypeScript, prunes dev dependencies
  • Runtime stage: Copies only dist/, views/, public/, node_modules, and package.json
  • Security: Runs as non-root appuser
  • Health check: GET /.well-known/openid-configuration (HTTP 200)
# Build and run locally
docker build -t cred-auth .
docker run -p 3000:3000 --env-file .env cred-auth

Or with Docker Compose:

docker-compose up

Google Cloud Run

Deployed via Google Cloud Build with Kaniko for layer caching:

  1. Build: Kaniko builds the Docker image, tags it with $SHORT_SHA and the environment name
  2. Push: Image pushed to gcr.io/$PROJECT_ID/cred-auth
  3. Deploy: gcloud run services update applies the new image
Branch Environment
develop Development
main Production

CI/CD Pipeline (Cloud Build)

The cloudbuild.yaml defines two steps:

  1. Build — Kaniko with 168h layer cache TTL
  2. Deploygcloud run services update with commit SHA label and environment variables

Substitution variables:

Variable Default Description
_CRED_ENV dev Target environment
_IMAGE_NAME cred-auth Docker image name
_DEPLOY_REGION us-central1 Cloud Run region
_SERVICE_NAME Cloud Run service name

Environment Variables

Server

Variable Default Description
PORT 3000 HTTP port
NODE_ENV development Environment (development or production)
OAUTH_ISSUER http://localhost:3000 Issuer URL (auto-upgraded to HTTPS in production)

Database

Variable Default Description
DB_CONNECTION PostgreSQL connection string
DB_SSL SSL mode: true, no-verify, or false

JWT Signing

Two signing methods are available. JWT_SECRET takes precedence if both are set.

Variable Default Description
JWT_SECRET HMAC secret for HS256 signing (compatible with cred-api-commercial)
JWT_ISSUER cred-api-commercial Issuer claim for HS256 tokens
OAUTH_JWKS_PRIVATE_KEY Base64-encoded RSA PEM for RS256 signing

OAuth / OIDC

Variable Default Description
OAUTH_COOKIE_KEYS JSON array of signing keys (each >= 32 chars). Required in production.
OAUTH_DYNAMIC_REGISTRATION false Enable RFC 7591 dynamic client registration
OAUTH_ADAPTER_DEBUG false Log SQL queries from the OIDC adapter

Token TTLs

Variable Default Description
OAUTH_TTL_ACCESS_TOKEN 3600 (1h) Access token lifetime in seconds
OAUTH_TTL_AUTH_CODE 600 (10m) Authorization code lifetime
OAUTH_TTL_REFRESH_TOKEN 1209600 (14d) Refresh token lifetime
OAUTH_TTL_SESSION 1209600 (14d) Session lifetime
OAUTH_TTL_GRANT 1209600 (14d) Grant lifetime
OAUTH_TTL_INTERACTION 3600 (1h) Interaction (login/consent) lifetime
OAUTH_TTL_REG_TOKEN 604800 (7d) Registration access token lifetime

Security

Variable Default Description
CORS_ORIGINS Comma-separated allowed origins
TRUST_PROXY false Trust X-Forwarded-For headers
THROTTLE_SHORT_TTL 10000 Short burst window (ms)
THROTTLE_SHORT_LIMIT 10 Short burst max requests
THROTTLE_MEDIUM_TTL 60000 Medium window (ms)
THROTTLE_MEDIUM_LIMIT 100 Medium max requests
THROTTLE_LONG_TTL 3600000 Long window (ms)
THROTTLE_LONG_LIMIT 1000 Long max requests
THROTTLE_LOGIN_PER_MINUTE 5 Login attempts per minute
THROTTLE_LOGIN_PER_HOUR 20 Login attempts per hour
CSRF_SECRET Dedicated CSRF signing secret
CSRF_TOKEN_TTL 900000 (15m) CSRF token validity (ms)

Generating Keys

RSA Private Key (for OAUTH_JWKS_PRIVATE_KEY)

# Generate RSA private key
openssl genrsa -out private.pem 2048

# Base64 encode for the environment variable
# macOS/Linux:
base64 -i private.pem

# Windows PowerShell:
[Convert]::ToBase64String([IO.File]::ReadAllBytes("private.pem"))

Generate two random strings of at least 32 characters each:

node -e "console.log(JSON.stringify([require('crypto').randomBytes(32).toString('hex'), require('crypto').randomBytes(32).toString('hex')]))"

Health Check

The Docker container includes a health check that hits the OIDC discovery endpoint:

GET /.well-known/openid-configuration → HTTP 200

Cloud Run uses this for readiness and liveness probes.


Production Checklist

  • OAUTH_ISSUER set to https://auth.credplatform.com
  • OAUTH_COOKIE_KEYS set with strong, unique keys
  • OAUTH_JWKS_PRIVATE_KEY or JWT_SECRET configured
  • DB_CONNECTION pointing to production PostgreSQL
  • DB_SSL set appropriately (e.g., true or no-verify)
  • CORS_ORIGINS restricted to known frontend origins
  • TRUST_PROXY=true (behind Cloud Run / load balancer)
  • NODE_ENV=production
  • Database tables ("User", "OAuthPayload") created with indexes
  • At least one OAuth client registered (manually or via dynamic registration)