Skip to content

Commit 55b4711

Browse files
update: adds raw user info to thirdparty response
- BitBucket - Cleans up primary email logic - Moves `email` key from `from_id_token_payload` to `from_user_info_api` in `raw_user_info_from_provider` - Github - Changes `id` field access to be concrete to be in-line with API Spec - Adds raw user info to output - Makes primary email logic consistent with BitBucket - LinkedIn - Changes `sub` field access to be concrete to be in-line with API Spec - Adds raw user info to output Co-authored-by: Viraj Kanwade <viraj.kanwade@forgeahead.io>
1 parent f1b9103 commit 55b4711

File tree

5 files changed

+67
-48
lines changed

5 files changed

+67
-48
lines changed

CHANGELOG.md

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
## [unreleased]
1010

11+
## [0.28.0]
12+
- Adds `raw_user_info_from_provider` to `UserInfo` data in LinkedIn and Github third-party recipes.
13+
- **[Breaking] Bitbucket third-party recipe**
14+
- Moves `email` from `from_id_token_payload` to `from_user_info_api` in `raw_user_info_from_provider`.
15+
- Keeps the API consistent with the Node SDK.
16+
- Migration:
17+
```
18+
- user_info.raw_user_info_from_provider.from_id_token_payload["email"]
19+
+ user_info.raw_user_info_from_provider.from_user_info_api["email"]
20+
```
21+
1122
## [0.27.0] - 2024-12-30
1223
1324
- Added OAuth2Provider recipe
@@ -277,7 +288,7 @@ async def change_email(req: ChangeEmailBody, session: SessionContainer = Depends
277288
# Update the email
278289
await update_email_or_password(
279290
session.get_recipe_user_id(),
280-
email,
291+
email,
281292
)
282293

283294
# ...
@@ -360,7 +371,7 @@ from supertokens_python.types import RecipeUserId
360371

361372
def functions_override(original_implementation: RecipeInterface):
362373
o_create_new_session = original_implementation.create_new_session
363-
374+
364375
async def n_create_new_session(
365376
user_id: str,
366377
recipe_user_id: RecipeUserId,
@@ -377,7 +388,7 @@ def functions_override(original_implementation: RecipeInterface):
377388
return await o_create_new_session(user_id, recipe_user_id, access_token_payload, session_data_in_database, disable_anti_csrf, tenant_id, user_context)
378389

379390
original_implementation.create_new_session = n_create_new_session
380-
391+
381392
return original_implementation
382393

383394
session.init(override=session.InputOverrideConfig(functions=functions_override))
@@ -395,7 +406,7 @@ from supertokens_python.types import RecipeUserId
395406

396407
def functions_override(original_implementation: RecipeInterface):
397408
o_create_new_session = original_implementation.create_new_session
398-
409+
399410
async def n_create_new_session(
400411
user_id: str,
401412
recipe_user_id: RecipeUserId,
@@ -412,7 +423,7 @@ def functions_override(original_implementation: RecipeInterface):
412423
return await o_create_new_session(user_id, recipe_user_id, access_token_payload, session_data_in_database, disable_anti_csrf, tenant_id, user_context)
413424

414425
original_implementation.create_new_session = n_create_new_session
415-
426+
416427
return original_implementation
417428

418429
session.init(override=session.InputOverrideConfig(functions=functions_override))
@@ -632,7 +643,7 @@ thirdparty.init(
632643
third_party_id="google",
633644
# rest of the config
634645
),
635-
646+
636647
# Add the following line to make this provider available in non-public tenants by default
637648
include_in_non_public_tenants_by_default=True
638649
),
@@ -641,7 +652,7 @@ thirdparty.init(
641652
third_party_id="github",
642653
# rest of the config
643654
),
644-
655+
645656
# Add the following line to make this provider available in non-public tenants by default
646657
include_in_non_public_tenants_by_default=True
647658
),
@@ -733,7 +744,7 @@ for tenant in tenants_res.tenants:
733744

734745
- The way to get user information has changed:
735746
- If you are using `get_users_by_email` from `thirdpartyemailpassword` recipe:
736-
747+
737748
Before:
738749
```python
739750
from supertokens_python.recipe.thirdpartyemailpassword.syncio import get_users_by_email
@@ -745,20 +756,20 @@ for tenant in tenants_res.tenants:
745756
```python
746757
from supertokens_python.recipe.thirdparty.syncio import get_users_by_email as get_users_by_email_third_party
747758
from supertokens_python.recipe.emailpassword.syncio import get_user_by_email as get_user_by_email_emailpassword
748-
759+
749760
third_party_user_info = get_users_by_email_third_party("public", "test@example.com")
750761

751762
email_password_user_info = get_user_by_email_emailpassword("public", "test@example.com")
752763

753764
if email_password_user_info is not None:
754765
print(email_password_user_info)
755-
766+
756767
if len(third_party_user_info) > 0:
757768
print(third_party_user_info)
758769
```
759770

760771
- If you are using `get_user_id` from `thirdpartyemailpassword` recipe:
761-
772+
762773
Before:
763774
```python
764775
from supertokens_python.recipe.thirdpartyemailpassword.syncio import get_user_by_id
@@ -783,9 +794,9 @@ for tenant in tenants_res.tenants:
783794
else:
784795
print(thirdparty_user)
785796
```
786-
797+
787798
- If you are using `get_users_by_email` from `thirdpartypasswordless` recipe:
788-
799+
789800
Before:
790801
```python
791802
from supertokens_python.recipe.thirdpartypasswordless.syncio import get_users_by_email
@@ -797,20 +808,20 @@ for tenant in tenants_res.tenants:
797808
```python
798809
from supertokens_python.recipe.thirdparty.syncio import get_users_by_email as get_users_by_email_third_party
799810
from supertokens_python.recipe.passwordless.syncio import get_user_by_email as get_user_by_email_passwordless
800-
811+
801812
third_party_user_info = get_users_by_email_third_party("public", "test@example.com")
802813

803814
passwordless_user_info = get_user_by_email_passwordless("public", "test@example.com")
804815

805816
if passwordless_user_info is not None:
806817
print(passwordless_user_info)
807-
818+
808819
if len(third_party_user_info) > 0:
809820
print(third_party_user_info)
810821
```
811822

812823
- If you are using `get_user_id` from `thirdpartypasswordless` recipe:
813-
824+
814825
Before:
815826
```python
816827
from supertokens_python.recipe.thirdpartypasswordless.syncio import get_user_by_id
@@ -1022,7 +1033,7 @@ With this update, verify_session will return a 401 error if it detects multiple
10221033
)
10231034
```
10241035

1025-
- In the session recipe, if there is an `UNAUTHORISED` or `TOKEN_THEFT_DETECTED` error, the session tokens are cleared in the response regardless of if you have provided your own `error_handlers` in `session.init`
1036+
- In the session recipe, if there is an `UNAUTHORISED` or `TOKEN_THEFT_DETECTED` error, the session tokens are cleared in the response regardless of if you have provided your own `error_handlers` in `session.init`
10261037

10271038
## [0.17.0] - 2023-11-14
10281039
- Fixes `create_reset_password_link` in the emailpassword recipe wherein we passed the `rid` instead of the token in the link

supertokens_python/recipe/thirdparty/providers/bitbucket.py

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,13 @@
1414

1515
from __future__ import annotations
1616

17-
from typing import Dict, Any, Optional
18-
1917
from supertokens_python.recipe.thirdparty.provider import (
2018
ProviderConfigForClient,
2119
ProviderInput,
2220
Provider,
2321
)
22+
from typing import Dict, Any, Optional
2423
from .custom import GenericProvider, NewProvider
25-
2624
from .utils import do_get_request
2725
from ..types import RawUserInfoFromProvider, UserInfo, UserInfoEmail
2826

@@ -66,25 +64,22 @@ async def get_user_info(
6664
headers=headers,
6765
)
6866

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["email"] = (
75-
user_info_from_email
76-
)
67+
raw_user_info_from_provider.from_user_info_api["email"] = user_info_from_email
7768

78-
email = None
79-
is_verified = False
69+
# Get the primary email from the Email response
70+
# Create an object if primary email found
71+
primary_email_info: UserInfoEmail | None = None
8072
for email_info in user_info_from_email["values"]:
8173
if email_info["is_primary"]:
82-
email = email_info["email"]
83-
is_verified = email_info["is_confirmed"]
74+
primary_email_info = UserInfoEmail(
75+
email=email_info["email"],
76+
is_verified=email_info["is_confirmed"],
77+
)
78+
break
8479

8580
return UserInfo(
8681
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),
82+
email=primary_email_info,
8883
raw_user_info_from_provider=raw_user_info_from_provider,
8984
)
9085

supertokens_python/recipe/thirdparty/providers/github.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@
2222
)
2323
from supertokens_python.recipe.thirdparty.types import UserInfo, UserInfoEmail
2424

25-
from .custom import GenericProvider, NewProvider
2625
from ..provider import Provider, ProviderConfigForClient, ProviderInput
26+
from ..types import RawUserInfoFromProvider
27+
from .custom import GenericProvider, NewProvider
2728

2829

2930
class GithubImpl(GenericProvider):
@@ -45,24 +46,31 @@ async def get_user_info(
4546
"Accept": "application/vnd.github.v3+json",
4647
}
4748

48-
raw_response = {}
49-
50-
email_info: List[Any] = await do_get_request("https://api.github.com/user/emails", headers=headers) # type: ignore
49+
# https://docs.github.com/en/rest/users/emails?apiVersion=2022-11-28
5150
user_info = await do_get_request("https://api.github.com/user", headers=headers)
52-
53-
raw_response["emails"] = email_info
54-
raw_response["user"] = user_info
51+
user_email_info: List[Any] = await do_get_request("https://api.github.com/user/emails", headers=headers) # type: ignore
52+
53+
raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
54+
raw_user_info_from_provider.from_user_info_api = user_info
55+
raw_user_info_from_provider.from_user_info_api["emails"] = user_email_info
56+
57+
# Get the primary email from the Email response
58+
# Create an object if primary email found
59+
primary_email_info: UserInfoEmail | None = None
60+
for email_detail in user_email_info:
61+
if email_detail["primary"]:
62+
primary_email_info = UserInfoEmail(
63+
email=email_detail["email"],
64+
is_verified=email_detail["verified"],
65+
)
66+
break
5567

5668
result = UserInfo(
57-
third_party_user_id=str(user_info.get("id")),
69+
third_party_user_id=str(user_info["id"]),
70+
email=primary_email_info,
71+
raw_user_info_from_provider=raw_user_info_from_provider,
5872
)
5973

60-
for info in email_info:
61-
if info.get("primary"):
62-
result.email = UserInfoEmail(
63-
email=info.get("email"), is_verified=info.get("verified")
64-
)
65-
6674
return result
6775

6876

supertokens_python/recipe/thirdparty/providers/linkedin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,12 @@ async def get_user_info(
6767
raw_user_info_from_provider.from_user_info_api = user_info
6868

6969
return UserInfo(
70-
third_party_user_id=raw_user_info_from_provider.from_user_info_api.get("sub"), # type: ignore
70+
third_party_user_id=raw_user_info_from_provider.from_user_info_api["sub"], # type: ignore
7171
email=UserInfoEmail(
7272
email=raw_user_info_from_provider.from_user_info_api.get("email"), # type: ignore
7373
is_verified=raw_user_info_from_provider.from_user_info_api.get("email_verified"), # type: ignore
7474
),
75+
raw_user_info_from_provider=raw_user_info_from_provider,
7576
)
7677

7778

supertokens_python/recipe/thirdparty/types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ def to_json(self) -> Dict[str, Any]:
5858

5959

6060
class UserInfoEmail:
61+
"""
62+
Details about the user's - generally primary - email.
63+
"""
64+
6165
def __init__(self, email: str, is_verified: bool):
6266
self.id: str = email
6367
self.is_verified: bool = is_verified

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy