diff --git a/README.md b/README.md index 8b4f0f1..1972bac 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,29 @@ Only providers with credentials configured will appear as login options. Set the | Google | `SOCIAL_AUTH_GOOGLE_OAUTH2_KEY`, `SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET` | | Microsoft | `SOCIAL_AUTH_MICROSOFT_GRAPH_KEY`, `SOCIAL_AUTH_MICROSOFT_GRAPH_SECRET` | | Auth0 | `SOCIAL_AUTH_AUTH0_DOMAIN`, `SOCIAL_AUTH_AUTH0_KEY`, `SOCIAL_AUTH_AUTH0_SECRET` | +| Generic OIDC | `SOCIAL_AUTH_OIDC_OIDC_ENDPOINT`, `SOCIAL_AUTH_OIDC_KEY`, `SOCIAL_AUTH_OIDC_SECRET` | For Auth0, set the callback URL to: `https://your-domain/api/1/auth/social/complete/auth0/` +### Generic OIDC Provider + +The generic OIDC provider works with any OpenID Connect compliant identity provider, including: +- **Authentik** - Self-hosted identity provider +- **Authelia** - Self-hosted authentication server +- **Keycloak** - Open source identity management +- **Okta** - Enterprise identity platform +- And any other OIDC-compliant provider + +Configuration: +- `SOCIAL_AUTH_OIDC_OIDC_ENDPOINT`: The OIDC discovery endpoint (e.g., `https://authentik.example.com/application/o//`) +- `SOCIAL_AUTH_OIDC_KEY`: Client ID from your identity provider +- `SOCIAL_AUTH_OIDC_SECRET`: Client secret from your identity provider +- `SOCIAL_AUTH_OIDC_NAME` (optional): Custom button text (default: "SSO") + +Set the callback URL to: `https://your-domain/api/1/auth/social/complete/oidc/` + +**Note on MFA:** Multi-factor authentication is handled by your identity provider. Enable MFA in Authentik, Authelia, or your chosen provider to require 2FA for Tabby Web logins. + ## Adding Tabby app versions * `docker-compose run tabby /manage.sh add_version 1.0.163` diff --git a/backend/tabby/app/api/auth.py b/backend/tabby/app/api/auth.py index 69ca9de..73d28c9 100644 --- a/backend/tabby/app/api/auth.py +++ b/backend/tabby/app/api/auth.py @@ -37,6 +37,12 @@ PROVIDER_CONFIG = { 'cls': 'btn-dark', 'env_prefix': 'SOCIAL_AUTH_AUTH0', }, + 'oidc': { + 'name': 'SSO', # Generic name, can be overridden via SOCIAL_AUTH_OIDC_NAME + 'icon': 'openid', # OpenID icon + 'cls': 'btn-info', + 'env_prefix': 'SOCIAL_AUTH_OIDC', + }, } @@ -48,9 +54,19 @@ def is_provider_configured(env_prefix: str) -> bool: if env_prefix == 'SOCIAL_AUTH_AUTH0': domain = getattr(settings, f'{env_prefix}_DOMAIN', None) return bool(key and secret and domain) + # For generic OIDC, also need OIDC_ENDPOINT + if env_prefix == 'SOCIAL_AUTH_OIDC': + endpoint = getattr(settings, f'{env_prefix}_OIDC_ENDPOINT', None) + return bool(key and secret and endpoint) return bool(key and secret) +def get_provider_display_name(env_prefix: str, default_name: str) -> str: + """Get custom display name for a provider, if configured.""" + custom_name = getattr(settings, f'{env_prefix}_NAME', None) + return custom_name if custom_name else default_name + + class LogoutView(APIView): def post(self, request, format=None): logout(request) @@ -66,7 +82,9 @@ class ProvidersView(APIView): if is_provider_configured(config['env_prefix']): providers.append({ 'id': provider_id, - 'name': config['name'], + 'name': get_provider_display_name( + config['env_prefix'], config['name'] + ), 'icon': config['icon'], 'cls': config['cls'], }) diff --git a/backend/tabby/settings.py b/backend/tabby/settings.py index d01d06c..f4e70c7 100644 --- a/backend/tabby/settings.py +++ b/backend/tabby/settings.py @@ -141,6 +141,7 @@ AUTHENTICATION_BACKENDS = ( "social_core.backends.microsoft.MicrosoftOAuth2", "social_core.backends.google.GoogleOAuth2", "social_core.backends.auth0.Auth0OAuth2", + "social_core.backends.open_id_connect.OpenIdConnectAuth", "django.contrib.auth.backends.ModelBackend", ) @@ -188,6 +189,10 @@ for key in [ "SOCIAL_AUTH_AUTH0_DOMAIN", "SOCIAL_AUTH_AUTH0_KEY", "SOCIAL_AUTH_AUTH0_SECRET", + "SOCIAL_AUTH_OIDC_OIDC_ENDPOINT", + "SOCIAL_AUTH_OIDC_KEY", + "SOCIAL_AUTH_OIDC_SECRET", + "SOCIAL_AUTH_OIDC_NAME", "CONNECTION_GATEWAY_AUTH_CA", "CONNECTION_GATEWAY_AUTH_CERTIFICATE", "CONNECTION_GATEWAY_AUTH_KEY", diff --git a/frontend/src/login/components/login.component.ts b/frontend/src/login/components/login.component.ts index b6905c4..dd86565 100644 --- a/frontend/src/login/components/login.component.ts +++ b/frontend/src/login/components/login.component.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core' import { HttpClient } from '@angular/common/http' import { LoginService, CommonService } from 'src/common' -import { faGithub, faGitlab, faGoogle, faMicrosoft, IconDefinition } from '@fortawesome/free-brands-svg-icons' +import { faGithub, faGitlab, faGoogle, faMicrosoft, faOpenid, IconDefinition } from '@fortawesome/free-brands-svg-icons' import { faKey } from '@fortawesome/free-solid-svg-icons' interface Provider { @@ -18,6 +18,7 @@ const iconMap: Record = { gitlab: faGitlab, google: faGoogle, microsoft: faMicrosoft, + openid: faOpenid, // Used for generic OIDC providers (Authentik, Authelia, Keycloak, etc.) key: faKey, // Used for Auth0 and other providers without brand icons }