From 71f87e06d780be708c8bbb9b5b3e587dec9fcb20 Mon Sep 17 00:00:00 2001 From: Stefan Stammberger Date: Sun, 28 Nov 2021 08:42:52 +0100 Subject: [PATCH] feat: implement local cookie authentication This is only useful for application running directly on the Raspiblitz. This will create a file in ~/.blitz_api/.cookie with a valid JWT token. Local applications can use this to authenticate to the API without having to ask for the password. This is similar to how Bitcoin Cores cookie auth works. --- .env_sample | 5 +++++ app/auth/auth_handler.py | 38 ++++++++++++++++++++++++++++++++++++++ app/main.py | 8 ++++++++ 3 files changed, 51 insertions(+) diff --git a/.env_sample b/.env_sample index 1a34f8e..e1f846f 100644 --- a/.env_sample +++ b/.env_sample @@ -6,6 +6,11 @@ jwt_expiry_time=300 # login password login_password=12345678 +# Enable this if you want to run blitz_gui locally. +# This will create a file called ~/blitz_api/.cookie with a +# JWT token. +# enable_local_cookie_auth = false + # Amount of seconds the app will wait until it'll # send another hardware update gather_hw_info_interval = 2 diff --git a/app/auth/auth_handler.py b/app/auth/auth_handler.py index 386264c..b18df83 100644 --- a/app/auth/auth_handler.py +++ b/app/auth/auth_handler.py @@ -1,3 +1,5 @@ +import asyncio +import os import time from typing import Dict @@ -29,3 +31,39 @@ def decodeJWT(token: str) -> dict: except Exception as e: print(f"Unable to decode jwt_token {e}") return {} + + +def handle_local_cookie(): + remove_local_cookie() + + blitz_path = os.path.join(os.path.expanduser("~"), ".blitz_api") + full_cookie_file_path = os.path.join(blitz_path, ".cookie") + enabled = config("enable_local_cookie_auth", default=False, cast=bool) + + if not os.path.exists(blitz_path): + os.makedirs(blitz_path) + + if enabled: + f = open(full_cookie_file_path, "w") + f.write(signJWT()["access_token"]) + f.close() + + +def remove_local_cookie(): + full_cookie_file_path = os.path.join( + os.path.expanduser("~"), ".blitz_api", ".cookie" + ) + + if os.path.exists(path=full_cookie_file_path): + os.remove(full_cookie_file_path) + + +def register_cookie_updater(): + # We need to update the cookie file once the cookie is expired + async def _cookie_updater(): + while True: + await asyncio.sleep(JWT_EXPIRY_TIME - 10) + handle_local_cookie() + + loop = asyncio.get_event_loop() + loop.create_task(_cookie_updater()) diff --git a/app/main.py b/app/main.py index ef94d4a..f970bae 100644 --- a/app/main.py +++ b/app/main.py @@ -15,6 +15,11 @@ from starlette import status from starlette.middleware.cors import CORSMiddleware from starlette.responses import RedirectResponse +from app.auth.auth_handler import ( + handle_local_cookie, + register_cookie_updater, + remove_local_cookie, +) from app.external.fastapi_versioning import VersionedFastAPI from app.external.sse_startlette import EventSourceResponse from app.repositories.bitcoin import ( @@ -73,11 +78,14 @@ async def on_startup(): await redis_plugin.init_app(app, config=config) await redis_plugin.init() await register_all_handlers(redis_plugin.redis) + handle_local_cookie() + register_cookie_updater() @app.on_event("shutdown") async def on_shutdown() -> None: await redis_plugin.terminate() + remove_local_cookie() @app.get("/")