sources/oauth: add Sign in with Apple (#1635)
* sources/oauth: add apple sign in support Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * website/docs: apple sign in docs Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * website/docs: fix missing apple in sidebar Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> * sources/oauth: add fallback values for name and slug Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
		| @ -7,14 +7,15 @@ from structlog.stdlib import get_logger | |||||||
| LOGGER = get_logger() | LOGGER = get_logger() | ||||||
|  |  | ||||||
| AUTHENTIK_SOURCES_OAUTH_TYPES = [ | AUTHENTIK_SOURCES_OAUTH_TYPES = [ | ||||||
|  |     "authentik.sources.oauth.types.apple", | ||||||
|  |     "authentik.sources.oauth.types.azure_ad", | ||||||
|     "authentik.sources.oauth.types.discord", |     "authentik.sources.oauth.types.discord", | ||||||
|     "authentik.sources.oauth.types.facebook", |     "authentik.sources.oauth.types.facebook", | ||||||
|     "authentik.sources.oauth.types.github", |     "authentik.sources.oauth.types.github", | ||||||
|     "authentik.sources.oauth.types.google", |     "authentik.sources.oauth.types.google", | ||||||
|  |     "authentik.sources.oauth.types.oidc", | ||||||
|     "authentik.sources.oauth.types.reddit", |     "authentik.sources.oauth.types.reddit", | ||||||
|     "authentik.sources.oauth.types.twitter", |     "authentik.sources.oauth.types.twitter", | ||||||
|     "authentik.sources.oauth.types.azure_ad", |  | ||||||
|     "authentik.sources.oauth.types.oidc", |  | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| """OAuth Clients""" | """OAuth Clients""" | ||||||
| from typing import Any, Optional | from typing import Any, Optional | ||||||
| from urllib.parse import urlencode | from urllib.parse import quote, urlencode | ||||||
|  |  | ||||||
| from django.http import HttpRequest | from django.http import HttpRequest | ||||||
| from requests import Session | from requests import Session | ||||||
| @ -58,7 +58,7 @@ class BaseOAuthClient: | |||||||
|         args = self.get_redirect_args() |         args = self.get_redirect_args() | ||||||
|         additional = parameters or {} |         additional = parameters or {} | ||||||
|         args.update(additional) |         args.update(additional) | ||||||
|         params = urlencode(args) |         params = urlencode(args, quote_via=quote) | ||||||
|         LOGGER.info("redirect args", **args) |         LOGGER.info("redirect args", **args) | ||||||
|         authorization_url = self.source.type.authorization_url or "" |         authorization_url = self.source.type.authorization_url or "" | ||||||
|         if self.source.type.urls_customizable and self.source.authorization_url: |         if self.source.type.urls_customizable and self.source.authorization_url: | ||||||
|  | |||||||
| @ -20,10 +20,16 @@ class OAuth2Client(BaseOAuthClient): | |||||||
|         "Accept": "application/json", |         "Accept": "application/json", | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     def get_request_arg(self, key: str, default: Optional[Any] = None) -> Any: | ||||||
|  |         """Depending on request type, get data from post or get""" | ||||||
|  |         if self.request.method == "POST": | ||||||
|  |             return self.request.POST.get(key, default) | ||||||
|  |         return self.request.GET.get(key, default) | ||||||
|  |  | ||||||
|     def check_application_state(self) -> bool: |     def check_application_state(self) -> bool: | ||||||
|         "Check optional state parameter." |         "Check optional state parameter." | ||||||
|         stored = self.request.session.get(self.session_key, None) |         stored = self.request.session.get(self.session_key, None) | ||||||
|         returned = self.request.GET.get("state", None) |         returned = self.get_request_arg("state", None) | ||||||
|         check = False |         check = False | ||||||
|         if stored is not None: |         if stored is not None: | ||||||
|             if returned is not None: |             if returned is not None: | ||||||
| @ -38,23 +44,31 @@ class OAuth2Client(BaseOAuthClient): | |||||||
|         "Generate state optional parameter." |         "Generate state optional parameter." | ||||||
|         return get_random_string(32) |         return get_random_string(32) | ||||||
|  |  | ||||||
|  |     def get_client_id(self) -> str: | ||||||
|  |         """Get client id""" | ||||||
|  |         return self.source.consumer_key | ||||||
|  |  | ||||||
|  |     def get_client_secret(self) -> str: | ||||||
|  |         """Get client secret""" | ||||||
|  |         return self.source.consumer_secret | ||||||
|  |  | ||||||
|     def get_access_token(self, **request_kwargs) -> Optional[dict[str, Any]]: |     def get_access_token(self, **request_kwargs) -> Optional[dict[str, Any]]: | ||||||
|         "Fetch access token from callback request." |         "Fetch access token from callback request." | ||||||
|         callback = self.request.build_absolute_uri(self.callback or self.request.path) |         callback = self.request.build_absolute_uri(self.callback or self.request.path) | ||||||
|         if not self.check_application_state(): |         if not self.check_application_state(): | ||||||
|             LOGGER.warning("Application state check failed.") |             LOGGER.warning("Application state check failed.") | ||||||
|             return None |             return None | ||||||
|         if "code" in self.request.GET: |         code = self.get_request_arg("code", None) | ||||||
|             args = { |         if not code: | ||||||
|                 "client_id": self.source.consumer_key, |  | ||||||
|                 "redirect_uri": callback, |  | ||||||
|                 "client_secret": self.source.consumer_secret, |  | ||||||
|                 "code": self.request.GET["code"], |  | ||||||
|                 "grant_type": "authorization_code", |  | ||||||
|             } |  | ||||||
|         else: |  | ||||||
|             LOGGER.warning("No code returned by the source") |             LOGGER.warning("No code returned by the source") | ||||||
|             return None |             return None | ||||||
|  |         args = { | ||||||
|  |             "client_id": self.get_client_id(), | ||||||
|  |             "client_secret": self.get_client_secret(), | ||||||
|  |             "redirect_uri": callback, | ||||||
|  |             "code": code, | ||||||
|  |             "grant_type": "authorization_code", | ||||||
|  |         } | ||||||
|         try: |         try: | ||||||
|             access_token_url = self.source.type.access_token_url or "" |             access_token_url = self.source.type.access_token_url or "" | ||||||
|             if self.source.type.urls_customizable and self.source.access_token_url: |             if self.source.type.urls_customizable and self.source.access_token_url: | ||||||
| @ -75,7 +89,7 @@ class OAuth2Client(BaseOAuthClient): | |||||||
|     def get_redirect_args(self) -> dict[str, str]: |     def get_redirect_args(self) -> dict[str, str]: | ||||||
|         "Get request parameters for redirect url." |         "Get request parameters for redirect url." | ||||||
|         callback = self.request.build_absolute_uri(self.callback) |         callback = self.request.build_absolute_uri(self.callback) | ||||||
|         client_id: str = self.source.consumer_key |         client_id: str = self.get_client_id() | ||||||
|         args: dict[str, str] = { |         args: dict[str, str] = { | ||||||
|             "client_id": client_id, |             "client_id": client_id, | ||||||
|             "redirect_uri": callback, |             "redirect_uri": callback, | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ | |||||||
| from typing import TYPE_CHECKING, Optional, Type | from typing import TYPE_CHECKING, Optional, Type | ||||||
|  |  | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.templatetags.static import static |  | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.translation import gettext_lazy as _ | ||||||
| from rest_framework.serializers import Serializer | from rest_framework.serializers import Serializer | ||||||
| @ -49,7 +48,7 @@ class OAuthSource(Source): | |||||||
|     consumer_secret = models.TextField() |     consumer_secret = models.TextField() | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def type(self) -> "SourceType": |     def type(self) -> Type["SourceType"]: | ||||||
|         """Return the provider instance for this source""" |         """Return the provider instance for this source""" | ||||||
|         from authentik.sources.oauth.types.manager import MANAGER |         from authentik.sources.oauth.types.manager import MANAGER | ||||||
|  |  | ||||||
| @ -67,6 +66,7 @@ class OAuthSource(Source): | |||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def ui_login_button(self) -> UILoginButton: |     def ui_login_button(self) -> UILoginButton: | ||||||
|  |         provider_type = self.type | ||||||
|         return UILoginButton( |         return UILoginButton( | ||||||
|             challenge=RedirectChallenge( |             challenge=RedirectChallenge( | ||||||
|                 instance={ |                 instance={ | ||||||
| @ -77,7 +77,7 @@ class OAuthSource(Source): | |||||||
|                     ), |                     ), | ||||||
|                 } |                 } | ||||||
|             ), |             ), | ||||||
|             icon_url=static(f"authentik/sources/{self.provider_type}.svg"), |             icon_url=provider_type().icon_url(), | ||||||
|             name=self.name, |             name=self.name, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
| @ -173,6 +173,16 @@ class OpenIDConnectOAuthSource(OAuthSource): | |||||||
|         verbose_name_plural = _("OpenID OAuth Sources") |         verbose_name_plural = _("OpenID OAuth Sources") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AppleOAuthSource(OAuthSource): | ||||||
|  |     """Login using a apple.com.""" | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |  | ||||||
|  |         abstract = True | ||||||
|  |         verbose_name = _("Apple OAuth Source") | ||||||
|  |         verbose_name_plural = _("Apple OAuth Sources") | ||||||
|  |  | ||||||
|  |  | ||||||
| class UserOAuthSourceConnection(UserSourceConnection): | class UserOAuthSourceConnection(UserSourceConnection): | ||||||
|     """Authorized remote OAuth provider.""" |     """Authorized remote OAuth provider.""" | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										102
									
								
								authentik/sources/oauth/types/apple.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								authentik/sources/oauth/types/apple.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | |||||||
|  | """Apple OAuth Views""" | ||||||
|  | from base64 import b64decode | ||||||
|  | from json import loads | ||||||
|  | from time import time | ||||||
|  | from typing import Any, Optional | ||||||
|  |  | ||||||
|  | from jwt import encode | ||||||
|  | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
|  | from authentik.sources.oauth.clients.oauth2 import OAuth2Client | ||||||
|  | from authentik.sources.oauth.types.manager import MANAGER, SourceType | ||||||
|  | from authentik.sources.oauth.views.callback import OAuthCallback | ||||||
|  | from authentik.sources.oauth.views.redirect import OAuthRedirect | ||||||
|  |  | ||||||
|  | LOGGER = get_logger() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AppleOAuthClient(OAuth2Client): | ||||||
|  |     """Apple OAuth2 client""" | ||||||
|  |  | ||||||
|  |     def get_client_id(self) -> str: | ||||||
|  |         parts = self.source.consumer_key.split(";") | ||||||
|  |         if len(parts) < 3: | ||||||
|  |             return self.source.consumer_key | ||||||
|  |         return parts[0] | ||||||
|  |  | ||||||
|  |     def get_client_secret(self) -> str: | ||||||
|  |         now = time() | ||||||
|  |         parts = self.source.consumer_key.split(";") | ||||||
|  |         if len(parts) < 3: | ||||||
|  |             raise ValueError( | ||||||
|  |                 ( | ||||||
|  |                     "Apple Source client_id should be formatted like " | ||||||
|  |                     "services_id_identifier;apple_team_id;key_id" | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         LOGGER.debug("got values from client_id", team=parts[1], kid=parts[2]) | ||||||
|  |         payload = { | ||||||
|  |             "iss": parts[1], | ||||||
|  |             "iat": now, | ||||||
|  |             "exp": now + 86400 * 180, | ||||||
|  |             "aud": "https://appleid.apple.com", | ||||||
|  |             "sub": self.source.consumer_key, | ||||||
|  |         } | ||||||
|  |         # pyright: reportGeneralTypeIssues=false | ||||||
|  |         jwt = encode(payload, self.source.consumer_secret, "ES256", {"kid": parts[2]}) | ||||||
|  |         LOGGER.debug("signing payload as secret key", payload=payload, jwt=jwt) | ||||||
|  |         return jwt | ||||||
|  |  | ||||||
|  |     def get_profile_info(self, token: dict[str, str]) -> Optional[dict[str, Any]]: | ||||||
|  |         id_token = token.get("id_token") | ||||||
|  |         _, raw_payload, _ = id_token.split(".") | ||||||
|  |         payload = loads(b64decode(raw_payload.encode().decode())) | ||||||
|  |         return payload | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AppleOAuthRedirect(OAuthRedirect): | ||||||
|  |     """Apple OAuth2 Redirect""" | ||||||
|  |  | ||||||
|  |     client_class = AppleOAuthClient | ||||||
|  |  | ||||||
|  |     def get_additional_parameters(self, source):  # pragma: no cover | ||||||
|  |         return { | ||||||
|  |             "scope": "name email", | ||||||
|  |             "response_mode": "form_post", | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class AppleOAuth2Callback(OAuthCallback): | ||||||
|  |     """Apple OAuth2 Callback""" | ||||||
|  |  | ||||||
|  |     client_class = AppleOAuthClient | ||||||
|  |  | ||||||
|  |     def get_user_id(self, info: dict[str, Any]) -> Optional[str]: | ||||||
|  |         return info["sub"] | ||||||
|  |  | ||||||
|  |     def get_user_enroll_context( | ||||||
|  |         self, | ||||||
|  |         info: dict[str, Any], | ||||||
|  |     ) -> dict[str, Any]: | ||||||
|  |         print(info) | ||||||
|  |         return { | ||||||
|  |             "email": info.get("email"), | ||||||
|  |             "name": info.get("name"), | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @MANAGER.type() | ||||||
|  | class AppleType(SourceType): | ||||||
|  |     """Apple Type definition""" | ||||||
|  |  | ||||||
|  |     callback_view = AppleOAuth2Callback | ||||||
|  |     redirect_view = AppleOAuthRedirect | ||||||
|  |     name = "Apple" | ||||||
|  |     slug = "apple" | ||||||
|  |  | ||||||
|  |     authorization_url = "https://appleid.apple.com/auth/authorize" | ||||||
|  |     access_token_url = "https://appleid.apple.com/auth/token"  # nosec | ||||||
|  |     profile_url = "" | ||||||
|  |  | ||||||
|  |     def icon_url(self) -> str: | ||||||
|  |         return "https://appleid.cdn-apple.com/appleid/button/logo" | ||||||
| @ -1,7 +1,8 @@ | |||||||
| """Source type manager""" | """Source type manager""" | ||||||
| from enum import Enum | from enum import Enum | ||||||
| from typing import Callable, Optional | from typing import Callable, Optional, Type | ||||||
|  |  | ||||||
|  | from django.templatetags.static import static | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
| from authentik.sources.oauth.views.callback import OAuthCallback | from authentik.sources.oauth.views.callback import OAuthCallback | ||||||
| @ -22,8 +23,8 @@ class SourceType: | |||||||
|  |  | ||||||
|     callback_view = OAuthCallback |     callback_view = OAuthCallback | ||||||
|     redirect_view = OAuthRedirect |     redirect_view = OAuthRedirect | ||||||
|     name: str |     name: str = "default" | ||||||
|     slug: str |     slug: str = "default" | ||||||
|  |  | ||||||
|     urls_customizable = False |     urls_customizable = False | ||||||
|  |  | ||||||
| @ -32,12 +33,16 @@ class SourceType: | |||||||
|     access_token_url: Optional[str] = None |     access_token_url: Optional[str] = None | ||||||
|     profile_url: Optional[str] = None |     profile_url: Optional[str] = None | ||||||
|  |  | ||||||
|  |     def icon_url(self) -> str: | ||||||
|  |         """Get Icon URL for login""" | ||||||
|  |         return static(f"authentik/sources/{self.slug}.svg") | ||||||
|  |  | ||||||
|  |  | ||||||
| class SourceTypeManager: | class SourceTypeManager: | ||||||
|     """Manager to hold all Source types.""" |     """Manager to hold all Source types.""" | ||||||
|  |  | ||||||
|     def __init__(self) -> None: |     def __init__(self) -> None: | ||||||
|         self.__sources: list[SourceType] = [] |         self.__sources: list[Type[SourceType]] = [] | ||||||
|  |  | ||||||
|     def type(self): |     def type(self): | ||||||
|         """Class decorator to register classes inline.""" |         """Class decorator to register classes inline.""" | ||||||
| @ -56,14 +61,14 @@ class SourceTypeManager: | |||||||
|         """Get list of tuples of all registered names""" |         """Get list of tuples of all registered names""" | ||||||
|         return [(x.slug, x.name) for x in self.__sources] |         return [(x.slug, x.name) for x in self.__sources] | ||||||
|  |  | ||||||
|     def find_type(self, type_name: str) -> SourceType: |     def find_type(self, type_name: str) -> Type[SourceType]: | ||||||
|         """Find type based on source""" |         """Find type based on source""" | ||||||
|         found_type = None |         found_type = None | ||||||
|         for src_type in self.__sources: |         for src_type in self.__sources: | ||||||
|             if src_type.slug == type_name: |             if src_type.slug == type_name: | ||||||
|                 return src_type |                 return src_type | ||||||
|         if not found_type: |         if not found_type: | ||||||
|             found_type = SourceType() |             found_type = SourceType | ||||||
|             LOGGER.warning( |             LOGGER.warning( | ||||||
|                 "no matching type found, using default", |                 "no matching type found, using default", | ||||||
|                 wanted=type_name, |                 wanted=type_name, | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ class OAuthCallback(OAuthClientMixin, View): | |||||||
|     source: OAuthSource |     source: OAuthSource | ||||||
|  |  | ||||||
|     # pylint: disable=too-many-return-statements |     # pylint: disable=too-many-return-statements | ||||||
|     def get(self, request: HttpRequest, *_, **kwargs) -> HttpResponse: |     def dispatch(self, request: HttpRequest, *_, **kwargs) -> HttpResponse: | ||||||
|         """View Get handler""" |         """View Get handler""" | ||||||
|         slug = kwargs.get("source_slug", "") |         slug = kwargs.get("source_slug", "") | ||||||
|         try: |         try: | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| """Dispatch OAuth views to respective views""" | """Dispatch OAuth views to respective views""" | ||||||
| from django.shortcuts import get_object_or_404 | from django.shortcuts import get_object_or_404 | ||||||
|  | from django.utils.decorators import method_decorator | ||||||
| from django.views import View | from django.views import View | ||||||
|  | from django.views.decorators.csrf import csrf_exempt | ||||||
| from structlog.stdlib import get_logger | from structlog.stdlib import get_logger | ||||||
|  |  | ||||||
| from authentik.sources.oauth.models import OAuthSource | from authentik.sources.oauth.models import OAuthSource | ||||||
| @ -9,6 +11,7 @@ from authentik.sources.oauth.types.manager import MANAGER, RequestKind | |||||||
| LOGGER = get_logger() | LOGGER = get_logger() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @method_decorator(csrf_exempt, name="dispatch") | ||||||
| class DispatcherView(View): | class DispatcherView(View): | ||||||
|     """Dispatch OAuth Redirect/Callback views to their proper class based on URL parameters""" |     """Dispatch OAuth Redirect/Callback views to their proper class based on URL parameters""" | ||||||
|  |  | ||||||
|  | |||||||
| @ -119,7 +119,7 @@ class TestIdentificationStage(APITestCase): | |||||||
|                             "to": "/source/oauth/login/test/", |                             "to": "/source/oauth/login/test/", | ||||||
|                             "type": ChallengeTypes.REDIRECT.value, |                             "type": ChallengeTypes.REDIRECT.value, | ||||||
|                         }, |                         }, | ||||||
|                         "icon_url": "/static/authentik/sources/.svg", |                         "icon_url": "/static/authentik/sources/default.svg", | ||||||
|                         "name": "test", |                         "name": "test", | ||||||
|                     } |                     } | ||||||
|                 ], |                 ], | ||||||
| @ -170,7 +170,7 @@ class TestIdentificationStage(APITestCase): | |||||||
|                             "to": "/source/oauth/login/test/", |                             "to": "/source/oauth/login/test/", | ||||||
|                             "type": ChallengeTypes.REDIRECT.value, |                             "type": ChallengeTypes.REDIRECT.value, | ||||||
|                         }, |                         }, | ||||||
|                         "icon_url": "/static/authentik/sources/.svg", |                         "icon_url": "/static/authentik/sources/default.svg", | ||||||
|                         "name": "test", |                         "name": "test", | ||||||
|                     } |                     } | ||||||
|                 ], |                 ], | ||||||
| @ -226,7 +226,7 @@ class TestIdentificationStage(APITestCase): | |||||||
|                 }, |                 }, | ||||||
|                 "sources": [ |                 "sources": [ | ||||||
|                     { |                     { | ||||||
|                         "icon_url": "/static/authentik/sources/.svg", |                         "icon_url": "/static/authentik/sources/default.svg", | ||||||
|                         "name": "test", |                         "name": "test", | ||||||
|                         "challenge": { |                         "challenge": { | ||||||
|                             "component": "xak-flow-redirect", |                             "component": "xak-flow-redirect", | ||||||
| @ -280,7 +280,7 @@ class TestIdentificationStage(APITestCase): | |||||||
|                             "to": "/source/oauth/login/test/", |                             "to": "/source/oauth/login/test/", | ||||||
|                             "type": ChallengeTypes.REDIRECT.value, |                             "type": ChallengeTypes.REDIRECT.value, | ||||||
|                         }, |                         }, | ||||||
|                         "icon_url": "/static/authentik/sources/.svg", |                         "icon_url": "/static/authentik/sources/default.svg", | ||||||
|                         "name": "test", |                         "name": "test", | ||||||
|                     } |                     } | ||||||
|                 ], |                 ], | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ msgid "" | |||||||
| msgstr "" | msgstr "" | ||||||
| "Project-Id-Version: PACKAGE VERSION\n" | "Project-Id-Version: PACKAGE VERSION\n" | ||||||
| "Report-Msgid-Bugs-To: \n" | "Report-Msgid-Bugs-To: \n" | ||||||
| "POT-Creation-Date: 2021-10-11 14:12+0000\n" | "POT-Creation-Date: 2021-10-18 10:40+0000\n" | ||||||
| "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||||
| "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||||
| "Language-Team: LANGUAGE <LL@li.org>\n" | "Language-Team: LANGUAGE <LL@li.org>\n" | ||||||
| @ -308,6 +308,10 @@ msgstr "" | |||||||
| msgid "Notification Webhook Mappings" | msgid "Notification Webhook Mappings" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | #: authentik/events/monitored_tasks.py:122 | ||||||
|  | msgid "Task has not been run yet." | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/flows/api/flows.py:350 | #: authentik/flows/api/flows.py:350 | ||||||
| #, python-format | #, python-format | ||||||
| msgid "Flow not applicable to current user/request: %(messages)s" | msgid "Flow not applicable to current user/request: %(messages)s" | ||||||
| @ -385,33 +389,33 @@ msgstr "" | |||||||
| msgid "Invalid kubeconfig" | msgid "Invalid kubeconfig" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/outposts/models.py:164 | #: authentik/outposts/models.py:167 | ||||||
| msgid "Outpost Service-Connection" | msgid "Outpost Service-Connection" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/outposts/models.py:165 | #: authentik/outposts/models.py:168 | ||||||
| msgid "Outpost Service-Connections" | msgid "Outpost Service-Connections" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/outposts/models.py:201 | #: authentik/outposts/models.py:204 | ||||||
| msgid "" | msgid "" | ||||||
| "Certificate/Key used for authentication. Can be left empty for no " | "Certificate/Key used for authentication. Can be left empty for no " | ||||||
| "authentication." | "authentication." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/outposts/models.py:243 | #: authentik/outposts/models.py:246 | ||||||
| msgid "Docker Service-Connection" | msgid "Docker Service-Connection" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/outposts/models.py:244 | #: authentik/outposts/models.py:247 | ||||||
| msgid "Docker Service-Connections" | msgid "Docker Service-Connections" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/outposts/models.py:290 | #: authentik/outposts/models.py:293 | ||||||
| msgid "Kubernetes Service-Connection" | msgid "Kubernetes Service-Connection" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/outposts/models.py:291 | #: authentik/outposts/models.py:294 | ||||||
| msgid "Kubernetes Service-Connections" | msgid "Kubernetes Service-Connections" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @ -988,108 +992,116 @@ msgstr "" | |||||||
| msgid "Password does not match Active Directory Complexity." | msgid "Password does not match Active Directory Complexity." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:25 | #: authentik/sources/oauth/models.py:24 | ||||||
| msgid "Request Token URL" | msgid "Request Token URL" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:27 | #: authentik/sources/oauth/models.py:26 | ||||||
| msgid "" | msgid "" | ||||||
| "URL used to request the initial token. This URL is only required for OAuth 1." | "URL used to request the initial token. This URL is only required for OAuth 1." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:33 | #: authentik/sources/oauth/models.py:32 | ||||||
| msgid "Authorization URL" | msgid "Authorization URL" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:34 | #: authentik/sources/oauth/models.py:33 | ||||||
| msgid "URL the user is redirect to to conest the flow." | msgid "URL the user is redirect to to conest the flow." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:39 | #: authentik/sources/oauth/models.py:38 | ||||||
| msgid "Access Token URL" | msgid "Access Token URL" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:40 | #: authentik/sources/oauth/models.py:39 | ||||||
| msgid "URL used by authentik to retrieve tokens." | msgid "URL used by authentik to retrieve tokens." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:45 | #: authentik/sources/oauth/models.py:44 | ||||||
| msgid "Profile URL" | msgid "Profile URL" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:46 | #: authentik/sources/oauth/models.py:45 | ||||||
| msgid "URL used by authentik to get user information." | msgid "URL used by authentik to get user information." | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:102 | #: authentik/sources/oauth/models.py:101 | ||||||
| msgid "OAuth Source" | msgid "OAuth Source" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:103 | #: authentik/sources/oauth/models.py:102 | ||||||
| msgid "OAuth Sources" | msgid "OAuth Sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:112 | #: authentik/sources/oauth/models.py:111 | ||||||
| msgid "GitHub OAuth Source" | msgid "GitHub OAuth Source" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:113 | #: authentik/sources/oauth/models.py:112 | ||||||
| msgid "GitHub OAuth Sources" | msgid "GitHub OAuth Sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:122 | #: authentik/sources/oauth/models.py:121 | ||||||
| msgid "Twitter OAuth Source" | msgid "Twitter OAuth Source" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:123 | #: authentik/sources/oauth/models.py:122 | ||||||
| msgid "Twitter OAuth Sources" | msgid "Twitter OAuth Sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:132 | #: authentik/sources/oauth/models.py:131 | ||||||
| msgid "Facebook OAuth Source" | msgid "Facebook OAuth Source" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:133 | #: authentik/sources/oauth/models.py:132 | ||||||
| msgid "Facebook OAuth Sources" | msgid "Facebook OAuth Sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:142 | #: authentik/sources/oauth/models.py:141 | ||||||
| msgid "Discord OAuth Source" | msgid "Discord OAuth Source" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:143 | #: authentik/sources/oauth/models.py:142 | ||||||
| msgid "Discord OAuth Sources" | msgid "Discord OAuth Sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:152 | #: authentik/sources/oauth/models.py:151 | ||||||
| msgid "Google OAuth Source" | msgid "Google OAuth Source" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:153 | #: authentik/sources/oauth/models.py:152 | ||||||
| msgid "Google OAuth Sources" | msgid "Google OAuth Sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:162 | #: authentik/sources/oauth/models.py:161 | ||||||
| msgid "Azure AD OAuth Source" | msgid "Azure AD OAuth Source" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:163 | #: authentik/sources/oauth/models.py:162 | ||||||
| msgid "Azure AD OAuth Sources" | msgid "Azure AD OAuth Sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:172 | #: authentik/sources/oauth/models.py:171 | ||||||
| msgid "OpenID OAuth Source" | msgid "OpenID OAuth Source" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:173 | #: authentik/sources/oauth/models.py:172 | ||||||
| msgid "OpenID OAuth Sources" | msgid "OpenID OAuth Sources" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:188 | #: authentik/sources/oauth/models.py:181 | ||||||
|  | msgid "Apple OAuth Source" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: authentik/sources/oauth/models.py:182 | ||||||
|  | msgid "Apple OAuth Sources" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: authentik/sources/oauth/models.py:197 | ||||||
| msgid "User OAuth Source Connection" | msgid "User OAuth Source Connection" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/sources/oauth/models.py:189 | #: authentik/sources/oauth/models.py:198 | ||||||
| msgid "User OAuth Source Connections" | msgid "User OAuth Source Connections" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @ -1214,19 +1226,19 @@ msgstr "" | |||||||
| msgid "Duo Devices" | msgid "Duo Devices" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/stages/authenticator_sms/models.py:97 | #: authentik/stages/authenticator_sms/models.py:158 | ||||||
| msgid "SMS Authenticator Setup Stage" | msgid "SMS Authenticator Setup Stage" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/stages/authenticator_sms/models.py:98 | #: authentik/stages/authenticator_sms/models.py:159 | ||||||
| msgid "SMS Authenticator Setup Stages" | msgid "SMS Authenticator Setup Stages" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/stages/authenticator_sms/models.py:116 | #: authentik/stages/authenticator_sms/models.py:176 | ||||||
| msgid "SMS Device" | msgid "SMS Device" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/stages/authenticator_sms/models.py:117 | #: authentik/stages/authenticator_sms/models.py:177 | ||||||
| msgid "SMS Devices" | msgid "SMS Devices" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| @ -1291,19 +1303,19 @@ msgstr "" | |||||||
| msgid "Authenticator Validation Stages" | msgid "Authenticator Validation Stages" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/stages/authenticator_webauthn/models.py:49 | #: authentik/stages/authenticator_webauthn/models.py:51 | ||||||
| msgid "WebAuthn Authenticator Setup Stage" | msgid "WebAuthn Authenticator Setup Stage" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/stages/authenticator_webauthn/models.py:50 | #: authentik/stages/authenticator_webauthn/models.py:52 | ||||||
| msgid "WebAuthn Authenticator Setup Stages" | msgid "WebAuthn Authenticator Setup Stages" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/stages/authenticator_webauthn/models.py:78 | #: authentik/stages/authenticator_webauthn/models.py:85 | ||||||
| msgid "WebAuthn Device" | msgid "WebAuthn Device" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
| #: authentik/stages/authenticator_webauthn/models.py:79 | #: authentik/stages/authenticator_webauthn/models.py:86 | ||||||
| msgid "WebAuthn Devices" | msgid "WebAuthn Devices" | ||||||
| msgstr "" | msgstr "" | ||||||
|  |  | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ class OAUth1Type(SourceType): | |||||||
|     urls_customizable = False |     urls_customizable = False | ||||||
|  |  | ||||||
|  |  | ||||||
| SOURCE_TYPE_MOCK = Mock(return_value=OAUth1Type()) | SOURCE_TYPE_MOCK = Mock(return_value=OAUth1Type) | ||||||
|  |  | ||||||
|  |  | ||||||
| @skipUnless(platform.startswith("linux"), "requires local docker") | @skipUnless(platform.startswith("linux"), "requires local docker") | ||||||
|  | |||||||
| @ -252,7 +252,7 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> { | |||||||
|                         ?writeOnly=${this.instance !== undefined} |                         ?writeOnly=${this.instance !== undefined} | ||||||
|                         name="consumerSecret" |                         name="consumerSecret" | ||||||
|                     > |                     > | ||||||
|                         <input type="text" value="" class="pf-c-form-control" required /> |                         <textarea class="pf-c-form-control"></textarea> | ||||||
|                     </ak-form-element-horizontal> |                     </ak-form-element-horizontal> | ||||||
|                 </div> |                 </div> | ||||||
|             </ak-form-group> |             </ak-form-group> | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ In authentik, create an application which uses this provider. Optionally apply a | |||||||
|  |  | ||||||
| ### Step 3 | ### Step 3 | ||||||
|  |  | ||||||
| Obtain your Metadata URL from Authentik. | Obtain your Metadata URL from authentik. | ||||||
|  |  | ||||||
| 1. Click on the BookStack Provider | 1. Click on the BookStack Provider | ||||||
| 2. Click the Metadata Tab | 2. Click the Metadata Tab | ||||||
| @ -69,7 +69,7 @@ Modify the following Example SAML config and paste incorporate into your `.env` | |||||||
| AUTH_METHOD=saml2 | AUTH_METHOD=saml2 | ||||||
| # Set the display name to be shown on the login button. | # Set the display name to be shown on the login button. | ||||||
| # (Login with <name>) | # (Login with <name>) | ||||||
| SAML2_NAME=Authentik | SAML2_NAME=authentik | ||||||
| # Name of the attribute which provides the user's email address | # Name of the attribute which provides the user's email address | ||||||
| SAML2_EMAIL_ATTRIBUTE=email | SAML2_EMAIL_ATTRIBUTE=email | ||||||
| # Name of the attribute to use as an ID for the SAML user. | # Name of the attribute to use as an ID for the SAML user. | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ The following placeholders will be used: | |||||||
| - `port.company` is the FQDN of Portainer. | - `port.company` is the FQDN of Portainer. | ||||||
| - `authentik.company` is the FQDN of authentik. | - `authentik.company` is the FQDN of authentik. | ||||||
|  |  | ||||||
| ### Step 1 - Authentik | ### Step 1 - authentik | ||||||
|  |  | ||||||
| In authentik, under _Providers_, create an _OAuth2/OpenID Provider_ with these settings: | In authentik, under _Providers_, create an _OAuth2/OpenID Provider_ with these settings: | ||||||
|  |  | ||||||
| @ -57,7 +57,7 @@ Portainer by default shows commas between each item in the Scopes field.  Do **N | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ### Step 3 - Authentik | ### Step 3 - authentik | ||||||
|  |  | ||||||
| In authentik, create an application which uses this provider. Optionally apply access restrictions to the application using policy bindings. | In authentik, create an application which uses this provider. Optionally apply access restrictions to the application using policy bindings. | ||||||
|  |  | ||||||
|  | |||||||
| @ -76,9 +76,9 @@ auth: | |||||||
|         # The auth url to send users to if they want to authenticate using OpenID Connect. |         # The auth url to send users to if they want to authenticate using OpenID Connect. | ||||||
|         authurl: https://authentik.company/application/o/vikunja/ |         authurl: https://authentik.company/application/o/vikunja/ | ||||||
|         # The client ID used to authenticate Vikunja at the OpenID Connect provider. |         # The client ID used to authenticate Vikunja at the OpenID Connect provider. | ||||||
|         clientid: THIS IS THE CLIENT ID YOU COPIED FROM STEP 1 in Authentik |         clientid: THIS IS THE CLIENT ID YOU COPIED FROM STEP 1 in authentik | ||||||
|         # The client secret used to authenticate Vikunja at the OpenID Connect provider. |         # The client secret used to authenticate Vikunja at the OpenID Connect provider. | ||||||
|         clientsecret: THIS IS THE CLIENT SECRET YOU COPIED FROM STEP 1 in Authentik |         clientsecret: THIS IS THE CLIENT SECRET YOU COPIED FROM STEP 1 in authentik | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| :::note | :::note | ||||||
|  | |||||||
| @ -39,7 +39,7 @@ import TabItem from '@theme/TabItem'; | |||||||
|     {label: 'Standalone', value: 'standalone'}, |     {label: 'Standalone', value: 'standalone'}, | ||||||
|   ]}> |   ]}> | ||||||
|   <TabItem value="docker"> |   <TabItem value="docker"> | ||||||
| If your Wekan is running in docker, add the following environment variables for Authentik | If your Wekan is running in docker, add the following environment variables for authentik | ||||||
|  |  | ||||||
| ```yaml | ```yaml | ||||||
| environment: | environment: | ||||||
| @ -58,11 +58,11 @@ environment: | |||||||
| ``` | ``` | ||||||
|   </TabItem> |   </TabItem> | ||||||
|   <TabItem value="standalone"> |   <TabItem value="standalone"> | ||||||
|      |  | ||||||
| edit `.env` and add the following: | edit `.env` and add the following: | ||||||
|  |  | ||||||
| ```ini | ```ini | ||||||
|      # Authentik OAUTH Config |      # authentik OAUTH Config | ||||||
|       OAUTH2_ENABLED='true' |       OAUTH2_ENABLED='true' | ||||||
|       OAUTH2_LOGIN_STYLE='redirect' |       OAUTH2_LOGIN_STYLE='redirect' | ||||||
|       OAUTH2_CLIENT_ID='<Client ID from above>' |       OAUTH2_CLIENT_ID='<Client ID from above>' | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ The following placeholders will be used: | |||||||
| - `wp.company` is the FQDN of Wordpress. | - `wp.company` is the FQDN of Wordpress. | ||||||
| - `authentik.company` is the FQDN of authentik. | - `authentik.company` is the FQDN of authentik. | ||||||
|  |  | ||||||
| ### Step 1 - Authentik | ### Step 1 - authentik | ||||||
|  |  | ||||||
| In authentik, under _Providers_, create an _OAuth2/OpenID Provider_ with these settings: | In authentik, under _Providers_, create an _OAuth2/OpenID Provider_ with these settings: | ||||||
|  |  | ||||||
| @ -63,7 +63,7 @@ Only settings that have been modified from default have been listed. | |||||||
| Review each setting and choose the ones that you require for your installation.  Examples of popular settings are _Link Existing Users_, _Create user if does not exist_, and _Enforce Privacy_ | Review each setting and choose the ones that you require for your installation.  Examples of popular settings are _Link Existing Users_, _Create user if does not exist_, and _Enforce Privacy_ | ||||||
| ::: | ::: | ||||||
|  |  | ||||||
| ### Step 3 - Authentik | ### Step 3 - authentik | ||||||
|  |  | ||||||
| In authentik, create an application which uses this provider. Optionally apply access restrictions to the application using policy bindings. | In authentik, create an application which uses this provider. Optionally apply access restrictions to the application using policy bindings. | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								website/docs/integrations/sources/apple/app_id.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								website/docs/integrations/sources/apple/app_id.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 62 KiB | 
							
								
								
									
										
											BIN
										
									
								
								website/docs/integrations/sources/apple/app_service_config.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								website/docs/integrations/sources/apple/app_service_config.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 120 KiB | 
							
								
								
									
										67
									
								
								website/docs/integrations/sources/apple/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								website/docs/integrations/sources/apple/index.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | |||||||
|  | --- | ||||||
|  | title: Apple | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | Allows users to authenticate using their Apple ID. | ||||||
|  |  | ||||||
|  | ## Preparation | ||||||
|  |  | ||||||
|  | :::warning | ||||||
|  | An Apple developer account is required for this. | ||||||
|  | ::: | ||||||
|  |  | ||||||
|  | The following placeholders will be used: | ||||||
|  |  | ||||||
|  | - `authentik.company` is the FQDN of the authentik install. | ||||||
|  |  | ||||||
|  | ## Apple | ||||||
|  |  | ||||||
|  | 1. Log into your Apple developer account, and navigate to **Certificates, IDs & Profiles**, then click **Identifiers** in the sidebar. | ||||||
|  | 2. Register a new Identifier with the type of **App IDs**, and the subtype **App**. | ||||||
|  | 3. Choose a name that users will recognise for the **Description** field. | ||||||
|  | 4. For your bundle ID, use the reverse domain of authentik, in this case `company.authentik`. | ||||||
|  | 5. Scroll down the list of capabilities, and check the box next to **Sign In with Apple**. | ||||||
|  | 6. At the top, click **Continue** and **Register**. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 7. Register another new Identifier with the type of **Services IDs**. | ||||||
|  | 8. Again, choose the same name as above for your **Description** field. | ||||||
|  | 9. Use the same identifier as above, but add a suffix like `signin` or `oauth`, as identifiers are unique. | ||||||
|  | 10. At the top, click **Continue** and **Register**. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 11. Once back at the overview list, click on the just-created Identifier. | ||||||
|  | 12. Enable the checkbox next to **Sign In with Apple**, and click **Configure** | ||||||
|  | 13. Under domains, enter `authentik.company`. | ||||||
|  | 14. Under **Return URLs**, enter `https://authentik.company/source/oauth/callback/apple/`. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 15. Click on **Keys** in the sidebar. Register a new Key with any name, and select **Sign in with Apple**. | ||||||
|  | 16. Click on **Configure**, and select the App ID you've created above. | ||||||
|  | 17. At the top, click **Save**, **Continue** and **Register**. | ||||||
|  | 18. Download the Key file and note the **Key ID**. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 19. Note the Team ID, visible at the top of the page. | ||||||
|  |  | ||||||
|  | ## authentik | ||||||
|  |  | ||||||
|  | 20. Under _Resources -> Sources_ Click **Create Apple OAuth Source** | ||||||
|  |  | ||||||
|  | 21. **Name**: `Apple` | ||||||
|  | 22. **Slug**: `apple` | ||||||
|  | 23. **Consumer Key:** The identifier from step 9, then `;`, then your Team ID from step 19, then `;`, then the Key ID from step 18. | ||||||
|  |  | ||||||
|  |     Example: `io.goauthentik.dev-local;JQNH45HN7V;XFBNJ82BV6` | ||||||
|  |  | ||||||
|  | 24. **Consumer Secret:** Paste the contents of the keyfile you've downloaded | ||||||
|  |  | ||||||
|  | Save, and you now have Apple as a source. | ||||||
|  |  | ||||||
|  | :::note | ||||||
|  | For more details on how-to have the new source display on the Login Page see the Sources page. | ||||||
|  | ::: | ||||||
							
								
								
									
										
											BIN
										
									
								
								website/docs/integrations/sources/apple/key.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								website/docs/integrations/sources/apple/key.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 71 KiB | 
							
								
								
									
										
											BIN
										
									
								
								website/docs/integrations/sources/apple/service_id.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								website/docs/integrations/sources/apple/service_id.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 53 KiB | 
| @ -33,7 +33,7 @@ Here is an example of a completed OAuth2 screen for Discord. | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Authentik | ## authentik | ||||||
|  |  | ||||||
| 8. Under _Resources -> Sources_ Click **Create Discord OAuth Source** | 8. Under _Resources -> Sources_ Click **Create Discord OAuth Source** | ||||||
|  |  | ||||||
| @ -43,7 +43,7 @@ Here is an example of a completed OAuth2 screen for Discord. | |||||||
| 12. **Consumer Secret:** Client Secret from step 5 | 12. **Consumer Secret:** Client Secret from step 5 | ||||||
| 13. **Provider type:** Discord | 13. **Provider type:** Discord | ||||||
|  |  | ||||||
| Here is an exmple of a complete Authentik Discord OAuth Source | Here is an example of a complete authentik Discord OAuth Source | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -51,4 +51,4 @@ Save, and you now have Discord as a source. | |||||||
|  |  | ||||||
| :::note | :::note | ||||||
| For more details on how-to have the new source display on the Login Page see the Sources page | For more details on how-to have the new source display on the Login Page see the Sources page | ||||||
| ::: | ::: | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ The following placeholders will be used: | |||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 2. **Application Name:** Choose a name users will recognize ie: Authentik | 2. **Application Name:** Choose a name users will recognize ie: authentik | ||||||
| 3. **Homepage URL**:: www.my.company | 3. **Homepage URL**:: www.my.company | ||||||
| 4. **Authorization callback URL**: https://authentik.company/source/oauth/callback/github | 4. **Authorization callback URL**: https://authentik.company/source/oauth/callback/github | ||||||
| 5. Click **Register Application** | 5. Click **Register Application** | ||||||
| @ -27,9 +27,9 @@ Example screenshot | |||||||
|  |  | ||||||
|  |  | ||||||
| 6. Copy the **Client ID** and _save it for later_ | 6. Copy the **Client ID** and _save it for later_ | ||||||
| 7. Click **Generate a new client secret** and _save it for later_  You will not be able to see the secret again, so be sure to copy it now.   | 7. Click **Generate a new client secret** and _save it for later_  You will not be able to see the secret again, so be sure to copy it now. | ||||||
|  |  | ||||||
| ## Authentik | ## authentik | ||||||
|  |  | ||||||
| 8. Under _Resources -> Sources_ Click **Create Github OAuth Source** | 8. Under _Resources -> Sources_ Click **Create Github OAuth Source** | ||||||
|  |  | ||||||
| @ -49,7 +49,7 @@ As of June 20 2021 these URLS are correct. Here is the Github reference URL http | |||||||
| 15. **Access token URL:** `https://github.com/login/oauth/access_token` | 15. **Access token URL:** `https://github.com/login/oauth/access_token` | ||||||
| 16. **Profile URL:** `https://api.github.com/user` | 16. **Profile URL:** `https://api.github.com/user` | ||||||
|  |  | ||||||
| Here is an exmple of a complete Authentik Github OAuth Source | Here is an example of a complete authentik Github OAuth Source | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ The following placeholders will be used: | |||||||
| You will need to create a new project, and OAuth credentials in the Google Developer console.  The developer console can be overwhelming at first. | You will need to create a new project, and OAuth credentials in the Google Developer console.  The developer console can be overwhelming at first. | ||||||
|  |  | ||||||
| 1. Visit https://console.developers.google.com/ to create a new project | 1. Visit https://console.developers.google.com/ to create a new project | ||||||
| 2. Create a New project.  | 2. Create a New project. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -62,7 +62,7 @@ _I'm only going to list the mandatory/important fields to complete._ | |||||||
| 24. Click **Create** | 24. Click **Create** | ||||||
| 25. Copy and store _Your Client ID_ and _Your Client Secret_ for later | 25. Copy and store _Your Client ID_ and _Your Client Secret_ for later | ||||||
|  |  | ||||||
| ## Authentik | ## authentik | ||||||
|  |  | ||||||
| 26. Under _Resources -> Sources_ Click **Create Google OAuth Source** | 26. Under _Resources -> Sources_ Click **Create Google OAuth Source** | ||||||
|  |  | ||||||
| @ -72,7 +72,7 @@ _I'm only going to list the mandatory/important fields to complete._ | |||||||
| 30. **Consumer Secret:** Your Client Secret from step 25 | 30. **Consumer Secret:** Your Client Secret from step 25 | ||||||
| 31. **Provider Type:** Google | 31. **Provider Type:** Google | ||||||
|  |  | ||||||
| Here is an exmple of a complete Authentik Google OAuth Source | Here is an example of a complete authentik Google OAuth Source | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -80,4 +80,4 @@ Save, and you now have Google as a source. | |||||||
|  |  | ||||||
| :::note | :::note | ||||||
| For more details on how-to have the new source display on the Login Page see the Sources page | For more details on how-to have the new source display on the Login Page see the Sources page | ||||||
| ::: | ::: | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ Allows users to authenticate using their Plex credentials | |||||||
|  |  | ||||||
| None | None | ||||||
|  |  | ||||||
| ## Authentik -> Sources | ## authentik -> Sources | ||||||
|  |  | ||||||
| Add _Plex_ as a _source_ | Add _Plex_ as a _source_ | ||||||
|  |  | ||||||
|  | |||||||
| @ -73,7 +73,7 @@ server { | |||||||
|         # authentik-specific config |         # authentik-specific config | ||||||
|         auth_request        /akprox/auth/nginx; |         auth_request        /akprox/auth/nginx; | ||||||
|         error_page          401 = @akprox_signin; |         error_page          401 = @akprox_signin; | ||||||
|         # For domain level, use the below error_page to redirect to your Authentik server with the full redirect path |         # For domain level, use the below error_page to redirect to your authentik server with the full redirect path | ||||||
|         # error_page          401 =302 https://authentik.company/akprox/start?rd=$scheme://$http_host$request_uri; |         # error_page          401 =302 https://authentik.company/akprox/start?rd=$scheme://$http_host$request_uri; | ||||||
|  |  | ||||||
|         # translate headers from the outposts back to the actual upstream |         # translate headers from the outposts back to the actual upstream | ||||||
|  | |||||||
| @ -69,7 +69,7 @@ error_reporting: | |||||||
|  |  | ||||||
| ### Upgrading | ### Upgrading | ||||||
|  |  | ||||||
| This upgrade only applies if you are upgrading from a running 0.9 instance. Authentik detects this on startup, and automatically executes this upgrade. | This upgrade only applies if you are upgrading from a running 0.9 instance. authentik detects this on startup, and automatically executes this upgrade. | ||||||
|  |  | ||||||
| Because this upgrade brings the new OAuth2 Provider, the old providers will be lost in the process. Make sure to take note of the providers you want to bring over. | Because this upgrade brings the new OAuth2 Provider, the old providers will be lost in the process. Make sure to take note of the providers you want to bring over. | ||||||
|  |  | ||||||
|  | |||||||
| @ -64,6 +64,7 @@ module.exports = { | |||||||
|                     label: "as Source", |                     label: "as Source", | ||||||
|                     items: [ |                     items: [ | ||||||
|                         "integrations/sources/index", |                         "integrations/sources/index", | ||||||
|  |                         "integrations/sources/apple/index", | ||||||
|                         "integrations/sources/active-directory/index", |                         "integrations/sources/active-directory/index", | ||||||
|                         "integrations/sources/discord/index", |                         "integrations/sources/discord/index", | ||||||
|                         "integrations/sources/github/index", |                         "integrations/sources/github/index", | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens L
					Jens L