Skip to content

update: adds raw user info to thirdparty response #558

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [unreleased]

## [0.28.0]
- Adds `raw_user_info_from_provider` to `UserInfo` data in LinkedIn and Github third-party recipes.
- **[Breaking] Bitbucket third-party recipe**
- Moves `email` from `from_id_token_payload` to `from_user_info_api` in `raw_user_info_from_provider`.
- Keeps the API consistent with the Node SDK.
- Migration:
```
- user_info.raw_user_info_from_provider.from_id_token_payload["email"]
+ user_info.raw_user_info_from_provider.from_user_info_api["email"]
```
- Updates timestamps to use UTC instead of GMT as the timezone

## [0.27.0] - 2024-12-30
Expand Down Expand Up @@ -280,7 +289,7 @@ async def change_email(req: ChangeEmailBody, session: SessionContainer = Depends
# Update the email
await update_email_or_password(
session.get_recipe_user_id(),
email,
email,
)

# ...
Expand Down Expand Up @@ -363,7 +372,7 @@ from supertokens_python.types import RecipeUserId

def functions_override(original_implementation: RecipeInterface):
o_create_new_session = original_implementation.create_new_session

async def n_create_new_session(
user_id: str,
recipe_user_id: RecipeUserId,
Expand All @@ -380,7 +389,7 @@ def functions_override(original_implementation: RecipeInterface):
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)

original_implementation.create_new_session = n_create_new_session

return original_implementation

session.init(override=session.InputOverrideConfig(functions=functions_override))
Expand All @@ -398,7 +407,7 @@ from supertokens_python.types import RecipeUserId

def functions_override(original_implementation: RecipeInterface):
o_create_new_session = original_implementation.create_new_session

async def n_create_new_session(
user_id: str,
recipe_user_id: RecipeUserId,
Expand All @@ -415,7 +424,7 @@ def functions_override(original_implementation: RecipeInterface):
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)

original_implementation.create_new_session = n_create_new_session

return original_implementation

session.init(override=session.InputOverrideConfig(functions=functions_override))
Expand Down Expand Up @@ -635,7 +644,7 @@ thirdparty.init(
third_party_id="google",
# rest of the config
),

# Add the following line to make this provider available in non-public tenants by default
include_in_non_public_tenants_by_default=True
),
Expand All @@ -644,7 +653,7 @@ thirdparty.init(
third_party_id="github",
# rest of the config
),

# Add the following line to make this provider available in non-public tenants by default
include_in_non_public_tenants_by_default=True
),
Expand Down Expand Up @@ -736,7 +745,7 @@ for tenant in tenants_res.tenants:

- The way to get user information has changed:
- If you are using `get_users_by_email` from `thirdpartyemailpassword` recipe:

Before:
```python
from supertokens_python.recipe.thirdpartyemailpassword.syncio import get_users_by_email
Expand All @@ -748,20 +757,20 @@ for tenant in tenants_res.tenants:
```python
from supertokens_python.recipe.thirdparty.syncio import get_users_by_email as get_users_by_email_third_party
from supertokens_python.recipe.emailpassword.syncio import get_user_by_email as get_user_by_email_emailpassword

third_party_user_info = get_users_by_email_third_party("public", "[email protected]")

email_password_user_info = get_user_by_email_emailpassword("public", "[email protected]")

if email_password_user_info is not None:
print(email_password_user_info)

if len(third_party_user_info) > 0:
print(third_party_user_info)
```

- If you are using `get_user_id` from `thirdpartyemailpassword` recipe:

Before:
```python
from supertokens_python.recipe.thirdpartyemailpassword.syncio import get_user_by_id
Expand All @@ -786,9 +795,9 @@ for tenant in tenants_res.tenants:
else:
print(thirdparty_user)
```

- If you are using `get_users_by_email` from `thirdpartypasswordless` recipe:

Before:
```python
from supertokens_python.recipe.thirdpartypasswordless.syncio import get_users_by_email
Expand All @@ -800,20 +809,20 @@ for tenant in tenants_res.tenants:
```python
from supertokens_python.recipe.thirdparty.syncio import get_users_by_email as get_users_by_email_third_party
from supertokens_python.recipe.passwordless.syncio import get_user_by_email as get_user_by_email_passwordless

third_party_user_info = get_users_by_email_third_party("public", "[email protected]")

passwordless_user_info = get_user_by_email_passwordless("public", "[email protected]")

if passwordless_user_info is not None:
print(passwordless_user_info)

if len(third_party_user_info) > 0:
print(third_party_user_info)
```

- If you are using `get_user_id` from `thirdpartypasswordless` recipe:

Before:
```python
from supertokens_python.recipe.thirdpartypasswordless.syncio import get_user_by_id
Expand Down Expand Up @@ -1025,7 +1034,7 @@ With this update, verify_session will return a 401 error if it detects multiple
)
```

- 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`
- 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`

## [0.17.0] - 2023-11-14
- Fixes `create_reset_password_link` in the emailpassword recipe wherein we passed the `rid` instead of the token in the link
Expand Down
27 changes: 11 additions & 16 deletions supertokens_python/recipe/thirdparty/providers/bitbucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@

from __future__ import annotations

from typing import Dict, Any, Optional

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

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

Expand Down Expand Up @@ -66,25 +64,22 @@ async def get_user_info(
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
)
raw_user_info_from_provider.from_user_info_api["email"] = user_info_from_email

email = None
is_verified = False
# Get the primary email from the Email response
# Create an object if primary email found
primary_email_info: UserInfoEmail | None = None
for email_info in user_info_from_email["values"]:
if email_info["is_primary"]:
email = email_info["email"]
is_verified = email_info["is_confirmed"]
primary_email_info = UserInfoEmail(
email=email_info["email"],
is_verified=email_info["is_confirmed"],
)
break

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),
email=primary_email_info,
raw_user_info_from_provider=raw_user_info_from_provider,
)

Expand Down
36 changes: 22 additions & 14 deletions supertokens_python/recipe/thirdparty/providers/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@
)
from supertokens_python.recipe.thirdparty.types import UserInfo, UserInfoEmail

from .custom import GenericProvider, NewProvider
from ..provider import Provider, ProviderConfigForClient, ProviderInput
from ..types import RawUserInfoFromProvider
from .custom import GenericProvider, NewProvider


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

raw_response = {}

email_info: List[Any] = await do_get_request("https://api.github.com/user/emails", headers=headers) # type: ignore
# https://docs.github.com/en/rest/users/emails?apiVersion=2022-11-28
user_info = await do_get_request("https://api.github.com/user", headers=headers)

raw_response["emails"] = email_info
raw_response["user"] = user_info
user_email_info: List[Any] = await do_get_request("https://api.github.com/user/emails", headers=headers) # type: ignore

raw_user_info_from_provider = RawUserInfoFromProvider({}, {})
raw_user_info_from_provider.from_user_info_api = user_info
raw_user_info_from_provider.from_user_info_api["emails"] = user_email_info

# Get the primary email from the Email response
# Create an object if primary email found
primary_email_info: UserInfoEmail | None = None
for email_detail in user_email_info:
if email_detail["primary"]:
primary_email_info = UserInfoEmail(
email=email_detail["email"],
is_verified=email_detail["verified"],
)
break

result = UserInfo(
third_party_user_id=str(user_info.get("id")),
third_party_user_id=str(user_info["id"]),
email=primary_email_info,
raw_user_info_from_provider=raw_user_info_from_provider,
)

for info in email_info:
if info.get("primary"):
result.email = UserInfoEmail(
email=info.get("email"), is_verified=info.get("verified")
)

return result


Expand Down
3 changes: 2 additions & 1 deletion supertokens_python/recipe/thirdparty/providers/linkedin.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,12 @@ async def get_user_info(
raw_user_info_from_provider.from_user_info_api = user_info

return UserInfo(
third_party_user_id=raw_user_info_from_provider.from_user_info_api.get("sub"), # type: ignore
third_party_user_id=raw_user_info_from_provider.from_user_info_api["sub"], # type: ignore
email=UserInfoEmail(
email=raw_user_info_from_provider.from_user_info_api.get("email"), # type: ignore
is_verified=raw_user_info_from_provider.from_user_info_api.get("email_verified"), # type: ignore
),
raw_user_info_from_provider=raw_user_info_from_provider,
)


Expand Down
4 changes: 4 additions & 0 deletions supertokens_python/recipe/thirdparty/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ def to_json(self) -> Dict[str, Any]:


class UserInfoEmail:
"""
Details about the user's - generally primary - email.
"""

def __init__(self, email: str, is_verified: bool):
self.id: str = email
self.is_verified: bool = is_verified
Expand Down
Loading