Skip to content

Commit 3c78c0f

Browse files
committed
fix: bitbucket gitlab impl and other fixes
1 parent 248678b commit 3c78c0f

File tree

6 files changed

+127
-21
lines changed

6 files changed

+127
-21
lines changed

supertokens_python/recipe/thirdparty/provider.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,7 @@ def __init__(
155155
third_party_id: str,
156156
name: Optional[str] = None,
157157
authorization_endpoint: Optional[str] = None,
158-
authorization_endpoint_query_params: Optional[
159-
Dict[str, Union[str, None]]
160-
] = None,
158+
authorization_endpoint_query_params: Optional[Dict[str, Any]] = None,
161159
token_endpoint: Optional[str] = None,
162160
token_endpoint_body_params: Optional[Dict[str, Union[str, None]]] = None,
163161
user_info_endpoint: Optional[str] = None,

supertokens_python/recipe/thirdparty/providers/bitbucket.py

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,79 @@
1414

1515
from __future__ import annotations
1616

17-
from supertokens_python.recipe.thirdparty.provider import Provider
17+
from typing import Dict, Any, Optional
18+
19+
from supertokens_python.recipe.thirdparty.provider import (
20+
ProviderConfigForClient,
21+
ProviderInput,
22+
Provider,
23+
)
1824
from .custom import GenericProvider, NewProvider
19-
from ..provider import Provider, ProviderInput
25+
26+
from .utils import do_get_request
27+
from ..types import RawUserInfoFromProvider, UserInfo, UserInfoEmail
2028

2129

2230
class BitbucketImpl(GenericProvider):
23-
pass
31+
async def get_config_for_client_type(
32+
self, client_type: Optional[str], user_context: Dict[str, Any]
33+
) -> ProviderConfigForClient:
34+
config = await super().get_config_for_client_type(client_type, user_context)
35+
36+
if config.scope is None:
37+
config.scope = ["account", "email"]
38+
39+
return config
40+
41+
async def get_user_info(
42+
self, oauth_tokens: Dict[str, Any], user_context: Dict[str, Any]
43+
) -> UserInfo:
44+
_ = user_context
45+
access_token = oauth_tokens.get("access_token")
46+
if access_token is None:
47+
raise Exception("Access token not found")
48+
49+
headers = {
50+
"Authorization": f"Bearer {access_token}",
51+
}
52+
53+
raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
54+
55+
user_info_from_access_token = await do_get_request(
56+
"https://api.bitbucket.org/2.0/user",
57+
query_params=None,
58+
headers=headers,
59+
)
60+
61+
raw_user_info_from_provider.from_user_info_api = user_info_from_access_token
62+
63+
user_info_from_email = await do_get_request(
64+
"https://api.bitbucket.org/2.0/user/emails",
65+
query_params=None,
66+
headers=headers,
67+
)
68+
69+
if raw_user_info_from_provider.from_id_token_payload is None:
70+
# Actually this should never happen but python type
71+
# checker is not agreeing so doing this:
72+
raw_user_info_from_provider.from_id_token_payload = {}
73+
74+
raw_user_info_from_provider.from_id_token_payload[
75+
"email"
76+
] = user_info_from_email
77+
78+
email = None
79+
is_verified = False
80+
for email_info in user_info_from_email.values():
81+
if email_info["is_primary"]:
82+
email = email_info["email"]
83+
is_verified = email_info["is_confirmed"]
84+
85+
return UserInfo(
86+
third_party_user_id=raw_user_info_from_provider.from_user_info_api["uuid"],
87+
email=None if email is None else UserInfoEmail(email, is_verified),
88+
raw_user_info_from_provider=raw_user_info_from_provider,
89+
)
2490

2591

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

38-
if input.config.user_info_endpoint is None:
39-
input.config.user_info_endpoint = "https://api.bitbucket.org/2.0/user"
104+
if input.config.authorization_endpoint_query_params is None:
105+
input.config.authorization_endpoint_query_params = {
106+
"audience": "api.atlassian.com",
107+
}
40108

41109
return NewProvider(input, BitbucketImpl)

supertokens_python/recipe/thirdparty/providers/config_utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
from supertokens_python.normalised_url_path import NormalisedURLPath
55
from .active_directory import ActiveDirectory
66
from .apple import Apple
7+
from .bitbucket import Bitbucket
78
from .boxy_saml import BoxySAML
89
from .discord import Discord
910
from .facebook import Facebook
1011
from .github import Github
12+
from .gitlab import Gitlab
1113
from .google_workspaces import GoogleWorkspaces
1214
from .google import Google
1315
from .linkedin import Linkedin
@@ -176,12 +178,16 @@ def create_provider(provider_input: ProviderInput) -> Provider:
176178
return ActiveDirectory(provider_input)
177179
if provider_input.config.third_party_id.startswith("apple"):
178180
return Apple(provider_input)
181+
if provider_input.config.third_party_id.startswith("bitbucket"):
182+
return Bitbucket(provider_input)
179183
if provider_input.config.third_party_id.startswith("discord"):
180184
return Discord(provider_input)
181185
if provider_input.config.third_party_id.startswith("facebook"):
182186
return Facebook(provider_input)
183187
if provider_input.config.third_party_id.startswith("github"):
184188
return Github(provider_input)
189+
if provider_input.config.third_party_id.startswith("gitlab"):
190+
return Gitlab(provider_input)
185191
if provider_input.config.third_party_id.startswith("google-workspaces"):
186192
return GoogleWorkspaces(provider_input)
187193
if provider_input.config.third_party_id.startswith("google"):

supertokens_python/recipe/thirdparty/providers/gitlab.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,43 @@
1414

1515
from __future__ import annotations
1616

17-
from supertokens_python.recipe.thirdparty.provider import Provider
17+
from typing import Optional, Dict, Any
18+
19+
from supertokens_python.recipe.thirdparty.provider import (
20+
Provider,
21+
ProviderConfigForClient,
22+
)
1823
from .custom import GenericProvider, NewProvider
1924
from ..provider import Provider, ProviderInput
2025

2126

2227
class GitlabImpl(GenericProvider):
23-
pass
28+
async def get_config_for_client_type(
29+
self, client_type: Optional[str], user_context: Dict[str, Any]
30+
) -> ProviderConfigForClient:
31+
config = await super().get_config_for_client_type(client_type, user_context)
32+
33+
if config.scope is None:
34+
config.scope = ["openid", "email"]
35+
36+
if config.oidc_discovery_endpoint is None:
37+
if config.additional_config is not None and config.additional_config.get(
38+
"gitlabBaseUrl"
39+
):
40+
config.oidc_discovery_endpoint = config.additional_config[
41+
"gitlabBaseUrl"
42+
]
43+
else:
44+
config.oidc_discovery_endpoint = "https://gitlab.com"
45+
46+
return config
2447

2548

2649
def Gitlab(input: ProviderInput) -> Provider: # pylint: disable=redefined-builtin
50+
if input.config.name is None:
51+
input.config.name = "Gitlab"
52+
53+
if input.config.oidc_discovery_endpoint is None:
54+
input.config.oidc_discovery_endpoint = "https://gitlab.com"
55+
2756
return NewProvider(input, GitlabImpl)

supertokens_python/recipe/thirdparty/providers/linkedin.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ async def get_user_info(
6464
)
6565
raw_user_info_from_provider.from_user_info_api = user_info
6666

67+
email_api_url = "https://api.linkedin.com/v2/emailAddress"
6768
email_info: Dict[str, Any] = await do_get_request(
68-
"https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))",
69+
email_api_url,
70+
query_params={"q": "members", "projection": "(elements*(handle~))"},
6971
headers=headers,
7072
)
7173

supertokens_python/recipe/thirdparty/providers/utils.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from httpx import AsyncClient
44

5+
from supertokens_python.logger import log_debug_message
56

67
DEV_OAUTH_CLIENT_IDS = [
78
"1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com",
@@ -33,12 +34,14 @@ async def do_get_request(
3334
if headers is None:
3435
headers = {}
3536

36-
# TODO logging
37-
3837
async with AsyncClient() as client:
39-
return (
40-
await client.get(url, params=query_params, headers=headers) # type:ignore
41-
).json()
38+
res = await client.get(url, params=query_params, headers=headers) # type:ignore
39+
40+
log_debug_message(
41+
"Received response with status %s and body %s", res.status_code, res.text
42+
)
43+
44+
return res.json()
4245

4346

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

57-
# TODO logging
58-
5960
async with AsyncClient() as client:
60-
return (
61-
await client.post(url, data=body_params, headers=headers) # type:ignore
62-
).json()
61+
res = await client.post(url, data=body_params, headers=headers) # type:ignore
62+
log_debug_message(
63+
"Received response with status %s and body %s", res.status_code, res.text
64+
)
65+
return res.json()

0 commit comments

Comments
 (0)