Add zulip-oidc-redirect-fix prose contract
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
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
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.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
---
|
||||
kind: responsibility
|
||||
name: zulip-oidc-redirect-fix
|
||||
status: active
|
||||
description: >
|
||||
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.
|
||||
agent: abiba
|
||||
triggers:
|
||||
- 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)
|
||||
```bash
|
||||
# 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
|
||||
```bash
|
||||
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:
|
||||
```bash
|
||||
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.
|
||||
|
||||
## Related Contracts
|
||||
|
||||
- `zulip-health.prose.md` — General Zulip health monitoring
|
||||
- `zulip-self-heal.prose.md` — RETIRED (pi extension removed)
|
||||
Reference in New Issue
Block a user