Skip to content

fix: Fixes issue with thirdparty passwordless user in dashboard #473

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 4 commits into from
Jan 23, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.18.7] - 2024-01-17

- Fixes `connection_uri` normalisation in the dashboard recipe.
- Fixes issue with fetching of thirdparty passwordless user in dashboard: https://github.com/supertokens/supertokens-python/issues/472

## [0.18.6] - 2024-01-12

Expand Down
42 changes: 12 additions & 30 deletions supertokens_python/recipe/dashboard/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,58 +309,40 @@ async def get_user_for_recipe_id(
recipe: Optional[str] = None

async def update_user_dict(
get_user_func1: Callable[[str], Awaitable[GetUserResult]],
get_user_func2: Callable[[str], Awaitable[GetUserResult]],
recipe1: str,
recipe2: str,
get_user_funcs: List[Callable[[str], Awaitable[GetUserResult]]],
recipes: List[str],
):
nonlocal user, user_id, recipe

try:
recipe_user = await get_user_func1(user_id) # type: ignore

if recipe_user is not None:
user = UserWithMetadata().from_dict(
recipe_user.__dict__, first_name="", last_name=""
)
recipe = recipe1
except Exception:
pass

if user is None:
for get_user_func, recipe_id in zip(get_user_funcs, recipes):
try:
recipe_user = await get_user_func2(user_id)
recipe_user = await get_user_func(user_id) # type: ignore

if recipe_user is not None:
user = UserWithMetadata().from_dict(
recipe_user.__dict__, first_name="", last_name=""
)
recipe = recipe2
recipe = recipe_id
break
except Exception:
pass

if recipe_id == EmailPasswordRecipe.recipe_id:
await update_user_dict(
ep_get_user_by_id,
tpep_get_user_by_id,
"emailpassword",
"thirdpartyemailpassword",
[ep_get_user_by_id, tpep_get_user_by_id],
["emailpassword", "thirdpartyemailpassword"],
)

elif recipe_id == ThirdPartyRecipe.recipe_id:
await update_user_dict(
tp_get_user_by_idx,
tpep_get_user_by_id,
"thirdparty",
"thirdpartyemailpassword",
[tp_get_user_by_idx, tpep_get_user_by_id, tppless_get_user_by_id],
["thirdparty", "thirdpartyemailpassword", "thirdpartypasswordless"],
)

elif recipe_id == PasswordlessRecipe.recipe_id:
await update_user_dict(
pless_get_user_by_id,
tppless_get_user_by_id,
"passwordless",
"thirdpartypasswordless",
[pless_get_user_by_id, tppless_get_user_by_id],
["passwordless", "thirdpartypasswordless"],
)

if user is not None and recipe is not None:
Expand Down
67 changes: 66 additions & 1 deletion tests/dashboard/test_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,20 @@
from supertokens_python.constants import DASHBOARD_VERSION
from supertokens_python.framework import BaseRequest
from supertokens_python.framework.fastapi import get_middleware
from supertokens_python.recipe import dashboard, emailpassword, session, usermetadata
from supertokens_python.recipe import (
dashboard,
emailpassword,
session,
usermetadata,
thirdpartypasswordless,
)
import supertokens_python.recipe.thirdpartypasswordless.asyncio as tplasync
from supertokens_python.recipe.dashboard import InputOverrideConfig
from supertokens_python.recipe.dashboard.interfaces import (
RecipeInterface as DashboardRI,
)
from supertokens_python.recipe.dashboard.utils import DashboardConfig
from supertokens_python.recipe.passwordless import ContactEmailOrPhoneConfig
from supertokens_python.recipe.usermetadata.asyncio import update_user_metadata
from tests.utils import (
clean_st,
Expand Down Expand Up @@ -203,3 +211,60 @@ async def test_that_first_connection_uri_is_selected_among_multiple_uris(
assert f'window.connectionURI = "{first_connection_uri}"' in str(res.text)
if st_config:
st_config.connection_uri = "http://localhost:3567"


async def test_that_get_user_works_with_combination_recipes(app: TestClient):
def override_dashboard_functions(oi: DashboardRI) -> DashboardRI:
async def should_allow_access(
_request: BaseRequest,
_config: DashboardConfig,
_user_context: Dict[str, Any],
) -> bool:
return True

oi.should_allow_access = should_allow_access
return oi

st_args = get_st_init_args(
[
session.init(get_token_transfer_method=lambda _, __, ___: "cookie"),
thirdpartypasswordless.init(
contact_config=ContactEmailOrPhoneConfig(),
flow_type="USER_INPUT_CODE",
),
usermetadata.init(),
dashboard.init(
api_key="someKey",
override=InputOverrideConfig(
functions=override_dashboard_functions,
),
),
]
)
init(**st_args)
start_st()

pluser = await tplasync.thirdparty_manually_create_or_update_user(
"public", "google", "googleid", "[email protected]"
)

res = app.get(
url="/auth/dashboard/api/user",
params={
"userId": "randomid",
"recipeId": "thirdparty",
},
)
res_json = res.json()
assert res_json["status"] == "NO_USER_FOUND_ERROR"

res = app.get(
url="/auth/dashboard/api/user",
params={
"userId": pluser.user.user_id,
"recipeId": "thirdparty",
},
)
res_json = res.json()
assert res_json["status"] == "OK"
assert res_json["user"]["id"] == pluser.user.user_id