From 5754927acae58dee82d880fcfa8ab566f2184e9c Mon Sep 17 00:00:00 2001 From: teo Date: Thu, 21 Nov 2024 15:52:53 +0200 Subject: [PATCH 1/4] remove old code --- syftbox/client/cli.py | 49 ------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/syftbox/client/cli.py b/syftbox/client/cli.py index affd0aa9..20a78a92 100644 --- a/syftbox/client/cli.py +++ b/syftbox/client/cli.py @@ -139,55 +139,6 @@ def report( rprint(f"[red]Error[/red]: {e}") raise Exit(1) -@app.command() -def forgot_password( - email: Annotated[str, EMAIL_OPTS], - server: Annotated[str, SERVER_OPTS] = DEFAULT_SERVER_URL, - config_path: Annotated[Path, CONFIG_OPTS] = DEFAULT_CONFIG_PATH, -): - from syftbox.lib.client_config import SyftClientConfig - from syftbox.client.cli_setup import prompt_email - config: SyftClientConfig = None - - try: - config = SyftClientConfig.load(config_path) - except: - pass - - server_url = config.server_url if config else server - - response = httpx.post( - f"{server_url}users/reset_password", - data={"email": email}, - ) - response.raise_for_status() - rprint("Forgot password request sent succesfully! Check your email!") - -@app.command() -def reset_password( - token: Annotated[str, TOKEN_OPTS], - email: Annotated[str, EMAIL_OPTS], - server: Annotated[str, SERVER_OPTS] = DEFAULT_SERVER_URL, - config_path: Annotated[Path, CONFIG_OPTS] = DEFAULT_CONFIG_PATH, -): - from syftbox.lib.client_config import SyftClientConfig - from syftbox.client.cli_setup import prompt_password - config: SyftClientConfig = None - - try: - config = SyftClientConfig.load(config_path) - except: - pass - - server_url = config.server_url if config else server - new_password = prompt_password() - - response = httpx.post( - f"{server_url}users/change_password", - data={"email": email, "token": token, "new_password": new_password}, - ) - response.raise_for_status() - rprint("Password updated succesfully!") def main(): From f50e30bb9b5471a851d192f4fca1b09e83638b68 Mon Sep 17 00:00:00 2001 From: teo Date: Thu, 21 Nov 2024 17:30:29 +0200 Subject: [PATCH 2/4] added token invalidation --- justfile | 8 +++++-- syftbox/client/auth.py | 22 +++++++++++++++---- syftbox/client/cli.py | 15 ++++++------- syftbox/client/cli_setup.py | 16 ++++++++++++-- syftbox/server/server.py | 7 ++++++ syftbox/server/settings.py | 10 ++++++++- syftbox/server/users/auth.py | 39 +++++++++++++++++++++++++++++++++- syftbox/server/users/router.py | 21 +++++++++++++++++- 8 files changed, 119 insertions(+), 19 deletions(-) diff --git a/justfile b/justfile index 4a022f78..ad4691e8 100644 --- a/justfile +++ b/justfile @@ -43,7 +43,7 @@ run-server port="5001" uvicorn_args="": # Run a local syftbox client on any available port between 8080-9000 [group('client')] -run-client name port="auto" server="http://localhost:5001": +run-client name port="auto" server="http://localhost:5001" reset-token="false": #!/bin/bash set -eou pipefail @@ -55,6 +55,9 @@ run-client name port="auto" server="http://localhost:5001": PORT="{{ port }}" if [[ "$PORT" == "auto" ]]; then PORT="0"; fi + RESET_TOKEN="" + if [[ "{{ reset-token }}" == "true" ]]; then RESET_TOKEN="--reset-token"; fi + # Working directory for client is .clients/ DATA_DIR=.clients/$EMAIL mkdir -p $DATA_DIR @@ -63,8 +66,9 @@ run-client name port="auto" server="http://localhost:5001": echo -e "Client : {{ _cyan }}http://localhost:$PORT{{ _nc }}" echo -e "Server : {{ _cyan }}{{ server }}{{ _nc }}" echo -e "Data Dir : $DATA_DIR" + echo -e "Reset Token: {{ reset-token }}" - uv run syftbox/client/cli.py --config=$DATA_DIR/config.json --data-dir=$DATA_DIR --email=$EMAIL --port=$PORT --server={{ server }} --no-open-dir + uv run syftbox/client/cli.py --config=$DATA_DIR/config.json --data-dir=$DATA_DIR --email=$EMAIL --port=$PORT --server={{ server }} --no-open-dir $RESET_TOKEN # --------------------------------------------------------------------------------------------------------------------- diff --git a/syftbox/client/auth.py b/syftbox/client/auth.py index c1d40360..4cf966ae 100644 --- a/syftbox/client/auth.py +++ b/syftbox/client/auth.py @@ -20,7 +20,7 @@ def has_valid_access_token(conf: SyftClientConfig, auth_client: httpx.Client) -> rprint(f"[red]An unexpected error occurred: {response.text}, re-authenticating.[/red]") return False - authed_email = response.text + authed_email = response.text[1:-1] is_valid = authed_email == conf.email if not is_valid: rprint( @@ -45,6 +45,10 @@ def request_email_token(auth_client: httpx.Client, conf: SyftClientConfig) -> Op response.raise_for_status() return response.json().get("email_token", None) +def prompt_get_token_from_email(email): + return Prompt.ask( + f"[yellow]Please enter the token sent to {email}. Also check your spam folder[/yellow]" + ) def get_access_token( conf: SyftClientConfig, @@ -63,9 +67,7 @@ def get_access_token( str: access token """ if not email_token: - email_token = Prompt.ask( - f"[yellow]Please enter the token sent to {conf.email}. Also check your spam folder[/yellow]" - ) + email_token = prompt_get_token_from_email(conf.email) response = auth_client.post( "/auth/validate_email_token", @@ -81,6 +83,18 @@ def get_access_token( rprint(f"[red]An unexpected error occurred: {response.text}[/red]") typer.Exit(1) +def invalidate_client_token(conf: SyftClientConfig): + auth_client = httpx.Client(base_url=str(conf.server_url)) + + if has_valid_access_token(conf, auth_client): + response = auth_client.post( + "/auth/invalidate_access_token", + headers={"Authorization": f"Bearer {conf.access_token}"}, + ) + rprint(f"[bold]{response.text}[/bold]") + else: + rprint("[yellow]No valid access token found, skipping token reset[/yellow]") + def authenticate_user(conf: SyftClientConfig) -> str: auth_client = httpx.Client(base_url=str(conf.server_url)) diff --git a/syftbox/client/cli.py b/syftbox/client/cli.py index 20a78a92..a93ecdee 100644 --- a/syftbox/client/cli.py +++ b/syftbox/client/cli.py @@ -60,12 +60,10 @@ is_flag=True, help="Enable verbose mode", ) - - - -TOKEN_OPTS = Option( - "--token", - help="Token for password reset", +RESET_TOKEN_OPTS = Option( + "--reset-token", + is_flag=True, + help="Reset Token in order to invalidate the current one", ) # report command opts @@ -87,6 +85,7 @@ def client( port: Annotated[int, PORT_OPTS] = DEFAULT_PORT, open_dir: Annotated[bool, OPEN_OPTS] = True, verbose: Annotated[bool, VERBOSE_OPTS] = False, + reset_token: Annotated[bool, RESET_TOKEN_OPTS] = False, ): """Run the SyftBox client""" @@ -107,7 +106,8 @@ def client( rprint(f"[bold red]Error:[/bold red] Client cannot start because port {port} is already in use!") raise Exit(1) - client_config = setup_config_interactive(config_path, email, data_dir, server, port) + print(f"{reset_token=}") + client_config = setup_config_interactive(config_path, email, data_dir, server, port, reset_token=reset_token) migrate_datasite = get_migration_decision(client_config.data_dir) @@ -140,7 +140,6 @@ def report( raise Exit(1) - def main(): app() diff --git a/syftbox/client/cli_setup.py b/syftbox/client/cli_setup.py index 26ef9e2d..289afe8a 100644 --- a/syftbox/client/cli_setup.py +++ b/syftbox/client/cli_setup.py @@ -10,7 +10,7 @@ from rich.prompt import Confirm, Prompt from syftbox.__version__ import __version__ -from syftbox.client.auth import authenticate_user +from syftbox.client.auth import authenticate_user, invalidate_client_token from syftbox.client.client2 import METADATA_FILENAME from syftbox.lib.client_config import SyftClientConfig from syftbox.lib.constants import DEFAULT_DATA_DIR @@ -61,7 +61,13 @@ def get_migration_decision(data_dir: Path): def setup_config_interactive( - config_path: Path, email: str, data_dir: Path, server: str, port: int, skip_auth: bool = False + config_path: Path, + email: str, + data_dir: Path, + server: str, + port: int, + skip_auth: bool = False, + reset_token: bool = False, ) -> SyftClientConfig: """Setup the client configuration interactively. Called from CLI""" @@ -98,6 +104,12 @@ def setup_config_interactive( if port != conf.client_url.port: conf.set_port(port) + rprint(f"[bold]{reset_token}, {conf.access_token}[/bold]") + if reset_token: + if conf.access_token: + invalidate_client_token(conf) + conf.access_token = None + if not skip_auth: conf.access_token = authenticate_user(conf) diff --git a/syftbox/server/server.py b/syftbox/server/server.py index 711fd500..73617708 100644 --- a/syftbox/server/server.py +++ b/syftbox/server/server.py @@ -134,6 +134,12 @@ def init_db(settings: ServerSettings) -> None: con.commit() con.close() +def touch(path): + with open(path, 'a'): + os.utime(path, None) + +def init_banned_file(path): + touch(path) @contextlib.asynccontextmanager async def lifespan(app: FastAPI, settings: Optional[ServerSettings] = None): @@ -149,6 +155,7 @@ async def lifespan(app: FastAPI, settings: Optional[ServerSettings] = None): logger.info("> Creating Folders") create_folders(settings.folders) + init_banned_file(settings.banned_tokens_path) users = Users(path=settings.user_file_path) logger.info("> Loading Users") diff --git a/syftbox/server/settings.py b/syftbox/server/settings.py index b13f3304..a893573b 100644 --- a/syftbox/server/settings.py +++ b/syftbox/server/settings.py @@ -24,7 +24,7 @@ class ServerSettings(BaseSettings): data_folder: Path = Field(default=Path("data").resolve()) """Absolute path to the server data folder""" - + email_service_api_key: str = Field(default="") """API key for the email service""" @@ -71,6 +71,14 @@ def logs_folder(self) -> Path: @property def user_file_path(self) -> Path: return self.data_folder / "users.json" + + @property + def banned_tokens_path(self) -> Path: + return self.data_folder / "banned_tokens" + + @property + def banned_users_path(self) -> Path: + return self.data_folder / "banned_users" @classmethod def from_data_folder(cls, data_folder: Union[Path, str]) -> Self: diff --git a/syftbox/server/users/auth.py b/syftbox/server/users/auth.py index d73ad297..82cdc17d 100644 --- a/syftbox/server/users/auth.py +++ b/syftbox/server/users/auth.py @@ -1,6 +1,7 @@ import base64 from datetime import datetime, timezone import json +from pathlib import Path from typing_extensions import Annotated from fastapi import Depends, HTTPException, Header, Security from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer @@ -114,6 +115,12 @@ def validate_token(server_settings: ServerSettings, token: str) -> dict: Returns: dict: decoded payload """ + if check_line_in_file(token, server_settings.banned_tokens_path): + raise HTTPException( + status_code=401, + detail="Invalid Token", + headers={"WWW-Authenticate": "Bearer"}, + ) if not server_settings.auth_enabled: return _validate_base64(server_settings, token) else: @@ -171,10 +178,40 @@ def get_user_from_email_token( payload = validate_email_token(server_settings, credentials.credentials) return payload["email"] +def get_access_token( + credentials: Annotated[HTTPAuthorizationCredentials, Security(bearer_scheme)], + server_settings: Annotated[ServerSettings, Depends(get_server_settings)], +) -> str: + _ = validate_access_token(server_settings, credentials.credentials) + return credentials.credentials def get_current_user( credentials: Annotated[HTTPAuthorizationCredentials, Security(bearer_scheme)], server_settings: Annotated[ServerSettings, Depends(get_server_settings)], ) -> str: payload = validate_access_token(server_settings, credentials.credentials) - return payload["email"] \ No newline at end of file + return payload["email"] + + +def check_line_in_file(line: str, file_path: Path) -> bool: + with open(file_path, 'r') as f: + for file_line in f: + if file_line == line: + return True + return False + +def write_line_in_file(line: str, file_path: Path): + with open(file_path, 'a') as f: + f.writelines([line]) + +def delete_line_from_file(line: str, file_path: Path): + with open(file_path, 'r') as f: + file_lines = f.readlines() + if line not in file_lines: + return + file_lines.remove(line) + with open(file_path, 'w') as f: + f.writelines(file_lines) + +def invalidate_token(server_settings: ServerSettings, token: str): + write_line_in_file(token, server_settings.banned_tokens_path) diff --git a/syftbox/server/users/router.py b/syftbox/server/users/router.py index df2e1954..49b45db5 100644 --- a/syftbox/server/users/router.py +++ b/syftbox/server/users/router.py @@ -4,7 +4,7 @@ from syftbox.lib.email import send_token_email from syftbox.server.settings import ServerSettings, get_server_settings -from syftbox.server.users.auth import generate_access_token, generate_email_token, get_user_from_email_token, get_current_user +from syftbox.server.users.auth import generate_access_token, generate_email_token, get_access_token, get_user_from_email_token, get_current_user, invalidate_token router = APIRouter(prefix="/auth", tags=["authentication"]) @@ -58,6 +58,25 @@ def validate_email_token( return AccessTokenResponse(access_token=access_token) +@router.post("/invalidate_access_token") +def invalidate_access_token( + token: str = Depends(get_access_token), + server_settings: ServerSettings = Depends(get_server_settings), +) -> str: + """ + Invalidate the access token/ + + Args: + token (str): Access token. Defaults to Depends(get_access_token). + server_settings (ServerSettings, optional): server settings. Defaults to Depends(get_server_settings). + + Returns: + str: message + """ + invalidate_token(server_settings, token) + return "Token invalidation succesful!" + + @router.post("/whoami") def whoami( email: str = Depends(get_current_user), From 9ddd07b48e4ac8a4d79ac303f3377d057d5d40d2 Mon Sep 17 00:00:00 2001 From: teo Date: Mon, 25 Nov 2024 01:24:55 +0200 Subject: [PATCH 3/4] added db user table --- syftbox/server/sync/db.py | 43 +++++++++++++++++- syftbox/server/users/auth.py | 58 ++++++++++--------------- syftbox/server/users/router.py | 30 ++++++++++--- syftbox/server/users/user_store.py | 70 ++++++++++++++++++++++++++++++ 4 files changed, 160 insertions(+), 41 deletions(-) create mode 100644 syftbox/server/users/user_store.py diff --git a/syftbox/server/sync/db.py b/syftbox/server/sync/db.py index 879a4047..cae4d8fe 100644 --- a/syftbox/server/sync/db.py +++ b/syftbox/server/sync/db.py @@ -3,7 +3,7 @@ import sqlite3 import tempfile from pathlib import Path -from typing import Optional +from typing import Optional, Tuple from syftbox.server.settings import ServerSettings from syftbox.server.sync.models import FileMetadata @@ -28,9 +28,50 @@ def get_db(path: str): file_size INTEGER NOT NULL, last_modified TEXT NOT NULL ) """) + # Create the table if it doesn't exist + conn.execute(""" + CREATE TABLE IF NOT EXISTS users_credentials ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_email TEXT NOT NULL UNIQUE, + credentials TEXT NOT NULL ) + """) return conn +def get_user_by_email(conn: sqlite3.Connection, email: str): + conn.execute("SELECT id, user_email, credentials FROM users_credentials WHERE user_email = ?", (email,)) + user = conn.fetchone() + return user + + +# maybe we should do update_user instead of set_credentials? +def set_credentials(conn: sqlite3.Connection, email: str, credentials: str): + conn.execute(""" + UPDATE users_credentials + SET credentials = ? + WHERE user_email = ? + """, (credentials, email,)) + + +def add_user(conn: sqlite3.Connection, email: str, credentials: str): + conn.execute(""" + INSERT INTO users_credentials (user_email, credentials) + VALUES (?, ?) + """, (email, credentials)) + + +def get_all_users(conn: sqlite3.Connection): + conn.execute("SELECT * FROM users_credentials") + return conn.fecthall() + + +def delete_user(conn: sqlite3.Connection, email: str): + conn.execute(""" + DELETE FROM users_credentials + WHERE user_email = ? + """, (email,)) + + def save_file_metadata(conn: sqlite3.Connection, metadata: FileMetadata): # Insert the metadata into the database or update if a conflict on 'path' occurs conn.execute( diff --git a/syftbox/server/users/auth.py b/syftbox/server/users/auth.py index 82cdc17d..6a725aa1 100644 --- a/syftbox/server/users/auth.py +++ b/syftbox/server/users/auth.py @@ -8,6 +8,7 @@ import httpx import jwt from syftbox.server.settings import ServerSettings, get_server_settings +from syftbox.server.users.user_store import User, UserStore bearer_scheme = HTTPBearer() @@ -115,12 +116,6 @@ def validate_token(server_settings: ServerSettings, token: str) -> dict: Returns: dict: decoded payload """ - if check_line_in_file(token, server_settings.banned_tokens_path): - raise HTTPException( - status_code=401, - detail="Invalid Token", - headers={"WWW-Authenticate": "Bearer"}, - ) if not server_settings.auth_enabled: return _validate_base64(server_settings, token) else: @@ -151,6 +146,20 @@ def generate_email_token(server_settings: ServerSettings, email: str) -> str: def validate_access_token(server_settings: ServerSettings, token: str) -> dict: data = validate_token(server_settings, token) + user_store = UserStore(server_settings=server_settings) + user = user_store.get_user_by_email(data['email']) + if not user: + raise HTTPException( + status_code=404, + detail="User not found", + headers={"WWW-Authenticate": "Bearer"}, + ) + if user.credentials != token: + raise HTTPException( + status_code=401, + detail="Invalid Token", + headers={"WWW-Authenticate": "Bearer"}, + ) if data["type"] != ACCESS_TOKEN: raise HTTPException( status_code=401, @@ -178,13 +187,6 @@ def get_user_from_email_token( payload = validate_email_token(server_settings, credentials.credentials) return payload["email"] -def get_access_token( - credentials: Annotated[HTTPAuthorizationCredentials, Security(bearer_scheme)], - server_settings: Annotated[ServerSettings, Depends(get_server_settings)], -) -> str: - _ = validate_access_token(server_settings, credentials.credentials) - return credentials.credentials - def get_current_user( credentials: Annotated[HTTPAuthorizationCredentials, Security(bearer_scheme)], server_settings: Annotated[ServerSettings, Depends(get_server_settings)], @@ -193,25 +195,11 @@ def get_current_user( return payload["email"] -def check_line_in_file(line: str, file_path: Path) -> bool: - with open(file_path, 'r') as f: - for file_line in f: - if file_line == line: - return True - return False - -def write_line_in_file(line: str, file_path: Path): - with open(file_path, 'a') as f: - f.writelines([line]) - -def delete_line_from_file(line: str, file_path: Path): - with open(file_path, 'r') as f: - file_lines = f.readlines() - if line not in file_lines: - return - file_lines.remove(line) - with open(file_path, 'w') as f: - f.writelines(file_lines) - -def invalidate_token(server_settings: ServerSettings, token: str): - write_line_in_file(token, server_settings.banned_tokens_path) +def set_token(server_settings: ServerSettings, email: str, token: str): + user_store = UserStore(server_settings=server_settings) + user_store.update_user(User(email=email, credentials=token)) + + +def delete_token(server_settings: ServerSettings, email: str): + user_store = UserStore(server_settings=server_settings) + user_store.update_user(User(email=email, credentials="")) \ No newline at end of file diff --git a/syftbox/server/users/router.py b/syftbox/server/users/router.py index 49b45db5..22e2d96f 100644 --- a/syftbox/server/users/router.py +++ b/syftbox/server/users/router.py @@ -1,10 +1,11 @@ from typing import Optional -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, EmailStr from syftbox.lib.email import send_token_email from syftbox.server.settings import ServerSettings, get_server_settings -from syftbox.server.users.auth import generate_access_token, generate_email_token, get_access_token, get_user_from_email_token, get_current_user, invalidate_token +from syftbox.server.users.auth import delete_token, generate_access_token, generate_email_token, get_user_from_email_token, get_current_user, set_token +from syftbox.server.users.user_store import User, UserStore router = APIRouter(prefix="/auth", tags=["authentication"]) @@ -29,6 +30,12 @@ def get_token(req: EmailTokenRequest, server_settings: ServerSettings = Depends( email = req.email token = generate_email_token(server_settings, email) + user_store = UserStore(server_settings=server_settings) + if not user_store.exists(email=email): + user_store.add_user(User(email=email, credentials="")) + else: + user_store.update_user(User(email=email, credentials="")) + response = EmailTokenResponse() if server_settings.auth_enabled: send_token_email(server_settings, email, token) @@ -54,26 +61,39 @@ def validate_email_token( Returns: AccessTokenResponse: access token """ + user_store = UserStore(server_settings=server_settings) + user = user_store.get_user_by_email(email=email) access_token = generate_access_token(server_settings, email) + if user: + if user.credentials is not None: + set_token(server_settings, email, access_token) + else: + # what happens if there is already some credentials set? + # it looks like if someone steals the email token, they can generate the access token + pass + else: + raise HTTPException(status_code=404, detail="User not found! Please register") return AccessTokenResponse(access_token=access_token) @router.post("/invalidate_access_token") def invalidate_access_token( - token: str = Depends(get_access_token), + email: str = Depends(get_current_user), server_settings: ServerSettings = Depends(get_server_settings), ) -> str: """ Invalidate the access token/ Args: - token (str): Access token. Defaults to Depends(get_access_token). + email (str, optional): The user email, extracted from the access token in the Authorization header. + Defaults to Depends(get_current_user). + server_settings (ServerSettings, optional): server settings. Defaults to Depends(get_server_settings). Returns: str: message """ - invalidate_token(server_settings, token) + delete_token(server_settings, email) return "Token invalidation succesful!" diff --git a/syftbox/server/users/user_store.py b/syftbox/server/users/user_store.py new file mode 100644 index 00000000..bcb7e9a6 --- /dev/null +++ b/syftbox/server/users/user_store.py @@ -0,0 +1,70 @@ +from pydantic import BaseModel, EmailStr +from syftbox.server.settings import ServerSettings +from syftbox.server.sync import db +from syftbox.server.sync.db import get_db +from syftbox.server.sync.models import AbsolutePath, RelativePath + + +class User(BaseModel): + email: EmailStr + credentials: str + + +class UserStore: + def __init__(self, server_settings: ServerSettings) -> None: + self. server_settings = server_settings + + @property + def db_path(self) -> AbsolutePath: + return self.server_settings.file_db_path + + def exists(self, email: str): + user = self.get_user_by_email(email=email) + if user: + return True + return False + + def delete_user(self, email: str): + conn = get_db(self.db_path) + cursor = conn.cursor() + cursor.execute("BEGIN IMMEDIATE;") + try: + db.delete_user(cursor, email) + except ValueError: + pass + conn.commit() + cursor.close() + + def get_user_by_email(self, email: str): + with get_db(self.db_path) as conn: + cursor = conn.cursor() + user = db.get_user_by_email(cursor, email) + # ignoring id for now + return User(email=user[1], credentials=user[2]) + + def get_all_users(self): + with get_db(self.db_path) as conn: + cursor = conn.cursor() + users = db.get_all_users(cursor) + return users + + def add_user(self, user: User): + conn = get_db(self.db_path) + cursor = conn.cursor() + cursor.execute("BEGIN IMMEDIATE;") + db.add_user(cursor, user.email, user.credentials) + conn.commit() + cursor.close() + conn.close() + + def update_user(self, user: User): + conn = get_db(self.db_path) + cursor = conn.cursor() + cursor.execute("BEGIN IMMEDIATE;") + db.set_credentials(cursor, user.email, user.credentials) + conn.commit() + cursor.close() + conn.close() + + + \ No newline at end of file From fb6c2ddeeff4d916ae66936e245ff0ad9472f627 Mon Sep 17 00:00:00 2001 From: teo Date: Tue, 3 Dec 2024 10:39:12 +0200 Subject: [PATCH 4/4] change token validation funciton --- syftbox/server/users/auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/syftbox/server/users/auth.py b/syftbox/server/users/auth.py index 4ffb267f..26edef0b 100644 --- a/syftbox/server/users/auth.py +++ b/syftbox/server/users/auth.py @@ -67,7 +67,7 @@ def generate_email_token(server_settings: ServerSettings, email: str) -> str: def validate_access_token(server_settings: ServerSettings, token: str) -> dict: - data = validate_token(server_settings, token) + data = _validate_jwt(server_settings, token) user_store = UserStore(server_settings=server_settings) user = user_store.get_user_by_email(data['email']) if not user: