authentik.sources.oauth.types.github

GitHub OAuth Views

 1"""GitHub OAuth Views"""
 2
 3from typing import Any
 4
 5from requests.exceptions import RequestException
 6
 7from authentik.sources.oauth.clients.oauth2 import OAuth2Client
 8from authentik.sources.oauth.models import AuthorizationCodeAuthMethod, OAuthSource
 9from authentik.sources.oauth.types.registry import SourceType, registry
10from authentik.sources.oauth.views.callback import OAuthCallback
11from authentik.sources.oauth.views.redirect import OAuthRedirect
12
13
14class GitHubOAuthRedirect(OAuthRedirect):
15    """GitHub OAuth2 Redirect"""
16
17    def get_additional_parameters(self, source):  # pragma: no cover
18        return {
19            "scope": ["read:user", "user:email"],
20        }
21
22
23class GitHubOAuth2Client(OAuth2Client):
24    """GitHub OAuth2 Client"""
25
26    def get_github_emails(self, token: dict[str, str]) -> list[dict[str, Any]]:
27        """Get Emails from the GitHub API"""
28        profile_url = self.source.source_type.profile_url or ""
29        if self.source.source_type.urls_customizable and self.source.profile_url:
30            profile_url = self.source.profile_url
31        profile_url += "/emails"
32        response = self.do_request("get", profile_url, token=token)
33        try:
34            response.raise_for_status()
35        except RequestException as exc:
36            self.logger.warning("Unable to fetch github emails", exc=exc)
37            return []
38        return response.json()
39
40
41class GitHubOAuth2Callback(OAuthCallback):
42    """GitHub OAuth2 Callback"""
43
44    client_class = GitHubOAuth2Client
45
46
47@registry.register()
48class GitHubType(SourceType):
49    """GitHub Type definition"""
50
51    callback_view = GitHubOAuth2Callback
52    redirect_view = GitHubOAuthRedirect
53    verbose_name = "GitHub"
54    name = "github"
55
56    urls_customizable = True
57
58    authorization_url = "https://github.com/login/oauth/authorize"
59    access_token_url = "https://github.com/login/oauth/access_token"  # nosec
60    profile_url = "https://api.github.com/user"
61    oidc_well_known_url = (
62        "https://token.actions.githubusercontent.com/.well-known/openid-configuration"
63    )
64    oidc_jwks_url = "https://token.actions.githubusercontent.com/.well-known/jwks"
65
66    authorization_code_auth_method = AuthorizationCodeAuthMethod.POST_BODY
67
68    def get_base_user_properties(
69        self,
70        source: OAuthSource,
71        client: GitHubOAuth2Client,
72        token: dict[str, str],
73        info: dict[str, Any],
74        **kwargs,
75    ) -> dict[str, Any]:
76        chosen_email = info.get("email")
77        if not chosen_email:
78            # The GitHub Userprofile API only returns an email address if the profile
79            # has a public email address set (despite us asking for user:email, this behaviour
80            # doesn't change.). So we fetch all the user's email addresses
81            emails = client.get_github_emails(token)
82            for email in emails:
83                if email.get("primary", False):
84                    chosen_email = email.get("email", None)
85        return {
86            "username": info.get("login"),
87            "email": chosen_email,
88            "name": info.get("name"),
89        }
class GitHubOAuthRedirect(authentik.sources.oauth.views.redirect.OAuthRedirect):
15class GitHubOAuthRedirect(OAuthRedirect):
16    """GitHub OAuth2 Redirect"""
17
18    def get_additional_parameters(self, source):  # pragma: no cover
19        return {
20            "scope": ["read:user", "user:email"],
21        }

GitHub OAuth2 Redirect

def get_additional_parameters(self, source):
18    def get_additional_parameters(self, source):  # pragma: no cover
19        return {
20            "scope": ["read:user", "user:email"],
21        }

Return additional redirect parameters for this source.

class GitHubOAuth2Client(authentik.sources.oauth.clients.oauth2.OAuth2Client):
24class GitHubOAuth2Client(OAuth2Client):
25    """GitHub OAuth2 Client"""
26
27    def get_github_emails(self, token: dict[str, str]) -> list[dict[str, Any]]:
28        """Get Emails from the GitHub API"""
29        profile_url = self.source.source_type.profile_url or ""
30        if self.source.source_type.urls_customizable and self.source.profile_url:
31            profile_url = self.source.profile_url
32        profile_url += "/emails"
33        response = self.do_request("get", profile_url, token=token)
34        try:
35            response.raise_for_status()
36        except RequestException as exc:
37            self.logger.warning("Unable to fetch github emails", exc=exc)
38            return []
39        return response.json()

GitHub OAuth2 Client

def get_github_emails(self, token: dict[str, str]) -> list[dict[str, typing.Any]]:
27    def get_github_emails(self, token: dict[str, str]) -> list[dict[str, Any]]:
28        """Get Emails from the GitHub API"""
29        profile_url = self.source.source_type.profile_url or ""
30        if self.source.source_type.urls_customizable and self.source.profile_url:
31            profile_url = self.source.profile_url
32        profile_url += "/emails"
33        response = self.do_request("get", profile_url, token=token)
34        try:
35            response.raise_for_status()
36        except RequestException as exc:
37            self.logger.warning("Unable to fetch github emails", exc=exc)
38            return []
39        return response.json()

Get Emails from the GitHub API

class GitHubOAuth2Callback(authentik.sources.oauth.views.callback.OAuthCallback):
42class GitHubOAuth2Callback(OAuthCallback):
43    """GitHub OAuth2 Callback"""
44
45    client_class = GitHubOAuth2Client

GitHub OAuth2 Callback

client_class = <class 'GitHubOAuth2Client'>
@registry.register()
class GitHubType(authentik.sources.oauth.types.registry.SourceType):
48@registry.register()
49class GitHubType(SourceType):
50    """GitHub Type definition"""
51
52    callback_view = GitHubOAuth2Callback
53    redirect_view = GitHubOAuthRedirect
54    verbose_name = "GitHub"
55    name = "github"
56
57    urls_customizable = True
58
59    authorization_url = "https://github.com/login/oauth/authorize"
60    access_token_url = "https://github.com/login/oauth/access_token"  # nosec
61    profile_url = "https://api.github.com/user"
62    oidc_well_known_url = (
63        "https://token.actions.githubusercontent.com/.well-known/openid-configuration"
64    )
65    oidc_jwks_url = "https://token.actions.githubusercontent.com/.well-known/jwks"
66
67    authorization_code_auth_method = AuthorizationCodeAuthMethod.POST_BODY
68
69    def get_base_user_properties(
70        self,
71        source: OAuthSource,
72        client: GitHubOAuth2Client,
73        token: dict[str, str],
74        info: dict[str, Any],
75        **kwargs,
76    ) -> dict[str, Any]:
77        chosen_email = info.get("email")
78        if not chosen_email:
79            # The GitHub Userprofile API only returns an email address if the profile
80            # has a public email address set (despite us asking for user:email, this behaviour
81            # doesn't change.). So we fetch all the user's email addresses
82            emails = client.get_github_emails(token)
83            for email in emails:
84                if email.get("primary", False):
85                    chosen_email = email.get("email", None)
86        return {
87            "username": info.get("login"),
88            "email": chosen_email,
89            "name": info.get("name"),
90        }

GitHub Type definition

callback_view = <class 'GitHubOAuth2Callback'>
redirect_view = <class 'GitHubOAuthRedirect'>
verbose_name = 'GitHub'
name = 'github'
urls_customizable = True
authorization_url = 'https://github.com/login/oauth/authorize'
access_token_url = 'https://github.com/login/oauth/access_token'
profile_url = 'https://apiauthentik.sources.oauth.types.github.com/user'
oidc_well_known_url = 'https://token.actions.githubusercontent.com/.well-known/openid-configuration'
oidc_jwks_url = 'https://token.actions.githubusercontent.com/.well-known/jwks'
authorization_code_auth_method = AuthorizationCodeAuthMethod.POST_BODY
def get_base_user_properties( self, source: authentik.sources.oauth.models.OAuthSource, client: GitHubOAuth2Client, token: dict[str, str], info: dict[str, typing.Any], **kwargs) -> dict[str, typing.Any]:
69    def get_base_user_properties(
70        self,
71        source: OAuthSource,
72        client: GitHubOAuth2Client,
73        token: dict[str, str],
74        info: dict[str, Any],
75        **kwargs,
76    ) -> dict[str, Any]:
77        chosen_email = info.get("email")
78        if not chosen_email:
79            # The GitHub Userprofile API only returns an email address if the profile
80            # has a public email address set (despite us asking for user:email, this behaviour
81            # doesn't change.). So we fetch all the user's email addresses
82            emails = client.get_github_emails(token)
83            for email in emails:
84                if email.get("primary", False):
85                    chosen_email = email.get("email", None)
86        return {
87            "username": info.get("login"),
88            "email": chosen_email,
89            "name": info.get("name"),
90        }

Get base user properties for enrollment/update