Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,39 @@ That's where different endpoint paths can be found.

Detailed information can be found in the `ProviderConfig <https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig>`_ specification.

You can also define ``OIDC_ISSUER`` to change the default provider name in the UI, even when the ``OIDC_DOMAIN`` is set.
Configuration Options
~~~~~~~~~~~~~~~~~~~~~

You can customize the provider display name:

.. code-block:: python

OIDC_PROVIDER_NAME = "My Company SSO"

The ``OIDC_PROVIDER_NAME`` controls how the provider appears in the Sentry UI. The name is also used to generate
login, identity-linking, and organization authentication settings screens.

Note: The ``OIDC_ISSUER`` setting is used internally for token validation and is not displayed to users.
Use ``OIDC_PROVIDER_NAME`` to customize the display name instead.
If ``OIDC_PROVIDER_NAME`` is not set, the provider name falls back to the configured or discovered issuer for
backwards compatibility, and then to ``OIDC``.

Custom Icon
~~~~~~~~~~~

To add a custom icon for your OIDC provider, add custom CSS to your self-hosted Sentry deployment.
The icon CSS class is based on the stable provider key, so it stays ``oidc`` even when
``OIDC_PROVIDER_NAME`` is customized.

For a quick overview, the CSS hook is:

.. code-block:: css

.provider-logo.oidc {
background-image: url('/static/your-oidc-icon.svg');
}

See `Custom Provider Icon <docs/custom-provider-icon.md>`_ for self-hosted Sentry image examples.

If your provider doesn't support the ``OIDC_DOMAIN``, then you have to set these
required endpoints by yourself (autorization_endpoint, token_endpoint, userinfo_endpoint, issuer).
Expand Down
49 changes: 49 additions & 0 deletions docs/custom-provider-icon.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Custom Provider Icon

Sentry does not provide a regular admin menu for adding custom CSS to the login page. For self-hosted Sentry, add the icon styling as part of your Sentry deployment.

The OIDC provider uses the stable provider key `oidc` for its logo CSS class. This means the selector remains `.provider-logo.oidc` even when `OIDC_PROVIDER_NAME` is set to a custom display name such as `"Acme Corp SSO"`.

## Bundled Icon

The recommended approach is to build a small custom Sentry image that ships the icon with Sentry's static files and appends the CSS to Sentry's frontend stylesheet.

```dockerfile
FROM getsentry/sentry:<version>

COPY your-oidc-icon.svg /usr/src/sentry/src/sentry/static/sentry/images/your-oidc-icon.svg

RUN cat >> /usr/src/sentry/src/sentry/static/sentry/dist/entrypoints/sentry.css <<'EOF'
.provider-logo.oidc {
background-image: url("/_static/sentry/images/your-oidc-icon.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
EOF
```

Use the same image for the Sentry web container so the login page can serve the updated stylesheet.

## External Icon URL

If the icon should be hosted outside the Sentry image, make sure the host is allowed by your deployment's `CSP_IMG_SRC` configuration.

```dockerfile
FROM getsentry/sentry:<version>

RUN cat >> /usr/src/sentry/src/sentry/static/sentry/dist/entrypoints/sentry.css <<'EOF'
.provider-logo.oidc {
background-image: url("https://example.com/your-oidc-icon.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
EOF
```

## Notes

- Keep the selector as `.provider-logo.oidc`; it is based on the provider key, not the display name.
- Prefer a bundled static asset when the deployment enforces a restrictive content security policy.
- Avoid reverse-proxy HTML or CSS injection unless you cannot build a custom image. It is more fragile than baking the CSS into the image.
3 changes: 3 additions & 0 deletions oidc/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@
config_issuer = getattr(settings, "OIDC_ISSUER", None)
if config_issuer:
ISSUER = config_issuer

# Provider name for display in the Sentry UI.
PROVIDER_NAME = getattr(settings, "OIDC_PROVIDER_NAME", None) or ISSUER or "OIDC"
4 changes: 2 additions & 2 deletions oidc/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
CLIENT_ID,
CLIENT_SECRET,
DATA_VERSION,
ISSUER,
PROVIDER_NAME,
SCOPE,
TOKEN_ENDPOINT,
USERINFO_ENDPOINT,
Expand Down Expand Up @@ -45,7 +45,7 @@ def get_authorize_params(self, state, redirect_uri):


class OIDCProvider(OAuth2Provider):
name = ISSUER
name = PROVIDER_NAME
key = "oidc"

def __init__(self, domain=None, domains=None, version=None, **config):
Expand Down
4 changes: 2 additions & 2 deletions oidc/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from sentry.utils import json
from sentry.utils.signing import urlsafe_b64decode

from .constants import ERR_INVALID_RESPONSE, ISSUER
from .constants import ERR_INVALID_RESPONSE, PROVIDER_NAME

logger = logging.getLogger("sentry.auth.oidc")

Expand Down Expand Up @@ -77,7 +77,7 @@ def oidc_configure_view(
else:
domains = config.get("domains")
return DeferredResponse(
"oidc/configure.html", {"provider_name": ISSUER or "", "domains": domains or []}
"oidc/configure.html", {"provider_name": PROVIDER_NAME, "domains": domains or []}
)


Expand Down
26 changes: 26 additions & 0 deletions tests/test_provider.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import importlib

import pytest
from django.test import override_settings
from sentry import auth
from sentry.auth.exceptions import IdentityNotValid
from sentry.models import AuthIdentity, AuthProvider
Expand All @@ -7,6 +10,7 @@

from oidc.constants import DATA_VERSION
from oidc.provider import OIDCProvider
from oidc.views import oidc_configure_view


@control_silo_test
Expand Down Expand Up @@ -61,3 +65,25 @@ def test_build_config(self):
}
result = provider.build_config(state)
assert result == {"domains": ["example.com"], "version": DATA_VERSION}

def test_provider_name_defaults_to_oidc(self):
provider = self.auth_provider_inst.get_provider()
assert provider.name == "OIDC"

@override_settings(OIDC_ISSUER="Custom Issuer")
def test_provider_name_falls_back_to_issuer(self):
import oidc.constants

try:
reloaded_constants = importlib.reload(oidc.constants)
assert reloaded_constants.PROVIDER_NAME == "Custom Issuer"
finally:
importlib.reload(oidc.constants)

def test_provider_key_is_oidc(self):
provider = self.auth_provider_inst.get_provider()
assert provider.key == "oidc"

def test_configure_view_uses_provider_name(self):
response = oidc_configure_view(None, self.organization, self.auth_provider_inst)
assert response.context["provider_name"] == "OIDC"
Loading