[GH-ISSUE #1990] OIDC Cookie Domain Scoping Issue (follow-up to #1740) #2052

Closed
opened 2026-04-16 09:00:34 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @88plug on GitHub (Dec 6, 2025).
Original GitHub issue: https://github.com/fosrl/pangolin/issues/1990

Describe the Bug

OIDC cookies are set with incorrect domain scope, breaking authentication across subdomains using default configuration.

This is a follow-up to #1740 which was closed as stale/not planned, but the issue persists with default settings.

When using OIDC authentication (e.g., Authentik, Keycloak) with resources on subdomains - with NO custom cookie configuration:

  1. p_oidc_state cookie is set with Domain=resource1.example.com instead of .example.com
  2. After the OIDC provider redirects back to the Pangolin base URL (pangolin.example.com), the browser refuses to send the cookie due to domain mismatch
  3. SameSite=lax (the default) further blocks the cookie on cross-site redirects
  4. p_session_token is also scoped to exact hostname, causing redirect loops

This occurs out-of-the-box with a standard multi-subdomain setup. No non-default cookie settings were configured. The root cause is that Pangolin's default cookie domain scoping doesn't account for
subdomain usage, which is the primary use case for a reverse proxy.

Environment

  • OS Type & Version: Ubuntu 24.04 LTS
  • Pangolin Version: 1.12.x (latest)
  • Gerbil Version: latest
  • Traefik Version: v3.4
  • Newt Version: N/A
  • Olm Version: N/A
  • Configuration: DEFAULT (no custom cookie settings)

To Reproduce

  1. Install Pangolin using default configuration (official installer or docker-compose)
  2. Configure an OIDC provider (e.g., Authentik) - standard setup per docs
  3. Create a resource accessible at resource1.example.com
  4. Pangolin dashboard at pangolin.example.com
  5. Do NOT modify any cookie-related settings - use defaults
  6. Attempt to access resource1.example.com which requires OIDC auth
  7. Observe that p_oidc_state cookie is set with Domain=resource1.example.com
  8. OIDC provider authenticates and redirects to pangolin.example.com
  9. Browser does not send the cookie (domain mismatch + SameSite=lax)
  10. Authentication fails or enters a redirect loop

No workarounds or custom configuration applied - this is purely default behavior.

Expected Behavior

With default configuration, OIDC-related cookies (p_oidc_state, p_session_token) should be set with first-level domain scope:

  • Domain=.example.com (note the leading dot)
  • This allows cookies to be shared across all subdomains by default
  • SameSite attribute should be appropriate for OIDC redirect flows out-of-the-box

A reverse proxy serving multiple subdomains is the standard use case - cookie scoping should work correctly without requiring manual intervention or third-party plugins.

Current workaround requires Traefik's proxy-cookie plugin to rewrite cookie domains, but this should not be necessary with sensible defaults.

Originally created by @88plug on GitHub (Dec 6, 2025). Original GitHub issue: https://github.com/fosrl/pangolin/issues/1990 ### Describe the Bug OIDC cookies are set with incorrect domain scope, breaking authentication across subdomains using default configuration. This is a follow-up to #1740 which was closed as stale/not planned, but the issue persists with default settings. When using OIDC authentication (e.g., Authentik, Keycloak) with resources on subdomains - with NO custom cookie configuration: 1. `p_oidc_state` cookie is set with `Domain=resource1.example.com` instead of `.example.com` 2. After the OIDC provider redirects back to the Pangolin base URL (`pangolin.example.com`), the browser refuses to send the cookie due to domain mismatch 3. `SameSite=lax` (the default) further blocks the cookie on cross-site redirects 4. `p_session_token` is also scoped to exact hostname, causing redirect loops This occurs out-of-the-box with a standard multi-subdomain setup. No non-default cookie settings were configured. The root cause is that Pangolin's default cookie domain scoping doesn't account for subdomain usage, which is the primary use case for a reverse proxy. ### Environment - OS Type & Version: Ubuntu 24.04 LTS - Pangolin Version: 1.12.x (latest) - Gerbil Version: latest - Traefik Version: v3.4 - Newt Version: N/A - Olm Version: N/A - Configuration: DEFAULT (no custom cookie settings) ### To Reproduce 1. Install Pangolin using default configuration (official installer or docker-compose) 2. Configure an OIDC provider (e.g., Authentik) - standard setup per docs 3. Create a resource accessible at `resource1.example.com` 4. Pangolin dashboard at `pangolin.example.com` 5. Do NOT modify any cookie-related settings - use defaults 6. Attempt to access `resource1.example.com` which requires OIDC auth 7. Observe that `p_oidc_state` cookie is set with `Domain=resource1.example.com` 8. OIDC provider authenticates and redirects to `pangolin.example.com` 9. Browser does not send the cookie (domain mismatch + SameSite=lax) 10. Authentication fails or enters a redirect loop No workarounds or custom configuration applied - this is purely default behavior. ### Expected Behavior With default configuration, OIDC-related cookies (`p_oidc_state`, `p_session_token`) should be set with first-level domain scope: - `Domain=.example.com` (note the leading dot) - This allows cookies to be shared across all subdomains by default - `SameSite` attribute should be appropriate for OIDC redirect flows out-of-the-box A reverse proxy serving multiple subdomains is the standard use case - cookie scoping should work correctly without requiring manual intervention or third-party plugins. Current workaround requires Traefik's proxy-cookie plugin to rewrite cookie domains, but this should not be necessary with sensible defaults.
GiteaMirror added the stale label 2026-04-16 09:00:34 -05:00
Author
Owner

@miloschwartz commented on GitHub (Dec 6, 2025):

Hey, thanks for posting these issues. I also see the other one you opened. There may be something funky going on, so I will try to reproduce.

However, I do want to clarify one thing about how the system works because it's not super obvious.

The root cause: p_session_token cookie is scoped to the exact hostname (Domain=example.com) instead of the wildcard (Domain=.example.com)

The session tokens are always supposed to be set on the root domain (dashboard_url from the config). The magic actually happens in Badger for handling the resource sessions.

You visit a resource, badger doesn't see a resource session cookie at resource1.example.com, so it redirects you to dashboard url to complete auth. You complete auth on dashboard url. Now dashboard url has a session cookie at dashboard.example.com. It checks if you should actually have access to the resource you were trying to visit based on the session (roles, etc). If allowed, it generates a "transfer token" which is just a short lived token that is appended to the url upon redirect back to the resource, like resource1.example.com/?transfer=abc123. Badger sees the transfer token, and exchanges it for a full session token. Badger, which is inside the reverse proxy serving the domain of the resource, sets the cookie at resource1.example.com, and lets you through.

Therefore, cookies are always set at exact domains. This complicated mess is required because we support the case where your dashboard url is on a different base domain than one or more resources, in which case you can't set cross domain cookies securely. This is kind of like a super basic internal only OAuth2 flow.

The p_oidc_state cookie thing you brought up sounds like it could be an issue. The state cookie should be set on the domain that initiates and completes the OIDC flow because it contains state info (OIDC state + redirect + etc). I'll take a look too see if I can reproduce this and fix.

<!-- gh-comment-id:3620637404 --> @miloschwartz commented on GitHub (Dec 6, 2025): Hey, thanks for posting these issues. I also see the other one you opened. There may be something funky going on, so I will try to reproduce. However, I do want to clarify one thing about how the system works because it's not super obvious. > The root cause: p_session_token cookie is scoped to the exact hostname (Domain=example.com) instead of the wildcard (Domain=.example.com) The session tokens are always supposed to be set on the root domain (`dashboard_url` from the config). The magic actually happens in Badger for handling the resource sessions. You visit a resource, badger doesn't see a resource session cookie at `resource1.example.com`, so it redirects you to dashboard url to complete auth. You complete auth on dashboard url. Now dashboard url has a session cookie at `dashboard.example.com`. It checks if you should actually have access to the resource you were trying to visit based on the session (roles, etc). If allowed, it generates a "transfer token" which is just a short lived token that is appended to the url upon redirect back to the resource, like `resource1.example.com/?transfer=abc123`. Badger sees the transfer token, and exchanges it for a full session token. Badger, which is inside the reverse proxy serving the domain of the resource, sets the cookie at `resource1.example.com`, and lets you through. Therefore, cookies are always set at exact domains. This complicated mess is required because we support the case where your dashboard url is on a different base domain than one or more resources, in which case you can't set cross domain cookies securely. This is kind of like a super basic internal only OAuth2 flow. The `p_oidc_state` cookie thing you brought up sounds like it could be an issue. The state cookie should be set on the domain that initiates and completes the OIDC flow because it contains state info (OIDC state + redirect + etc). I'll take a look too see if I can reproduce this and fix.
Author
Owner

@miloschwartz commented on GitHub (Dec 6, 2025):

@88plug Can you update to 1.12.x and let me know if you still encounter a redirect issue? I was unable to reproduce this. I setup an IdP and used it as the login method when redirected to the login page from an unauthenticated resource.

<!-- gh-comment-id:3620819206 --> @miloschwartz commented on GitHub (Dec 6, 2025): @88plug Can you update to 1.12.x and let me know if you still encounter a redirect issue? I was unable to reproduce this. I setup an IdP and used it as the login method when redirected to the login page from an unauthenticated resource.
Author
Owner

@github-actions[bot] commented on GitHub (Dec 21, 2025):

This issue has been automatically marked as stale due to 14 days of inactivity. It will be closed in 14 days if no further activity occurs.

<!-- gh-comment-id:3678242834 --> @github-actions[bot] commented on GitHub (Dec 21, 2025): This issue has been automatically marked as stale due to 14 days of inactivity. It will be closed in 14 days if no further activity occurs.
Author
Owner

@github-actions[bot] commented on GitHub (Jan 4, 2026):

This issue has been automatically closed due to inactivity. If you believe this is still relevant, please open a new issue with up-to-date information.

<!-- gh-comment-id:3707481313 --> @github-actions[bot] commented on GitHub (Jan 4, 2026): This issue has been automatically closed due to inactivity. If you believe this is still relevant, please open a new issue with up-to-date information.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/pangolin#2052