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