sources/oauth: add Twitch OAuth source (#3746)
* sources/oauth: add Twitch OAuth source Signed-off-by: Lukas Vögl <lukas@voegl.org> * website/integrations: add Twitch OAuth source documentation Signed-off-by: Lukas Vögl <lukas@voegl.org> Signed-off-by: Lukas Vögl <lukas@voegl.org>
This commit is contained in:
parent
a96b7dd9a3
commit
3ecc715e91
|
@ -17,6 +17,7 @@ AUTHENTIK_SOURCES_OAUTH_TYPES = [
|
||||||
"authentik.sources.oauth.types.reddit",
|
"authentik.sources.oauth.types.reddit",
|
||||||
"authentik.sources.oauth.types.twitter",
|
"authentik.sources.oauth.types.twitter",
|
||||||
"authentik.sources.oauth.types.mailcow",
|
"authentik.sources.oauth.types.mailcow",
|
||||||
|
"authentik.sources.oauth.types.twitch",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -115,6 +115,16 @@ class GitHubOAuthSource(OAuthSource):
|
||||||
verbose_name_plural = _("GitHub OAuth Sources")
|
verbose_name_plural = _("GitHub OAuth Sources")
|
||||||
|
|
||||||
|
|
||||||
|
class TwitchOAuthSource(OAuthSource):
|
||||||
|
"""Social Login using Twitch."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
|
||||||
|
abstract = True
|
||||||
|
verbose_name = _("Twitch OAuth Source")
|
||||||
|
verbose_name_plural = _("Twitch OAuth Sources")
|
||||||
|
|
||||||
|
|
||||||
class MailcowOAuthSource(OAuthSource):
|
class MailcowOAuthSource(OAuthSource):
|
||||||
"""Social Login using Mailcow."""
|
"""Social Login using Mailcow."""
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
"""Twitch Type tests"""
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
from authentik.sources.oauth.models import OAuthSource
|
||||||
|
from authentik.sources.oauth.types.twitch import TwitchOAuth2Callback
|
||||||
|
|
||||||
|
# https://dev.twitch.tv/docs/authentication/getting-tokens-oidc/#getting-claims-information-from-an-access-token
|
||||||
|
TWITCH_USER = {
|
||||||
|
"aud": "ym2tq9o71tikh2zyebksiture1hzg5",
|
||||||
|
"exp": 1665261184,
|
||||||
|
"iat": 1665260184,
|
||||||
|
"iss": "https://id.twitch.tv/oauth2",
|
||||||
|
"sub": "603916897",
|
||||||
|
"email": "foo@bar.baz",
|
||||||
|
"preferred_username": "FooBar",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestTypeTwitch(TestCase):
|
||||||
|
"""OAuth Source tests"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.source = OAuthSource.objects.create(
|
||||||
|
name="test",
|
||||||
|
slug="test",
|
||||||
|
provider_type="twitch",
|
||||||
|
authorization_url="",
|
||||||
|
profile_url="",
|
||||||
|
consumer_key="",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_enroll_context(self):
|
||||||
|
"""Test twitch Enrollment context"""
|
||||||
|
ak_context = TwitchOAuth2Callback().get_user_enroll_context(TWITCH_USER)
|
||||||
|
self.assertEqual(ak_context["username"], TWITCH_USER["preferred_username"])
|
||||||
|
self.assertEqual(ak_context["email"], TWITCH_USER["email"])
|
||||||
|
self.assertEqual(ak_context["name"], TWITCH_USER["preferred_username"])
|
|
@ -0,0 +1,60 @@
|
||||||
|
"""Twitch OAuth Views"""
|
||||||
|
from json import dumps
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from authentik.sources.oauth.clients.oauth2 import UserprofileHeaderAuthClient
|
||||||
|
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 TwitchClient(UserprofileHeaderAuthClient):
|
||||||
|
"""Twitch needs the token_type to be capitalized for the request header."""
|
||||||
|
|
||||||
|
def get_profile_info(self, token: dict[str, str]) -> Optional[dict[str, Any]]:
|
||||||
|
token["token_type"] = token["token_type"].capitalize()
|
||||||
|
return super().get_profile_info(token)
|
||||||
|
|
||||||
|
|
||||||
|
class TwitchOAuthRedirect(OAuthRedirect):
|
||||||
|
"""Twitch OAuth2 Redirect"""
|
||||||
|
|
||||||
|
def get_additional_parameters(self, source): # pragma: no cover
|
||||||
|
claims = {"userinfo": {"email": None, "preferred_username": None}}
|
||||||
|
return {
|
||||||
|
"scope": ["openid"],
|
||||||
|
"claims": dumps(claims),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TwitchOAuth2Callback(OAuthCallback):
|
||||||
|
"""Twitch OAuth2 Callback"""
|
||||||
|
|
||||||
|
client_class = TwitchClient
|
||||||
|
|
||||||
|
def get_user_id(self, info: dict[str, str]) -> str:
|
||||||
|
return info.get("sub", "")
|
||||||
|
|
||||||
|
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("preferred_username"),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@registry.register()
|
||||||
|
class TwitchType(SourceType):
|
||||||
|
"""Twitch Type definition"""
|
||||||
|
|
||||||
|
callback_view = TwitchOAuth2Callback
|
||||||
|
redirect_view = TwitchOAuthRedirect
|
||||||
|
name = "Twitch"
|
||||||
|
slug = "twitch"
|
||||||
|
|
||||||
|
authorization_url = "https://id.twitch.tv/oauth2/authorize"
|
||||||
|
access_token_url = "https://id.twitch.tv/oauth2/token" # nosec
|
||||||
|
profile_url = "https://id.twitch.tv/oauth2/userinfo"
|
|
@ -35104,6 +35104,7 @@ components:
|
||||||
- reddit
|
- reddit
|
||||||
- twitter
|
- twitter
|
||||||
- mailcow
|
- mailcow
|
||||||
|
- twitch
|
||||||
type: string
|
type: string
|
||||||
ProxyMode:
|
ProxyMode:
|
||||||
enum:
|
enum:
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 23.0.6, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 2400 2800" style="enable-background:new 0 0 2400 2800;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
.st1{fill:#9146FF;}
|
||||||
|
</style>
|
||||||
|
<title>Asset 2</title>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="2200,1300 1800,1700 1400,1700 1050,2050 1050,1700 600,1700 600,200 2200,200 "/>
|
||||||
|
<g>
|
||||||
|
<g id="Layer_1-2">
|
||||||
|
<path class="st1" d="M500,0L0,500v1800h600v500l500-500h400l900-900V0H500z M2200,1300l-400,400h-400l-350,350v-350H600V200h1600 V1300z"/>
|
||||||
|
<rect x="1700" y="550" class="st1" width="200" height="600"/>
|
||||||
|
<rect x="1150" y="550" class="st1" width="200" height="600"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 887 B |
|
@ -0,0 +1,60 @@
|
||||||
|
---
|
||||||
|
title: Twitch
|
||||||
|
---
|
||||||
|
|
||||||
|
<span class="badge badge--secondary">Support level: Community</span>
|
||||||
|
|
||||||
|
Allows users to authenticate using their Twitch credentials
|
||||||
|
|
||||||
|
## Preparation
|
||||||
|
|
||||||
|
The following placeholders will be used:
|
||||||
|
|
||||||
|
- `authentik.company` is the FQDN of the authentik install.
|
||||||
|
|
||||||
|
## Twitch
|
||||||
|
|
||||||
|
1. Click **Register Your Application** in the Twitch Developers Console https://dev.twitch.tv/console
|
||||||
|
|
||||||
|
![Register Your Application Button](twitch1.png)
|
||||||
|
|
||||||
|
2. Name your Application
|
||||||
|
|
||||||
|
3. Add https://authentik.company/source/oauth/callback/twitch in the **OAuth Redirect URLs** field
|
||||||
|
|
||||||
|
4. Select a Category for your Application
|
||||||
|
|
||||||
|
5. Click **Create** to finish the registration of your Application
|
||||||
|
|
||||||
|
![Create Application](twitch2.png)
|
||||||
|
|
||||||
|
6. Click **Manage** on your newly created Application
|
||||||
|
|
||||||
|
![Manage Application](twitch3.png)
|
||||||
|
|
||||||
|
7. Copy your Client ID and save it for later
|
||||||
|
|
||||||
|
8. Click **New Secret** to create a new Secret
|
||||||
|
|
||||||
|
9. Copy the above Secret and also save it for later
|
||||||
|
|
||||||
|
![Copy Keys](twitch4.png)
|
||||||
|
|
||||||
|
## authentik
|
||||||
|
|
||||||
|
10. Under _Directory -> Federation & Social login_ Click **Create Twitch OAuth Source**
|
||||||
|
|
||||||
|
11. **Name:** Choose a name (For the example I used Twitch)
|
||||||
|
12. **Slug:** twitch (You can choose a different slug, if you do you will need to update the Twitch redirect URL and point it to the correct slug.)
|
||||||
|
13. **Consumer Key:** Client ID from step 7
|
||||||
|
14. **Consumer Secret:** Secret from step 9
|
||||||
|
|
||||||
|
Here is an example of a complete authentik Twitch OAuth Source
|
||||||
|
|
||||||
|
![Authentik Source Example](twitch5.png)
|
||||||
|
|
||||||
|
Save, and you now have Twitch as a source.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
For more details on how-to have the new source display on the Login Page see [here](../).
|
||||||
|
:::
|
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
After Width: | Height: | Size: 67 KiB |
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
Binary file not shown.
After Width: | Height: | Size: 97 KiB |
|
@ -137,6 +137,7 @@ module.exports = {
|
||||||
"sources/github/index",
|
"sources/github/index",
|
||||||
"sources/google/index",
|
"sources/google/index",
|
||||||
"sources/mailcow/index",
|
"sources/mailcow/index",
|
||||||
|
"sources/twitch/index",
|
||||||
"sources/plex/index",
|
"sources/plex/index",
|
||||||
"sources/twitter/index",
|
"sources/twitter/index",
|
||||||
],
|
],
|
||||||
|
|
Reference in New Issue