Skip to content

Feat: Interface and routing for email-password login #290

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 5 commits into from
Feb 27, 2023
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
4 changes: 4 additions & 0 deletions supertokens_python/recipe/dashboard/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# under the License.
from .api_key_protector import api_key_protector
from .dashboard import handle_dashboard_api
from .signin import handle_sign_in_api
from .signout import handle_signout
from .userdetails.user_delete import handle_user_delete
from .userdetails.user_email_verify_get import handle_user_email_verify_get
from .userdetails.user_email_verify_put import handle_user_email_verify_put
Expand Down Expand Up @@ -45,4 +47,6 @@
"handle_user_sessions_post",
"handle_user_password_put",
"handle_email_verify_token_post",
"handle_sign_in_api",
"handle_signout",
]
23 changes: 23 additions & 0 deletions supertokens_python/recipe/dashboard/api/signin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
#
# This software is licensed under the Apache License, Version 2.0 (the
# "License") as published by the Apache Software Foundation.
#
# You may not use this file except in compliance with the License. You may
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions

# pylint: disable=unused-argument
async def handle_sign_in_api(api_implementation: APIInterface, api_options: APIOptions):
pass
28 changes: 28 additions & 0 deletions supertokens_python/recipe/dashboard/api/signout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
#
# This software is licensed under the Apache License, Version 2.0 (the
# "License") as published by the Apache Software Foundation.
#
# You may not use this file except in compliance with the License. You may
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from supertokens_python.recipe.dashboard.interfaces import APIInterface, APIOptions

from ..interfaces import SignOutOK


# pylint: disable=unused-argument
async def handle_signout(
api_implementation: APIInterface, api_options: APIOptions
) -> SignOutOK:
return SignOutOK()
2 changes: 2 additions & 0 deletions supertokens_python/recipe/dashboard/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@
USER_SESSION_API = "/api/user/sessions"
USER_PASSWORD_API = "/api/user/password"
USER_EMAIL_VERIFY_TOKEN_API = "/api/user/email/verify/token"
EMAIL_PASSWORD_SIGN_IN = "/api/signin"
EMAIL_PASSSWORD_SIGNOUT = "/api/signout"
5 changes: 5 additions & 0 deletions supertokens_python/recipe/dashboard/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,8 @@ def __init__(self, error: str) -> None:

def to_json(self) -> Dict[str, Any]:
return {"status": self.status, "error": self.error}


class SignOutOK(APIResponse):
def to_json(self):
return {"status": "OK"}
12 changes: 10 additions & 2 deletions supertokens_python/recipe/dashboard/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
handle_metadata_get,
handle_metadata_put,
handle_sessions_get,
handle_sign_in_api,
handle_signout,
handle_user_delete,
handle_user_email_verify_get,
handle_user_email_verify_put,
Expand All @@ -52,6 +54,8 @@

from .constants import (
DASHBOARD_API,
EMAIL_PASSSWORD_SIGNOUT,
EMAIL_PASSWORD_SIGN_IN,
USER_API,
USER_EMAIL_VERIFY_API,
USER_EMAIL_VERIFY_TOKEN_API,
Expand All @@ -78,7 +82,7 @@ def __init__(
self,
recipe_id: str,
app_info: AppInfo,
api_key: str,
api_key: Union[str, None],
override: Union[InputOverrideConfig, None] = None,
):
super().__init__(recipe_id, app_info)
Expand Down Expand Up @@ -136,6 +140,8 @@ async def handle_api_request(
return await handle_dashboard_api(self.api_implementation, api_options)
if request_id == VALIDATE_KEY_API:
return await handle_validate_key_api(self.api_implementation, api_options)
if request_id == EMAIL_PASSWORD_SIGN_IN:
return await handle_sign_in_api(self.api_implementation, api_options)

# Do API key validation for the remaining APIs
api_function: Optional[
Expand Down Expand Up @@ -171,6 +177,8 @@ async def handle_api_request(
api_function = handle_user_password_put
elif request_id == USER_EMAIL_VERIFY_TOKEN_API:
api_function = handle_email_verify_token_post
elif request_id == EMAIL_PASSSWORD_SIGNOUT:
api_function = handle_signout

if api_function is not None:
return await api_key_protector(
Expand All @@ -189,7 +197,7 @@ def get_all_cors_headers(self) -> List[str]:

@staticmethod
def init(
api_key: str,
api_key: Union[str, None],
override: Union[InputOverrideConfig, None] = None,
):
def func(app_info: AppInfo):
Expand Down
26 changes: 20 additions & 6 deletions supertokens_python/recipe/dashboard/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@

from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union

if TYPE_CHECKING:
from supertokens_python.framework.request import BaseRequest

from supertokens_python.recipe.emailpassword import EmailPasswordRecipe
from supertokens_python.recipe.emailpassword.asyncio import (
get_user_by_id as ep_get_user_by_id,
Expand Down Expand Up @@ -46,6 +49,8 @@
from ...supertokens import AppInfo
from .constants import (
DASHBOARD_API,
EMAIL_PASSSWORD_SIGNOUT,
EMAIL_PASSWORD_SIGN_IN,
USER_API,
USER_EMAIL_VERIFY_API,
USER_EMAIL_VERIFY_TOKEN_API,
Expand Down Expand Up @@ -162,21 +167,18 @@ def __init__(

class DashboardConfig:
def __init__(
self,
api_key: str,
override: OverrideConfig,
self, api_key: Union[str, None], override: OverrideConfig, auth_mode: str
):
self.api_key = api_key
self.override = override
self.auth_mode = auth_mode


def validate_and_normalise_user_input(
# app_info: AppInfo,
api_key: str,
api_key: Union[str, None],
override: Optional[InputOverrideConfig] = None,
) -> DashboardConfig:
if api_key.strip() == "":
raise Exception("apiKey provided to Dashboard recipe cannot be empty")

if override is None:
override = InputOverrideConfig()
Expand All @@ -187,6 +189,7 @@ def validate_and_normalise_user_input(
functions=override.functions,
apis=override.apis,
),
"api-key" if api_key else "email-password",
)


Expand Down Expand Up @@ -230,6 +233,10 @@ def get_api_if_matched(path: NormalisedURLPath, method: str) -> Optional[str]:
return USER_PASSWORD_API
if path_str.endswith(USER_EMAIL_VERIFY_TOKEN_API) and method == "post":
return USER_EMAIL_VERIFY_TOKEN_API
if path_str.endswith(EMAIL_PASSWORD_SIGN_IN) and method == "post":
return EMAIL_PASSWORD_SIGN_IN
if path_str.endswith(EMAIL_PASSSWORD_SIGNOUT) and method == "post":
return EMAIL_PASSSWORD_SIGNOUT

return None

Expand Down Expand Up @@ -385,3 +392,10 @@ def is_recipe_initialised(recipeId: str) -> bool:
pass

return isRecipeInitialised


def validate_APIKey(req: BaseRequest, config: DashboardConfig) -> bool:
apiKeyHeaderValue = req.get_header("authorization")
if not apiKeyHeaderValue:
return False
return apiKeyHeaderValue == config.api_key