From 62581732b4da19a5d456e378058752cbc66fbeab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arturo=20Filast=C3=B2?= Date: Thu, 21 Mar 2024 10:29:46 +0100 Subject: [PATCH] Add support for setting and returning account_id (#828) * Add support for setting and returning account_id * fix get_user_session_from_login_token --- ooniapi/common/src/common/config.py | 1 + .../services/ooniauth/src/ooniauth/routers/v1.py | 2 ++ .../services/ooniauth/src/ooniauth/routers/v2.py | 13 ++++++++++++- ooniapi/services/ooniauth/src/ooniauth/utils.py | 8 ++++++++ ooniapi/services/ooniauth/tests/test_auth_v2.py | 2 ++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/ooniapi/common/src/common/config.py b/ooniapi/common/src/common/config.py index 78722aef..47194843 100644 --- a/ooniapi/common/src/common/config.py +++ b/ooniapi/common/src/common/config.py @@ -15,6 +15,7 @@ class Settings(BaseSettings): statsd_prefix: str = "ooniapi" jwt_encryption_key: str = "CHANGEME" prometheus_metrics_password: str = "CHANGEME" + account_id_hashing_key: str = "CHANGEME" session_expiry_days: int = 10 login_expiry_days: int = 10 diff --git a/ooniapi/services/ooniauth/src/ooniauth/routers/v1.py b/ooniapi/services/ooniauth/src/ooniauth/routers/v1.py index 6d8ca12d..bf0b2332 100644 --- a/ooniapi/services/ooniauth/src/ooniauth/routers/v1.py +++ b/ooniapi/services/ooniauth/src/ooniauth/routers/v1.py @@ -138,6 +138,7 @@ async def user_login( token = create_session_token( key=settings.jwt_encryption_key, + hashing_key=settings.account_id_hashing_key, email_address=email_address, role=role, session_expiry_days=settings.session_expiry_days, @@ -173,6 +174,7 @@ async def user_refresh_token( newtoken = create_session_token( key=settings.jwt_encryption_key, + hashing_key=settings.account_id_hashing_key, email_address=tok["email_address"], role=tok["role"], session_expiry_days=settings.session_expiry_days, diff --git a/ooniapi/services/ooniauth/src/ooniauth/routers/v2.py b/ooniapi/services/ooniauth/src/ooniauth/routers/v2.py index 164945fb..0811830c 100644 --- a/ooniapi/services/ooniauth/src/ooniauth/routers/v2.py +++ b/ooniapi/services/ooniauth/src/ooniauth/routers/v2.py @@ -15,6 +15,7 @@ from ..utils import ( create_session_token, get_account_role, + hash_email_address, send_login_email, format_login_url, VALID_REDIRECT_TO_FQDN, @@ -105,6 +106,7 @@ class UserSession(BaseModel): session_token: str redirect_to: str email_address: str + account_id: str role: str login_time: Optional[datetime] is_logged_in: bool = False @@ -120,6 +122,7 @@ def maybe_get_user_session_from_header( return None email_address = token["email_address"] + account_id = token["account_id"] role = get_account_role(admin_emails=admin_emails, email_address=email_address) login_time = datetime.fromtimestamp(token["login_time"]) redirect_to = "" @@ -128,6 +131,7 @@ def maybe_get_user_session_from_header( session_token="", redirect_to=redirect_to, email_address=email_address, + account_id=account_id, role=role, login_time=login_time, is_logged_in=True, @@ -135,7 +139,7 @@ def maybe_get_user_session_from_header( def get_user_session_from_login_token( - login_token: str, jwt_encryption_key: str, admin_emails: List[str] + login_token: str, jwt_encryption_key: str, hashing_key: str, admin_emails: List[str] ) -> UserSession: try: d = decode_jwt( @@ -144,9 +148,13 @@ def get_user_session_from_login_token( audience="register", ) email_address = d["email_address"] + account_id = hash_email_address( + email_address=d["email_address"], key=hashing_key + ) role = get_account_role(admin_emails=admin_emails, email_address=email_address) return UserSession( session_token="", + account_id=account_id, redirect_to=d["redirect_to"], email_address=d["email_address"], role=role, @@ -180,6 +188,7 @@ async def create_user_session( login_token=req.login_token, admin_emails=settings.admin_emails, jwt_encryption_key=settings.jwt_encryption_key, + hashing_key=settings.account_id_hashing_key, ) else: user_session = maybe_get_user_session_from_header( @@ -194,6 +203,7 @@ async def create_user_session( assert user_session.login_time user_session.session_token = create_session_token( key=settings.jwt_encryption_key, + hashing_key=settings.account_id_hashing_key, role=user_session.role, session_expiry_days=settings.session_expiry_days, login_expiry_days=settings.login_expiry_days, @@ -219,6 +229,7 @@ async def get_user_session( session_token="", redirect_to="", email_address="", + account_id="", role="", login_time=None, is_logged_in=False, diff --git a/ooniapi/services/ooniauth/src/ooniauth/utils.py b/ooniapi/services/ooniauth/src/ooniauth/utils.py index 6e813d2f..ae4f4ad4 100644 --- a/ooniapi/services/ooniauth/src/ooniauth/utils.py +++ b/ooniapi/services/ooniauth/src/ooniauth/utils.py @@ -1,3 +1,4 @@ +import hashlib import time from typing import List, Optional from textwrap import dedent @@ -17,6 +18,11 @@ ) +def hash_email_address(email_address: str, key: str) -> str: + em = email_address.encode() + return hashlib.blake2b(em, key=key.encode("utf-8"), digest_size=16).hexdigest() + + def format_login_url(redirect_to: str, registration_token: str) -> str: login_fqdm = urlparse(redirect_to).netloc e = urlencode(dict(token=registration_token)) @@ -25,6 +31,7 @@ def format_login_url(redirect_to: str, registration_token: str) -> str: def create_session_token( key: str, + hashing_key: str, email_address: str, role: str, session_expiry_days: int, @@ -44,6 +51,7 @@ def create_session_token( "aud": "user_auth", "login_time": login_time, "role": role, + "account_id": hash_email_address(email_address, hashing_key), "email_address": email_address, } return create_jwt(payload=payload, key=key) diff --git a/ooniapi/services/ooniauth/tests/test_auth_v2.py b/ooniapi/services/ooniauth/tests/test_auth_v2.py index fa16e1e5..5c39cb93 100644 --- a/ooniapi/services/ooniauth/tests/test_auth_v2.py +++ b/ooniapi/services/ooniauth/tests/test_auth_v2.py @@ -187,6 +187,8 @@ def test_admin_register_and_get_metadata( j = client.get("/api/v2/ooniauth/user-session", headers=h).json() assert j["role"] == "admin" assert j["is_logged_in"] == True + assert j["email_address"] == admin_email + assert len(j["account_id"]) > 1 def test_user_register_timetravel(