Note: This issue was generated with AI assistance (GitHub Copilot) based on automated log analysis and triage.
Filed by @canonical/solutions-qa
Summary
When external_hostname config is not set and an oauth relation is established, the charm constructs an invalid OAuth redirect URL using the Juju application name instead of a routable external hostname, causing OAuth configuration to fail with "Invalid URL" error.
Test Observer Details
Error Details
unit-target-0: 2026-01-18 10:22:50 ERROR unit.target/0.juju-log oauth:23:
Invalid OAuth client config: Invalid URL https://target/auth/oidc/callback
The charm constructs the redirect URL as https://target/auth/oidc/callback where "target" is the Juju application name. This fails URL validation in the OAuth library because "target" is not a valid FQDN (lacks a TLD like .com or .local).
Root Cause
File: src/charm.py#L280
def _get_external_hostname(self) -> str:
"""Extract and return hostname from site_url or default to [application name].
Returns:
The site hostname defined as part of the site_url configuration or a default value.
"""
return (
typing.cast(str, self.config["external_hostname"])
if self.config["external_hostname"]
else self.app.name # ← BUG: Defaults to application name
)
This method is called by OAuthObserver._generate_client_config() in src/oauth_observer.py#L81 to construct the OAuth redirect URI:
self.client_config = ClientConfig(
redirect_uri=f"https://{self._external_hostname_callback()}/auth/oidc/callback",
# ...
)
The OAuth library validates URLs using a regex pattern that requires domain names to have at least one dot (e.g., example.com, discourse.local). Single-word hostnames like "target" are rejected.
Validation Code: lib/charms/hydra/v0/oauth.py
Impact
- Integration tests fail: All test deployments without explicit
external_hostname config fail
- Misleading error symptoms: The test shows "database-relation-changed hook failed" but the real error is OAuth configuration
- Silent failure in production: Users deploying with oauth relation but no
external_hostname config will get blocked status with unclear error message
- Affects current main branch: Bug still exists in latest code (commit 1420720)
Expected Behavior
The charm should handle missing external_hostname config gracefully when oauth relation is present. Options:
-
Require configuration (simplest):
if self.model.get_relation(OAUTH_RELATION_NAME) and not self.config["external_hostname"]:
self.unit.status = BlockedStatus("external_hostname config required for OAuth")
return
-
Derive from ingress relation (better UX):
- The charm already has nginx-route relation which provides external routing
- Could extract hostname from ingress relation data instead of using app name
- Falls back to blocking if neither config nor ingress provides valid hostname
-
Validate before use:
- Check that
self.app.name matches URL requirements before using as default
- Block with clear message if validation fails
Reproduction
Deploy discourse-k8s with oauth and nginx-route relations but without external_hostname config:
applications:
target:
charm: discourse-k8s
channel: latest/edge
# external_hostname NOT configured
hydra:
charm: hydra
content-cache-k8s:
charm: content-cache-k8s
relations:
- [hydra:oauth, target:oauth]
- [content-cache-k8s:nginx-proxy, target:nginx-route]
Result: Charm enters error state with "Invalid OAuth client config: Invalid URL https://target/auth/oidc/callback"
Additional Context
- The database hook failure shown in juju status is a symptom, not the root cause
- OAuth error occurs first (10:22:50), database hook fails later as a consequence
- Other charms using the OAuth library would have the same issue if they default to application name
Note: This issue was generated with AI assistance (GitHub Copilot) based on automated log analysis and triage.
Filed by @canonical/solutions-qa
Summary
When
external_hostnameconfig is not set and an oauth relation is established, the charm constructs an invalid OAuth redirect URL using the Juju application name instead of a routable external hostname, causing OAuth configuration to fail with "Invalid URL" error.Test Observer Details
Error Details
The charm constructs the redirect URL as
https://target/auth/oidc/callbackwhere "target" is the Juju application name. This fails URL validation in the OAuth library because "target" is not a valid FQDN (lacks a TLD like.comor.local).Root Cause
File: src/charm.py#L280
This method is called by
OAuthObserver._generate_client_config()in src/oauth_observer.py#L81 to construct the OAuth redirect URI:The OAuth library validates URLs using a regex pattern that requires domain names to have at least one dot (e.g.,
example.com,discourse.local). Single-word hostnames like "target" are rejected.Validation Code: lib/charms/hydra/v0/oauth.py
Impact
external_hostnameconfig failexternal_hostnameconfig will get blocked status with unclear error messageExpected Behavior
The charm should handle missing
external_hostnameconfig gracefully when oauth relation is present. Options:Require configuration (simplest):
Derive from ingress relation (better UX):
Validate before use:
self.app.namematches URL requirements before using as defaultReproduction
Deploy discourse-k8s with oauth and nginx-route relations but without
external_hostnameconfig:Result: Charm enters error state with "Invalid OAuth client config: Invalid URL https://target/auth/oidc/callback"
Additional Context