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:
p_oidc_state cookie is set with Domain=resource1.example.com instead of .example.com
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
SameSite=lax (the default) further blocks the cookie on cross-site redirects
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
Install Pangolin using default configuration (official installer or docker-compose)
Configure an OIDC provider (e.g., Authentik) - standard setup per docs
Create a resource accessible at resource1.example.com
Pangolin dashboard at pangolin.example.com
Do NOT modify any cookie-related settings - use defaults
Attempt to access resource1.example.com which requires OIDC auth
Observe that p_oidc_state cookie is set with Domain=resource1.example.com
OIDC provider authenticates and redirects to pangolin.example.com
Browser does not send the cookie (domain mismatch + SameSite=lax)
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.
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.
@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.
@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.
@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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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:
p_oidc_statecookie is set withDomain=resource1.example.cominstead of.example.compangolin.example.com), the browser refuses to send the cookie due to domain mismatchSameSite=lax(the default) further blocks the cookie on cross-site redirectsp_session_tokenis also scoped to exact hostname, causing redirect loopsThis 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
To Reproduce
resource1.example.compangolin.example.comresource1.example.comwhich requires OIDC authp_oidc_statecookie is set withDomain=resource1.example.compangolin.example.comNo 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)SameSiteattribute should be appropriate for OIDC redirect flows out-of-the-boxA 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.
@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 session tokens are always supposed to be set on the root domain (
dashboard_urlfrom 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 atdashboard.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, likeresource1.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 atresource1.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_statecookie 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.@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.
@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.
@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.