sources/oauth: add patreon type (#5452)

* Models Update to include Patreon as Social Sign On

Signed-off-by: DerGardine <julian.burgschweiger@gmail.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add tests, use vanity as username

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: DerGardine <julian.burgschweiger@gmail.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
DerGardine 2023-05-03 12:49:43 +02:00 committed by GitHub
parent 0ae53b1ce8
commit a2994218e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 135 additions and 2 deletions

View File

@ -12,12 +12,13 @@ AUTHENTIK_SOURCES_OAUTH_TYPES = [
"authentik.sources.oauth.types.facebook",
"authentik.sources.oauth.types.github",
"authentik.sources.oauth.types.google",
"authentik.sources.oauth.types.mailcow",
"authentik.sources.oauth.types.oidc",
"authentik.sources.oauth.types.okta",
"authentik.sources.oauth.types.patreon",
"authentik.sources.oauth.types.reddit",
"authentik.sources.oauth.types.twitter",
"authentik.sources.oauth.types.mailcow",
"authentik.sources.oauth.types.twitch",
"authentik.sources.oauth.types.twitter",
]

View File

@ -163,6 +163,15 @@ class DiscordOAuthSource(OAuthSource):
verbose_name_plural = _("Discord OAuth Sources")
class PatreonOAuthSource(OAuthSource):
"""Social Login using Patreon."""
class Meta:
abstract = True
verbose_name = _("Patreon OAuth Source")
verbose_name_plural = _("Patreon OAuth Sources")
class GoogleOAuthSource(OAuthSource):
"""Social Login using Google or Google Workspace (GSuite)."""

View File

@ -0,0 +1,67 @@
"""Patreon Type tests"""
from django.test import RequestFactory, TestCase
from authentik.sources.oauth.models import OAuthSource
from authentik.sources.oauth.types.patreon import PatreonOAuthCallback
PATREON_USER = {
"data": {
"attributes": {
"about": None,
"created": "2017-10-20T21:36:23+00:00",
"discord_id": None,
"email": "corgi@example.com",
"facebook": None,
"facebook_id": None,
"first_name": "Corgi",
"full_name": "Corgi The Dev",
"gender": 0,
"has_password": True,
"image_url": "https://c8.patreon.com/2/400/0000000",
"is_deleted": False,
"is_email_verified": False,
"is_nuked": False,
"is_suspended": False,
"last_name": "The Dev",
"social_connections": {
"deviantart": None,
"discord": None,
"facebook": None,
"reddit": None,
"spotify": None,
"twitch": None,
"twitter": None,
"youtube": None,
},
"thumb_url": "https://c8.patreon.com/2/100/0000000",
"twitch": None,
"twitter": None,
"url": "https://www.patreon.com/corgithedev",
"vanity": "corgithedev",
"youtube": None,
},
"id": "0000000",
"relationships": {"pledges": {"data": []}},
"type": "user",
},
"links": {"self": "https://www.patreon.com/api/user/0000000"},
}
class TestTypePatreon(TestCase):
"""OAuth Source tests"""
def setUp(self):
self.source = OAuthSource.objects.create(
name="test",
slug="test",
provider_type="Patreon",
)
self.factory = RequestFactory()
def test_enroll_context(self):
"""Test Patreon Enrollment context"""
ak_context = PatreonOAuthCallback().get_user_enroll_context(PATREON_USER)
self.assertEqual(ak_context["username"], PATREON_USER["data"]["attributes"]["vanity"])
self.assertEqual(ak_context["email"], PATREON_USER["data"]["attributes"]["email"])
self.assertEqual(ak_context["name"], PATREON_USER["data"]["attributes"]["full_name"])

View File

@ -0,0 +1,50 @@
"""Patreon OAuth Views"""
from typing import Any
from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient
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 PatreonOAuthRedirect(OAuthRedirect):
"""Patreon OAuth2 Redirect"""
def get_additional_parameters(self, source: OAuthSource): # pragma: no cover
return {
"scope": ["openid", "email", "profile"],
}
class PatreonOAuthCallback(OAuthCallback):
"""Patreon OAuth2 Callback"""
client_class: UserprofileHeaderAuthClient
def get_user_id(self, info: dict[str, str]) -> str:
return info.get("data", {}).get("id")
def get_user_enroll_context(
self,
info: dict[str, Any],
) -> dict[str, Any]:
return {
"username": info.get("data", {}).get("attributes", {}).get("vanity"),
"email": info.get("data", {}).get("attributes", {}).get("email"),
"name": info.get("data", {}).get("attributes", {}).get("full_name"),
}
@registry.register()
class PatreonType(SourceType):
"""OpenIDConnect Type definition"""
callback_view = PatreonOAuthCallback
redirect_view = PatreonOAuthRedirect
name = "Patreon"
slug = "patreon"
authorization_url = "https://www.patreon.com/oauth2/authorize"
access_token_url = "https://www.patreon.com/api/oauth2/token" # nosec
profile_url = "https://www.patreon.com/api/oauth2/api/current_user"

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="256px" height="247px" viewBox="0 0 256 247" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M45.1355837,0 L45.1355837,246.35001 L0,246.35001 L0,0 L45.1355837,0 Z M163.657111,0 C214.65668,0 256,41.3433196 256,92.3428889 C256,143.342458 214.65668,184.685778 163.657111,184.685778 C112.657542,184.685778 71.3142222,143.342458 71.3142222,92.3428889 C71.3142222,41.3433196 112.657542,0 163.657111,0 Z" fill="#FF424D"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 587 B