From 0a5a5fa94fd86f731cc945f155db0e79f5aa6d9b Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Thu, 23 Jul 2026 09:39:48 +0530 Subject: [PATCH 1/6] fix(deploy): repair crash-looping CLI flags, wire missing security flags --enable-totp-login/--enable-mfa/--enable-email-otp/--enable-sms-otp don't exist in the authorizer binary; cobra rejects unknown flags so every deploy from this Dockerfile crash-loops on boot. Renamed to the current --disable-totp-login/--disable-webauthn-mfa/--disable-email-otp/ --disable-sms-otp/--disable-mfa flags. Also added --url (CWE-640 host-header-injection mitigation, was never wired to any env var), --oauth2-1-strict, and --enable-org-discovery, and surfaced AUTHORIZER_URL/OAUTH2_1_STRICT in app.json. Re-pinned the base image to 2.4.0-rc.7: 2.3.0 predates the flag rename above, so it doesn't have the --disable-* flags either. Move to the stable 2.4.0 tag once it ships. --- Dockerfile | 18 +++++++++++++----- app.json | 9 +++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index adc99c9..08d1cb0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,9 @@ # Base runs as USER authorizer (uid 1000). For SQLite, ensure mounted /data is writable by that user. -FROM quay.io/authorizer/authorizer:2.3.0 +# Pinned to the 2.4.0 release candidate because it is the first published +# image with the current CLI flag surface (--url, --oauth2-1-strict, +# --enable-org-discovery, --disable-totp-login/-webauthn-mfa/-email-otp/ +# -sms-otp/-mfa). Re-pin to the stable 2.4.0 tag once it ships. +FROM quay.io/authorizer/authorizer:2.4.0-rc.7 # Override so CMD runs in a shell and env vars (e.g. for Heroku) are expanded. See base image comment. # Use exec-form CMD with a single string so /bin/sh -c gets one argument; shell-form CMD can be split and drop into a shell. ENTRYPOINT ["/bin/sh", "-c"] @@ -28,6 +32,7 @@ CMD ["exec ./authorizer \\\n\ --allowed-origins=\"${ALLOWED_ORIGINS}\" \\\n\ --default-authorize-response-type=\"${DEFAULT_AUTHORIZE_RESPONSE_TYPE}\" \\\n\ --default-authorize-response-mode=\"${DEFAULT_AUTHORIZE_RESPONSE_MODE}\" \\\n\ + --oauth2-1-strict=\"${OAUTH2_1_STRICT:-false}\" \\\n\ --organization-name=\"${ORGANIZATION_NAME}\" \\\n\ --organization-logo=\"${ORGANIZATION_LOGO}\" \\\n\ --smtp-host=\"${SMTP_HOST}\" \\\n\ @@ -38,6 +43,7 @@ CMD ["exec ./authorizer \\\n\ --smtp-sender-name=\"${SENDER_NAME}\" \\\n\ --reset-password-url=\"${RESET_PASSWORD_URL}\" \\\n\ --backchannel-logout-uri=\"${BACKCHANNEL_LOGOUT_URI}\" \\\n\ + --url=\"${AUTHORIZER_URL}\" \\\n\ --env=\"${ENV}\" \\\n\ --host=\"${HOST:-0.0.0.0}\" \\\n\ --metrics-port=\"${METRICS_PORT:-8081}\" \\\n\ @@ -51,6 +57,7 @@ CMD ["exec ./authorizer \\\n\ --rate-limit-burst=\"${RATE_LIMIT_BURST:-20}\" \\\n\ --rate-limit-fail-closed=\"${RATE_LIMIT_FAIL_CLOSED:-false}\" \\\n\ --enable-login-page=\"${ENABLE_LOGIN_PAGE:-true}\" \\\n\ + --enable-org-discovery=\"${ENABLE_ORG_DISCOVERY:-false}\" \\\n\ --enable-playground=\"${ENABLE_PLAYGROUND:-true}\" \\\n\ --disable-admin-header-auth=\"${DISABLE_ADMIN_HEADER_AUTH:-true}\" \\\n\ --enable-graphql-introspection=\"${ENABLE_GRAPHQL_INTROSPECTION:-true}\" \\\n\ @@ -84,16 +91,17 @@ CMD ["exec ./authorizer \\\n\ --smtp-local-name=\"${SMTP_LOCAL_NAME}\" \\\n\ --smtp-skip-tls-verification=\"${SMTP_SKIP_TLS_VERIFICATION:-false}\" \\\n\ --enable-strong-password=\"${ENABLE_STRONG_PASSWORD:-true}\" \\\n\ - --enable-totp-login=\"${ENABLE_TOTP_LOGIN:-false}\" \\\n\ --enable-basic-authentication=\"${ENABLE_BASIC_AUTHENTICATION:-true}\" \\\n\ --enable-email-verification=\"${ENABLE_EMAIL_VERIFICATION:-false}\" \\\n\ --enable-mobile-basic-authentication=\"${ENABLE_MOBILE_BASIC_AUTHENTICATION:-true}\" \\\n\ --enable-phone-verification=\"${ENABLE_PHONE_VERIFICATION:-false}\" \\\n\ --enable-magic-link-login=\"${ENABLE_MAGIC_LINK_LOGIN:-false}\" \\\n\ --enforce-mfa=\"${ENFORCE_MFA:-true}\" \\\n\ - --enable-mfa=\"${ENABLE_MFA:-false}\" \\\n\ - --enable-email-otp=\"${ENABLE_EMAIL_OTP:-false}\" \\\n\ - --enable-sms-otp=\"${ENABLE_SMS_OTP:-false}\" \\\n\ + --disable-totp-login=\"${DISABLE_TOTP_LOGIN:-false}\" \\\n\ + --disable-webauthn-mfa=\"${DISABLE_WEBAUTHN_MFA:-false}\" \\\n\ + --disable-email-otp=\"${DISABLE_EMAIL_OTP:-false}\" \\\n\ + --disable-sms-otp=\"${DISABLE_SMS_OTP:-false}\" \\\n\ + --disable-mfa=\"${DISABLE_MFA:-false}\" \\\n\ --enable-signup=\"${ENABLE_SIGNUP:-true}\" \\\n\ --twilio-account-sid=\"${TWILIO_ACCOUNT_SID}\" \\\n\ --twilio-api-key=\"${TWILIO_API_KEY}\" \\\n\ diff --git a/app.json b/app.json index 1315a4b..57d801c 100644 --- a/app.json +++ b/app.json @@ -49,6 +49,15 @@ "generator": "secret", "required": true }, + "AUTHORIZER_URL": { + "description": "Canonical/trusted base URL of this deployment (e.g. https://your-app.herokuapp.com). When set, it is the only source used to build verification/reset/magic-link URLs, the JWT iss claim, and OIDC discovery URLs. Leaving it empty exposes host-header-injection account takeover (CWE-640) — set this once you know your Heroku app URL.", + "required": false + }, + "OAUTH2_1_STRICT": { + "description": "Set to 'true' to enforce OAuth 2.1 restrictions (reject implicit/hybrid-with-token response types, require PKCE S256). Breaking; opt-in.", + "value": "false", + "required": false + }, "TRUSTED_PROXIES": { "description": "Comma-separated CIDRs of reverse proxies whose X-Forwarded-For will be honoured. Heroku routes traffic through their own router, so set this to the Heroku router CIDR or per-IP rate limiting will key on the router IP. See https://docs.authorizer.dev/core/security#trusted-proxies", "required": false From 1331696a15f2577b538d82a03196d448d60b6a07 Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Fri, 7 Aug 2026 18:00:26 +0530 Subject: [PATCH 2/6] fix: pin 2.4.0-rc.15 and wire --encryption-key Mirrors the same change in the helm chart. Without a distinct encryption key, rotating the JWT secret locks out every enrolled TOTP user with no re-encryption path. --- Dockerfile | 3 ++- README.md | 1 + app.json | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 08d1cb0..6dc589c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ # image with the current CLI flag surface (--url, --oauth2-1-strict, # --enable-org-discovery, --disable-totp-login/-webauthn-mfa/-email-otp/ # -sms-otp/-mfa). Re-pin to the stable 2.4.0 tag once it ships. -FROM quay.io/authorizer/authorizer:2.4.0-rc.7 +FROM quay.io/authorizer/authorizer:2.4.0-rc.15 # Override so CMD runs in a shell and env vars (e.g. for Heroku) are expanded. See base image comment. # Use exec-form CMD with a single string so /bin/sh -c gets one argument; shell-form CMD can be split and drop into a shell. ENTRYPOINT ["/bin/sh", "-c"] @@ -18,6 +18,7 @@ CMD ["exec ./authorizer \\\n\ --redis-url=\"${REDIS_URL}\" \\\n\ --jwt-type=\"${JWT_TYPE}\" \\\n\ --jwt-secret=\"${JWT_SECRET}\" \\\n\ + --encryption-key=\"${ENCRYPTION_KEY}\" \\\n\ --jwt-private-key=\"${JWT_PRIVATE_KEY}\" \\\n\ --jwt-public-key=\"${JWT_PUBLIC_KEY}\" \\\n\ --jwt-role-claim=\"${JWT_ROLE_CLAIM}\" \\\n\ diff --git a/README.md b/README.md index 86b2cb2..fbaf330 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Authorizer v2 requires the following variables. Configure them in Heroku's **Con | `DATABASE_URL` | Database connection string | *(auto-configured by Heroku add-on)* | | `JWT_TYPE` | JWT signing algorithm | `HS256` | | `JWT_SECRET` | JWT signing secret | `test` | +| `ENCRYPTION_KEY` | At-rest key for TOTP secrets and OTP digests. Required with `RS*`/`ES*` | *(output of `openssl rand -hex 32`)* | | `ADMIN_SECRET` | Admin secret for admin operations | `admin` | | `CLIENT_ID` | Client identifier **(required)** | `123456` | | `CLIENT_SECRET` | Client secret **(required)** | `secret` | diff --git a/app.json b/app.json index 57d801c..eface0d 100644 --- a/app.json +++ b/app.json @@ -44,6 +44,11 @@ "generator": "secret", "required": true }, + "ENCRYPTION_KEY": { + "description": "Encrypts TOTP secrets and OTP digests at rest. Required when JWT_TYPE is RS*/ES*; with HS* it falls back to JWT_SECRET, but a distinct value keeps JWT secret rotation from locking out enrolled TOTP users.", + "generator": "secret", + "required": false + }, "ADMIN_SECRET": { "description": "Secret for admin API access. Required, must be non-empty (the previous insecure 'password' default has been removed in the April 2026 security release).", "generator": "secret", From b2695b2684cd0435e0b53864ba317c8dd3cb765b Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Fri, 7 Aug 2026 18:02:02 +0530 Subject: [PATCH 3/6] feat: wire 2.4.0 flags, document boot-fatal SMTP combo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds --fga-allow-unconstrained-agents, --microsoft-allowed-tenants and --oauth-allow-unverified-provider-email. Both booleans default false, matching the server's secure-by-default posture. 2.4.0 also made --enable-email-verification with no SMTP a fatal boot error rather than a per-user quirk. Nothing here sets it, so no template ships a crash-looping default, but an operator can set it from the platform env — so the READMEs now say what happens. --- Dockerfile | 3 +++ README.md | 28 +++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6dc589c..0ad3c64 100644 --- a/Dockerfile +++ b/Dockerfile @@ -83,6 +83,7 @@ CMD ["exec ./authorizer \\\n\ --database-cert-key=\"${DATABASE_CERT_KEY}\" \\\n\ --fga-store=\"${FGA_STORE}\" \\\n\ --fga-store-url=\"${FGA_STORE_URL}\" \\\n\ + --fga-allow-unconstrained-agents=\"${FGA_ALLOW_UNCONSTRAINED_AGENTS:-false}\" \\\n\ --couchbase-bucket=\"${COUCHBASE_BUCKET}\" \\\n\ --couchbase-scope=\"${COUCHBASE_SCOPE}\" \\\n\ --couchbase-ram-quota=\"${COUCHBASE_RAM_QUOTA}\" \\\n\ @@ -120,7 +121,9 @@ CMD ["exec ./authorizer \\\n\ --microsoft-client-id=\"${MICROSOFT_CLIENT_ID}\" \\\n\ --microsoft-client-secret=\"${MICROSOFT_CLIENT_SECRET}\" \\\n\ --microsoft-tenant-id=\"${MICROSOFT_TENANT_ID}\" \\\n\ + --microsoft-allowed-tenants=\"${MICROSOFT_ALLOWED_TENANTS}\" \\\n\ --microsoft-scopes=\"${MICROSOFT_SCOPES}\" \\\n\ + --oauth-allow-unverified-provider-email=\"${OAUTH_ALLOW_UNVERIFIED_PROVIDER_EMAIL:-false}\" \\\n\ --apple-client-id=\"${APPLE_CLIENT_ID}\" \\\n\ --apple-client-secret=\"${APPLE_CLIENT_SECRET}\" \\\n\ --apple-scopes=\"${APPLE_SCOPES}\" \\\n\ diff --git a/README.md b/README.md index fbaf330..6e3b090 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,33 @@ Authorizer v2 requires the following variables. Configure them in Heroku's **Con | `CLIENT_ID` | Client identifier **(required)** | `123456` | | `CLIENT_SECRET` | Client secret **(required)** | `secret` | -These are mapped to CLI flags at startup. Please refer to the [server configuration docs](https://docs.authorizer.dev/core/server-config) for all available flags. +These are mapped to CLI flags at startup. + +### Upgrading to 2.4.0 + +`ENABLE_EMAIL_VERIFICATION=true` with no SMTP configured is now a **fatal boot +error**, not a warning. Every account-recovery route ends at the same mailbox, +so without a mail path a user is created unverified and can never recover. If +you set it, also set `SMTP_HOST`, `SMTP_PORT` and `SMTP_SENDER_EMAIL` — all +three — or the container will exit on start. + +`APP_COOKIE_SAME_SITE` is now validated at boot too: an unrecognised value +exits rather than silently falling back to `lax`. + +Two optional flags were added for the 2.4.0 security changes, both defaulting +to the secure behaviour: + +- `OAUTH_ALLOW_UNVERIFIED_PROVIDER_EMAIL` — a social login whose provider did + not attest the email address no longer reaches an existing account. Set + `true` only as a temporary compatibility measure. +- `FGA_ALLOW_UNCONSTRAINED_AGENTS` — a delegated (agent-acting-for-user) check + against an authorization model with no `type agent` now denies. Set `true` + only while migrating a model. + +`MICROSOFT_ALLOWED_TENANTS` restricts which Entra tenants may sign in when +`MICROSOFT_TENANT_ID` is a multi-tenant alias (`common`/`organizations`/ +`consumers`). + Please refer to the [server configuration docs](https://docs.authorizer.dev/core/server-config) for all available flags. ## Notes From 00fcb7ca36393587d8a5092e2418fd3a9627befd Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Fri, 7 Aug 2026 19:03:23 +0530 Subject: [PATCH 4/6] fix: pin 2.4.0-rc.16, the first RC with these flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rc.15 was tagged and released but its build was cancelled, so no image was ever pushed — and it was cut from a commit predating the audit, so it could not have carried these flags anyway. rc.16 is the first RC that has them; verified against the published image. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 0ad3c64..8dbd5e8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ # image with the current CLI flag surface (--url, --oauth2-1-strict, # --enable-org-discovery, --disable-totp-login/-webauthn-mfa/-email-otp/ # -sms-otp/-mfa). Re-pin to the stable 2.4.0 tag once it ships. -FROM quay.io/authorizer/authorizer:2.4.0-rc.15 +FROM quay.io/authorizer/authorizer:2.4.0-rc.16 # Override so CMD runs in a shell and env vars (e.g. for Heroku) are expanded. See base image comment. # Use exec-form CMD with a single string so /bin/sh -c gets one argument; shell-form CMD can be split and drop into a shell. ENTRYPOINT ["/bin/sh", "-c"] From 1841b478d493047584fa5a99e443151ba776c859 Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Fri, 7 Aug 2026 21:39:27 +0530 Subject: [PATCH 5/6] fix: pin 2.4.0-rc.17 rc.17 adds the verify-email decision core and the empty-subject token rejection, which rc.16 was built one commit too early to include. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8dbd5e8..9774f97 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ # image with the current CLI flag surface (--url, --oauth2-1-strict, # --enable-org-discovery, --disable-totp-login/-webauthn-mfa/-email-otp/ # -sms-otp/-mfa). Re-pin to the stable 2.4.0 tag once it ships. -FROM quay.io/authorizer/authorizer:2.4.0-rc.16 +FROM quay.io/authorizer/authorizer:2.4.0-rc.17 # Override so CMD runs in a shell and env vars (e.g. for Heroku) are expanded. See base image comment. # Use exec-form CMD with a single string so /bin/sh -c gets one argument; shell-form CMD can be split and drop into a shell. ENTRYPOINT ["/bin/sh", "-c"] From 1365e35e60fc88fa477560c1ab5de9b68aaa498d Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Fri, 7 Aug 2026 23:15:50 +0530 Subject: [PATCH 6/6] fix: pin 2.4.0-rc.18 rc.18 bumps cel-go to v0.29.2 for GHSA-gcjh-h69q-9w9g. Verified from the published image: the binary links cel-go@v0.29.2. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9774f97..b53f1ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ # image with the current CLI flag surface (--url, --oauth2-1-strict, # --enable-org-discovery, --disable-totp-login/-webauthn-mfa/-email-otp/ # -sms-otp/-mfa). Re-pin to the stable 2.4.0 tag once it ships. -FROM quay.io/authorizer/authorizer:2.4.0-rc.17 +FROM quay.io/authorizer/authorizer:2.4.0-rc.18 # Override so CMD runs in a shell and env vars (e.g. for Heroku) are expanded. See base image comment. # Use exec-form CMD with a single string so /bin/sh -c gets one argument; shell-form CMD can be split and drop into a shell. ENTRYPOINT ["/bin/sh", "-c"]