diff --git a/authentik/sources/oauth/apps.py b/authentik/sources/oauth/apps.py index f2c110a0bf5f..36c9a58ef88d 100644 --- a/authentik/sources/oauth/apps.py +++ b/authentik/sources/oauth/apps.py @@ -12,6 +12,7 @@ "authentik.sources.oauth.types.discord", "authentik.sources.oauth.types.facebook", "authentik.sources.oauth.types.github", + "authentik.sources.oauth.types.gitlab", "authentik.sources.oauth.types.google", "authentik.sources.oauth.types.mailcow", "authentik.sources.oauth.types.oidc", diff --git a/authentik/sources/oauth/models.py b/authentik/sources/oauth/models.py index 6dae77750a40..b2dc3c9ea06a 100644 --- a/authentik/sources/oauth/models.py +++ b/authentik/sources/oauth/models.py @@ -118,6 +118,15 @@ class Meta: verbose_name_plural = _("GitHub OAuth Sources") +class GitLabOAuthSource(OAuthSource): + """Social Login using GitLab.com or a GitLab Instance.""" + + class Meta: + abstract = True + verbose_name = _("GitLab OAuth Source") + verbose_name_plural = _("GitLab OAuth Sources") + + class TwitchOAuthSource(OAuthSource): """Social Login using Twitch.""" diff --git a/authentik/sources/oauth/tests/test_type_gitlab.py b/authentik/sources/oauth/tests/test_type_gitlab.py new file mode 100644 index 000000000000..99bfa25baeec --- /dev/null +++ b/authentik/sources/oauth/tests/test_type_gitlab.py @@ -0,0 +1,30 @@ +"""GitLab Type tests""" + +from django.test import TestCase + +from authentik.sources.oauth.models import OAuthSource +from authentik.sources.oauth.types.gitlab import GitLabOAuthCallback + +GITLAB_USER = { + "preferred_username": "dev_gitlab", + "email": "dev@gitlab.com", + "name": "Dev", +} + + +class TestTypeGitLab(TestCase): + """OAuth Source tests for GitLab""" + + def setUp(self): + self.source = OAuthSource.objects.create( + name="gitlab_test", + slug="gitlab_test", + provider_type="gitlab", + ) + + def test_enroll_context(self): + """Test GitLab Enrollment context""" + ak_context = GitLabOAuthCallback().get_user_enroll_context(GITLAB_USER) + self.assertEqual(ak_context["username"], GITLAB_USER["preferred_username"]) + self.assertEqual(ak_context["email"], GITLAB_USER["email"]) + self.assertEqual(ak_context["name"], GITLAB_USER["name"]) diff --git a/authentik/sources/oauth/types/gitlab.py b/authentik/sources/oauth/types/gitlab.py new file mode 100644 index 000000000000..3d90ea7e5d6a --- /dev/null +++ b/authentik/sources/oauth/types/gitlab.py @@ -0,0 +1,54 @@ +""" +GitLab OAuth Views + +See https://docs.gitlab.com/ee/integration/oauth_provider.html +and https://docs.gitlab.com/ee/integration/openid_connect_provider.html +""" + +from typing import Any + +from authentik.sources.oauth.models import OAuthSource +from authentik.sources.oauth.types.registry import SourceType, registry +from authentik.sources.oauth.views.callback import OAuthCallback +from authentik.sources.oauth.views.redirect import OAuthRedirect + + +class GitLabOAuthRedirect(OAuthRedirect): + """GitLab OAuth2 Redirect""" + + def get_additional_parameters(self, source: OAuthSource): + return { + "scope": ["read_user", "openid", "profile", "email"], + } + + +class GitLabOAuthCallback(OAuthCallback): + """GitLab OAuth2 Callback""" + + def get_user_enroll_context( + self, + info: dict[str, Any], + ) -> dict[str, Any]: + return { + "username": info.get("preferred_username"), + "email": info.get("email"), + "name": info.get("name"), + } + + +@registry.register() +class GitLabType(SourceType): + """GitLab Type definition""" + + callback_view = GitLabOAuthCallback + redirect_view = GitLabOAuthRedirect + verbose_name = "GitLab" + name = "gitlab" + + urls_customizable = True + + authorization_url = "https://gitlab.com/oauth/authorize" + access_token_url = "https://gitlab.com/oauth/token" # nosec + profile_url = "https://gitlab.com/oauth/userinfo" + oidc_well_known_url = "https://gitlab.com/.well-known/openid-configuration" + oidc_jwks_url = "https://gitlab.com/oauth/discovery/keys" diff --git a/blueprints/schema.json b/blueprints/schema.json index bd3c1cd29f44..80046626f96c 100644 --- a/blueprints/schema.json +++ b/blueprints/schema.json @@ -4444,6 +4444,7 @@ "discord", "facebook", "github", + "gitlab", "google", "mailcow", "okta", diff --git a/schema.yml b/schema.yml index 38c0d042839c..b766290d0684 100644 --- a/schema.yml +++ b/schema.yml @@ -29699,7 +29699,7 @@ components: * `authentik.events` - authentik Events AppleChallengeResponseRequest: type: object - description: Pseudo class for plex response + description: Pseudo class for apple response properties: component: type: string @@ -41406,6 +41406,7 @@ components: - discord - facebook - github + - gitlab - google - mailcow - okta @@ -41421,6 +41422,7 @@ components: * `discord` - Discord * `facebook` - Facebook * `github` - GitHub + * `gitlab` - GitLab * `google` - Google * `mailcow` - Mailcow * `okta` - Okta diff --git a/web/src/admin/sources/oauth/OAuthSourceViewPage.ts b/web/src/admin/sources/oauth/OAuthSourceViewPage.ts index f70c13038e56..df544acb2483 100644 --- a/web/src/admin/sources/oauth/OAuthSourceViewPage.ts +++ b/web/src/admin/sources/oauth/OAuthSourceViewPage.ts @@ -44,6 +44,8 @@ export function ProviderToLabel(provider?: ProviderTypeEnum): string { return "Facebook"; case ProviderTypeEnum.Github: return "GitHub"; + case ProviderTypeEnum.Gitlab: + return "GitLab"; case ProviderTypeEnum.Google: return "Google"; case ProviderTypeEnum.Mailcow: