Files
prose-contracts/zulip-oidc-redirect-fix.prose.md
root 4819247e39
PR Pipeline — Authorize → Validate → Review → Merge / auth (push) Successful in 2s
PR Pipeline — Authorize → Validate → Review → Merge / validate (push) Successful in 1s
PR Pipeline — Authorize → Validate → Review → Merge / lint (push) Successful in 1s
PR Pipeline — Authorize → Validate → Review → Merge / ai-review (push) Successful in 2s
PR Pipeline — Authorize → Validate → Review → Merge / gate (push) Successful in 0s
Add zulip-oidc-redirect-fix prose contract
Fixes Zulip OIDC authentication where redirect_uri used internal IP
(192.168.68.19) instead of public domain (chat.sysloggh.net) after
server restart. Monkey-patches social_core.strategy.BaseStrategy
to use ROOT_DOMAIN_URI for all redirect URI construction.
2026-07-09 00:02:18 +00:00

3.4 KiB

kind, name, status, description, agent, triggers
kind name status description agent triggers
responsibility zulip-oidc-redirect-fix active Fixes Zulip OIDC authentication when the redirect_uri sent to Authentik uses the internal IP (192.168.68.19) instead of the public domain (chat.sysloggh.net). Applied via monkey-patch in ZULIP_CUSTOM_SETTINGS. Survives container restarts through compose.override.yaml. abiba
Zulip OIDC login returns "Redirect URI Error" from Authentik
redirect_uri in OAuth URL shows 192.168.68.19 instead of chat.sysloggh.net
After Zulip server restart, Authentik SSO login broken

Maintains

  • zulip-oidc: { redirect_uri: "https://chat.sysloggh.net/complete/oidc/", scheme: "https", host: "chat.sysloggh.net" }
  • authentik-acceptance: { status: "accepted" | "rejected" }
  • patched-strategy: { module: "social_core.strategy.BaseStrategy", method: "absolute_uri", root: "ROOT_DOMAIN_URI" }

Detection

Rule 1: Wrong redirect_uri host

  • Detect: curl -sk -L "https://chat.sysloggh.net/accounts/login/social/oidc/authentik" 2>&1 | grep "redirect_uri=https://192.168.68.19"
  • Status: CRITICAL — Authentik will reject
  • Trigger fix → Execute self-heal

Rule 2: OIDC flow broken

  • Detect: curl -sk -o /dev/null -w "%{http_code}" "https://chat.sysloggh.net/accounts/login/social/oidc/authentik" → chain ends at Authentik 400
  • Diagnose: Check redirect_uri in the 302 Location header chain

Fix (Self-Heal)

Two layers applied:

Layer 1: Live patch (inside container, immediate)

# Add to /home/zulip/deployments/current/zproject/computed_settings.py:
SOCIAL_AUTH_REDIRECT_IS_HTTPS = True

import urllib.parse
from social_core.strategy import BaseStrategy
_original_absolute_uri = BaseStrategy.absolute_uri
def _patched_absolute_uri(self, path=None):
    from django.conf import settings
    root = getattr(settings, "ROOT_DOMAIN_URI", "https://chat.sysloggh.net")
    if path is not None:
        return urllib.parse.urljoin(root, path)
    return root
BaseStrategy.absolute_uri = _patched_absolute_uri

# Restart Django
supervisorctl restart zulip-django

Layer 2: Persistent fix (compose.override.yaml)

The patch is baked into the ZULIP_CUSTOM_SETTINGS env var in /opt/zulip/compose.override.yaml. Survives Docker container restarts.

Verification

STEP1=$(curl -sk -w "%{redirect_url}" \
  "https://chat.sysloggh.net/accounts/login/social/oidc/authentik" -o /dev/null)
curl -sk -D- "$STEP1" -o /dev/null 2>&1 | grep "redirect_uri="
# Expected: redirect_uri=https://chat.sysloggh.net/complete/oidc/
# Wrong:    redirect_uri=https://192.168.68.19/complete/oidc/

Rollback

Remove the patch block from compose.override.yaml and restart the container:

docker compose -f /opt/zulip/compose.yaml -f /opt/zulip/compose.override.yaml up -d zulip

Root Cause

After Zulip restart, social-auth-core computes the OIDC redirect_uri via Django's request.build_absolute_uri()request.get_host(). The upstream Netbird/Traefik proxy (72.61.0.17) forwards Host: 192.168.68.19 instead of Host: chat.sysloggh.net, and without HTTP_HOST in nginx's uwsgi_params, Django falls back to the server's IP.

The monkey-patch overrides BaseStrategy.absolute_uri() to always use ROOT_DOMAIN_URI (https://chat.sysloggh.net) regardless of the request's Host header.

  • zulip-health.prose.md — General Zulip health monitoring
  • zulip-self-heal.prose.md — RETIRED (pi extension removed)