Skip to content

Commit

Permalink
fix: bitbucket gitlab impl and other fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
KShivendu committed Aug 10, 2023
1 parent 248678b commit 3c78c0f
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 21 deletions.
4 changes: 1 addition & 3 deletions supertokens_python/recipe/thirdparty/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,7 @@ def __init__(
third_party_id: str,
name: Optional[str] = None,
authorization_endpoint: Optional[str] = None,
authorization_endpoint_query_params: Optional[
Dict[str, Union[str, None]]
] = None,
authorization_endpoint_query_params: Optional[Dict[str, Any]] = None,
token_endpoint: Optional[str] = None,
token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
user_info_endpoint: Optional[str] = None,
Expand Down
78 changes: 73 additions & 5 deletions supertokens_python/recipe/thirdparty/providers/bitbucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,79 @@

from __future__ import annotations

from supertokens_python.recipe.thirdparty.provider import Provider
from typing import Dict, Any, Optional

from supertokens_python.recipe.thirdparty.provider import (
ProviderConfigForClient,
ProviderInput,
Provider,
)
from .custom import GenericProvider, NewProvider
from ..provider import Provider, ProviderInput

from .utils import do_get_request
from ..types import RawUserInfoFromProvider, UserInfo, UserInfoEmail


class BitbucketImpl(GenericProvider):
pass
async def get_config_for_client_type(
self, client_type: Optional[str], user_context: Dict[str, Any]
) -> ProviderConfigForClient:
config = await super().get_config_for_client_type(client_type, user_context)

if config.scope is None:
config.scope = ["account", "email"]

return config

async def get_user_info(
self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
) -> UserInfo:
_ = user_context
access_token = oauth_tokens.get("access_token")
if access_token is None:
raise Exception("Access token not found")

headers = {
"Authorization": f"Bearer {access_token}",
}

raw_user_info_from_provider = RawUserInfoFromProvider({}, {})

user_info_from_access_token = await do_get_request(
"https://api.bitbucket.org/2.0/user",
query_params=None,
headers=headers,
)

raw_user_info_from_provider.from_user_info_api = user_info_from_access_token

user_info_from_email = await do_get_request(
"https://api.bitbucket.org/2.0/user/emails",
query_params=None,
headers=headers,
)

if raw_user_info_from_provider.from_id_token_payload is None:
# Actually this should never happen but python type
# checker is not agreeing so doing this:
raw_user_info_from_provider.from_id_token_payload = {}

raw_user_info_from_provider.from_id_token_payload[
"email"
] = user_info_from_email

email = None
is_verified = False
for email_info in user_info_from_email.values():
if email_info["is_primary"]:
email = email_info["email"]
is_verified = email_info["is_confirmed"]

return UserInfo(
third_party_user_id=raw_user_info_from_provider.from_user_info_api["uuid"],
email=None if email is None else UserInfoEmail(email, is_verified),
raw_user_info_from_provider=raw_user_info_from_provider,
)


def Bitbucket(input: ProviderInput) -> Provider: # pylint: disable=redefined-builtin
Expand All @@ -35,7 +101,9 @@ def Bitbucket(input: ProviderInput) -> Provider: # pylint: disable=redefined-bu
if input.config.token_endpoint is None:
input.config.token_endpoint = "https://bitbucket.org/site/oauth2/access_token"

if input.config.user_info_endpoint is None:
input.config.user_info_endpoint = "https://api.bitbucket.org/2.0/user"
if input.config.authorization_endpoint_query_params is None:
input.config.authorization_endpoint_query_params = {
"audience": "api.atlassian.com",
}

return NewProvider(input, BitbucketImpl)
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
from supertokens_python.normalised_url_path import NormalisedURLPath
from .active_directory import ActiveDirectory
from .apple import Apple
from .bitbucket import Bitbucket
from .boxy_saml import BoxySAML
from .discord import Discord
from .facebook import Facebook
from .github import Github
from .gitlab import Gitlab
from .google_workspaces import GoogleWorkspaces
from .google import Google
from .linkedin import Linkedin
Expand Down Expand Up @@ -176,12 +178,16 @@ def create_provider(provider_input: ProviderInput) -> Provider:
return ActiveDirectory(provider_input)
if provider_input.config.third_party_id.startswith("apple"):
return Apple(provider_input)
if provider_input.config.third_party_id.startswith("bitbucket"):
return Bitbucket(provider_input)
if provider_input.config.third_party_id.startswith("discord"):
return Discord(provider_input)
if provider_input.config.third_party_id.startswith("facebook"):
return Facebook(provider_input)
if provider_input.config.third_party_id.startswith("github"):
return Github(provider_input)
if provider_input.config.third_party_id.startswith("gitlab"):
return Gitlab(provider_input)
if provider_input.config.third_party_id.startswith("google-workspaces"):
return GoogleWorkspaces(provider_input)
if provider_input.config.third_party_id.startswith("google"):
Expand Down
33 changes: 31 additions & 2 deletions supertokens_python/recipe/thirdparty/providers/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,43 @@

from __future__ import annotations

from supertokens_python.recipe.thirdparty.provider import Provider
from typing import Optional, Dict, Any

from supertokens_python.recipe.thirdparty.provider import (
Provider,
ProviderConfigForClient,
)
from .custom import GenericProvider, NewProvider
from ..provider import Provider, ProviderInput


class GitlabImpl(GenericProvider):
pass
async def get_config_for_client_type(
self, client_type: Optional[str], user_context: Dict[str, Any]
) -> ProviderConfigForClient:
config = await super().get_config_for_client_type(client_type, user_context)

if config.scope is None:
config.scope = ["openid", "email"]

if config.oidc_discovery_endpoint is None:
if config.additional_config is not None and config.additional_config.get(
"gitlabBaseUrl"
):
config.oidc_discovery_endpoint = config.additional_config[
"gitlabBaseUrl"
]
else:
config.oidc_discovery_endpoint = "https://gitlab.com"

return config


def Gitlab(input: ProviderInput) -> Provider: # pylint: disable=redefined-builtin
if input.config.name is None:
input.config.name = "Gitlab"

if input.config.oidc_discovery_endpoint is None:
input.config.oidc_discovery_endpoint = "https://gitlab.com"

return NewProvider(input, GitlabImpl)
4 changes: 3 additions & 1 deletion supertokens_python/recipe/thirdparty/providers/linkedin.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,10 @@ async def get_user_info(
)
raw_user_info_from_provider.from_user_info_api = user_info

email_api_url = "https://api.linkedin.com/v2/emailAddress"
email_info: Dict[str, Any] = await do_get_request(
"https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))",
email_api_url,
query_params={"q": "members", "projection": "(elements*(handle~))"},
headers=headers,
)

Expand Down
23 changes: 13 additions & 10 deletions supertokens_python/recipe/thirdparty/providers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from httpx import AsyncClient

from supertokens_python.logger import log_debug_message

DEV_OAUTH_CLIENT_IDS = [
"1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com",
Expand Down Expand Up @@ -33,12 +34,14 @@ async def do_get_request(
if headers is None:
headers = {}

# TODO logging

async with AsyncClient() as client:
return (
await client.get(url, params=query_params, headers=headers) # type:ignore
).json()
res = await client.get(url, params=query_params, headers=headers) # type:ignore

log_debug_message(
"Received response with status %s and body %s", res.status_code, res.text
)

return res.json()


async def do_post_request(
Expand All @@ -54,9 +57,9 @@ async def do_post_request(
headers["content-type"] = "application/x-www-form-urlencoded"
headers["accept"] = "application/json"

# TODO logging

async with AsyncClient() as client:
return (
await client.post(url, data=body_params, headers=headers) # type:ignore
).json()
res = await client.post(url, data=body_params, headers=headers) # type:ignore
log_debug_message(
"Received response with status %s and body %s", res.status_code, res.text
)
return res.json()

0 comments on commit 3c78c0f

Please sign in to comment.