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, andpackage.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:
- Build: Kaniko builds the Docker image, tags it with
$SHORT_SHAand the environment name - Push: Image pushed to
gcr.io/$PROJECT_ID/cred-auth - Deploy:
gcloud run services updateapplies the new image
| Branch | Environment |
|---|---|
develop |
Development |
main |
Production |
CI/CD Pipeline (Cloud Build)
The cloudbuild.yaml defines two steps:
- Build — Kaniko with 168h layer cache TTL
- Deploy —
gcloud run services updatewith 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"))
Cookie Keys (for OAUTH_COOKIE_KEYS)
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_ISSUERset tohttps://auth.credplatform.com -
OAUTH_COOKIE_KEYSset with strong, unique keys -
OAUTH_JWKS_PRIVATE_KEYorJWT_SECRETconfigured -
DB_CONNECTIONpointing to production PostgreSQL -
DB_SSLset appropriately (e.g.,trueorno-verify) -
CORS_ORIGINSrestricted 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)